@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,454 @@
|
|
|
1
|
+
// This trusted module runs only in the Host-managed worker, never in the model's JS context.
|
|
2
|
+
/** @returns {Promise<import('./engine.js').CodeModeEngine>} */
|
|
3
|
+
export async function createCodeModeEngine(options) {
|
|
4
|
+
const { newQuickJSWASMModuleFromVariant } = await import(options.coreModuleUrl);
|
|
5
|
+
const variant = (await import(options.variantModuleUrl)).default;
|
|
6
|
+
const quickjs = await newQuickJSWASMModuleFromVariant(variant);
|
|
7
|
+
let runtime;
|
|
8
|
+
let context;
|
|
9
|
+
let disposed = false;
|
|
10
|
+
return {
|
|
11
|
+
async execute(input) {
|
|
12
|
+
if (disposed || runtime)
|
|
13
|
+
throw diagnostic('MAR_AGENT_CODE_FAILED', 'Engine is not reusable.');
|
|
14
|
+
runtime = quickjs.newRuntime();
|
|
15
|
+
runtime.setMemoryLimit(input.limits.memoryBytes);
|
|
16
|
+
runtime.setMaxStackSize(input.limits.stackBytes);
|
|
17
|
+
const deadline = Date.now() + input.limits.timeoutMs;
|
|
18
|
+
runtime.setInterruptHandler(() => input.signal.aborted || Date.now() >= deadline);
|
|
19
|
+
context = runtime.newContext();
|
|
20
|
+
const vm = context;
|
|
21
|
+
let wake;
|
|
22
|
+
let bridgeFailure;
|
|
23
|
+
const pending = new Set();
|
|
24
|
+
const deferreds = new Set();
|
|
25
|
+
const unconsumedToolCalls = new Set();
|
|
26
|
+
const sleepTimers = new Set();
|
|
27
|
+
const state = new Map(input.state.map((entry) => [entry.key, entry.value]));
|
|
28
|
+
const stateMutations = [];
|
|
29
|
+
let outputBytes = 0;
|
|
30
|
+
const notify = () => {
|
|
31
|
+
wake?.();
|
|
32
|
+
wake = undefined;
|
|
33
|
+
};
|
|
34
|
+
const fail = (handle) => {
|
|
35
|
+
const error = vm.dump(handle);
|
|
36
|
+
handle.dispose();
|
|
37
|
+
if (input.signal.aborted)
|
|
38
|
+
throw input.signal.reason;
|
|
39
|
+
if (Date.now() >= deadline)
|
|
40
|
+
throw diagnostic('MAR_AGENT_CODE_TIMEOUT', 'JavaScript execution exceeded its deadline.');
|
|
41
|
+
const message = String(error?.message ?? error).slice(0, 4096);
|
|
42
|
+
const code = message.includes('out of memory')
|
|
43
|
+
? 'MAR_AGENT_CODE_MEMORY_LIMIT'
|
|
44
|
+
: typeof error?.code === 'string'
|
|
45
|
+
? error.code
|
|
46
|
+
: 'MAR_AGENT_CODE_FAILED';
|
|
47
|
+
throw diagnostic(code, message);
|
|
48
|
+
};
|
|
49
|
+
const guestError = (code, message) => {
|
|
50
|
+
const handle = vm.newError({ name: 'Error', message });
|
|
51
|
+
const value = vm.newString(code);
|
|
52
|
+
vm.setProp(handle, 'code', value);
|
|
53
|
+
value.dispose();
|
|
54
|
+
return handle;
|
|
55
|
+
};
|
|
56
|
+
runtime.setModuleLoader(() => ({
|
|
57
|
+
error: guestError('MAR_AGENT_CODE_IMPORT_DENIED', 'Module imports are unavailable.')
|
|
58
|
+
}));
|
|
59
|
+
const setup = vm.evalCode(`
|
|
60
|
+
for (const name of ['console', 'process', 'require', 'module', 'Buffer', 'fetch',
|
|
61
|
+
'WebSocket', 'WebAssembly', 'SharedArrayBuffer', 'Atomics', 'text', 'image'])
|
|
62
|
+
delete globalThis[name];
|
|
63
|
+
globalThis.__parseJson = JSON.parse;
|
|
64
|
+
globalThis.__serialize = value => {
|
|
65
|
+
return JSON.stringify(value, (_key, item) => {
|
|
66
|
+
if (['bigint', 'function', 'symbol', 'undefined'].includes(typeof item))
|
|
67
|
+
throw Object.assign(new TypeError('Only JSON values may cross the tool bridge.'),
|
|
68
|
+
{ code: 'MAR_AGENT_TOOL_INPUT_INVALID' });
|
|
69
|
+
return item;
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
globalThis.__text = value => {
|
|
73
|
+
if (value === null || ['string', 'number', 'boolean', 'bigint', 'undefined'].includes(typeof value))
|
|
74
|
+
return String(value);
|
|
75
|
+
return JSON.stringify(value, (_key, item) => {
|
|
76
|
+
if (['bigint', 'function', 'symbol', 'undefined'].includes(typeof item))
|
|
77
|
+
throw Object.assign(new TypeError('Only JSON values may be emitted.'),
|
|
78
|
+
{ code: 'MAR_AGENT_TOOL_INPUT_INVALID' });
|
|
79
|
+
return item;
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
globalThis.__deepFreeze = function deepFreeze(value) {
|
|
83
|
+
if (value && typeof value === 'object' && !Object.isFrozen(value)) {
|
|
84
|
+
for (const item of Object.values(value)) deepFreeze(item);
|
|
85
|
+
Object.freeze(value);
|
|
86
|
+
}
|
|
87
|
+
return value;
|
|
88
|
+
};
|
|
89
|
+
`);
|
|
90
|
+
if (setup.error)
|
|
91
|
+
fail(setup.error);
|
|
92
|
+
setup.value.dispose();
|
|
93
|
+
const parseJson = vm.getProp(vm.global, '__parseJson');
|
|
94
|
+
const serialize = vm.getProp(vm.global, '__serialize');
|
|
95
|
+
const formatText = vm.getProp(vm.global, '__text');
|
|
96
|
+
const deepFreeze = vm.getProp(vm.global, '__deepFreeze');
|
|
97
|
+
const parse = (value) => {
|
|
98
|
+
const handle = vm.newString(JSON.stringify(value));
|
|
99
|
+
const parsed = vm.callFunction(parseJson, vm.undefined, handle);
|
|
100
|
+
handle.dispose();
|
|
101
|
+
if (parsed.error)
|
|
102
|
+
fail(parsed.error);
|
|
103
|
+
return parsed.value;
|
|
104
|
+
};
|
|
105
|
+
const jsonValue = (handle) => {
|
|
106
|
+
const result = vm.callFunction(serialize, vm.undefined, handle);
|
|
107
|
+
if (result.error)
|
|
108
|
+
return { error: result.error };
|
|
109
|
+
try {
|
|
110
|
+
return { value: JSON.parse(vm.getString(result.value)) };
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
result.value.dispose();
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const method = (object, name, callback) => {
|
|
117
|
+
const handle = vm.newFunction(name, callback);
|
|
118
|
+
vm.setProp(object, name, handle);
|
|
119
|
+
handle.dispose();
|
|
120
|
+
};
|
|
121
|
+
const key = (handle) => {
|
|
122
|
+
if (!handle || vm.typeof(handle) !== 'string')
|
|
123
|
+
return {
|
|
124
|
+
error: guestError('MAR_AGENT_TOOL_INPUT_INVALID', 'State key must be a string.')
|
|
125
|
+
};
|
|
126
|
+
const value = vm.getString(handle);
|
|
127
|
+
if (!value || Buffer.byteLength(value) > input.limits.stateKeyBytes)
|
|
128
|
+
return {
|
|
129
|
+
error: guestError('MAR_AGENT_TOOL_INPUT_INVALID', 'State key is invalid or too large.')
|
|
130
|
+
};
|
|
131
|
+
return { value };
|
|
132
|
+
};
|
|
133
|
+
const validateState = () => {
|
|
134
|
+
if (state.size > input.limits.stateEntries)
|
|
135
|
+
return guestError('MAR_AGENT_TOOL_OUTPUT_LIMIT', 'Code Mode state has too many entries.');
|
|
136
|
+
const entries = [...state].map(([entryKey, value]) => ({ key: entryKey, value }));
|
|
137
|
+
if (Buffer.byteLength(JSON.stringify(entries)) > input.limits.stateTotalBytes)
|
|
138
|
+
return guestError('MAR_AGENT_TOOL_OUTPUT_LIMIT', 'Code Mode state is too large.');
|
|
139
|
+
};
|
|
140
|
+
const tools = vm.newObject();
|
|
141
|
+
let toolCallSequence = 0;
|
|
142
|
+
for (const name of input.tools) {
|
|
143
|
+
method(tools, name, (args = vm.undefined) => {
|
|
144
|
+
const parsed = jsonValue(args);
|
|
145
|
+
if (parsed.error)
|
|
146
|
+
return parsed;
|
|
147
|
+
const operationId = ++toolCallSequence;
|
|
148
|
+
unconsumedToolCalls.add(operationId);
|
|
149
|
+
const deferred = vm.newPromise();
|
|
150
|
+
deferreds.add(deferred);
|
|
151
|
+
const job = Promise.resolve()
|
|
152
|
+
.then(() => input.onToolCall({ name, arguments: parsed.value }))
|
|
153
|
+
.then((output) => {
|
|
154
|
+
if (disposed || input.signal.aborted)
|
|
155
|
+
return;
|
|
156
|
+
const handle = parse(output);
|
|
157
|
+
try {
|
|
158
|
+
deferred.resolve(handle);
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
handle.dispose();
|
|
162
|
+
}
|
|
163
|
+
}, (error) => {
|
|
164
|
+
if (disposed || input.signal.aborted)
|
|
165
|
+
return;
|
|
166
|
+
const handle = vm.newError({
|
|
167
|
+
name: 'Error',
|
|
168
|
+
message: String(error?.message ?? error).slice(0, 4096)
|
|
169
|
+
});
|
|
170
|
+
const code = vm.newString(String(error?.code ?? 'MAR_AGENT_HOST_OPERATION_FAILED'));
|
|
171
|
+
vm.setProp(handle, 'code', code);
|
|
172
|
+
code.dispose();
|
|
173
|
+
try {
|
|
174
|
+
deferred.reject(handle);
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
handle.dispose();
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
.catch((error) => {
|
|
181
|
+
bridgeFailure = diagnostic('MAR_AGENT_CODE_ENGINE_FAILED', String(error?.message ?? error).slice(0, 4096));
|
|
182
|
+
})
|
|
183
|
+
.finally(() => {
|
|
184
|
+
pending.delete(job);
|
|
185
|
+
deferreds.delete(deferred);
|
|
186
|
+
if (deferred.alive)
|
|
187
|
+
deferred.dispose();
|
|
188
|
+
notify();
|
|
189
|
+
});
|
|
190
|
+
pending.add(job);
|
|
191
|
+
const operation = vm.newObject();
|
|
192
|
+
const id = vm.newNumber(operationId);
|
|
193
|
+
vm.setProp(operation, 'id', id);
|
|
194
|
+
vm.setProp(operation, 'promise', deferred.handle);
|
|
195
|
+
id.dispose();
|
|
196
|
+
return operation;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
vm.setProp(vm.global, 'tools', tools);
|
|
200
|
+
tools.dispose();
|
|
201
|
+
const consumeToolCall = vm.newFunction('__consumeToolCall', (idHandle) => {
|
|
202
|
+
if (idHandle && vm.typeof(idHandle) === 'number')
|
|
203
|
+
unconsumedToolCalls.delete(vm.getNumber(idHandle));
|
|
204
|
+
return vm.undefined;
|
|
205
|
+
});
|
|
206
|
+
vm.setProp(vm.global, '__consumeToolCall', consumeToolCall);
|
|
207
|
+
consumeToolCall.dispose();
|
|
208
|
+
const out = vm.newObject();
|
|
209
|
+
method(out, 'text', (handle) => {
|
|
210
|
+
const result = vm.callFunction(formatText, vm.undefined, handle ?? vm.undefined);
|
|
211
|
+
if (result.error)
|
|
212
|
+
return { error: result.error };
|
|
213
|
+
try {
|
|
214
|
+
const value = vm.getString(result.value);
|
|
215
|
+
outputBytes += Buffer.byteLength(value) + 1;
|
|
216
|
+
if (outputBytes > input.limits.outputBytes)
|
|
217
|
+
return {
|
|
218
|
+
error: guestError('MAR_AGENT_TOOL_OUTPUT_LIMIT', 'Text output limit exceeded.')
|
|
219
|
+
};
|
|
220
|
+
input.onText(value);
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
result.value.dispose();
|
|
224
|
+
}
|
|
225
|
+
return vm.undefined;
|
|
226
|
+
});
|
|
227
|
+
method(out, 'image', (handle) => {
|
|
228
|
+
const parsed = jsonValue(handle ?? vm.undefined);
|
|
229
|
+
if (parsed.error)
|
|
230
|
+
return parsed;
|
|
231
|
+
input.onImage(parsed.value);
|
|
232
|
+
return vm.undefined;
|
|
233
|
+
});
|
|
234
|
+
vm.setProp(vm.global, 'out', out);
|
|
235
|
+
out.dispose();
|
|
236
|
+
const stateObject = vm.newObject();
|
|
237
|
+
method(stateObject, 'get', (keyHandle) => {
|
|
238
|
+
const parsed = key(keyHandle);
|
|
239
|
+
if (parsed.error)
|
|
240
|
+
return parsed;
|
|
241
|
+
return state.has(parsed.value) ? parse(state.get(parsed.value)) : vm.undefined;
|
|
242
|
+
});
|
|
243
|
+
method(stateObject, 'has', (keyHandle) => {
|
|
244
|
+
const parsed = key(keyHandle);
|
|
245
|
+
if (parsed.error)
|
|
246
|
+
return parsed;
|
|
247
|
+
return state.has(parsed.value) ? vm.true : vm.false;
|
|
248
|
+
});
|
|
249
|
+
method(stateObject, 'set', (keyHandle, valueHandle = vm.undefined) => {
|
|
250
|
+
const parsedKey = key(keyHandle);
|
|
251
|
+
if (parsedKey.error)
|
|
252
|
+
return parsedKey;
|
|
253
|
+
const parsedValue = jsonValue(valueHandle);
|
|
254
|
+
if (parsedValue.error)
|
|
255
|
+
return parsedValue;
|
|
256
|
+
if (Buffer.byteLength(JSON.stringify(parsedValue.value)) > input.limits.stateValueBytes)
|
|
257
|
+
return { error: guestError('MAR_AGENT_TOOL_INPUT_INVALID', 'State value is too large.') };
|
|
258
|
+
const previous = state.has(parsedKey.value)
|
|
259
|
+
? { exists: true, value: state.get(parsedKey.value) }
|
|
260
|
+
: { exists: false };
|
|
261
|
+
state.set(parsedKey.value, parsedValue.value);
|
|
262
|
+
const invalid = validateState();
|
|
263
|
+
if (invalid) {
|
|
264
|
+
if (previous.exists)
|
|
265
|
+
state.set(parsedKey.value, previous.value);
|
|
266
|
+
else
|
|
267
|
+
state.delete(parsedKey.value);
|
|
268
|
+
return { error: invalid };
|
|
269
|
+
}
|
|
270
|
+
if (stateMutations.length >= input.limits.stateMutations) {
|
|
271
|
+
if (previous.exists)
|
|
272
|
+
state.set(parsedKey.value, previous.value);
|
|
273
|
+
else
|
|
274
|
+
state.delete(parsedKey.value);
|
|
275
|
+
return {
|
|
276
|
+
error: guestError('MAR_AGENT_TOOL_OUTPUT_LIMIT', 'Too many state mutations.')
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
stateMutations.push({ operation: 'set', key: parsedKey.value, value: parsedValue.value });
|
|
280
|
+
return vm.undefined;
|
|
281
|
+
});
|
|
282
|
+
method(stateObject, 'delete', (keyHandle) => {
|
|
283
|
+
const parsed = key(keyHandle);
|
|
284
|
+
if (parsed.error)
|
|
285
|
+
return parsed;
|
|
286
|
+
if (!state.has(parsed.value))
|
|
287
|
+
return vm.false;
|
|
288
|
+
if (stateMutations.length >= input.limits.stateMutations)
|
|
289
|
+
return {
|
|
290
|
+
error: guestError('MAR_AGENT_TOOL_OUTPUT_LIMIT', 'Too many state mutations.')
|
|
291
|
+
};
|
|
292
|
+
state.delete(parsed.value);
|
|
293
|
+
stateMutations.push({ operation: 'delete', key: parsed.value });
|
|
294
|
+
return vm.true;
|
|
295
|
+
});
|
|
296
|
+
vm.setProp(vm.global, 'state', stateObject);
|
|
297
|
+
stateObject.dispose();
|
|
298
|
+
const catalog = vm.newObject();
|
|
299
|
+
method(catalog, 'tools', () => {
|
|
300
|
+
const value = parse(input.catalog);
|
|
301
|
+
const frozen = vm.callFunction(deepFreeze, vm.undefined, value);
|
|
302
|
+
value.dispose();
|
|
303
|
+
if (frozen.error)
|
|
304
|
+
return { error: frozen.error };
|
|
305
|
+
return frozen.value;
|
|
306
|
+
});
|
|
307
|
+
vm.setProp(vm.global, 'catalog', catalog);
|
|
308
|
+
catalog.dispose();
|
|
309
|
+
const cell = vm.newObject();
|
|
310
|
+
method(cell, 'sleep', (durationHandle) => {
|
|
311
|
+
if (!durationHandle || vm.typeof(durationHandle) !== 'number')
|
|
312
|
+
return {
|
|
313
|
+
error: guestError('MAR_AGENT_TOOL_INPUT_INVALID', 'Sleep duration must be an integer.')
|
|
314
|
+
};
|
|
315
|
+
const duration = vm.getNumber(durationHandle);
|
|
316
|
+
if (!Number.isSafeInteger(duration) || duration < 0 || duration > input.limits.sleepMs)
|
|
317
|
+
return {
|
|
318
|
+
error: guestError('MAR_AGENT_TOOL_INPUT_INVALID', 'Sleep duration is out of range.')
|
|
319
|
+
};
|
|
320
|
+
const deferred = vm.newPromise();
|
|
321
|
+
deferreds.add(deferred);
|
|
322
|
+
let timer;
|
|
323
|
+
const job = new Promise((resolve) => {
|
|
324
|
+
timer = setTimeout(resolve, duration);
|
|
325
|
+
sleepTimers.add(timer);
|
|
326
|
+
})
|
|
327
|
+
.then(() => {
|
|
328
|
+
sleepTimers.delete(timer);
|
|
329
|
+
if (!disposed && !input.signal.aborted)
|
|
330
|
+
deferred.resolve(vm.undefined);
|
|
331
|
+
})
|
|
332
|
+
.catch((error) => {
|
|
333
|
+
bridgeFailure = diagnostic('MAR_AGENT_CODE_ENGINE_FAILED', String(error?.message ?? error).slice(0, 4096));
|
|
334
|
+
})
|
|
335
|
+
.finally(() => {
|
|
336
|
+
pending.delete(job);
|
|
337
|
+
deferreds.delete(deferred);
|
|
338
|
+
if (deferred.alive)
|
|
339
|
+
deferred.dispose();
|
|
340
|
+
notify();
|
|
341
|
+
});
|
|
342
|
+
pending.add(job);
|
|
343
|
+
return deferred.handle;
|
|
344
|
+
});
|
|
345
|
+
vm.setProp(vm.global, 'cell', cell);
|
|
346
|
+
cell.dispose();
|
|
347
|
+
const lock = vm.evalCode(`
|
|
348
|
+
const rawTools = globalThis.tools;
|
|
349
|
+
const consumeToolCall = globalThis.__consumeToolCall;
|
|
350
|
+
const wrappedTools = Object.fromEntries(Object.keys(rawTools).map(name => [name, (...args) => {
|
|
351
|
+
const operation = rawTools[name](...args);
|
|
352
|
+
const consume = () => consumeToolCall(operation.id);
|
|
353
|
+
return Object.freeze({
|
|
354
|
+
then(onFulfilled, onRejected) {
|
|
355
|
+
consume();
|
|
356
|
+
return operation.promise.then(onFulfilled, onRejected);
|
|
357
|
+
},
|
|
358
|
+
catch(onRejected) {
|
|
359
|
+
consume();
|
|
360
|
+
return operation.promise.catch(onRejected);
|
|
361
|
+
},
|
|
362
|
+
finally(onFinally) {
|
|
363
|
+
consume();
|
|
364
|
+
return operation.promise.finally(onFinally);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
}]));
|
|
368
|
+
globalThis.tools = new Proxy(wrappedTools, { get(target, name) {
|
|
369
|
+
if (name === 'then') return undefined;
|
|
370
|
+
if (!Object.prototype.hasOwnProperty.call(target, name))
|
|
371
|
+
return () => { throw Object.assign(new Error('The requested nested tool is unavailable.'),
|
|
372
|
+
{ code: 'MAR_AGENT_TOOL_NOT_AVAILABLE' }); };
|
|
373
|
+
return target[name];
|
|
374
|
+
}});
|
|
375
|
+
for (const name of ['tools', 'out', 'state', 'catalog', 'cell']) {
|
|
376
|
+
const value = globalThis[name];
|
|
377
|
+
Object.freeze(value);
|
|
378
|
+
Object.defineProperty(globalThis, name, {
|
|
379
|
+
value, writable: false, configurable: false, enumerable: true
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
delete globalThis.__parseJson;
|
|
383
|
+
delete globalThis.__serialize;
|
|
384
|
+
delete globalThis.__text;
|
|
385
|
+
delete globalThis.__deepFreeze;
|
|
386
|
+
delete globalThis.__consumeToolCall;
|
|
387
|
+
`);
|
|
388
|
+
if (lock.error)
|
|
389
|
+
fail(lock.error);
|
|
390
|
+
lock.value.dispose();
|
|
391
|
+
input.signal.addEventListener('abort', notify, { once: true });
|
|
392
|
+
let main;
|
|
393
|
+
try {
|
|
394
|
+
const evaluated = vm.evalCode(`(async () => {\n${input.code}\n})()`, 'code-mode.js');
|
|
395
|
+
if (evaluated.error)
|
|
396
|
+
fail(evaluated.error);
|
|
397
|
+
main = evaluated.value;
|
|
398
|
+
for (;;) {
|
|
399
|
+
input.signal.throwIfAborted();
|
|
400
|
+
if (bridgeFailure)
|
|
401
|
+
throw bridgeFailure;
|
|
402
|
+
const jobs = runtime.executePendingJobs(100);
|
|
403
|
+
if (jobs.error)
|
|
404
|
+
fail(jobs.error);
|
|
405
|
+
jobs.dispose();
|
|
406
|
+
const promiseState = vm.getPromiseState(main);
|
|
407
|
+
if (promiseState.type === 'rejected')
|
|
408
|
+
fail(promiseState.error);
|
|
409
|
+
if (promiseState.type === 'fulfilled') {
|
|
410
|
+
promiseState.value.dispose();
|
|
411
|
+
if (pending.size)
|
|
412
|
+
throw diagnostic('MAR_AGENT_CODE_INCOMPLETE', 'Unawaited asynchronous operations remain; completed tool operations are not rolled back.');
|
|
413
|
+
if (unconsumedToolCalls.size)
|
|
414
|
+
throw diagnostic('MAR_AGENT_CODE_INCOMPLETE', 'Tool calls must be awaited; completed tool operations are not rolled back.');
|
|
415
|
+
return { stateMutations };
|
|
416
|
+
}
|
|
417
|
+
if (runtime.hasPendingJob())
|
|
418
|
+
continue;
|
|
419
|
+
if (Date.now() >= deadline)
|
|
420
|
+
throw diagnostic('MAR_AGENT_CODE_TIMEOUT', 'JavaScript execution exceeded its deadline.');
|
|
421
|
+
if (!pending.size)
|
|
422
|
+
throw diagnostic('MAR_AGENT_CODE_INCOMPLETE', 'JavaScript returned an unresolved promise without active operations.');
|
|
423
|
+
await new Promise((resolve) => {
|
|
424
|
+
wake = resolve;
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
finally {
|
|
429
|
+
input.signal.removeEventListener('abort', notify);
|
|
430
|
+
disposed = true;
|
|
431
|
+
for (const timer of sleepTimers)
|
|
432
|
+
clearTimeout(timer);
|
|
433
|
+
main?.dispose();
|
|
434
|
+
for (const deferred of deferreds)
|
|
435
|
+
if (deferred.alive)
|
|
436
|
+
deferred.dispose();
|
|
437
|
+
parseJson.dispose();
|
|
438
|
+
serialize.dispose();
|
|
439
|
+
formatText.dispose();
|
|
440
|
+
deepFreeze.dispose();
|
|
441
|
+
}
|
|
442
|
+
},
|
|
443
|
+
async dispose() {
|
|
444
|
+
disposed = true;
|
|
445
|
+
if (context?.alive)
|
|
446
|
+
context.dispose();
|
|
447
|
+
if (runtime?.alive)
|
|
448
|
+
runtime.dispose();
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
function diagnostic(code, message) {
|
|
453
|
+
return Object.assign(new Error(message), { code });
|
|
454
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface CodeModeStateEntry {
|
|
2
|
+
readonly key: string;
|
|
3
|
+
readonly value: unknown;
|
|
4
|
+
}
|
|
5
|
+
export type CodeModeStateMutation = {
|
|
6
|
+
readonly operation: 'set';
|
|
7
|
+
readonly key: string;
|
|
8
|
+
readonly value: unknown;
|
|
9
|
+
} | {
|
|
10
|
+
readonly operation: 'delete';
|
|
11
|
+
readonly key: string;
|
|
12
|
+
};
|
|
13
|
+
type StateRecord = {
|
|
14
|
+
readonly type: string;
|
|
15
|
+
readonly payload: unknown;
|
|
16
|
+
};
|
|
17
|
+
export declare function parseCodeModeStateMutations(value: unknown): CodeModeStateMutation[];
|
|
18
|
+
export declare function restoreCodeModeState(records: readonly StateRecord[]): CodeModeStateEntry[];
|
|
19
|
+
export declare function applyCodeModeStateMutations(entries: readonly CodeModeStateEntry[], mutations: readonly CodeModeStateMutation[]): CodeModeStateEntry[];
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { MarAgentError } from '../../error.js';
|
|
2
|
+
import { TOOL_EXECUTION_LIMITS as limits } from '../execution-limits.js';
|
|
3
|
+
export function parseCodeModeStateMutations(value) {
|
|
4
|
+
if (!Array.isArray(value) || value.length > limits.codeStateMaxMutations)
|
|
5
|
+
throw invalidState();
|
|
6
|
+
return value.map((item) => {
|
|
7
|
+
if (!isObject(item) || !validKey(item.key))
|
|
8
|
+
throw invalidState();
|
|
9
|
+
if (item.operation === 'delete' &&
|
|
10
|
+
Object.keys(item).every((key) => ['operation', 'key'].includes(key)))
|
|
11
|
+
return { operation: 'delete', key: item.key };
|
|
12
|
+
if (item.operation === 'set' &&
|
|
13
|
+
Object.keys(item).every((key) => ['operation', 'key', 'value'].includes(key)) &&
|
|
14
|
+
Object.prototype.hasOwnProperty.call(item, 'value'))
|
|
15
|
+
return { operation: 'set', key: item.key, value: cloneStateValue(item.value) };
|
|
16
|
+
throw invalidState();
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export function restoreCodeModeState(records) {
|
|
20
|
+
let state = [];
|
|
21
|
+
for (const record of records) {
|
|
22
|
+
if (record.type !== 'code.state.updated')
|
|
23
|
+
continue;
|
|
24
|
+
if (!isObject(record.payload) ||
|
|
25
|
+
!Object.keys(record.payload).every((key) => key === 'mutations'))
|
|
26
|
+
throw corruptState();
|
|
27
|
+
let mutations;
|
|
28
|
+
try {
|
|
29
|
+
mutations = parseCodeModeStateMutations(record.payload.mutations);
|
|
30
|
+
state = applyCodeModeStateMutations(state, mutations);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (error instanceof MarAgentError)
|
|
34
|
+
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Code Mode state is corrupt.', {
|
|
35
|
+
cause: error
|
|
36
|
+
});
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return state;
|
|
41
|
+
}
|
|
42
|
+
export function applyCodeModeStateMutations(entries, mutations) {
|
|
43
|
+
const state = new Map(entries.map((entry) => [entry.key, cloneStateValue(entry.value)]));
|
|
44
|
+
for (const mutation of mutations) {
|
|
45
|
+
if (mutation.operation === 'delete')
|
|
46
|
+
state.delete(mutation.key);
|
|
47
|
+
else
|
|
48
|
+
state.set(mutation.key, cloneStateValue(mutation.value));
|
|
49
|
+
}
|
|
50
|
+
if (state.size > limits.codeStateMaxEntries)
|
|
51
|
+
throw new MarAgentError('MAR_AGENT_TOOL_OUTPUT_LIMIT', 'Code Mode state has too many entries.');
|
|
52
|
+
const result = [...state].map(([key, value]) => ({ key, value }));
|
|
53
|
+
if (Buffer.byteLength(JSON.stringify(result)) > limits.codeStateMaxTotalBytes)
|
|
54
|
+
throw new MarAgentError('MAR_AGENT_TOOL_OUTPUT_LIMIT', 'Code Mode state is too large.');
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
function validKey(value) {
|
|
58
|
+
return (typeof value === 'string' &&
|
|
59
|
+
value.length > 0 &&
|
|
60
|
+
Buffer.byteLength(value) <= limits.codeStateMaxKeyBytes);
|
|
61
|
+
}
|
|
62
|
+
function cloneStateValue(value) {
|
|
63
|
+
let serialized;
|
|
64
|
+
try {
|
|
65
|
+
serialized = JSON.stringify(value);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
throw invalidState();
|
|
69
|
+
}
|
|
70
|
+
if (serialized === undefined || Buffer.byteLength(serialized) > limits.codeStateMaxValueBytes)
|
|
71
|
+
throw invalidState();
|
|
72
|
+
return JSON.parse(serialized);
|
|
73
|
+
}
|
|
74
|
+
function invalidState() {
|
|
75
|
+
return new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Code Mode state mutation is invalid.');
|
|
76
|
+
}
|
|
77
|
+
function corruptState() {
|
|
78
|
+
return new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Code Mode state is corrupt.');
|
|
79
|
+
}
|
|
80
|
+
function isObject(value) {
|
|
81
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
82
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
2
|
+
// The worker evaluates only a trusted engine module. Model source is passed as data to that engine.
|
|
3
|
+
const lines = createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
4
|
+
const send = (message) => process.stdout.write(JSON.stringify(message) + '\n');
|
|
5
|
+
const pending = new Map();
|
|
6
|
+
const controller = new AbortController();
|
|
7
|
+
let sequence = 0;
|
|
8
|
+
let started = false;
|
|
9
|
+
lines.on('line', async (line) => {
|
|
10
|
+
let engine;
|
|
11
|
+
try {
|
|
12
|
+
const frame = JSON.parse(line);
|
|
13
|
+
if (started) {
|
|
14
|
+
const deferred = pending.get(frame.id);
|
|
15
|
+
if (!deferred)
|
|
16
|
+
return;
|
|
17
|
+
pending.delete(frame.id);
|
|
18
|
+
if (frame.ok)
|
|
19
|
+
deferred.resolve(frame.output);
|
|
20
|
+
else
|
|
21
|
+
deferred.reject(Object.assign(new Error(frame.error.message), { code: frame.error.code }));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
started = true;
|
|
25
|
+
const { createCodeModeEngine } = await import(frame.engine.moduleUrl);
|
|
26
|
+
engine = await createCodeModeEngine(frame.engine.options ?? {});
|
|
27
|
+
const result = await engine.execute({
|
|
28
|
+
code: frame.code,
|
|
29
|
+
tools: frame.tools,
|
|
30
|
+
catalog: frame.catalog,
|
|
31
|
+
state: frame.state,
|
|
32
|
+
limits: frame.limits,
|
|
33
|
+
signal: controller.signal,
|
|
34
|
+
onText: (value) => send({ kind: 'text', value }),
|
|
35
|
+
onImage: (value) => send({ kind: 'image', value }),
|
|
36
|
+
onToolCall: (call) => new Promise((resolve, reject) => {
|
|
37
|
+
const id = ++sequence;
|
|
38
|
+
pending.set(id, { resolve, reject });
|
|
39
|
+
send({ kind: 'call', id, name: call.name, arguments: call.arguments });
|
|
40
|
+
})
|
|
41
|
+
});
|
|
42
|
+
if (pending.size)
|
|
43
|
+
throw Object.assign(new Error('Unawaited tool calls remain.'), {
|
|
44
|
+
code: 'MAR_AGENT_CODE_INCOMPLETE'
|
|
45
|
+
});
|
|
46
|
+
await engine.dispose();
|
|
47
|
+
engine = undefined;
|
|
48
|
+
await new Promise((resolve) => process.stdout.write(JSON.stringify({ kind: 'done', stateMutations: result.stateMutations }) + '\n', resolve));
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
controller.abort(error);
|
|
52
|
+
for (const deferred of pending.values())
|
|
53
|
+
deferred.reject(error);
|
|
54
|
+
pending.clear();
|
|
55
|
+
try {
|
|
56
|
+
await engine?.dispose();
|
|
57
|
+
}
|
|
58
|
+
catch { }
|
|
59
|
+
await new Promise((resolve) => process.stdout.write(JSON.stringify({
|
|
60
|
+
kind: 'error',
|
|
61
|
+
code: typeof error?.code === 'string' ? error.code : 'MAR_AGENT_CODE_ENGINE_FAILED',
|
|
62
|
+
message: String(error?.message ?? error).slice(0, 4096)
|
|
63
|
+
}) + '\n', resolve));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { type LocalProcessExecutor } from '../host/local-process-executor.js';
|
|
2
|
-
import type { ClientToolDefinition } from '../model/contracts.js';
|
|
2
|
+
import type { ClientToolDefinition, ModelMessage } from '../model/contracts.js';
|
|
3
3
|
import type { ToolExecutionContext, ToolExecutionResult } from '../sdk/types.js';
|
|
4
4
|
import { WorkspacePathResolver } from './shared/path-resolver.js';
|
|
5
|
+
import type { CodeModeEngineFactory } from './code-mode/engine.js';
|
|
5
6
|
export declare const codeModeToolDefinition: ClientToolDefinition;
|
|
6
|
-
/**
|
|
7
|
-
export declare function
|
|
7
|
+
/** Deferred definitions travel in search results, not in the stable Code Mode description. */
|
|
8
|
+
export declare function projectCodeModeTools(tools: readonly ClientToolDefinition[], residentTools?: readonly ClientToolDefinition[]): ClientToolDefinition[];
|
|
9
|
+
/** Preserve native search definitions while providing the same discovery evidence to other protocols. */
|
|
10
|
+
export declare function projectCodeModeSearchResult(message: ModelMessage, codeModeEnabled: boolean): ModelMessage;
|
|
8
11
|
export declare class CodeModeTool {
|
|
9
12
|
#private;
|
|
10
13
|
private readonly paths;
|
|
11
14
|
private readonly environment;
|
|
12
15
|
private readonly executor;
|
|
13
|
-
|
|
16
|
+
private readonly engineFactory;
|
|
17
|
+
constructor(paths: WorkspacePathResolver, environment?: NodeJS.ProcessEnv, executor?: LocalProcessExecutor, engineFactory?: CodeModeEngineFactory);
|
|
14
18
|
execute(value: unknown, context: ToolExecutionContext): Promise<ToolExecutionResult>;
|
|
15
19
|
dispose(sessionId?: string): Promise<void>;
|
|
16
20
|
}
|