@myagentroam/agent 0.9.135 → 0.9.137
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/main.js +1 -1
- package/dist/host/local-agent-host.d.ts +9 -7
- package/dist/host/local-agent-host.js +77 -5
- package/dist/host/local-process-executor.d.ts +2 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -0
- package/dist/model/catalog-file.d.ts +2 -0
- package/dist/model/configuration.d.ts +5 -0
- package/dist/model/configuration.js +8 -0
- package/dist/model/contracts.d.ts +7 -0
- package/dist/model/openai-responses.d.ts +1 -1
- package/dist/model/openai-responses.js +85 -20
- package/dist/prompts/index.d.ts +2 -1
- package/dist/prompts/index.js +2 -2
- package/dist/prompts/workflow.d.ts +1 -1
- package/dist/prompts/workflow.js +9 -2
- package/dist/runtime/context-gc-runtime.d.ts +1 -0
- package/dist/runtime/context-gc-runtime.js +3 -0
- package/dist/runtime/context-gc-tokens.d.ts +1 -0
- package/dist/runtime/context-gc-tokens.js +1 -1
- package/dist/runtime/context-gc.d.ts +1 -0
- package/dist/runtime/context-gc.js +85 -8
- package/dist/runtime/context-projection.js +19 -2
- package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +29 -2
- package/dist/sdk/agent.js +249 -26
- package/dist/sdk/mcp-catalog.d.ts +13 -0
- package/dist/sdk/mcp-catalog.js +219 -0
- package/dist/sdk/types.d.ts +53 -1
- package/dist/session/context-gc-checkpoint.d.ts +7 -1
- package/dist/session/context-gc-checkpoint.js +28 -2
- package/dist/session/context-gc-replacements.d.ts +7 -0
- package/dist/session/context-gc-replacements.js +26 -0
- package/dist/session/jsonl-store.d.ts +6 -0
- package/dist/session/jsonl-store.js +92 -54
- package/dist/subagent/scheduler.d.ts +3 -3
- package/dist/subagent/session-controller.d.ts +1 -1
- package/dist/tools/agent-cancel.js +1 -0
- package/dist/tools/agent-message.js +1 -0
- package/dist/tools/agent-start.js +1 -0
- package/dist/tools/agent-wait.js +1 -0
- package/dist/tools/apply-patch.js +2 -1
- package/dist/tools/callers.d.ts +6 -0
- package/dist/tools/callers.js +23 -0
- package/dist/tools/code-mode/engine.d.ts +43 -0
- package/dist/tools/code-mode/engine.js +1 -0
- package/dist/tools/code-mode/quickjs-engine.d.ts +2 -0
- package/dist/tools/code-mode/quickjs-engine.js +454 -0
- package/dist/tools/code-mode/state.d.ts +20 -0
- package/dist/tools/code-mode/state.js +82 -0
- package/dist/tools/code-mode/worker.d.ts +1 -0
- package/dist/tools/code-mode/worker.js +65 -0
- package/dist/tools/code-mode.d.ts +8 -4
- package/dist/tools/code-mode.js +154 -120
- package/dist/tools/exec.js +1 -0
- package/dist/tools/execution-limits.d.ts +8 -0
- package/dist/tools/execution-limits.js +8 -0
- package/dist/tools/local-registry.d.ts +2 -0
- package/dist/tools/local-registry.js +1 -1
- package/dist/tools/question.js +1 -0
- package/dist/tools/read.js +1 -0
- package/dist/tools/registry.js +3 -0
- package/dist/tools/runtime-context.d.ts +2 -0
- package/dist/tools/runtime.js +3 -1
- package/dist/tools/skill.js +1 -0
- package/dist/tools/todo-read.js +1 -0
- package/dist/tools/todo-write.js +1 -0
- package/dist/tools/tool-search.d.ts +43 -0
- package/dist/tools/tool-search.js +284 -0
- package/dist/tools/view-image.js +1 -0
- package/dist/tools/web-fetch.js +1 -0
- package/dist/tools/web-search.js +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { MarAgentError } from '../error.js';
|
|
2
|
+
export const MCP_DEFINITION_LIMITS = Object.freeze({
|
|
3
|
+
depth: 64,
|
|
4
|
+
nodes: 32768,
|
|
5
|
+
definitionBytes: 256 * 1024,
|
|
6
|
+
catalogBytes: 16 * 1024 * 1024,
|
|
7
|
+
tools: 10000,
|
|
8
|
+
servers: 256
|
|
9
|
+
});
|
|
10
|
+
function invalid() {
|
|
11
|
+
throw new MarAgentError('MAR_AGENT_MCP_CATALOG_INVALID', 'MCP catalog contains invalid or oversized definitions.');
|
|
12
|
+
}
|
|
13
|
+
const record = (value) => value !== null &&
|
|
14
|
+
typeof value === 'object' &&
|
|
15
|
+
!Array.isArray(value) &&
|
|
16
|
+
[Object.prototype, null].includes(Object.getPrototypeOf(value));
|
|
17
|
+
const text = (value, maximum) => typeof value === 'string' && value.length > 0 && value.length <= maximum;
|
|
18
|
+
const onlyKeys = (value, keys) => Object.keys(value).every((key) => keys.includes(key));
|
|
19
|
+
/** Bound all JSON, including unknown extension keywords, before recursive lowering. */
|
|
20
|
+
function assertJson(value) {
|
|
21
|
+
const stack = [{ value, depth: 0 }];
|
|
22
|
+
const seen = new Set();
|
|
23
|
+
let nodes = 0;
|
|
24
|
+
while (stack.length) {
|
|
25
|
+
const current = stack.pop();
|
|
26
|
+
if (current.exit) {
|
|
27
|
+
seen.delete(current.value);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (++nodes > MCP_DEFINITION_LIMITS.nodes || current.depth > MCP_DEFINITION_LIMITS.depth)
|
|
31
|
+
invalid();
|
|
32
|
+
const item = current.value;
|
|
33
|
+
if (item === null ||
|
|
34
|
+
typeof item === 'string' ||
|
|
35
|
+
typeof item === 'boolean' ||
|
|
36
|
+
(typeof item === 'number' && Number.isFinite(item)))
|
|
37
|
+
continue;
|
|
38
|
+
if (!Array.isArray(item) && !record(item))
|
|
39
|
+
invalid();
|
|
40
|
+
if (seen.has(item))
|
|
41
|
+
invalid();
|
|
42
|
+
seen.add(item);
|
|
43
|
+
stack.push({ value: item, depth: current.depth, exit: true });
|
|
44
|
+
for (const child of Object.values(item))
|
|
45
|
+
stack.push({ value: child, depth: current.depth + 1 });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function assertSchema(schema) {
|
|
49
|
+
if (!record(schema) || schema.type !== 'object')
|
|
50
|
+
invalid();
|
|
51
|
+
assertJson(schema);
|
|
52
|
+
const stack = [schema];
|
|
53
|
+
const types = new Set(['null', 'boolean', 'object', 'array', 'number', 'integer', 'string']);
|
|
54
|
+
while (stack.length) {
|
|
55
|
+
const value = stack.pop();
|
|
56
|
+
if (typeof value === 'boolean')
|
|
57
|
+
continue;
|
|
58
|
+
if (!record(value))
|
|
59
|
+
invalid();
|
|
60
|
+
if (value.type !== undefined) {
|
|
61
|
+
const entries = Array.isArray(value.type) ? value.type : [value.type];
|
|
62
|
+
if (!entries.length ||
|
|
63
|
+
new Set(entries).size !== entries.length ||
|
|
64
|
+
entries.some((type) => typeof type !== 'string' || !types.has(type)))
|
|
65
|
+
invalid();
|
|
66
|
+
}
|
|
67
|
+
if (value.required !== undefined &&
|
|
68
|
+
(!Array.isArray(value.required) ||
|
|
69
|
+
value.required.some((name) => typeof name !== 'string') ||
|
|
70
|
+
new Set(value.required).size !== value.required.length))
|
|
71
|
+
invalid();
|
|
72
|
+
for (const key of [
|
|
73
|
+
'properties',
|
|
74
|
+
'patternProperties',
|
|
75
|
+
'$defs',
|
|
76
|
+
'definitions',
|
|
77
|
+
'dependentSchemas'
|
|
78
|
+
]) {
|
|
79
|
+
const children = value[key];
|
|
80
|
+
if (children === undefined)
|
|
81
|
+
continue;
|
|
82
|
+
if (!record(children))
|
|
83
|
+
invalid();
|
|
84
|
+
stack.push(...Object.values(children));
|
|
85
|
+
}
|
|
86
|
+
for (const key of ['allOf', 'anyOf', 'oneOf', 'prefixItems']) {
|
|
87
|
+
const children = value[key];
|
|
88
|
+
if (children === undefined)
|
|
89
|
+
continue;
|
|
90
|
+
if (!Array.isArray(children) || !children.length)
|
|
91
|
+
invalid();
|
|
92
|
+
stack.push(...children);
|
|
93
|
+
}
|
|
94
|
+
for (const key of [
|
|
95
|
+
'additionalProperties',
|
|
96
|
+
'unevaluatedProperties',
|
|
97
|
+
'additionalItems',
|
|
98
|
+
'unevaluatedItems',
|
|
99
|
+
'contains',
|
|
100
|
+
'propertyNames',
|
|
101
|
+
'not',
|
|
102
|
+
'if',
|
|
103
|
+
'then',
|
|
104
|
+
'else',
|
|
105
|
+
'items'
|
|
106
|
+
]) {
|
|
107
|
+
const child = value[key];
|
|
108
|
+
if (child !== undefined) {
|
|
109
|
+
if (key === 'items' && Array.isArray(child))
|
|
110
|
+
stack.push(...child);
|
|
111
|
+
else
|
|
112
|
+
stack.push(child);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
for (const key of ['$ref', '$id', '$schema', 'title', 'description', 'format', 'pattern'])
|
|
116
|
+
if (value[key] !== undefined && typeof value[key] !== 'string')
|
|
117
|
+
invalid();
|
|
118
|
+
for (const key of ['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum', 'multipleOf'])
|
|
119
|
+
if (value[key] !== undefined &&
|
|
120
|
+
(typeof value[key] !== 'number' ||
|
|
121
|
+
!Number.isFinite(value[key]) ||
|
|
122
|
+
(key === 'multipleOf' && Number(value[key]) <= 0)))
|
|
123
|
+
invalid();
|
|
124
|
+
for (const key of [
|
|
125
|
+
'minLength',
|
|
126
|
+
'maxLength',
|
|
127
|
+
'minItems',
|
|
128
|
+
'maxItems',
|
|
129
|
+
'minProperties',
|
|
130
|
+
'maxProperties'
|
|
131
|
+
])
|
|
132
|
+
if (value[key] !== undefined && (!Number.isSafeInteger(value[key]) || Number(value[key]) < 0))
|
|
133
|
+
invalid();
|
|
134
|
+
if (value.enum !== undefined && (!Array.isArray(value.enum) || !value.enum.length))
|
|
135
|
+
invalid();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** Shared Host and Node publication boundary, not a provider-specific conversion. */
|
|
139
|
+
export function assertMcpToolDefinition(value) {
|
|
140
|
+
if (!record(value) ||
|
|
141
|
+
!text(value.name, 256) ||
|
|
142
|
+
!/^mcp__[A-Za-z0-9_-]+__[A-Za-z0-9_-]+$/u.test(value.name) ||
|
|
143
|
+
typeof value.description !== 'string' ||
|
|
144
|
+
value.description.length > 65536 ||
|
|
145
|
+
(value.parallelSafety !== undefined &&
|
|
146
|
+
!['safe', 'serial'].includes(String(value.parallelSafety))) ||
|
|
147
|
+
(value.agentScope !== undefined &&
|
|
148
|
+
!['ROOT_AND_SUBAGENTS', 'ROOT_ONLY'].includes(String(value.agentScope))) ||
|
|
149
|
+
(value.allowedCallers !== undefined &&
|
|
150
|
+
(!Array.isArray(value.allowedCallers) ||
|
|
151
|
+
!value.allowedCallers.length ||
|
|
152
|
+
value.allowedCallers.some((caller) => caller !== 'direct' && caller !== 'programmatic'))) ||
|
|
153
|
+
(value.inputMode !== undefined && value.inputMode !== 'json'))
|
|
154
|
+
invalid();
|
|
155
|
+
assertJson(value);
|
|
156
|
+
assertSchema(value.inputSchema);
|
|
157
|
+
if (value.outputSchema !== undefined)
|
|
158
|
+
assertSchema(value.outputSchema);
|
|
159
|
+
if (Buffer.byteLength(JSON.stringify(value)) > MCP_DEFINITION_LIMITS.definitionBytes)
|
|
160
|
+
invalid();
|
|
161
|
+
}
|
|
162
|
+
export function assertMcpToolCatalog(value) {
|
|
163
|
+
if (!record(value) ||
|
|
164
|
+
typeof value.revision !== 'string' ||
|
|
165
|
+
value.revision.length > 512 ||
|
|
166
|
+
!onlyKeys(value, ['revision', 'servers', 'tools']) ||
|
|
167
|
+
!Array.isArray(value.servers) ||
|
|
168
|
+
value.servers.length > MCP_DEFINITION_LIMITS.servers ||
|
|
169
|
+
!Array.isArray(value.tools) ||
|
|
170
|
+
value.tools.length > MCP_DEFINITION_LIMITS.tools)
|
|
171
|
+
invalid();
|
|
172
|
+
const servers = new Set();
|
|
173
|
+
for (const server of value.servers) {
|
|
174
|
+
if (!record(server) ||
|
|
175
|
+
!text(server.id, 512) ||
|
|
176
|
+
!text(server.name, 256) ||
|
|
177
|
+
servers.has(server.id) ||
|
|
178
|
+
!onlyKeys(server, ['id', 'name', 'description', 'instructions']) ||
|
|
179
|
+
(server.description !== undefined &&
|
|
180
|
+
(typeof server.description !== 'string' || server.description.length > 65536)) ||
|
|
181
|
+
(server.instructions !== undefined &&
|
|
182
|
+
(typeof server.instructions !== 'string' || server.instructions.length > 65536)))
|
|
183
|
+
invalid();
|
|
184
|
+
servers.add(server.id);
|
|
185
|
+
}
|
|
186
|
+
const names = new Set();
|
|
187
|
+
for (const tool of value.tools) {
|
|
188
|
+
if (!record(tool) ||
|
|
189
|
+
!text(tool.serverId, 512) ||
|
|
190
|
+
!servers.has(tool.serverId) ||
|
|
191
|
+
!onlyKeys(tool, [
|
|
192
|
+
'definition',
|
|
193
|
+
'serverId',
|
|
194
|
+
'rawName',
|
|
195
|
+
'sourceIdentity',
|
|
196
|
+
'definitionRevision',
|
|
197
|
+
'lifetime',
|
|
198
|
+
'exposure',
|
|
199
|
+
'annotations'
|
|
200
|
+
]) ||
|
|
201
|
+
!text(tool.rawName, 256) ||
|
|
202
|
+
!text(tool.sourceIdentity, 512) ||
|
|
203
|
+
!text(tool.definitionRevision, 512) ||
|
|
204
|
+
!['session', 'execution'].includes(String(tool.lifetime)) ||
|
|
205
|
+
!['direct', 'deferred'].includes(String(tool.exposure)))
|
|
206
|
+
invalid();
|
|
207
|
+
assertMcpToolDefinition(tool.definition);
|
|
208
|
+
if (tool.annotations !== undefined) {
|
|
209
|
+
if (!record(tool.annotations))
|
|
210
|
+
invalid();
|
|
211
|
+
assertJson(tool.annotations);
|
|
212
|
+
}
|
|
213
|
+
if (names.has(tool.definition.name))
|
|
214
|
+
invalid();
|
|
215
|
+
names.add(tool.definition.name);
|
|
216
|
+
}
|
|
217
|
+
if (Buffer.byteLength(JSON.stringify(value)) > MCP_DEFINITION_LIMITS.catalogBytes)
|
|
218
|
+
invalid();
|
|
219
|
+
}
|
package/dist/sdk/types.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { HostDescription } from '../host/contracts.js';
|
|
2
2
|
import type { MarAgentModelConfiguration, MarAgentReasoningEffort } from '../model/configuration.js';
|
|
3
|
+
import type { ClientToolDefinition } from '../model/contracts.js';
|
|
3
4
|
import type { MarAgentSessionPage, MarAgentSessionSummary } from '../session/catalog.js';
|
|
4
5
|
import type { SessionListInput, SessionSource } from '../session/types.js';
|
|
6
|
+
import type { CodeModeStateEntry, CodeModeStateMutation } from '../tools/code-mode/state.js';
|
|
5
7
|
/** @deprecated MAR Agent always executes tools directly through the Host. */
|
|
6
8
|
export type AccessMode = 'full-access';
|
|
7
9
|
export type ExecutionMode = 'run' | 'plan' | 'compact';
|
|
@@ -22,6 +24,8 @@ export interface ToolExecutionContext {
|
|
|
22
24
|
codeMode?: {
|
|
23
25
|
supportsImages?: boolean;
|
|
24
26
|
tools: readonly import('../model/contracts.js').ClientToolDefinition[];
|
|
27
|
+
state?: readonly CodeModeStateEntry[];
|
|
28
|
+
commitState?(mutations: readonly CodeModeStateMutation[]): Promise<readonly CodeModeStateEntry[]>;
|
|
25
29
|
invoke(call: ToolCall, signal: AbortSignal): Promise<ToolExecutionResult>;
|
|
26
30
|
};
|
|
27
31
|
}
|
|
@@ -29,6 +33,34 @@ export interface McpToolCatalogContext extends Pick<ToolExecutionContext, 'sessi
|
|
|
29
33
|
executionScopeId?: string;
|
|
30
34
|
claimExecutionScope?: boolean;
|
|
31
35
|
}
|
|
36
|
+
export interface McpToolCatalog {
|
|
37
|
+
readonly revision: string;
|
|
38
|
+
readonly servers: readonly {
|
|
39
|
+
readonly id: string;
|
|
40
|
+
readonly name: string;
|
|
41
|
+
readonly description?: string;
|
|
42
|
+
readonly instructions?: string;
|
|
43
|
+
}[];
|
|
44
|
+
readonly tools: readonly McpCatalogTool[];
|
|
45
|
+
}
|
|
46
|
+
export interface McpCatalogTool {
|
|
47
|
+
readonly definition: import('../model/contracts.js').ClientToolDefinition;
|
|
48
|
+
readonly serverId: string;
|
|
49
|
+
readonly rawName: string;
|
|
50
|
+
readonly sourceIdentity: string;
|
|
51
|
+
readonly definitionRevision: string;
|
|
52
|
+
readonly lifetime: 'session' | 'execution';
|
|
53
|
+
readonly exposure: 'direct' | 'deferred';
|
|
54
|
+
/** Untrusted MCP hints; never an authorization or parallel-safety decision. */
|
|
55
|
+
readonly annotations?: Readonly<Record<string, unknown>>;
|
|
56
|
+
}
|
|
57
|
+
export interface McpToolCallContext extends Pick<ToolExecutionContext, 'sessionId' | 'executionId'> {
|
|
58
|
+
readonly mode?: ExecutionMode;
|
|
59
|
+
readonly expectedTool?: {
|
|
60
|
+
readonly sourceIdentity: string;
|
|
61
|
+
readonly definitionRevision: string;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
32
64
|
export interface HostSkill {
|
|
33
65
|
readonly name: string;
|
|
34
66
|
readonly description: string;
|
|
@@ -51,6 +83,8 @@ export type MarAgentToolArtifact = MarAgentToolImageArtifact;
|
|
|
51
83
|
export interface ToolExecutionResult {
|
|
52
84
|
content: string;
|
|
53
85
|
data?: unknown;
|
|
86
|
+
/** Runtime-only definitions used to encode a Tool Search output. */
|
|
87
|
+
toolSearchTools?: readonly ClientToolDefinition[];
|
|
54
88
|
truncated?: boolean;
|
|
55
89
|
images?: readonly {
|
|
56
90
|
mimeType: string;
|
|
@@ -78,7 +112,8 @@ export interface MarAgentHost {
|
|
|
78
112
|
questions: unknown[];
|
|
79
113
|
}, signal: AbortSignal): Promise<Record<string, unknown>>;
|
|
80
114
|
listMcpTools?(context?: McpToolCatalogContext): Promise<readonly import('../model/contracts.js').ClientToolDefinition[]>;
|
|
81
|
-
|
|
115
|
+
listMcpToolCatalog?(context: McpToolCatalogContext): Promise<McpToolCatalog>;
|
|
116
|
+
callMcpTool?(call: ToolCall, signal: AbortSignal, context?: McpToolCallContext): Promise<ToolExecutionResult>;
|
|
82
117
|
listSessionResources?(sessionId: string): Promise<HostSessionResourceSnapshot>;
|
|
83
118
|
disposeSessionResources?(sessionId: string): Promise<void>;
|
|
84
119
|
dispose(): Promise<void>;
|
|
@@ -106,6 +141,20 @@ export type MarAgentEvent = MarAgentEventBase & ({
|
|
|
106
141
|
text: string;
|
|
107
142
|
modelId: string;
|
|
108
143
|
modelName: string;
|
|
144
|
+
} | {
|
|
145
|
+
type: 'model.performance';
|
|
146
|
+
latencyMs: number;
|
|
147
|
+
outputTokens?: number | undefined;
|
|
148
|
+
outputDurationMs?: number | undefined;
|
|
149
|
+
tokensPerSecond?: number | undefined;
|
|
150
|
+
} | {
|
|
151
|
+
/** Legacy history event; new executions publish model.performance instead. */
|
|
152
|
+
type: 'message.performance';
|
|
153
|
+
itemId: string;
|
|
154
|
+
ttftMs: number;
|
|
155
|
+
outputTokens?: number | undefined;
|
|
156
|
+
outputDurationMs?: number | undefined;
|
|
157
|
+
tokensPerSecond?: number | undefined;
|
|
109
158
|
} | {
|
|
110
159
|
type: 'plan.completed';
|
|
111
160
|
itemId: string;
|
|
@@ -137,11 +186,13 @@ export type MarAgentEvent = MarAgentEventBase & ({
|
|
|
137
186
|
type: 'tool.started';
|
|
138
187
|
toolName: string;
|
|
139
188
|
callId: string;
|
|
189
|
+
parentCallId?: string;
|
|
140
190
|
input?: unknown;
|
|
141
191
|
} | {
|
|
142
192
|
type: 'tool.completed';
|
|
143
193
|
toolName: string;
|
|
144
194
|
callId: string;
|
|
195
|
+
parentCallId?: string;
|
|
145
196
|
summary: string;
|
|
146
197
|
truncated?: boolean;
|
|
147
198
|
artifacts?: readonly MarAgentToolArtifact[];
|
|
@@ -150,6 +201,7 @@ export type MarAgentEvent = MarAgentEventBase & ({
|
|
|
150
201
|
type: 'tool.failed';
|
|
151
202
|
toolName: string;
|
|
152
203
|
callId: string;
|
|
204
|
+
parentCallId?: string;
|
|
153
205
|
code: string;
|
|
154
206
|
message?: string;
|
|
155
207
|
} | {
|
|
@@ -50,7 +50,7 @@ export type ContextGcReplacement = {
|
|
|
50
50
|
readonly evidenceSequences: readonly number[];
|
|
51
51
|
} | {
|
|
52
52
|
readonly kind: 'TOOL_CALL_INPUT';
|
|
53
|
-
readonly toolName: 'apply_patch' | 'exec';
|
|
53
|
+
readonly toolName: 'apply_patch' | 'exec' | 'code_mode';
|
|
54
54
|
readonly callId: string;
|
|
55
55
|
readonly target: ContextGcTarget;
|
|
56
56
|
readonly replacementArguments: unknown;
|
|
@@ -62,6 +62,12 @@ export type ContextGcReplacement = {
|
|
|
62
62
|
readonly target: ContextGcTarget;
|
|
63
63
|
readonly replacementContent: string;
|
|
64
64
|
readonly evidenceSequence: number;
|
|
65
|
+
} | {
|
|
66
|
+
readonly kind: 'CODE_MODE_OUTPUT';
|
|
67
|
+
readonly callId: string;
|
|
68
|
+
readonly target: ContextGcTarget;
|
|
69
|
+
readonly replacementContent: string;
|
|
70
|
+
readonly evidenceSequence: number;
|
|
65
71
|
} | {
|
|
66
72
|
readonly kind: 'SUBAGENT_OUTPUT';
|
|
67
73
|
readonly toolName: 'agent_wait' | 'agent_message' | 'agent_cancel';
|
|
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { posix, win32 } from 'node:path';
|
|
3
3
|
import { isDeepStrictEqual } from 'node:util';
|
|
4
4
|
import { parseReadContextOutput } from '../tools/read-context.js';
|
|
5
|
-
import { contextGcApplyPatchInputReplacement,
|
|
5
|
+
import { contextGcApplyPatchInputReplacement, contextGcCodeModeInputReplacement, contextGcCodeModeOutputReplacement, contextGcExecInputReplacement, contextGcExecOutputReplacement, contextGcPatchResultFromCompletedPayload, contextGcStaleReadReplacement, contextGcWebOutputReplacement, normalizeContextGcCodeModeInput, normalizeContextGcExecResult } from './context-gc-replacements.js';
|
|
6
6
|
import { contextGcSubagentNeedsRetainedPriorFinal, contextGcSubagentOutputReplacement, contextGcSubagentSnapshotIsPreciselyCovered, contextGcSubagentSnapshotIsSuperseded, normalizeContextGcSubagentSnapshot } from './context-gc-subagent.js';
|
|
7
7
|
export const CONTEXT_GC_CHECKPOINT_VERSION = 1;
|
|
8
8
|
export const CONTEXT_GC_MAX_TARGETS = 4_096;
|
|
@@ -266,6 +266,25 @@ function replacementMatchesRecords(replacement, records, recordsBySequence, work
|
|
|
266
266
|
result !== undefined &&
|
|
267
267
|
isDeepStrictEqual(replacement.replacementArguments, contextGcApplyPatchInputReplacement(source.payload.arguments, result, replacement.evidenceSequence, applyPatchReplacementFormatVersion(replacement.replacementArguments))));
|
|
268
268
|
}
|
|
269
|
+
if (replacement.kind === 'TOOL_CALL_INPUT' && replacement.toolName === 'code_mode') {
|
|
270
|
+
const evidence = successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, 'code_mode');
|
|
271
|
+
const input = normalizeContextGcCodeModeInput(source.payload.arguments);
|
|
272
|
+
return (evidence?.callId === replacement.callId &&
|
|
273
|
+
evidence.call.sequence === replacement.target.sequence &&
|
|
274
|
+
input !== undefined &&
|
|
275
|
+
isDeepStrictEqual(replacement.replacementArguments, contextGcCodeModeInputReplacement(input)));
|
|
276
|
+
}
|
|
277
|
+
if (replacement.kind === 'CODE_MODE_OUTPUT') {
|
|
278
|
+
const evidence = successfulToolEvidence(records, recordsBySequence, replacement.evidenceSequence, 'code_mode');
|
|
279
|
+
return (evidence?.callId === replacement.callId &&
|
|
280
|
+
evidence.result.sequence === replacement.target.sequence &&
|
|
281
|
+
isRecord(evidence.result.payload) &&
|
|
282
|
+
typeof evidence.result.payload.content === 'string' &&
|
|
283
|
+
isRecord(evidence.completed.payload) &&
|
|
284
|
+
typeof evidence.completed.payload.truncated === 'boolean' &&
|
|
285
|
+
replacement.replacementContent ===
|
|
286
|
+
contextGcCodeModeOutputReplacement(evidence.result.payload.content, evidence.completed.payload.truncated));
|
|
287
|
+
}
|
|
269
288
|
if (replacement.kind === 'SUBAGENT_OUTPUT') {
|
|
270
289
|
const retainedPriorFinal = replacement.retainedPriorFinal;
|
|
271
290
|
const sourceEvidence = successfulToolEvidence(records, recordsBySequence, source.sequence, replacement.toolName);
|
|
@@ -605,7 +624,9 @@ function isContextGcReplacement(value) {
|
|
|
605
624
|
'evidenceSequence'
|
|
606
625
|
]) &&
|
|
607
626
|
value.target.role === 'tool_call' &&
|
|
608
|
-
(value.toolName === 'apply_patch' ||
|
|
627
|
+
(value.toolName === 'apply_patch' ||
|
|
628
|
+
value.toolName === 'exec' ||
|
|
629
|
+
value.toolName === 'code_mode') &&
|
|
609
630
|
value.target.toolName === value.toolName &&
|
|
610
631
|
Array.isArray(value.providerAliasTargets) &&
|
|
611
632
|
value.providerAliasTargets.every((target) => isContextGcTarget(target) &&
|
|
@@ -661,6 +682,11 @@ function isContextGcReplacement(value) {
|
|
|
661
682
|
(value.target.toolName === undefined || value.target.toolName === value.toolName) &&
|
|
662
683
|
isNonemptyString(value.replacementContent) &&
|
|
663
684
|
isPositiveInteger(value.evidenceSequence));
|
|
685
|
+
if (value.kind === 'CODE_MODE_OUTPUT')
|
|
686
|
+
return (hasOnlyKeys(value, ['kind', 'callId', 'target', 'replacementContent', 'evidenceSequence']) &&
|
|
687
|
+
value.target.role === 'tool' &&
|
|
688
|
+
isNonemptyString(value.replacementContent) &&
|
|
689
|
+
isPositiveInteger(value.evidenceSequence));
|
|
664
690
|
return (value.kind === 'EXEC_OUTPUT' &&
|
|
665
691
|
hasOnlyKeys(value, ['kind', 'callId', 'target', 'replacementContent', 'evidenceSequence']) &&
|
|
666
692
|
value.target.role === 'tool' &&
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export type ContextGcWebToolName = 'web_search' | 'web_fetch';
|
|
2
|
+
export interface ContextGcCodeModeInput {
|
|
3
|
+
readonly code: string;
|
|
4
|
+
readonly inputMode: 'freeform' | 'json';
|
|
5
|
+
}
|
|
6
|
+
export declare function normalizeContextGcCodeModeInput(arguments_: unknown): ContextGcCodeModeInput | undefined;
|
|
7
|
+
export declare function contextGcCodeModeInputReplacement(input: ContextGcCodeModeInput): unknown;
|
|
8
|
+
export declare function contextGcCodeModeOutputReplacement(originalContent: string, truncated: boolean): string;
|
|
2
9
|
export interface ContextGcPatchFile {
|
|
3
10
|
readonly path: string;
|
|
4
11
|
readonly targetPath?: string;
|
|
@@ -3,6 +3,32 @@ const RAW_MODEL_CONTEXT_PREVIEW_CHARACTERS = 256;
|
|
|
3
3
|
const EXEC_INPUT_COMPLETE_CHARACTERS = 512;
|
|
4
4
|
const EXEC_INPUT_PREVIEW_CHARACTERS = 256;
|
|
5
5
|
const EXEC_OUTPUT_PREVIEW_CHARACTERS = 512;
|
|
6
|
+
const CODE_MODE_OUTPUT_PREVIEW_CHARACTERS = 512;
|
|
7
|
+
export function normalizeContextGcCodeModeInput(arguments_) {
|
|
8
|
+
if (typeof arguments_ === 'string' && arguments_.length > 0)
|
|
9
|
+
return { code: arguments_, inputMode: 'freeform' };
|
|
10
|
+
if (isRecord(arguments_) &&
|
|
11
|
+
Object.keys(arguments_).length === 1 &&
|
|
12
|
+
typeof arguments_.code === 'string' &&
|
|
13
|
+
arguments_.code.length > 0)
|
|
14
|
+
return { code: arguments_.code, inputMode: 'json' };
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
export function contextGcCodeModeInputReplacement(input) {
|
|
18
|
+
const replacement = `<context_gc kind="completed_code_mode_input" originalBytes="${Buffer.byteLength(input.code)}" lines="${lineCount(input.code)}" inputMode="${input.inputMode}">Completed Code Mode source was reclaimed after six successful rollouts. The paired output, images, completion evidence, and nested tool side effects remain in history.</context_gc>`;
|
|
19
|
+
return input.inputMode === 'freeform' ? replacement : { code: replacement };
|
|
20
|
+
}
|
|
21
|
+
export function contextGcCodeModeOutputReplacement(originalContent, truncated) {
|
|
22
|
+
const preview = unicodeHeadTail(originalContent, CODE_MODE_OUTPUT_PREVIEW_CHARACTERS);
|
|
23
|
+
return JSON.stringify({
|
|
24
|
+
originalBytes: Buffer.byteLength(originalContent),
|
|
25
|
+
lines: lineCount(originalContent),
|
|
26
|
+
truncated,
|
|
27
|
+
preview,
|
|
28
|
+
previewComplete: !truncated && preview === originalContent,
|
|
29
|
+
contextGc: 'Detailed Code Mode output was reclaimed after six successful rollouts. An incomplete preview is not the complete cell output. The source was not replayed and nested tool side effects were not changed.'
|
|
30
|
+
});
|
|
31
|
+
}
|
|
6
32
|
export function contextGcPatchResultFromCompletedPayload(payload) {
|
|
7
33
|
if (!isRecord(payload) || payload.type !== 'tool.completed')
|
|
8
34
|
return undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ModelUsage } from '../model/contracts.js';
|
|
2
2
|
import type { EnvironmentContextState } from '../prompts/core.js';
|
|
3
|
+
import { type CodeModeStateEntry } from '../tools/code-mode/state.js';
|
|
3
4
|
import { type TodoSnapshot } from '../tools/todo.js';
|
|
4
5
|
import { type ContextGcCheckpointDraft } from './context-gc-checkpoint.js';
|
|
5
6
|
import type { SessionListInput, SessionSource } from './types.js';
|
|
@@ -146,6 +147,11 @@ export declare class JsonlSessionStore {
|
|
|
146
147
|
timestamp?: string;
|
|
147
148
|
}): Promise<SessionRecord>;
|
|
148
149
|
read(sessionId: string): Promise<SessionRecord[]>;
|
|
150
|
+
readCodeModeState(sessionId: string): Promise<CodeModeStateEntry[]>;
|
|
151
|
+
commitCodeModeState(sessionId: string, mutations: unknown, input: {
|
|
152
|
+
readonly executionId: string;
|
|
153
|
+
readonly turnId: string;
|
|
154
|
+
}): Promise<CodeModeStateEntry[]>;
|
|
149
155
|
readExecutionState(sessionId: string): Promise<SessionExecutionState>;
|
|
150
156
|
readContext(sessionId: string): Promise<SessionRecord[]>;
|
|
151
157
|
contextGcProjectionHash(sessionId: string, boundarySequence: number): Promise<string>;
|