mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-08-02 21:15:14 +00:00
refactor(agent-core-v2): remove the agents_md context injection
This commit is contained in:
parent
9a73fe0c3b
commit
fa8cd4ce58
15 changed files with 9 additions and 638 deletions
54
packages/agent-core-v2/docs/state-manifest.d.ts
vendored
54
packages/agent-core-v2/docs/state-manifest.d.ts
vendored
|
|
@ -23,7 +23,7 @@
|
|||
// references become '(circular)', and class instances collapse to a '(ClassName)'
|
||||
// marker — the wire shape of an entry is the JSON projection of the type here.
|
||||
//
|
||||
// Index (Session: 28 keys · Agent: 70 keys)
|
||||
// Index (Session: 28 keys · Agent: 69 keys)
|
||||
// Session
|
||||
// cron.inFlight src/session/cron/sessionCronServiceImpl.ts
|
||||
// cron.lastSeenAt src/session/cron/sessionCronServiceImpl.ts
|
||||
|
|
@ -59,7 +59,6 @@
|
|||
// activityView.lastTurn src/agent/activityView/activityViewService.ts
|
||||
// activityView.lifecycle src/agent/activityView/activityViewService.ts
|
||||
// activityView.turn src/agent/activityView/activityViewService.ts
|
||||
// agentsMdReminder.seed src/agent/profile/agentsMdReminderService.ts
|
||||
// contextInjector.isNewTurn src/agent/contextInjector/contextInjectorService.ts
|
||||
// contextProjector.lastRepairSignature src/agent/contextProjector/contextProjectorService.ts
|
||||
// contextSize.lastEmittedTokens src/agent/contextSize/contextSizeService.ts
|
||||
|
|
@ -204,7 +203,6 @@ export interface SessionStateSnapshot {
|
|||
readonly skillActive?: boolean;
|
||||
readonly productName?: string;
|
||||
readonly replyStyleGuide?: string;
|
||||
readonly agentsMdStatus?: 'missing' | 'empty' | 'present';
|
||||
[key: string]: unknown;
|
||||
}) => string;
|
||||
readonly renderSystemPrompt?: (context: /* AgentProfileContext — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ {
|
||||
|
|
@ -220,7 +218,6 @@ export interface SessionStateSnapshot {
|
|||
readonly skillActive?: boolean;
|
||||
readonly productName?: string;
|
||||
readonly replyStyleGuide?: string;
|
||||
readonly agentsMdStatus?: 'missing' | 'empty' | 'present';
|
||||
[key: string]: unknown;
|
||||
}) => /* SystemPromptRenderResult — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ {
|
||||
readonly text: string;
|
||||
|
|
@ -235,15 +232,6 @@ export interface SessionStateSnapshot {
|
|||
} | {
|
||||
readonly disclosed: false;
|
||||
};
|
||||
readonly agentsMd: {
|
||||
readonly disclosed: true;
|
||||
readonly value: {
|
||||
readonly fingerprint: string;
|
||||
readonly status: /* AgentsMdStatus — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ 'missing' | 'empty' | 'present';
|
||||
};
|
||||
} | {
|
||||
readonly disclosed: false;
|
||||
};
|
||||
};
|
||||
};
|
||||
readonly promptPrefix?: (ctx: /* AgentProfilePromptPrefixContext — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ {
|
||||
|
|
@ -310,7 +298,6 @@ export interface SessionStateSnapshot {
|
|||
readonly skillActive?: boolean;
|
||||
readonly productName?: string;
|
||||
readonly replyStyleGuide?: string;
|
||||
readonly agentsMdStatus?: 'missing' | 'empty' | 'present';
|
||||
[key: string]: unknown;
|
||||
}) => string;
|
||||
readonly renderSystemPrompt?: (context: /* AgentProfileContext — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ {
|
||||
|
|
@ -326,7 +313,6 @@ export interface SessionStateSnapshot {
|
|||
readonly skillActive?: boolean;
|
||||
readonly productName?: string;
|
||||
readonly replyStyleGuide?: string;
|
||||
readonly agentsMdStatus?: 'missing' | 'empty' | 'present';
|
||||
[key: string]: unknown;
|
||||
}) => /* SystemPromptRenderResult — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ {
|
||||
readonly text: string;
|
||||
|
|
@ -341,15 +327,6 @@ export interface SessionStateSnapshot {
|
|||
} | {
|
||||
readonly disclosed: false;
|
||||
};
|
||||
readonly agentsMd: {
|
||||
readonly disclosed: true;
|
||||
readonly value: {
|
||||
readonly fingerprint: string;
|
||||
readonly status: /* AgentsMdStatus — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ 'missing' | 'empty' | 'present';
|
||||
};
|
||||
} | {
|
||||
readonly disclosed: false;
|
||||
};
|
||||
};
|
||||
};
|
||||
readonly promptPrefix?: (ctx: /* AgentProfilePromptPrefixContext — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ {
|
||||
|
|
@ -749,11 +726,6 @@ export interface SessionStateSnapshot {
|
|||
readonly renderGeneration: number;
|
||||
readonly localDate: string;
|
||||
readonly timeZone: string;
|
||||
} | {
|
||||
readonly kind: 'agents_md';
|
||||
readonly renderGeneration: number;
|
||||
readonly fingerprint: string;
|
||||
readonly status: /* AgentsMdStatus — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ 'missing' | 'empty' | 'present';
|
||||
} | {
|
||||
readonly kind: 'skills';
|
||||
readonly renderGeneration: number;
|
||||
|
|
@ -840,11 +812,6 @@ export interface AgentStateSnapshot {
|
|||
readonly renderGeneration: number;
|
||||
readonly localDate: string;
|
||||
readonly timeZone: string;
|
||||
} | {
|
||||
readonly kind: 'agents_md';
|
||||
readonly renderGeneration: number;
|
||||
readonly fingerprint: string;
|
||||
readonly status: /* AgentsMdStatus — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ 'missing' | 'empty' | 'present';
|
||||
} | {
|
||||
readonly kind: 'skills';
|
||||
readonly renderGeneration: number;
|
||||
|
|
@ -979,11 +946,6 @@ export interface AgentStateSnapshot {
|
|||
readonly renderGeneration: number;
|
||||
readonly localDate: string;
|
||||
readonly timeZone: string;
|
||||
} | {
|
||||
readonly kind: 'agents_md';
|
||||
readonly renderGeneration: number;
|
||||
readonly fingerprint: string;
|
||||
readonly status: /* AgentsMdStatus — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ 'missing' | 'empty' | 'present';
|
||||
} | {
|
||||
readonly kind: 'skills';
|
||||
readonly renderGeneration: number;
|
||||
|
|
@ -1050,11 +1012,6 @@ export interface AgentStateSnapshot {
|
|||
readonly renderGeneration: number;
|
||||
readonly localDate: string;
|
||||
readonly timeZone: string;
|
||||
} | {
|
||||
readonly kind: 'agents_md';
|
||||
readonly renderGeneration: number;
|
||||
readonly fingerprint: string;
|
||||
readonly status: /* AgentsMdStatus — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ 'missing' | 'empty' | 'present';
|
||||
} | {
|
||||
readonly kind: 'skills';
|
||||
readonly renderGeneration: number;
|
||||
|
|
@ -1162,7 +1119,7 @@ export interface AgentStateSnapshot {
|
|||
'llmRequester.lastConfigLogSignature': string | undefined;
|
||||
'llmRequester.mediaDegradedTurns': Set<number>;
|
||||
'llmRequester.mediaStrippedTurns': Map<number, /* MediaStripSnapshot — packages/agent-core-v2/src/agent/contextProjector/contextProjector.ts */ {
|
||||
readonly "__@mediaStripSnapshotBrand@2779": undefined;
|
||||
readonly "__@mediaStripSnapshotBrand@2736": undefined;
|
||||
}>;
|
||||
'llmRequester.turnConfigs': Map<number, /* TurnRequestConfig — packages/agent-core-v2/src/agent/llmRequester/llmRequesterService.ts */ {
|
||||
readonly resolved: /* ProfileModelContext — packages/agent-core-v2/src/agent/profile/profile.ts */ {
|
||||
|
|
@ -1238,13 +1195,6 @@ export interface AgentStateSnapshot {
|
|||
'permissionMode.lastMode': 'manual' | 'yolo' | 'auto' | undefined;
|
||||
// src/agent/plan/injection/planModeInjection.ts
|
||||
'plan.wasActive': boolean;
|
||||
// src/agent/profile/agentsMdReminderService.ts
|
||||
'agentsMdReminder.seed': /* AgentsMdSeed — packages/agent-core-v2/src/agent/profile/agentsMdReminderService.ts */ {
|
||||
readonly cwd: string;
|
||||
readonly fingerprint: string;
|
||||
readonly status: /* AgentsMdStatus — packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts */ 'missing' | 'empty' | 'present';
|
||||
readonly renderGeneration: number;
|
||||
} | undefined;
|
||||
// src/agent/profile/profileService.ts
|
||||
'profile.activeToolNamesOverlay': readonly string[] | undefined;
|
||||
'profile.agentsMdWarning': string | undefined;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ interface ConfigUpdatePayload {
|
|||
environmentDisclosure?: {
|
||||
cwd: string;
|
||||
date: { disclosed: true, value: { localDate: string, timeZone: string } } | { disclosed: false };
|
||||
agentsMd: { disclosed: true, value: { fingerprint: string, status: 'missing' | 'empty' | 'present' } } | { disclosed: false };
|
||||
};
|
||||
renderGeneration?: number;
|
||||
disallowedTools?: string[];
|
||||
|
|
@ -474,7 +473,6 @@ interface ProfileBindPayload {
|
|||
environmentDisclosure?: {
|
||||
cwd: string;
|
||||
date: { disclosed: true, value: { localDate: string, timeZone: string } } | { disclosed: false };
|
||||
agentsMd: { disclosed: true, value: { fingerprint: string, status: 'missing' | 'empty' | 'present' } } | { disclosed: false };
|
||||
};
|
||||
renderGeneration?: number;
|
||||
activeToolNames?: string[];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* `contextInjector` domain (L4) — shared disclosure-baseline helpers for the
|
||||
* reminder providers (`date_change`, `agents_md`, `skill_list`).
|
||||
* reminder providers (`date_change`, `skill_list`).
|
||||
*
|
||||
* A provider's baseline answers "what has the model already seen" from up to
|
||||
* three sources, compared by render generation with ties won by the earlier
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import type { ContentPart, Message } from '#/kosong/contract/message';
|
||||
|
||||
import type { AgentTaskStatus } from '#/agent/task/task';
|
||||
import type { AgentsMdStatus } from '#/app/agentProfileCatalog/agentProfileCatalog';
|
||||
|
||||
export type SkillSource = 'project' | 'user' | 'extra' | 'builtin';
|
||||
|
||||
|
|
@ -45,12 +44,6 @@ export type ContextInjectionDisclosure =
|
|||
readonly localDate: string;
|
||||
readonly timeZone: string;
|
||||
}
|
||||
| {
|
||||
readonly kind: 'agents_md';
|
||||
readonly renderGeneration: number;
|
||||
readonly fingerprint: string;
|
||||
readonly status: AgentsMdStatus;
|
||||
}
|
||||
| {
|
||||
readonly kind: 'skills';
|
||||
readonly renderGeneration: number;
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
/**
|
||||
* `profile` domain (L4) — `IAgentAgentsMdReminderService` contract.
|
||||
*
|
||||
* Defines the Agent-scope marker service that announces AGENTS.md content
|
||||
* changes (edits, creations, removals) through an `agents_md`
|
||||
* context-injection reminder when the files drift from what the system prompt
|
||||
* was rendered with.
|
||||
*/
|
||||
|
||||
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
|
||||
|
||||
export interface IAgentAgentsMdReminderService {
|
||||
readonly _serviceBrand: undefined;
|
||||
}
|
||||
|
||||
export const IAgentAgentsMdReminderService: ServiceIdentifier<IAgentAgentsMdReminderService> =
|
||||
createDecorator<IAgentAgentsMdReminderService>('agentAgentsMdReminderService');
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
/**
|
||||
* `profile` domain (L4) — `IAgentAgentsMdReminderService` implementation.
|
||||
*
|
||||
* Owns the `agents_md` context-injection provider. The AGENTS.md instruction
|
||||
* hierarchy is baked into the system prompt at (re)bind and after compaction,
|
||||
* so a user editing AGENTS.md mid-session leaves the model following stale
|
||||
* rules; this provider injects the fresh content at the next step boundary
|
||||
* when it differs. Unlike skills, removals and content edits announce too —
|
||||
* a deleted rule fails silently, never on invocation. The provider runs only
|
||||
* while the profile's rendered snapshot exists and matches the live cwd. The
|
||||
* baseline prefers the typed disclosure on the newest surviving `agents_md`
|
||||
* injection, then the persisted rendered snapshot, then a runtime seed kept
|
||||
* in `agentState`: a profile whose snapshot declares no AGENTS.md disclosure
|
||||
* is seeded with the first observed fingerprint (quietly), so a later
|
||||
* creation, edit, or removal still announces. The live content is re-read at
|
||||
* every step boundary — the candidate chain is a handful of small instruction
|
||||
* files, so no filesystem watch is kept. Bound at Agent scope.
|
||||
*/
|
||||
|
||||
import { Disposable } from '#/_base/di/lifecycle';
|
||||
import { LifecycleScope, ScopeActivation, registerScopedService } from '#/_base/di/scope';
|
||||
import { defineState } from '#/_base/state/stateRegistry';
|
||||
import {
|
||||
IAgentContextInjectorService,
|
||||
type ContextInjectionContext,
|
||||
type ContextInjectionResult,
|
||||
} from '#/agent/contextInjector/contextInjector';
|
||||
import {
|
||||
disclosureOfKind,
|
||||
pickDisclosureBaseline,
|
||||
} from '#/agent/contextInjector/disclosureBaseline';
|
||||
import { IAgentStateService } from '#/agent/state/agentState';
|
||||
import type { AgentsMdStatus } from '#/app/agentProfileCatalog/agentProfileCatalog';
|
||||
import { fingerprintDisclosureContent } from '#/app/agentProfileCatalog/profile-shared';
|
||||
import { IBootstrapService } from '#/app/bootstrap/bootstrap';
|
||||
import { IHostEnvironment } from '#/os/interface/hostEnvironment';
|
||||
import { IHostFileSystem } from '#/os/interface/hostFileSystem';
|
||||
|
||||
import { loadAgentsMdSnapshot } from './context';
|
||||
import { IAgentProfileService } from './profile';
|
||||
import { IAgentAgentsMdReminderService } from './agentsMdReminder';
|
||||
|
||||
const AGENTS_MD_INJECTION_VARIANT = 'agents_md';
|
||||
|
||||
const CURRENT_BLOCK_START = '<current-agents-md>';
|
||||
const CURRENT_BLOCK_END = '</current-agents-md>';
|
||||
|
||||
export const agentsMdReminderSeedKey = defineState<AgentsMdSeed | undefined>(
|
||||
'agentsMdReminder.seed',
|
||||
() => undefined,
|
||||
);
|
||||
|
||||
export class AgentAgentsMdReminderService extends Disposable implements IAgentAgentsMdReminderService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
@IAgentContextInjectorService dynamicInjector: IAgentContextInjectorService,
|
||||
@IAgentProfileService private readonly profile: IAgentProfileService,
|
||||
@IAgentStateService private readonly states: IAgentStateService,
|
||||
@IHostFileSystem private readonly fs: IHostFileSystem,
|
||||
@IHostEnvironment private readonly env: IHostEnvironment,
|
||||
@IBootstrapService private readonly bootstrap: IBootstrapService,
|
||||
) {
|
||||
super();
|
||||
this.states.register(agentsMdReminderSeedKey);
|
||||
this._register(
|
||||
dynamicInjector.register(AGENTS_MD_INJECTION_VARIANT, (ctx) => this.reminder(ctx)),
|
||||
);
|
||||
}
|
||||
|
||||
private async reminder({
|
||||
lastDisclosure,
|
||||
}: ContextInjectionContext): Promise<ContextInjectionResult | undefined> {
|
||||
try {
|
||||
let profileData = this.profile.data();
|
||||
if (profileData.environmentDisclosure?.cwd !== profileData.cwd) return undefined;
|
||||
const current = await loadAgentsMdSnapshot(
|
||||
{ fs: this.fs, homeDir: this.env.homeDir },
|
||||
profileData.cwd,
|
||||
this.bootstrap.homeDir,
|
||||
);
|
||||
profileData = this.profile.data();
|
||||
if (profileData.environmentDisclosure?.cwd !== profileData.cwd) return undefined;
|
||||
const renderGeneration = profileData.renderGeneration ?? 0;
|
||||
const fingerprint = fingerprintDisclosureContent(current.content);
|
||||
const baseline = pickDisclosureBaseline<AgentsMdDisclosure>(
|
||||
disclosureOfKind(lastDisclosure, 'agents_md'),
|
||||
this.contentFromProfile(),
|
||||
this.seed(),
|
||||
);
|
||||
if (baseline === undefined) {
|
||||
this.states.set(agentsMdReminderSeedKey, {
|
||||
fingerprint,
|
||||
status: current.status,
|
||||
renderGeneration,
|
||||
cwd: profileData.cwd,
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
if (baseline.fingerprint === fingerprint) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
content: buildAgentsMdReminder(current.content),
|
||||
disclosure: {
|
||||
kind: 'agents_md',
|
||||
renderGeneration,
|
||||
fingerprint,
|
||||
status: current.status,
|
||||
},
|
||||
};
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private contentFromProfile(): AgentsMdDisclosure | undefined {
|
||||
const profileData = this.profile.data();
|
||||
if (profileData.environmentDisclosure?.cwd !== profileData.cwd) return undefined;
|
||||
const agentsMd = profileData.environmentDisclosure.agentsMd;
|
||||
if (!agentsMd?.disclosed) return undefined;
|
||||
return {
|
||||
...agentsMd.value,
|
||||
renderGeneration: profileData.renderGeneration ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
private seed(): AgentsMdDisclosure | undefined {
|
||||
const seed = this.states.get(agentsMdReminderSeedKey);
|
||||
if (seed === undefined || seed.cwd !== this.profile.data().cwd) return undefined;
|
||||
return seed;
|
||||
}
|
||||
}
|
||||
|
||||
interface AgentsMdDisclosure {
|
||||
readonly fingerprint: string;
|
||||
readonly status: AgentsMdStatus;
|
||||
readonly renderGeneration: number;
|
||||
}
|
||||
|
||||
interface AgentsMdSeed extends AgentsMdDisclosure {
|
||||
readonly cwd: string;
|
||||
}
|
||||
|
||||
function buildAgentsMdReminder(current: string): string {
|
||||
const body =
|
||||
current.length > 0
|
||||
? 'The AGENTS.md instructions have changed since your system prompt was rendered. The content below is current and supersedes the AGENTS.md instructions in your system prompt.'
|
||||
: 'The AGENTS.md instructions that fed your system prompt have been removed (or are now empty); they no longer apply.';
|
||||
return `${body}\n\n${CURRENT_BLOCK_START}\n${current}\n${CURRENT_BLOCK_END}\n\nDO NOT mention this to the user explicitly.`;
|
||||
}
|
||||
|
||||
registerScopedService(
|
||||
LifecycleScope.Agent,
|
||||
IAgentAgentsMdReminderService,
|
||||
AgentAgentsMdReminderService,
|
||||
ScopeActivation.OnScopeCreated,
|
||||
'profile',
|
||||
);
|
||||
|
|
@ -20,7 +20,6 @@ import { dirname, join, normalize } from 'pathe';
|
|||
|
||||
import type { IHostFileSystem } from '#/os/interface/hostFileSystem';
|
||||
|
||||
import type { AgentsMdStatus } from '#/app/agentProfileCatalog/agentProfileCatalog';
|
||||
import type { SystemPromptContext } from './profile';
|
||||
|
||||
export const AGENTS_MD_RECOMMENDED_MAX_BYTES = 32 * 1024;
|
||||
|
|
@ -38,7 +37,6 @@ export interface PreparedSystemPromptContext extends SystemPromptContext {
|
|||
readonly agentsMd?: string;
|
||||
readonly additionalDirsInfo?: string;
|
||||
readonly agentsMdWarning?: string;
|
||||
readonly agentsMdStatus?: AgentsMdStatus;
|
||||
}
|
||||
|
||||
export interface PrepareSystemPromptContextOptions {
|
||||
|
|
@ -60,7 +58,6 @@ export async function prepareSystemPromptContext(
|
|||
return {
|
||||
cwdListing,
|
||||
agentsMd: agentsMdResult.content,
|
||||
agentsMdStatus: agentsMdResult.status,
|
||||
additionalDirsInfo,
|
||||
agentsMdWarning: agentsMdResult.warning,
|
||||
};
|
||||
|
|
@ -71,17 +68,8 @@ export async function loadAgentsMd(
|
|||
workDir: string,
|
||||
brandHome?: string,
|
||||
): Promise<string> {
|
||||
const result = await loadAgentsMdSnapshot(deps, workDir, brandHome);
|
||||
return result.content;
|
||||
}
|
||||
|
||||
export async function loadAgentsMdSnapshot(
|
||||
deps: ProfileContextDeps,
|
||||
workDir: string,
|
||||
brandHome?: string,
|
||||
): Promise<{ readonly content: string; readonly status: AgentsMdStatus }> {
|
||||
const result = await loadAgentsMdForRoots(deps, brandHome, [workDir]);
|
||||
return { content: result.content, status: result.status };
|
||||
return result.content;
|
||||
}
|
||||
|
||||
async function agentsMdCandidateGroups(
|
||||
|
|
@ -109,7 +97,6 @@ async function agentsMdCandidateGroups(
|
|||
interface LoadedAgentsMd {
|
||||
readonly content: string;
|
||||
readonly warning: string | undefined;
|
||||
readonly status: AgentsMdStatus;
|
||||
}
|
||||
|
||||
async function loadAgentsMdForRoots(
|
||||
|
|
@ -120,15 +107,12 @@ async function loadAgentsMdForRoots(
|
|||
const discovered: AgentFile[] = [];
|
||||
const seen = new Set<string>();
|
||||
const loadWarnings: string[] = [];
|
||||
let emptyFileFound = false;
|
||||
const warnLoad = (message: string): void => {
|
||||
loadWarnings.push(message);
|
||||
};
|
||||
|
||||
const collect = async (path: string): Promise<boolean> => {
|
||||
const file = await readAgentFile(deps, path, warnLoad, () => {
|
||||
emptyFileFound = true;
|
||||
});
|
||||
const file = await readAgentFile(deps, path, warnLoad);
|
||||
if (file === undefined) return false;
|
||||
const key = normalize(file.path);
|
||||
if (seen.has(key)) return false;
|
||||
|
|
@ -154,11 +138,7 @@ async function loadAgentsMdForRoots(
|
|||
);
|
||||
}
|
||||
const warning = loadWarnings.length > 0 ? loadWarnings.join('\n') : undefined;
|
||||
return {
|
||||
content,
|
||||
warning,
|
||||
status: content.length > 0 ? 'present' : emptyFileFound ? 'empty' : 'missing',
|
||||
};
|
||||
return { content, warning };
|
||||
}
|
||||
|
||||
async function loadAdditionalDirsInfo(
|
||||
|
|
@ -210,7 +190,6 @@ async function readAgentFile(
|
|||
deps: ProfileContextDeps,
|
||||
path: string,
|
||||
warn: (message: string) => void,
|
||||
markEmpty: () => void,
|
||||
): Promise<AgentFile | undefined> {
|
||||
if (!(await isFile(deps, path))) {
|
||||
if (await entryExists(deps, path)) {
|
||||
|
|
@ -226,7 +205,6 @@ async function readAgentFile(
|
|||
return undefined;
|
||||
}
|
||||
if (content.length === 0) {
|
||||
markEmpty();
|
||||
return undefined;
|
||||
}
|
||||
return { path, content };
|
||||
|
|
|
|||
|
|
@ -67,20 +67,14 @@ export interface AgentProfileContext {
|
|||
readonly skillActive?: boolean;
|
||||
readonly productName?: string;
|
||||
readonly replyStyleGuide?: string;
|
||||
readonly agentsMdStatus?: AgentsMdStatus;
|
||||
readonly [key: string]: unknown;
|
||||
}
|
||||
|
||||
export type AgentsMdStatus = 'missing' | 'empty' | 'present';
|
||||
|
||||
export interface EnvironmentDisclosureSnapshot {
|
||||
readonly cwd: string;
|
||||
readonly date:
|
||||
| { readonly disclosed: true; readonly value: { readonly localDate: string; readonly timeZone: string } }
|
||||
| { readonly disclosed: false };
|
||||
readonly agentsMd:
|
||||
| { readonly disclosed: true; readonly value: { readonly fingerprint: string; readonly status: AgentsMdStatus } }
|
||||
| { readonly disclosed: false };
|
||||
}
|
||||
|
||||
export interface SystemPromptRenderResult {
|
||||
|
|
@ -124,7 +118,6 @@ export function renderAgentProfile(
|
|||
environment: {
|
||||
cwd: context.cwd ?? '',
|
||||
date: { disclosed: false },
|
||||
agentsMd: { disclosed: false },
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@
|
|||
* parse the rendered text.
|
||||
*/
|
||||
|
||||
import { createHash } from 'node:crypto';
|
||||
|
||||
import { renderPrompt } from '#/_base/utils/render-prompt';
|
||||
|
||||
import {
|
||||
|
|
@ -183,7 +181,6 @@ function environmentForTemplate(
|
|||
context: AgentProfileContext,
|
||||
): EnvironmentDisclosureSnapshot {
|
||||
const usesNow = template.includes('${now}');
|
||||
const usesAgentsMd = template.includes('${agents_md}');
|
||||
return {
|
||||
cwd: context.cwd ?? '',
|
||||
date: usesNow
|
||||
|
|
@ -195,15 +192,6 @@ function environmentForTemplate(
|
|||
},
|
||||
}
|
||||
: { disclosed: false },
|
||||
agentsMd: usesAgentsMd
|
||||
? {
|
||||
disclosed: true,
|
||||
value: {
|
||||
fingerprint: fingerprintDisclosureContent(context.agentsMd ?? ''),
|
||||
status: context.agentsMdStatus ?? agentsMdStatus(context.agentsMd),
|
||||
},
|
||||
}
|
||||
: { disclosed: false },
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +199,6 @@ function undisclosedEnvironment(context: AgentProfileContext): EnvironmentDisclo
|
|||
return {
|
||||
cwd: context.cwd ?? '',
|
||||
date: { disclosed: false },
|
||||
agentsMd: { disclosed: false },
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -223,18 +210,9 @@ function mergeEnvironmentDisclosure(
|
|||
return {
|
||||
cwd: direct.cwd || base.cwd,
|
||||
date: direct.date.disclosed ? direct.date : base.date,
|
||||
agentsMd: direct.agentsMd.disclosed ? direct.agentsMd : base.agentsMd,
|
||||
};
|
||||
}
|
||||
|
||||
export function fingerprintDisclosureContent(content: string): string {
|
||||
return createHash('sha256').update(content, 'utf8').digest('hex');
|
||||
}
|
||||
|
||||
export function agentsMdStatus(content: string | undefined): 'missing' | 'empty' | 'present' {
|
||||
return content === undefined ? 'missing' : content.length === 0 ? 'empty' : 'present';
|
||||
}
|
||||
|
||||
function localDateKey(now: string | undefined): string {
|
||||
const date = now === undefined ? new Date() : new Date(now);
|
||||
if (Number.isNaN(date.getTime())) return localDateKey(undefined);
|
||||
|
|
|
|||
|
|
@ -529,8 +529,6 @@ export * from '#/agent/permissionRules/permissionRulesService';
|
|||
export * from '#/agent/profile/profile';
|
||||
export * from '#/agent/profile/profileService';
|
||||
export * from '#/agent/profile/context';
|
||||
export * from '#/agent/profile/agentsMdReminder';
|
||||
export * from '#/agent/profile/agentsMdReminderService';
|
||||
export * from '#/agent/prompt/prompt';
|
||||
export * from '#/agent/prompt/promptService';
|
||||
import '#/app/messageLegacy/errors';
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ function updateSystemPromptWithDate(
|
|||
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
|
||||
},
|
||||
},
|
||||
agentsMd: { disclosed: false },
|
||||
};
|
||||
profile.update({
|
||||
systemPrompt: systemPromptWithDate(iso),
|
||||
|
|
@ -77,7 +76,6 @@ function updateSystemPromptWithoutDate(profile: IAgentProfileService): void {
|
|||
const environment: EnvironmentDisclosureSnapshot = {
|
||||
cwd: profile.data().cwd,
|
||||
date: { disclosed: false },
|
||||
agentsMd: { disclosed: false },
|
||||
};
|
||||
profile.update({
|
||||
systemPrompt: 'You are a deterministic test agent.',
|
||||
|
|
|
|||
|
|
@ -1,299 +0,0 @@
|
|||
/**
|
||||
* Scenario: `agents_md` context injection announces AGENTS.md content changes.
|
||||
*
|
||||
* Exercises the real provider through the harness injector against a fake
|
||||
* host fs whose AGENTS.md files the test edits and deletes: the provider
|
||||
* re-reads the candidate chain on every injection, so a filesystem edit
|
||||
* lands on the next `inject()` call. Baselines come from typed reminder
|
||||
* metadata, the persisted rendered snapshot, and a runtime seed recorded on
|
||||
* first observation for prompts that never disclose AGENTS.md. Edits,
|
||||
* creations, and removals all announce. Run: `pnpm --filter
|
||||
* @moonshot-ai/agent-core-v2 exec vitest run
|
||||
* test/agent/profile/agentsMdReminder.test.ts`.
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { IAgentContextInjectorService } from '#/agent/contextInjector/contextInjector';
|
||||
import { IAgentContextMemoryService } from '#/agent/contextMemory/contextMemory';
|
||||
import type { ContextMessage } from '#/agent/contextMemory/types';
|
||||
import { loadAgentsMd } from '#/agent/profile/context';
|
||||
import { IAgentProfileService } from '#/agent/profile/profile';
|
||||
import {
|
||||
fingerprintDisclosureContent,
|
||||
agentsMdStatus,
|
||||
} from '#/app/agentProfileCatalog/profile-shared';
|
||||
import type { EnvironmentDisclosureSnapshot } from '#/app/agentProfileCatalog/agentProfileCatalog';
|
||||
import type { HostFileStat, IHostFileSystem } from '#/os/interface/hostFileSystem';
|
||||
|
||||
import { createFakeHostFs } from '../../tools/fixtures/fake-exec';
|
||||
import { createTestAgent, execEnvServices, type TestAgentContext } from '../../harness';
|
||||
|
||||
const TEST_HOME_DIR = '/home/test';
|
||||
const BRAND_HOME_DIR = '/tmp/kimi-code-agent-app-v2-test';
|
||||
|
||||
function systemPromptWithAgentsMd(content: string): string {
|
||||
return [
|
||||
'You are a deterministic test agent.',
|
||||
'',
|
||||
'The applicable `AGENTS.md` instructions are:',
|
||||
'',
|
||||
'```````',
|
||||
content,
|
||||
'```````',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function updateSystemPromptWithAgentsMd(
|
||||
profile: IAgentProfileService,
|
||||
content: string,
|
||||
): void {
|
||||
const environment: EnvironmentDisclosureSnapshot = {
|
||||
cwd: profile.data().cwd,
|
||||
date: { disclosed: false },
|
||||
agentsMd: {
|
||||
disclosed: true,
|
||||
value: {
|
||||
fingerprint: fingerprintDisclosureContent(content),
|
||||
status: agentsMdStatus(content),
|
||||
},
|
||||
},
|
||||
};
|
||||
profile.update({
|
||||
systemPrompt: systemPromptWithAgentsMd(content),
|
||||
environmentDisclosure: environment,
|
||||
});
|
||||
}
|
||||
|
||||
function updateSystemPromptWithoutAgentsMd(profile: IAgentProfileService): void {
|
||||
const environment: EnvironmentDisclosureSnapshot = {
|
||||
cwd: profile.data().cwd,
|
||||
date: { disclosed: false },
|
||||
agentsMd: { disclosed: false },
|
||||
};
|
||||
profile.update({
|
||||
systemPrompt: 'You are a deterministic test agent.',
|
||||
environmentDisclosure: environment,
|
||||
});
|
||||
}
|
||||
|
||||
function agentsMdReminders(context: IAgentContextMemoryService): readonly ContextMessage[] {
|
||||
return context.get().filter((message) => {
|
||||
return message.origin?.kind === 'injection' && message.origin.variant === 'agents_md';
|
||||
});
|
||||
}
|
||||
|
||||
function messageText(message: ContextMessage): string {
|
||||
return message.content
|
||||
.map((part) => (part.type === 'text' ? part.text : ''))
|
||||
.join('');
|
||||
}
|
||||
|
||||
function deferred<T>(): {
|
||||
readonly promise: Promise<T>;
|
||||
readonly resolve: (value: T) => void;
|
||||
} {
|
||||
let resolve!: (value: T) => void;
|
||||
const promise = new Promise<T>((done) => {
|
||||
resolve = done;
|
||||
});
|
||||
return { promise, resolve };
|
||||
}
|
||||
|
||||
describe('AgentAgentsMdReminderService', () => {
|
||||
let cwd: string;
|
||||
let agentsMdPath: string;
|
||||
let files: Map<string, string>;
|
||||
let dirs: Set<string>;
|
||||
let hostFs: IHostFileSystem;
|
||||
let ctx: TestAgentContext;
|
||||
let context: IAgentContextMemoryService;
|
||||
let injector: IAgentContextInjectorService;
|
||||
let profile: IAgentProfileService;
|
||||
let readTextOverride: ((path: string) => Promise<string>) | undefined;
|
||||
|
||||
function fileStat(path: string): HostFileStat {
|
||||
return { isFile: true, isDirectory: false, size: files.get(path)?.length ?? 0 };
|
||||
}
|
||||
|
||||
async function currentAgentsMd(): Promise<string> {
|
||||
return loadAgentsMd({ fs: hostFs, homeDir: TEST_HOME_DIR }, cwd, BRAND_HOME_DIR);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
cwd = process.cwd();
|
||||
agentsMdPath = `${cwd}/AGENTS.md`;
|
||||
files = new Map();
|
||||
dirs = new Set([cwd, `${cwd}/.git`]);
|
||||
readTextOverride = undefined;
|
||||
hostFs = createFakeHostFs({
|
||||
stat: async (path: string) => {
|
||||
if (files.has(path)) return fileStat(path);
|
||||
if (dirs.has(path)) return { isFile: false, isDirectory: true, size: 0 };
|
||||
throw new Error(`ENOENT: ${path}`);
|
||||
},
|
||||
lstat: async (path: string) => {
|
||||
if (files.has(path)) return fileStat(path);
|
||||
if (dirs.has(path)) return { isFile: false, isDirectory: true, size: 0 };
|
||||
throw new Error(`ENOENT: ${path}`);
|
||||
},
|
||||
readText: async (path: string) => {
|
||||
if (readTextOverride !== undefined) return readTextOverride(path);
|
||||
const content = files.get(path);
|
||||
if (content === undefined) throw new Error(`ENOENT: ${path}`);
|
||||
return content;
|
||||
},
|
||||
readdir: async () => [],
|
||||
realpath: async (path: string) => path,
|
||||
});
|
||||
ctx = createTestAgent(execEnvServices({ hostFs }));
|
||||
context = ctx.get(IAgentContextMemoryService);
|
||||
injector = ctx.get(IAgentContextInjectorService);
|
||||
profile = ctx.get(IAgentProfileService);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
try {
|
||||
await ctx.expectResumeMatches();
|
||||
} finally {
|
||||
await ctx.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
it('stays quiet when no AGENTS.md exists and the prompt has no fenced block', async () => {
|
||||
updateSystemPromptWithoutAgentsMd(profile);
|
||||
|
||||
await injector.inject();
|
||||
|
||||
expect(agentsMdReminders(context)).toHaveLength(0);
|
||||
expect(context.get()).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('announces a file created after the silent seed', async () => {
|
||||
updateSystemPromptWithoutAgentsMd(profile);
|
||||
await injector.inject();
|
||||
expect(agentsMdReminders(context)).toHaveLength(0);
|
||||
|
||||
files.set(agentsMdPath, 'fresh rule');
|
||||
await injector.inject();
|
||||
|
||||
const reminders = agentsMdReminders(context);
|
||||
expect(reminders).toHaveLength(1);
|
||||
expect(messageText(reminders[0] as ContextMessage)).toContain('fresh rule');
|
||||
|
||||
await injector.inject();
|
||||
expect(agentsMdReminders(context)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('stays quiet when the file content matches the system prompt block', async () => {
|
||||
files.set(agentsMdPath, 'rule one');
|
||||
updateSystemPromptWithAgentsMd(profile, await currentAgentsMd());
|
||||
|
||||
await injector.inject();
|
||||
|
||||
expect(agentsMdReminders(context)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('injects the fresh content after an edit, then stays quiet', async () => {
|
||||
files.set(agentsMdPath, 'rule one');
|
||||
updateSystemPromptWithAgentsMd(profile, await currentAgentsMd());
|
||||
await injector.inject();
|
||||
|
||||
files.set(agentsMdPath, 'rule two');
|
||||
await injector.inject();
|
||||
|
||||
const reminders = agentsMdReminders(context);
|
||||
expect(reminders).toHaveLength(1);
|
||||
const first = reminders[0];
|
||||
expect(first).toBeDefined();
|
||||
const text = messageText(first as ContextMessage);
|
||||
expect(text).toContain('rule two');
|
||||
expect(text).toContain('supersedes');
|
||||
expect(text).toContain('DO NOT mention this to the user explicitly');
|
||||
expect(first?.origin).toMatchObject({
|
||||
kind: 'injection',
|
||||
variant: 'agents_md',
|
||||
disclosure: {
|
||||
kind: 'agents_md',
|
||||
fingerprint: fingerprintDisclosureContent(await currentAgentsMd()),
|
||||
status: 'present',
|
||||
},
|
||||
});
|
||||
|
||||
await injector.inject();
|
||||
expect(agentsMdReminders(context)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('announces an edit that lands while fresh content is being read', async () => {
|
||||
files.set(agentsMdPath, 'rule one');
|
||||
updateSystemPromptWithAgentsMd(profile, await currentAgentsMd());
|
||||
await injector.inject();
|
||||
|
||||
files.set(agentsMdPath, 'rule two');
|
||||
|
||||
const readStarted = deferred<void>();
|
||||
const releaseRead = deferred<void>();
|
||||
let pauseRead = true;
|
||||
readTextOverride = async (path) => {
|
||||
const content = files.get(path);
|
||||
if (content === undefined) throw new Error(`ENOENT: ${path}`);
|
||||
if (path === agentsMdPath && pauseRead) {
|
||||
pauseRead = false;
|
||||
readStarted.resolve(undefined);
|
||||
await releaseRead.promise;
|
||||
}
|
||||
return content;
|
||||
};
|
||||
|
||||
const firstInjection = injector.inject();
|
||||
await readStarted.promise;
|
||||
files.set(agentsMdPath, 'rule three');
|
||||
releaseRead.resolve(undefined);
|
||||
await firstInjection;
|
||||
|
||||
await injector.inject();
|
||||
|
||||
const reminders = agentsMdReminders(context);
|
||||
expect(reminders).toHaveLength(2);
|
||||
expect(messageText(reminders[0] as ContextMessage)).toContain('rule two');
|
||||
expect(messageText(reminders[1] as ContextMessage)).toContain('rule three');
|
||||
});
|
||||
|
||||
it('announces removal when the last AGENTS.md file disappears', async () => {
|
||||
files.set(agentsMdPath, 'rule one');
|
||||
updateSystemPromptWithAgentsMd(profile, await currentAgentsMd());
|
||||
await injector.inject();
|
||||
|
||||
files.clear();
|
||||
await injector.inject();
|
||||
|
||||
const reminders = agentsMdReminders(context);
|
||||
expect(reminders).toHaveLength(1);
|
||||
const first = reminders[0];
|
||||
expect(first).toBeDefined();
|
||||
expect(messageText(first as ContextMessage)).toContain('removed');
|
||||
expect(first?.origin).toMatchObject({
|
||||
disclosure: {
|
||||
kind: 'agents_md',
|
||||
fingerprint:
|
||||
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
|
||||
status: 'missing',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('announces a file created after an empty baseline', async () => {
|
||||
updateSystemPromptWithAgentsMd(profile, '');
|
||||
await injector.inject();
|
||||
|
||||
files.set(agentsMdPath, 'fresh rule');
|
||||
|
||||
await injector.inject();
|
||||
|
||||
const reminders = agentsMdReminders(context);
|
||||
expect(reminders).toHaveLength(1);
|
||||
const first = reminders[0];
|
||||
expect(first).toBeDefined();
|
||||
expect(messageText(first as ContextMessage)).toContain('fresh rule');
|
||||
});
|
||||
});
|
||||
|
|
@ -357,14 +357,6 @@ describe('AgentProfileService (wire-backed config.update)', () => {
|
|||
disclosed: true,
|
||||
value: { localDate: '2026-07-29', timeZone: 'Asia/Shanghai' },
|
||||
},
|
||||
agentsMd: {
|
||||
disclosed: true,
|
||||
value: {
|
||||
fingerprint:
|
||||
'a3a18567b84af6528d171050e54bcf75e0a1c94c8db8a165d03e9866cfa95bce',
|
||||
status: 'present',
|
||||
},
|
||||
},
|
||||
};
|
||||
svc.applyBindingSnapshot({
|
||||
cwd: '/work',
|
||||
|
|
|
|||
|
|
@ -141,14 +141,13 @@ describe('renderPromptTemplate', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('records the environment facts used by now and agents_md placeholders', () => {
|
||||
it('records the environment facts used by the now placeholder', () => {
|
||||
const result = renderPromptTemplateResult(
|
||||
'date=${now} agents=${agents_md}',
|
||||
{
|
||||
cwd: '/work',
|
||||
now: '2026-07-29T12:00:00',
|
||||
agentsMd: 'AGENTS',
|
||||
agentsMdStatus: 'present',
|
||||
},
|
||||
{ skillActive: true },
|
||||
);
|
||||
|
|
@ -159,13 +158,6 @@ describe('renderPromptTemplate', () => {
|
|||
disclosed: true,
|
||||
value: { localDate: '2026-07-29' },
|
||||
});
|
||||
expect(result.environment.agentsMd).toEqual({
|
||||
disclosed: true,
|
||||
value: {
|
||||
fingerprint: '370f346f47cb65d94fd904ecb4ceeb17d784965984b0f8e52cce79ff95451a54',
|
||||
status: 'present',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('merges disclosure metadata from a structured base_prompt render', () => {
|
||||
|
|
@ -181,14 +173,6 @@ describe('renderPromptTemplate', () => {
|
|||
disclosed: true,
|
||||
value: { localDate: '2026-07-28', timeZone: 'UTC' },
|
||||
},
|
||||
agentsMd: {
|
||||
disclosed: true,
|
||||
value: {
|
||||
fingerprint:
|
||||
'a3a18567b84af6528d171050e54bcf75e0a1c94c8db8a165d03e9866cfa95bce',
|
||||
status: 'present',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
|
@ -200,14 +184,6 @@ describe('renderPromptTemplate', () => {
|
|||
disclosed: true,
|
||||
value: { localDate: '2026-07-28', timeZone: 'UTC' },
|
||||
},
|
||||
agentsMd: {
|
||||
disclosed: true,
|
||||
value: {
|
||||
fingerprint:
|
||||
'a3a18567b84af6528d171050e54bcf75e0a1c94c8db8a165d03e9866cfa95bce',
|
||||
status: 'present',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -289,14 +265,13 @@ describe('renderSystemPrompt', () => {
|
|||
expect(overridden).not.toContain('Kimi Code CLI');
|
||||
});
|
||||
|
||||
it('returns disclosure metadata for the builtin now and agents_md sections', () => {
|
||||
it('returns disclosure metadata for the builtin now section', () => {
|
||||
const result = renderSystemPromptResult(
|
||||
'',
|
||||
{
|
||||
cwd: '/work',
|
||||
now: '2026-07-29T12:00:00',
|
||||
agentsMd: 'AGENTS',
|
||||
agentsMdStatus: 'present',
|
||||
},
|
||||
{ skillActive: true },
|
||||
);
|
||||
|
|
@ -307,12 +282,5 @@ describe('renderSystemPrompt', () => {
|
|||
disclosed: true,
|
||||
value: { localDate: '2026-07-29' },
|
||||
});
|
||||
expect(result.environment.agentsMd).toEqual({
|
||||
disclosed: true,
|
||||
value: {
|
||||
fingerprint: '370f346f47cb65d94fd904ecb4ceeb17d784965984b0f8e52cce79ff95451a54',
|
||||
status: 'present',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ describe('Agent config', () => {
|
|||
});
|
||||
|
||||
expect(ctx.newEvents()).toMatchInlineSnapshot(`
|
||||
[wire] config.update { "profileName": "test-profile", "systemPrompt": "Profile system prompt.", "environmentDisclosure": { "cwd": "<cwd>", "date": { "disclosed": false }, "agentsMd": { "disclosed": false } }, "renderGeneration": 2, "disallowedTools": [], "time": "<time>" }
|
||||
[wire] config.update { "profileName": "test-profile", "systemPrompt": "Profile system prompt.", "environmentDisclosure": { "cwd": "<cwd>", "date": { "disclosed": false } }, "renderGeneration": 2, "disallowedTools": [], "time": "<time>" }
|
||||
[emit] agent.status.updated { "model": "mock-model", "maxContextTokens": 1000000 }
|
||||
[wire] tools.set_active_tools { "names": [ "Read" ], "time": "<time>" }
|
||||
`);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue