@myagentroam/agent 0.9.159 → 0.9.161
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/agent-core/extensions/agents.d.ts +69 -0
- package/dist/agent-core/extensions/agents.js +160 -0
- package/dist/agent-core/extensions/contracts.d.ts +260 -0
- package/dist/agent-core/extensions/contracts.js +1 -0
- package/dist/agent-core/extensions/data.d.ts +84 -0
- package/dist/agent-core/extensions/data.js +83 -0
- package/dist/agent-core/extensions/environment.d.ts +27 -0
- package/dist/agent-core/extensions/environment.js +52 -0
- package/dist/agent-core/extensions/files.d.ts +64 -0
- package/dist/agent-core/extensions/files.js +26 -0
- package/dist/agent-core/extensions/hooks.d.ts +32 -0
- package/dist/agent-core/extensions/hooks.js +135 -0
- package/dist/agent-core/extensions/manager.d.ts +39 -0
- package/dist/agent-core/extensions/manager.js +458 -0
- package/dist/agent-core/extensions/mcp.d.ts +99 -0
- package/dist/agent-core/extensions/mcp.js +433 -0
- package/dist/agent-core/extensions/model-request.d.ts +60 -0
- package/dist/agent-core/extensions/model-request.js +167 -0
- package/dist/agent-core/extensions/model-stream.d.ts +9 -0
- package/dist/agent-core/extensions/model-stream.js +101 -0
- package/dist/agent-core/extensions/models.d.ts +43 -0
- package/dist/agent-core/extensions/models.js +1 -0
- package/dist/agent-core/extensions/network.d.ts +32 -0
- package/dist/agent-core/extensions/network.js +97 -0
- package/dist/agent-core/extensions/operation.d.ts +29 -0
- package/dist/agent-core/extensions/operation.js +98 -0
- package/dist/agent-core/extensions/processes.d.ts +52 -0
- package/dist/agent-core/extensions/processes.js +283 -0
- package/dist/agent-core/extensions/prompt-composition.d.ts +24 -0
- package/dist/agent-core/extensions/prompt-composition.js +106 -0
- package/dist/agent-core/extensions/prompt-input.d.ts +23 -0
- package/dist/agent-core/extensions/prompt-input.js +46 -0
- package/dist/agent-core/extensions/resources.d.ts +27 -0
- package/dist/agent-core/extensions/resources.js +221 -0
- package/dist/agent-core/extensions/services.d.ts +31 -0
- package/dist/agent-core/extensions/services.js +61 -0
- package/dist/agent-core/extensions/session-input.d.ts +151 -0
- package/dist/agent-core/extensions/session-input.js +351 -0
- package/dist/agent-core/extensions/session-messages.d.ts +41 -0
- package/dist/agent-core/extensions/session-messages.js +76 -0
- package/dist/agent-core/extensions/session.d.ts +68 -0
- package/dist/agent-core/extensions/session.js +593 -0
- package/dist/agent-core/extensions/settings.d.ts +59 -0
- package/dist/agent-core/extensions/settings.js +115 -0
- package/dist/agent-core/extensions/store.d.ts +10 -0
- package/dist/agent-core/extensions/store.js +131 -0
- package/dist/agent-core/extensions/tools.d.ts +61 -0
- package/dist/agent-core/extensions/tools.js +266 -0
- package/dist/agent-core/host/contracts.d.ts +25 -0
- package/dist/agent-core/host/local-agent-host.d.ts +10 -1
- package/dist/agent-core/host/local-agent-host.js +132 -20
- package/dist/agent-core/host/local-file-access.d.ts +30 -0
- package/dist/agent-core/host/local-file-access.js +107 -0
- package/dist/agent-core/host/local-network-access.d.ts +6 -0
- package/dist/agent-core/host/local-network-access.js +97 -0
- package/dist/agent-core/host/local-process-executor.d.ts +5 -1
- package/dist/agent-core/host/local-session-environment.d.ts +18 -0
- package/dist/agent-core/host/local-session-environment.js +102 -0
- package/dist/agent-core/model/anthropic-messages.js +73 -23
- package/dist/agent-core/model/contracts.d.ts +12 -0
- package/dist/agent-core/model/openai-responses.js +73 -12
- package/dist/agent-core/model/text-blocks.d.ts +7 -0
- package/dist/agent-core/model/text-blocks.js +40 -0
- package/dist/agent-core/prompts/index.d.ts +6 -2
- package/dist/agent-core/prompts/index.js +38 -24
- package/dist/agent-core/runtime/context/gc-tokens.js +6 -1
- package/dist/agent-core/runtime/context/normalize.js +21 -1
- package/dist/agent-core/runtime/context/projection.js +35 -1
- package/dist/agent-core/runtime/context/request-budget.js +5 -2
- package/dist/agent-core/runtime/context/restore.d.ts +5 -0
- package/dist/agent-core/runtime/context/restore.js +47 -4
- package/dist/agent-core/runtime/context/usage.js +2 -3
- package/dist/agent-core/runtime/contracts.d.ts +2 -0
- package/dist/agent-core/runtime/execution.js +5 -2
- package/dist/agent-core/sdk/agent.js +177 -40
- package/dist/agent-core/sdk/extension-agents.d.ts +9 -0
- package/dist/agent-core/sdk/extension-agents.js +51 -0
- package/dist/agent-core/sdk/extension-models.d.ts +13 -0
- package/dist/agent-core/sdk/extension-models.js +128 -0
- package/dist/agent-core/sdk/extension-session.d.ts +30 -0
- package/dist/agent-core/sdk/extension-session.js +103 -0
- package/dist/agent-core/sdk/extension-tools.d.ts +16 -0
- package/dist/agent-core/sdk/extension-tools.js +126 -0
- package/dist/agent-core/sdk/session.d.ts +26 -2
- package/dist/agent-core/sdk/session.js +1215 -506
- package/dist/agent-core/sdk/types.d.ts +3 -0
- package/dist/agent-core/session/context-gc-checkpoint.js +1 -1
- package/dist/agent-core/session/jsonl-store.d.ts +28 -8
- package/dist/agent-core/session/jsonl-store.js +124 -84
- package/dist/agent-core/session/types.d.ts +4 -1
- package/dist/agent-core/storage/file-lock.d.ts +4 -0
- package/dist/agent-core/storage/file-lock.js +83 -0
- package/dist/agent-core/subagent/contracts.d.ts +11 -1
- package/dist/agent-core/subagent/scheduler.d.ts +8 -0
- package/dist/agent-core/subagent/session-controller.d.ts +22 -1
- package/dist/agent-core/subagent/session-controller.js +355 -98
- package/dist/agent-core/tools/agent/agent-start.d.ts +1 -1
- package/dist/agent-core/tools/code-mode/cells.js +5 -15
- package/dist/agent-core/tools/code-mode/dispatch.d.ts +1 -0
- package/dist/agent-core/tools/code-mode/dispatch.js +13 -1
- package/dist/agent-core/tools/code-mode/index.js +1 -2
- package/dist/agent-core/tools/code-mode/observations.d.ts +5 -0
- package/dist/agent-core/tools/code-mode/observations.js +16 -5
- package/dist/agent-core/tools/code-mode/quickjs-engine.js +10 -5
- package/dist/agent-core/tools/execution-limits.d.ts +0 -1
- package/dist/agent-core/tools/execution-limits.js +0 -1
- package/dist/agent-core/tools/execution.d.ts +11 -2
- package/dist/agent-core/tools/execution.js +58 -21
- package/dist/agent-core/tools/input.d.ts +2 -0
- package/dist/agent-core/tools/input.js +36 -0
- package/dist/agent-core/tools/local/apply-patch.js +21 -17
- package/dist/agent-core/tools/local/exec.d.ts +4 -4
- package/dist/agent-core/tools/local/exec.js +242 -63
- package/dist/agent-core/tools/local/registry.d.ts +6 -0
- package/dist/agent-core/tools/local/registry.js +60 -9
- package/dist/agent-core/tools/mcp/catalog.d.ts +6 -2
- package/dist/agent-core/tools/mcp/catalog.js +4 -4
- package/dist/agent-core/tools/mcp/history.js +2 -2
- package/dist/agent-core/tools/mcp/tool-search.d.ts +3 -1
- package/dist/agent-core/tools/mcp/tool-search.js +2 -2
- package/dist/agent-core/tools/output-projection.d.ts +3 -1
- package/dist/agent-core/tools/output-projection.js +11 -2
- package/dist/agent-core/tools/runtime-context.d.ts +1 -0
- package/dist/agent-core/tools/runtime.d.ts +3 -1
- package/dist/agent-core/tools/runtime.js +22 -7
- package/dist/agent-core/tools/scheduler.d.ts +2 -0
- package/dist/agent-core/tools/scheduler.js +43 -6
- package/dist/index.d.ts +21 -0
- package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +5 -2
- package/package.json +1 -1
|
@@ -8,5 +8,5 @@ export declare function agentStartToolDefinitionWithModels(models: readonly {
|
|
|
8
8
|
}[]): ClientToolDefinition;
|
|
9
9
|
export declare function executeAgentStart(arguments_: unknown, context: ToolRuntimeContext): Promise<{
|
|
10
10
|
content: string;
|
|
11
|
-
data: import("../../subagent/contracts.js").
|
|
11
|
+
data: import("../../subagent/contracts.js").SubagentRunResult;
|
|
12
12
|
}>;
|
|
@@ -22,26 +22,16 @@ function options(value, wait) {
|
|
|
22
22
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
23
23
|
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Code Exec options must be an object.');
|
|
24
24
|
const input = value;
|
|
25
|
-
const keys = wait
|
|
26
|
-
? ['cellId', 'yieldMs', 'maxOutputTokens', 'action']
|
|
27
|
-
: ['yieldMs', 'maxOutputTokens'];
|
|
28
|
-
if (Object.keys(input).some((key) => !keys.includes(key)))
|
|
29
|
-
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Unknown Code Exec option.');
|
|
30
25
|
const yieldMs = input.yieldMs ?? (wait ? limits.codeDefaultWaitYieldMs : limits.codeDefaultYieldMs);
|
|
31
|
-
const maxOutputTokens = input.maxOutputTokens ?? limits.codeMaxOutputTokens;
|
|
32
26
|
if (!Number.isSafeInteger(yieldMs) ||
|
|
33
27
|
Number(yieldMs) < 0 ||
|
|
34
28
|
Number(yieldMs) > (wait ? limits.codeMaxWaitYieldMs : limits.codeMaxYieldMs) ||
|
|
35
|
-
!Number.isSafeInteger(maxOutputTokens) ||
|
|
36
|
-
Number(maxOutputTokens) < 1 ||
|
|
37
|
-
Number(maxOutputTokens) > limits.codeMaxOutputTokens ||
|
|
38
29
|
(wait && (typeof input.cellId !== 'string' || !input.cellId)) ||
|
|
39
30
|
(input.action !== undefined && input.action !== 'poll' && input.action !== 'cancel'))
|
|
40
31
|
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Code Exec options are out of range.');
|
|
41
32
|
return {
|
|
42
33
|
cellId: input.cellId,
|
|
43
34
|
yieldMs: Number(yieldMs),
|
|
44
|
-
maxOutputTokens: Number(maxOutputTokens),
|
|
45
35
|
action: input.action ?? 'poll'
|
|
46
36
|
};
|
|
47
37
|
}
|
|
@@ -208,12 +198,12 @@ export class CodeCells {
|
|
|
208
198
|
throw new MarAgentError('MAR_AGENT_INPUT_CAPABILITY_UNSUPPORTED', 'The current model cannot receive this cell image output.');
|
|
209
199
|
if (cell.terminal) {
|
|
210
200
|
if (cell.terminal.observations) {
|
|
211
|
-
const bounded = boundObservations(cell.terminal.observations,
|
|
201
|
+
const bounded = boundObservations(cell.terminal.observations, limits.codeMaxOutputTokens);
|
|
212
202
|
const visible = selectObservationImages(bounded.observations, cell.terminal.images ?? []);
|
|
213
203
|
const original = cell.terminal.data;
|
|
214
204
|
const data = {
|
|
215
205
|
...original,
|
|
216
|
-
output: truncateModelText(original.output ?? '',
|
|
206
|
+
output: truncateModelText(original.output ?? '', limits.codeMaxOutputTokens).content,
|
|
217
207
|
observations: visible.observations,
|
|
218
208
|
cached: true,
|
|
219
209
|
truncated: cell.terminal.truncated === true || bounded.truncated
|
|
@@ -233,7 +223,7 @@ export class CodeCells {
|
|
|
233
223
|
};
|
|
234
224
|
}
|
|
235
225
|
const snapshot = cell.terminal.data;
|
|
236
|
-
const bounded = truncateModelText(snapshot.output,
|
|
226
|
+
const bounded = truncateModelText(snapshot.output, limits.codeMaxOutputTokens);
|
|
237
227
|
const data = {
|
|
238
228
|
...snapshot,
|
|
239
229
|
output: bounded.content,
|
|
@@ -251,7 +241,7 @@ export class CodeCells {
|
|
|
251
241
|
const images = cell.images.splice(0);
|
|
252
242
|
const text = renderCapturedText(captured, images.length);
|
|
253
243
|
cell.yielded = false;
|
|
254
|
-
const bounded = truncateModelText(text,
|
|
244
|
+
const bounded = truncateModelText(text, limits.codeMaxOutputTokens);
|
|
255
245
|
const pending = cell.observations.splice(0);
|
|
256
246
|
if (!pending.length && captured.text)
|
|
257
247
|
pending.push({
|
|
@@ -261,7 +251,7 @@ export class CodeCells {
|
|
|
261
251
|
value: captured.text,
|
|
262
252
|
truncated: captured.droppedBytes > 0
|
|
263
253
|
});
|
|
264
|
-
const structured = boundObservations(pending,
|
|
254
|
+
const structured = boundObservations(pending, limits.codeMaxOutputTokens);
|
|
265
255
|
const visible = selectObservationImages(structured.observations, images, cell.imageOffset);
|
|
266
256
|
const remapped = visible.observations;
|
|
267
257
|
const visibleImages = visible.images;
|
|
@@ -14,6 +14,7 @@ export declare function createCodeToolDispatch(input: {
|
|
|
14
14
|
expectations: ReadonlyMap<string, McpToolCallContext['expectedTool']>;
|
|
15
15
|
parentCallId: string;
|
|
16
16
|
schedule: ReturnType<typeof createSessionToolScheduler>;
|
|
17
|
+
assertLive: () => void;
|
|
17
18
|
emit: (event: Payload) => Promise<void>;
|
|
18
19
|
}): (call: ToolCall, signal: AbortSignal) => Promise<ToolExecutionResult>;
|
|
19
20
|
export {};
|
|
@@ -8,24 +8,29 @@ import { executeRegisteredTool } from '../runtime.js';
|
|
|
8
8
|
export function createCodeToolDispatch(input) {
|
|
9
9
|
const definitions = new Map(input.tools.map((tool) => [tool.name, tool]));
|
|
10
10
|
return async (call, signal) => {
|
|
11
|
+
input.assertLive();
|
|
11
12
|
const definition = definitions.get(call.name);
|
|
12
13
|
if (!definition || !allowsToolCaller(definition, 'programmatic'))
|
|
13
14
|
throw new MarAgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'The requested nested tool is unavailable.');
|
|
14
15
|
return input.schedule(definition, async () => {
|
|
16
|
+
input.assertLive();
|
|
15
17
|
signal.throwIfAborted();
|
|
16
18
|
const callId = call.callId ?? randomUUID();
|
|
17
19
|
const identity = { toolName: call.name, callId, parentCallId: input.parentCallId };
|
|
18
20
|
await input.emit({ type: 'tool.started', ...identity, input: call.arguments });
|
|
19
21
|
try {
|
|
22
|
+
input.assertLive();
|
|
23
|
+
signal.throwIfAborted();
|
|
20
24
|
const expected = input.expectations.get(call.name);
|
|
21
25
|
const result = await executeRegisteredTool(call.name, call.arguments, callId, {
|
|
22
26
|
...input.context,
|
|
23
27
|
signal,
|
|
24
28
|
...(expected ? { expectedMcpTool: expected } : {})
|
|
25
|
-
});
|
|
29
|
+
}, 'programmatic');
|
|
26
30
|
const bounded = boundedModelToolOutput(call.name, result.content, result.data);
|
|
27
31
|
const data = structuredToolEventData(call.name, result.data);
|
|
28
32
|
const summary = result.content.slice(0, AGENT_EXECUTION_POLICY.toolEventSummaryCharacters);
|
|
33
|
+
input.assertLive();
|
|
29
34
|
await input.emit({
|
|
30
35
|
type: 'tool.completed',
|
|
31
36
|
...identity,
|
|
@@ -43,6 +48,13 @@ export function createCodeToolDispatch(input) {
|
|
|
43
48
|
};
|
|
44
49
|
}
|
|
45
50
|
catch (error) {
|
|
51
|
+
// Retirement must not enqueue a failure event into the replacement Session epoch.
|
|
52
|
+
try {
|
|
53
|
+
input.assertLive();
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
46
58
|
const diagnostic = describeMarAgentError(error, 'MAR_AGENT_HOST_OPERATION_FAILED');
|
|
47
59
|
await input.emit({
|
|
48
60
|
type: 'tool.failed',
|
|
@@ -39,7 +39,7 @@ Built-in namespaces:
|
|
|
39
39
|
- catalog.tools() returns a read-only snapshot of the tools callable in this cell, including name, description, inputSchema and inputMode. It does not discover or authorize additional tools.
|
|
40
40
|
- await cell.sleep(ms) performs a cancellable delay up to ${limits.codeSleepMaxMs} ms.
|
|
41
41
|
- await cell.yield_control() returns accumulated output to the model immediately while this cell continues running.
|
|
42
|
-
- Optionally start source with // @options: {"yieldMs": ${limits.codeDefaultYieldMs}
|
|
42
|
+
- Optionally start source with // @options: {"yieldMs": ${limits.codeDefaultYieldMs}}. yieldMs defaults to ${limits.codeDefaultYieldMs} ms and controls this call's wait window (0..${limits.codeMaxYieldMs}), not nested tool timeouts or wait windows. A running result includes cellId; use direct code_exec_wait for new output, completion or termination.
|
|
43
43
|
- Registered cells are Session warm resources: ending or cancelling the model Execution cancels its wait, not the cell. Reuse retained cellId; do not start duplicate work. Host cleanup or explicit termination stops the cell.
|
|
44
44
|
- tools, observe, state, catalog and cell are frozen bindings. Do not shadow their names.
|
|
45
45
|
|
|
@@ -85,7 +85,6 @@ export const codeExecWaitToolDefinition = {
|
|
|
85
85
|
default: limits.codeDefaultWaitYieldMs,
|
|
86
86
|
description: `Wait up to this many milliseconds. Defaults to ${limits.codeDefaultWaitYieldMs}; maximum ${limits.codeMaxWaitYieldMs}. Zero returns immediately without stopping the cell. Waiting does not restart the cell or extend nested tool timeouts.`
|
|
87
87
|
},
|
|
88
|
-
maxOutputTokens: { type: 'integer', minimum: 1, maximum: limits.codeMaxOutputTokens },
|
|
89
88
|
action: {
|
|
90
89
|
type: 'string',
|
|
91
90
|
enum: ['poll', 'cancel'],
|
|
@@ -85,6 +85,9 @@ export declare function boundObservations(values: readonly CodeObservation[], ma
|
|
|
85
85
|
export declare function newObservationId(): string;
|
|
86
86
|
export declare function observationModelParts(message: {
|
|
87
87
|
content?: string;
|
|
88
|
+
textBlocks?: readonly {
|
|
89
|
+
readonly text: string;
|
|
90
|
+
}[];
|
|
88
91
|
observations?: readonly CodeObservation[];
|
|
89
92
|
images?: readonly {
|
|
90
93
|
mimeType: string;
|
|
@@ -100,4 +103,6 @@ export declare function observationModelParts(message: {
|
|
|
100
103
|
dataBase64: string;
|
|
101
104
|
};
|
|
102
105
|
}> | undefined;
|
|
106
|
+
/** Uses the same visible text as both adapters, without pixels or private observation data. */
|
|
107
|
+
export declare function observationModelText(message: Parameters<typeof observationModelParts>[0]): string | undefined;
|
|
103
108
|
export declare function parseObservations(value: unknown): CodeObservation[] | undefined;
|
|
@@ -69,6 +69,11 @@ export function visibleObservation(value) {
|
|
|
69
69
|
export function renderObservations(values) {
|
|
70
70
|
return JSON.stringify(values.map(visibleObservation));
|
|
71
71
|
}
|
|
72
|
+
function visibleObservationText(value) {
|
|
73
|
+
const { content: _content, value: _value, ...header } = visibleObservation(value);
|
|
74
|
+
const body = observationContent(value);
|
|
75
|
+
return JSON.stringify(header) + (body.length ? `\n${body}` : '');
|
|
76
|
+
}
|
|
72
77
|
export function renderObservationResult(values, original) {
|
|
73
78
|
try {
|
|
74
79
|
const envelope = JSON.parse(original);
|
|
@@ -89,7 +94,7 @@ export function boundObservations(values, maximumTokens) {
|
|
|
89
94
|
const empty = original.type === 'tool_result'
|
|
90
95
|
? { ...original, result: { ...original.result, content: '', truncated: true } }
|
|
91
96
|
: { ...original, value: '', truncated: true };
|
|
92
|
-
const overhead = countModelTokens(
|
|
97
|
+
const overhead = countModelTokens(visibleObservationText(empty)) + 8;
|
|
93
98
|
if (overheadBudget + overhead > remaining) {
|
|
94
99
|
truncated = true;
|
|
95
100
|
break;
|
|
@@ -119,7 +124,7 @@ export function boundObservations(values, maximumTokens) {
|
|
|
119
124
|
value: bounded.content,
|
|
120
125
|
truncated: original.truncated === true || bodyTruncated
|
|
121
126
|
};
|
|
122
|
-
if (countModelTokens(
|
|
127
|
+
if (countModelTokens(visibleObservationText(value)) + 1 <=
|
|
123
128
|
remaining - (overheadBudget - overhead) ||
|
|
124
129
|
bodyBudget === 0)
|
|
125
130
|
break;
|
|
@@ -127,7 +132,7 @@ export function boundObservations(values, maximumTokens) {
|
|
|
127
132
|
}
|
|
128
133
|
overheadBudget -= overhead;
|
|
129
134
|
observations.push(value);
|
|
130
|
-
remaining -= countModelTokens(
|
|
135
|
+
remaining -= countModelTokens(visibleObservationText(value)) + 1;
|
|
131
136
|
truncated ||=
|
|
132
137
|
bodyTruncated ||
|
|
133
138
|
(original.type === 'tool_result'
|
|
@@ -140,7 +145,7 @@ export function newObservationId() {
|
|
|
140
145
|
return randomUUID();
|
|
141
146
|
}
|
|
142
147
|
export function observationModelParts(message) {
|
|
143
|
-
if (
|
|
148
|
+
if (message.textBlocks !== undefined || !message.observations?.length)
|
|
144
149
|
return undefined;
|
|
145
150
|
const prefix = [];
|
|
146
151
|
try {
|
|
@@ -154,7 +159,7 @@ export function observationModelParts(message) {
|
|
|
154
159
|
return [
|
|
155
160
|
...prefix,
|
|
156
161
|
...message.observations.flatMap((value) => {
|
|
157
|
-
const parts = [{ type: 'text', text:
|
|
162
|
+
const parts = [{ type: 'text', text: visibleObservationText(value) }];
|
|
158
163
|
for (const index of observationImages(value)) {
|
|
159
164
|
const image = message.images?.[index];
|
|
160
165
|
if (image)
|
|
@@ -164,6 +169,12 @@ export function observationModelParts(message) {
|
|
|
164
169
|
})
|
|
165
170
|
];
|
|
166
171
|
}
|
|
172
|
+
/** Uses the same visible text as both adapters, without pixels or private observation data. */
|
|
173
|
+
export function observationModelText(message) {
|
|
174
|
+
return observationModelParts(message)
|
|
175
|
+
?.flatMap((part) => (part.type === 'text' ? [part.text] : []))
|
|
176
|
+
.join('\n');
|
|
177
|
+
}
|
|
167
178
|
export function parseObservations(value) {
|
|
168
179
|
if (!Array.isArray(value) || value.length > 4096)
|
|
169
180
|
return undefined;
|
|
@@ -111,8 +111,13 @@ export async function createCodeModeEngine(options) {
|
|
|
111
111
|
'WebSocket', 'WebAssembly', 'SharedArrayBuffer', 'Atomics', 'text', 'image'])
|
|
112
112
|
delete globalThis[name];
|
|
113
113
|
globalThis.__parseJson = JSON.parse;
|
|
114
|
-
globalThis.__serialize = value => {
|
|
115
|
-
|
|
114
|
+
globalThis.__serialize = (value, toolInput = false) => {
|
|
115
|
+
let root = true;
|
|
116
|
+
return JSON.stringify(value, function (_key, item) {
|
|
117
|
+
const first = root;
|
|
118
|
+
root = false;
|
|
119
|
+
if (toolInput && !first && !Array.isArray(this) && typeof item === 'undefined')
|
|
120
|
+
return undefined;
|
|
116
121
|
if (['bigint', 'function', 'symbol', 'undefined'].includes(typeof item))
|
|
117
122
|
throw Object.assign(new TypeError('Only JSON values may cross the tool bridge.'),
|
|
118
123
|
{ code: 'MAR_AGENT_TOOL_INPUT_INVALID' });
|
|
@@ -183,8 +188,8 @@ export async function createCodeModeEngine(options) {
|
|
|
183
188
|
handle.dispose();
|
|
184
189
|
}
|
|
185
190
|
};
|
|
186
|
-
const jsonValue = (handle) => {
|
|
187
|
-
const result = callGuest(serialize, vm.undefined, handle);
|
|
191
|
+
const jsonValue = (handle, toolInput = false) => {
|
|
192
|
+
const result = callGuest(serialize, vm.undefined, handle, toolInput ? vm.true : vm.false);
|
|
188
193
|
if (result.error)
|
|
189
194
|
return { error: result.error };
|
|
190
195
|
try {
|
|
@@ -222,7 +227,7 @@ export async function createCodeModeEngine(options) {
|
|
|
222
227
|
let toolCallSequence = 0;
|
|
223
228
|
for (const name of input.tools) {
|
|
224
229
|
method(tools, name, (args = vm.undefined) => {
|
|
225
|
-
const parsed = jsonValue(args);
|
|
230
|
+
const parsed = jsonValue(args, true);
|
|
226
231
|
if (parsed.error)
|
|
227
232
|
return parsed;
|
|
228
233
|
const operationId = ++toolCallSequence;
|
|
@@ -3,6 +3,7 @@ import type { EventPayload } from '../runtime/events.js';
|
|
|
3
3
|
import type { ClientToolDefinition, McpToolCallContext, ToolExecutionContext, ToolExecutionResult } from './contracts.js';
|
|
4
4
|
import type { ToolRuntimeContext, ToolRuntimeImage } from './runtime-context.js';
|
|
5
5
|
import { type createSessionToolScheduler } from './scheduler.js';
|
|
6
|
+
import type { ToolCall } from './contracts.js';
|
|
6
7
|
export type ClientToolCall = Extract<ModelEvent, {
|
|
7
8
|
type: 'client_tool.call';
|
|
8
9
|
}>;
|
|
@@ -26,11 +27,19 @@ export interface RoundToolScope {
|
|
|
26
27
|
readonly expectations: ReadonlyMap<string, McpToolCallContext['expectedTool']>;
|
|
27
28
|
readonly extensionToolNames: ReadonlySet<string>;
|
|
28
29
|
readonly schedule: ReturnType<typeof createSessionToolScheduler>;
|
|
29
|
-
readonly
|
|
30
|
+
readonly assertLive: () => void;
|
|
31
|
+
readonly assertExecutionLive: () => void;
|
|
32
|
+
readonly emit: (event: EventPayload, assertLive?: () => void) => Promise<void>;
|
|
30
33
|
readonly searchMcpTools: NonNullable<ToolRuntimeContext['searchMcpTools']>;
|
|
31
34
|
readonly recentImages: () => ToolRuntimeImage[];
|
|
32
35
|
readonly captureCodeContext: () => Omit<ToolRuntimeContext, 'signal' | 'codeMode' | 'expectedMcpTool' | 'searchMcpTools'>;
|
|
33
|
-
readonly codeModeContext: (call:
|
|
36
|
+
readonly codeModeContext: (call: ToolCall & {
|
|
37
|
+
readonly callId: string;
|
|
38
|
+
}) => Promise<Omit<NonNullable<ToolExecutionContext['codeMode']>, 'invoke'>>;
|
|
34
39
|
}
|
|
40
|
+
/** Shared raw context construction for physical tools and native calls in a real model round. */
|
|
41
|
+
export declare function createRoundToolRuntimeContext(scope: RoundToolScope, call: ToolCall & {
|
|
42
|
+
readonly callId: string;
|
|
43
|
+
}, signal: AbortSignal): Promise<ToolRuntimeContext>;
|
|
35
44
|
/** Complete every call in model order even if individual execution fails. */
|
|
36
45
|
export declare function executeRoundTools(calls: readonly ClientToolCall[], scope: RoundToolScope): Promise<ExecutedToolCall[]>;
|
|
@@ -6,6 +6,62 @@ import { createCodeToolDispatch } from './code-mode/dispatch.js';
|
|
|
6
6
|
import { boundedModelToolOutput, structuredToolArtifacts, structuredToolEventData } from './output-projection.js';
|
|
7
7
|
import { executeRegisteredTool } from './runtime.js';
|
|
8
8
|
import { scheduleToolCallBatch } from './scheduler.js';
|
|
9
|
+
/** Shared raw context construction for physical tools and native calls in a real model round. */
|
|
10
|
+
export async function createRoundToolRuntimeContext(scope, call, signal) {
|
|
11
|
+
scope.assertLive();
|
|
12
|
+
signal.throwIfAborted();
|
|
13
|
+
const codeMode = await scope.codeModeContext(call);
|
|
14
|
+
scope.assertLive();
|
|
15
|
+
signal.throwIfAborted();
|
|
16
|
+
const assertSessionLive = scope.assertLive;
|
|
17
|
+
const assertExecutionLive = scope.assertExecutionLive;
|
|
18
|
+
const emitCodeEvent = scope.emit;
|
|
19
|
+
const retainSession = codeMode.retainSession?.bind(codeMode);
|
|
20
|
+
let retained = 0;
|
|
21
|
+
const assertDispatchLive = () => {
|
|
22
|
+
assertSessionLive();
|
|
23
|
+
if (retained === 0)
|
|
24
|
+
assertExecutionLive();
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
...scope.context,
|
|
28
|
+
...(scope.expectations.get(call.name)
|
|
29
|
+
? { expectedMcpTool: scope.expectations.get(call.name) }
|
|
30
|
+
: {}),
|
|
31
|
+
searchMcpTools: scope.searchMcpTools,
|
|
32
|
+
signal,
|
|
33
|
+
codeMode: {
|
|
34
|
+
...codeMode,
|
|
35
|
+
...(retainSession === undefined
|
|
36
|
+
? {}
|
|
37
|
+
: {
|
|
38
|
+
retainSession: async () => {
|
|
39
|
+
assertDispatchLive();
|
|
40
|
+
const release = await retainSession();
|
|
41
|
+
retained++;
|
|
42
|
+
let released = false;
|
|
43
|
+
return async () => {
|
|
44
|
+
if (released)
|
|
45
|
+
return;
|
|
46
|
+
released = true;
|
|
47
|
+
retained--;
|
|
48
|
+
await release();
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}),
|
|
52
|
+
invoke: createCodeToolDispatch({
|
|
53
|
+
context: scope.captureCodeContext(),
|
|
54
|
+
tools: scope.tools,
|
|
55
|
+
expectations: scope.expectations,
|
|
56
|
+
parentCallId: call.callId,
|
|
57
|
+
schedule: scope.schedule,
|
|
58
|
+
assertLive: assertDispatchLive,
|
|
59
|
+
emit: (event) => emitCodeEvent(event, assertDispatchLive)
|
|
60
|
+
})
|
|
61
|
+
},
|
|
62
|
+
...(scope.extensionToolNames.has(call.name) ? { recentImages: scope.recentImages() } : {})
|
|
63
|
+
};
|
|
64
|
+
}
|
|
9
65
|
/** Complete every call in model order even if individual execution fails. */
|
|
10
66
|
export async function executeRoundTools(calls, scope) {
|
|
11
67
|
const executeCall = async (call, signal = scope.context.signal, caller = 'direct', parentCallId) => {
|
|
@@ -21,29 +77,10 @@ export async function executeRoundTools(calls, scope) {
|
|
|
21
77
|
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', call.argumentError);
|
|
22
78
|
if (!scope.tools.some((tool) => tool.name === call.name && allowsToolCaller(tool, caller)))
|
|
23
79
|
throw new MarAgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Tool is unavailable in this execution.');
|
|
24
|
-
const
|
|
80
|
+
const context = await createRoundToolRuntimeContext(scope, call, signal);
|
|
25
81
|
const runTool = () => {
|
|
26
82
|
signal.throwIfAborted();
|
|
27
|
-
return executeRegisteredTool(call.name, call.arguments, call.callId,
|
|
28
|
-
...scope.context,
|
|
29
|
-
...(scope.expectations.get(call.name)
|
|
30
|
-
? { expectedMcpTool: scope.expectations.get(call.name) }
|
|
31
|
-
: {}),
|
|
32
|
-
searchMcpTools: scope.searchMcpTools,
|
|
33
|
-
signal,
|
|
34
|
-
codeMode: {
|
|
35
|
-
...codeMode,
|
|
36
|
-
invoke: createCodeToolDispatch({
|
|
37
|
-
context: scope.captureCodeContext(),
|
|
38
|
-
tools: scope.tools,
|
|
39
|
-
expectations: scope.expectations,
|
|
40
|
-
parentCallId: call.callId,
|
|
41
|
-
schedule: scope.schedule,
|
|
42
|
-
emit: scope.emit
|
|
43
|
-
})
|
|
44
|
-
},
|
|
45
|
-
...(scope.extensionToolNames.has(call.name) ? { recentImages: scope.recentImages() } : {})
|
|
46
|
-
});
|
|
83
|
+
return executeRegisteredTool(call.name, call.arguments, call.callId, context);
|
|
47
84
|
};
|
|
48
85
|
const output = call.name === 'code_exec' || call.name === 'code_exec_wait'
|
|
49
86
|
? await runTool()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Compatibility is not validation: required fields, types and values stay with the tool. */
|
|
2
|
+
export function normalizeToolInput(input, schema) {
|
|
3
|
+
const visit = (value, definition, depth) => {
|
|
4
|
+
if (depth > 64 || value === null || typeof value !== 'object')
|
|
5
|
+
return value;
|
|
6
|
+
const shape = record(definition) ? definition : {};
|
|
7
|
+
if (Array.isArray(value))
|
|
8
|
+
return value.map((item) => visit(item, shape.items, depth + 1));
|
|
9
|
+
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null)
|
|
10
|
+
return value;
|
|
11
|
+
const properties = record(shape.properties) ? shape.properties : {};
|
|
12
|
+
// Do not guess branch selection, resolve refs, or discard pattern-defined parameters.
|
|
13
|
+
const closed = shape.additionalProperties === false &&
|
|
14
|
+
!['$ref', 'anyOf', 'oneOf', 'allOf', 'if', 'patternProperties', 'dependentSchemas'].some((key) => Object.hasOwn(shape, key));
|
|
15
|
+
const result = {};
|
|
16
|
+
for (const [key, item] of Object.entries(value)) {
|
|
17
|
+
if (item === undefined)
|
|
18
|
+
continue;
|
|
19
|
+
const declared = Object.hasOwn(properties, key);
|
|
20
|
+
if (closed && !declared)
|
|
21
|
+
continue;
|
|
22
|
+
const child = declared ? properties[key] : shape.additionalProperties;
|
|
23
|
+
Object.defineProperty(result, key, {
|
|
24
|
+
value: visit(item, child, depth + 1),
|
|
25
|
+
enumerable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
configurable: true
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
return visit(input, schema, 0);
|
|
33
|
+
}
|
|
34
|
+
function record(value) {
|
|
35
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
36
|
+
}
|
|
@@ -8,7 +8,7 @@ export const applyPatchToolDefinition = {
|
|
|
8
8
|
name: 'apply_patch',
|
|
9
9
|
allowedCallers: ['direct', 'programmatic'],
|
|
10
10
|
parallelSafety: 'serial',
|
|
11
|
-
description: `Preferred for precise, reviewable local edits. Do not batch all task files merely to reduce tool calls. Group only a few tightly coupled edits whose combined patch is quick to generate and review; split broad or independent work into successive bounded patches before composing them. Submit a raw *** Begin Patch / *** End Patch patch. Supports Add File, Update File, Delete File, and Move to. Add File may replace an existing regular file, and Move to may replace an existing regular destination; duplicate operations on the same resolved path remain invalid. Update hunks accept @@ or @@ <change context
|
|
11
|
+
description: `Preferred for precise, reviewable local edits. Do not batch all task files merely to reduce tool calls. Group only a few tightly coupled edits whose combined patch is quick to generate and review; split broad or independent work into successive bounded patches before composing them. Submit a raw *** Begin Patch / *** End Patch patch. Supports Add File, Update File, Delete File, and Move to. Add File may replace an existing regular file, and Move to may replace an existing regular destination; duplicate operations on the same resolved path remain invalid. Update hunks accept @@ or @@ <change context>. Context-only hunks, including trailing hunks, and unchanged file sections are validated in order but are not written or reported as edits. The patch must include a change or move. Context is searched forward with Codex-style exact, whitespace-tolerant, then common Unicode punctuation-tolerant matching. Syntax and resolved paths are validated before writing, then file units are committed in order. Each file unit is atomic, and a Move keeps its source and destination atomic. On MAR_AGENT_PATCH_PARTIAL, earlier files remain applied; the error lists applied, failed, and pending paths, so retry only failed and pending paths. Existing file permissions, line endings, and final-newline state are preserved. Returns changed file paths and operations with per-file and total line counts when all units succeed. In Code Mode, the Promise resolves on success and rejects with code/message on failure; the returned envelope has data.files and data.totals, not an ok or success flag. Oversized patches are rejected without advertising defensive limits as recommended patch sizes. A first-unit MAR_AGENT_PATCH_CONFLICT applies nothing; hunk match conflicts identify the affected path and hunk so you can reread only that file before retrying.`,
|
|
12
12
|
inputMode: 'freeform',
|
|
13
13
|
outputSchema: {
|
|
14
14
|
type: 'object',
|
|
@@ -73,18 +73,28 @@ export class ApplyPatchTool {
|
|
|
73
73
|
const resolvedChanges = await this.#resolveChanges(changes);
|
|
74
74
|
const results = [];
|
|
75
75
|
let totalBytes = 0;
|
|
76
|
-
|
|
76
|
+
const pendingChanges = [];
|
|
77
|
+
for (const resolvedChange of resolvedChanges) {
|
|
78
|
+
if (resolvedChange.kind === 'update' &&
|
|
79
|
+
resolvedChange.change.hunks.every((hunk) => hunk.anchor))
|
|
80
|
+
totalBytes = (await this.#preflight(resolvedChange, totalBytes)).totalBytes;
|
|
81
|
+
else
|
|
82
|
+
pendingChanges.push(resolvedChange);
|
|
83
|
+
}
|
|
84
|
+
for (const [index, resolvedChange] of pendingChanges.entries()) {
|
|
77
85
|
try {
|
|
78
86
|
const preflight = await this.#preflight(resolvedChange, totalBytes);
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
if (preflight.mutations.length)
|
|
88
|
+
await this.#commit(preflight.mutations);
|
|
89
|
+
if (preflight.result)
|
|
90
|
+
results.push(preflight.result);
|
|
81
91
|
totalBytes = preflight.totalBytes;
|
|
82
92
|
}
|
|
83
93
|
catch (error) {
|
|
84
94
|
if (!results.length ||
|
|
85
95
|
(error instanceof MarAgentError && error.code === 'MAR_AGENT_PATCH_ROLLBACK_FAILED'))
|
|
86
96
|
throw error;
|
|
87
|
-
throw partialPatchError(error, results,
|
|
97
|
+
throw partialPatchError(error, results, pendingChanges.slice(index));
|
|
88
98
|
}
|
|
89
99
|
}
|
|
90
100
|
return {
|
|
@@ -185,6 +195,8 @@ export class ApplyPatchTool {
|
|
|
185
195
|
totalBytes = enforceContentLimits(content, totalBytes);
|
|
186
196
|
const addedLines = change.hunks.reduce((sum, hunk) => sum + hunk.addedLines, 0);
|
|
187
197
|
const deletedLines = change.hunks.reduce((sum, hunk) => sum + hunk.deletedLines, 0);
|
|
198
|
+
if (resolvedChange.kind === 'update' && !addedLines && !deletedLines)
|
|
199
|
+
return { mutations: [], totalBytes };
|
|
188
200
|
if (resolvedChange.kind === 'move') {
|
|
189
201
|
const moveChange = resolvedChange.change;
|
|
190
202
|
const targetStat = await optionalLstat(resolvedChange.target);
|
|
@@ -385,18 +397,8 @@ function parsePatch(patch) {
|
|
|
385
397
|
anchor: !addedLines && !deletedLines
|
|
386
398
|
});
|
|
387
399
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
const hunk = hunks[hunkIndex];
|
|
391
|
-
if (hunk.anchor) {
|
|
392
|
-
if (!hasFollowingChange)
|
|
393
|
-
invalid('Context-only patch hunks must precede a changing hunk.');
|
|
394
|
-
}
|
|
395
|
-
else
|
|
396
|
-
hasFollowingChange = true;
|
|
397
|
-
}
|
|
398
|
-
if (!hasFollowingChange && !targetPath)
|
|
399
|
-
invalid('Update operations must change or move a file.');
|
|
400
|
+
if (!hunks.length && !targetPath)
|
|
401
|
+
invalid('Update operations must contain hunks or move a file.');
|
|
400
402
|
changes.push({ kind: 'update', path, ...(targetPath ? { targetPath } : {}), hunks });
|
|
401
403
|
}
|
|
402
404
|
else
|
|
@@ -404,6 +406,8 @@ function parsePatch(patch) {
|
|
|
404
406
|
if (changes.length > TOOL_EXECUTION_LIMITS.patchMaxFiles)
|
|
405
407
|
throw new MarAgentError('MAR_AGENT_TOOL_OUTPUT_LIMIT', 'Patch exceeds the file limit.');
|
|
406
408
|
}
|
|
409
|
+
if (!changes.some((change) => change.kind !== 'update' || change.targetPath || change.hunks.some((hunk) => !hunk.anchor)))
|
|
410
|
+
invalid('Patch must change or move at least one file.');
|
|
407
411
|
return changes;
|
|
408
412
|
}
|
|
409
413
|
function applyHunks(original, hunks, path) {
|
|
@@ -2,6 +2,8 @@ import { type LocalProcessExecutor } from '../../host/local-process-executor.js'
|
|
|
2
2
|
import type { ClientToolDefinition } from '../../model/contracts.js';
|
|
3
3
|
import type { ExecResult } from '../contracts.js';
|
|
4
4
|
import { WorkspacePathResolver } from '../shared/path-resolver.js';
|
|
5
|
+
import { type CoreProcessInput, type CoreProcessHandle } from '../../extensions/processes.js';
|
|
6
|
+
import type { CoreOperationContext } from '../../extensions/operation.js';
|
|
5
7
|
export type { ExecResult } from '../contracts.js';
|
|
6
8
|
export type ExecInput = {
|
|
7
9
|
action?: 'start';
|
|
@@ -34,10 +36,8 @@ export declare class ExecTool {
|
|
|
34
36
|
private readonly shell?;
|
|
35
37
|
private readonly processExecutor;
|
|
36
38
|
constructor(paths: WorkspacePathResolver, maxOutputBytes?: number, maxProcesses?: number, environment?: NodeJS.ProcessEnv, shell?: string | undefined, processExecutor?: LocalProcessExecutor);
|
|
37
|
-
execute(arguments_: unknown, signal?: AbortSignal, owner?:
|
|
38
|
-
|
|
39
|
-
readonly executionId: string;
|
|
40
|
-
}): Promise<ExecResult>;
|
|
39
|
+
execute(arguments_: unknown, signal?: AbortSignal, owner?: NonNullable<import('../../host/local-process-executor.js').LocalProcessSpawnRequest['owner']>): Promise<ExecResult>;
|
|
40
|
+
spawn(input: CoreProcessInput, call: CoreOperationContext): Promise<CoreProcessHandle>;
|
|
41
41
|
listSessionResources(sessionId: string): {
|
|
42
42
|
processId: string;
|
|
43
43
|
status: "completed" | "cancelled" | "running";
|