@myagentroam/agent 0.9.142 → 0.9.143
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/model/anthropic-messages.js +24 -13
- package/dist/agent-core/model/contracts.d.ts +4 -0
- package/dist/agent-core/model/openai-responses.js +16 -9
- package/dist/agent-core/runtime/context/gc-tokens.js +4 -2
- package/dist/agent-core/runtime/context/gc.js +146 -13
- package/dist/agent-core/runtime/context/projection.js +31 -0
- package/dist/agent-core/runtime/events.d.ts +1 -0
- package/dist/agent-core/runtime/execution.js +4 -0
- package/dist/agent-core/session/context-gc-checkpoint.d.ts +3 -0
- package/dist/agent-core/session/context-gc-checkpoint.js +121 -22
- package/dist/agent-core/session/context-gc-observations.d.ts +38 -0
- package/dist/agent-core/session/context-gc-observations.js +150 -0
- package/dist/agent-core/session/context-gc-replacements.d.ts +1 -1
- package/dist/agent-core/session/context-gc-replacements.js +6 -2
- package/dist/agent-core/tools/code-mode/cells.d.ts +3 -0
- package/dist/agent-core/tools/code-mode/cells.js +59 -5
- package/dist/agent-core/tools/code-mode/dispatch.js +7 -4
- package/dist/agent-core/tools/code-mode/engine.d.ts +12 -0
- package/dist/agent-core/tools/code-mode/index.js +176 -13
- package/dist/agent-core/tools/code-mode/observations.d.ts +103 -0
- package/dist/agent-core/tools/code-mode/observations.js +195 -0
- package/dist/agent-core/tools/code-mode/quickjs-engine.js +91 -17
- package/dist/agent-core/tools/code-mode/worker.js +8 -1
- package/dist/agent-core/tools/contracts.d.ts +4 -0
- package/dist/agent-core/tools/execution.d.ts +2 -0
- package/dist/agent-core/tools/execution.js +10 -1
- package/dist/agent-core/tools/output-projection.js +35 -1
- package/package.json +1 -1
|
@@ -29,6 +29,7 @@ export async function createCodeModeEngine(options) {
|
|
|
29
29
|
const stateMutations = [];
|
|
30
30
|
let outputBytes = 0;
|
|
31
31
|
let textCalls = 0;
|
|
32
|
+
const origins = [];
|
|
32
33
|
const notify = () => {
|
|
33
34
|
wake?.();
|
|
34
35
|
wake = undefined;
|
|
@@ -72,6 +73,28 @@ export async function createCodeModeEngine(options) {
|
|
|
72
73
|
});
|
|
73
74
|
};
|
|
74
75
|
globalThis.__text = ${streamText.toString()};
|
|
76
|
+
globalThis.__snapshot = (() => {
|
|
77
|
+
const keys = Reflect.ownKeys, descriptor = Object.getOwnPropertyDescriptor;
|
|
78
|
+
const create = Object.create, stringify = JSON.stringify;
|
|
79
|
+
return (value, errorSnapshot) => {
|
|
80
|
+
const seen = create(null); let depth = 0;
|
|
81
|
+
function walk(item) {
|
|
82
|
+
if (item === null || typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean') return stringify(item);
|
|
83
|
+
if (typeof item !== 'object') throw new Error('Not an original JSON object.');
|
|
84
|
+
for (let i = 0; i < depth; i++) if (seen[i] === item) throw new Error('Cyclic object.');
|
|
85
|
+
seen[depth++] = item;
|
|
86
|
+
const names = keys(item); let text = '{';
|
|
87
|
+
for (let i = 0; i < names.length; i++) {
|
|
88
|
+
const name = names[i], field = descriptor(item, name);
|
|
89
|
+
if (errorSnapshot && depth === 1 && name !== 'code' && name !== 'message' && name !== 'name') continue;
|
|
90
|
+
if (typeof name !== 'string' || !field || !('value' in field)) throw new Error('Accessor object.');
|
|
91
|
+
text += stringify(name) + ':' + Number(field.enumerable) + Number(field.writable) + Number(field.configurable) + walk(field.value) + ';';
|
|
92
|
+
}
|
|
93
|
+
depth--; return text + '}';
|
|
94
|
+
}
|
|
95
|
+
try { return walk(value); } catch { return ''; }
|
|
96
|
+
};
|
|
97
|
+
})();
|
|
75
98
|
globalThis.__deepFreeze = function deepFreeze(value) {
|
|
76
99
|
if (value && typeof value === 'object' && !Object.isFrozen(value)) {
|
|
77
100
|
for (const item of Object.values(value)) deepFreeze(item);
|
|
@@ -86,6 +109,20 @@ export async function createCodeModeEngine(options) {
|
|
|
86
109
|
const parseJson = vm.getProp(vm.global, '__parseJson');
|
|
87
110
|
const serialize = vm.getProp(vm.global, '__serialize');
|
|
88
111
|
const formatText = vm.getProp(vm.global, '__text');
|
|
112
|
+
const snapshotFunction = vm.getProp(vm.global, '__snapshot');
|
|
113
|
+
const snapshot = (handle, errorSnapshot = false) => {
|
|
114
|
+
const value = vm.callFunction(snapshotFunction, vm.undefined, handle, errorSnapshot ? vm.true : vm.false);
|
|
115
|
+
if (value.error) {
|
|
116
|
+
value.error.dispose();
|
|
117
|
+
return '';
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
return vm.getString(value.value);
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
value.value.dispose();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
89
126
|
const deepFreeze = vm.getProp(vm.global, '__deepFreeze');
|
|
90
127
|
const parse = (value) => {
|
|
91
128
|
const handle = vm.newString(JSON.stringify(value));
|
|
@@ -142,11 +179,17 @@ export async function createCodeModeEngine(options) {
|
|
|
142
179
|
const deferred = vm.newPromise();
|
|
143
180
|
deferreds.add(deferred);
|
|
144
181
|
const job = Promise.resolve()
|
|
145
|
-
.then(() => input.onToolCall({ name, arguments: parsed.value }))
|
|
182
|
+
.then(() => input.onToolCall({ name, arguments: parsed.value, callId: String(operationId) }))
|
|
146
183
|
.then((output) => {
|
|
147
184
|
if (disposed || input.signal.aborted)
|
|
148
185
|
return;
|
|
149
186
|
const handle = parse(output);
|
|
187
|
+
origins.push({
|
|
188
|
+
handle: handle.dup(),
|
|
189
|
+
snapshot: snapshot(handle),
|
|
190
|
+
errorSnapshot: false,
|
|
191
|
+
operationId: String(operationId)
|
|
192
|
+
});
|
|
150
193
|
try {
|
|
151
194
|
deferred.resolve(handle);
|
|
152
195
|
}
|
|
@@ -163,6 +206,12 @@ export async function createCodeModeEngine(options) {
|
|
|
163
206
|
const code = vm.newString(String(error?.code ?? 'MAR_AGENT_HOST_OPERATION_FAILED'));
|
|
164
207
|
vm.setProp(handle, 'code', code);
|
|
165
208
|
code.dispose();
|
|
209
|
+
origins.push({
|
|
210
|
+
handle: handle.dup(),
|
|
211
|
+
snapshot: snapshot(handle, true),
|
|
212
|
+
errorSnapshot: true,
|
|
213
|
+
operationId: String(operationId)
|
|
214
|
+
});
|
|
166
215
|
try {
|
|
167
216
|
deferred.reject(handle);
|
|
168
217
|
}
|
|
@@ -198,8 +247,28 @@ export async function createCodeModeEngine(options) {
|
|
|
198
247
|
});
|
|
199
248
|
vm.setProp(vm.global, '__consumeToolCall', consumeToolCall);
|
|
200
249
|
consumeToolCall.dispose();
|
|
201
|
-
const
|
|
202
|
-
|
|
250
|
+
const observeValue = (handle, kind) => {
|
|
251
|
+
for (const origin of origins) {
|
|
252
|
+
if (handle &&
|
|
253
|
+
vm.eq(handle, origin.handle) &&
|
|
254
|
+
origin.snapshot &&
|
|
255
|
+
snapshot(handle, origin.errorSnapshot) === origin.snapshot) {
|
|
256
|
+
input.onObserve({ type: 'tool_result', operationId: origin.operationId });
|
|
257
|
+
return vm.undefined;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (kind === 'result')
|
|
261
|
+
return {
|
|
262
|
+
error: guestError('MAR_AGENT_TOOL_INPUT_INVALID', 'observe.result requires an unmodified original tool result or error.')
|
|
263
|
+
};
|
|
264
|
+
// A standalone image is a value, never an original tool-result observation.
|
|
265
|
+
if (kind === 'image') {
|
|
266
|
+
const parsed = jsonValue(handle);
|
|
267
|
+
if (parsed.error)
|
|
268
|
+
return parsed;
|
|
269
|
+
input.onObserve({ type: 'image', value: parsed.value });
|
|
270
|
+
return vm.undefined;
|
|
271
|
+
}
|
|
203
272
|
const separatorBytes = textCalls ? 1 : 0;
|
|
204
273
|
const capture = new BoundedTextOutput(Math.max(0, input.limits.outputBytes - outputBytes - separatorBytes), '');
|
|
205
274
|
const emit = vm.newFunction('textChunk', (chunk) => {
|
|
@@ -225,22 +294,23 @@ export async function createCodeModeEngine(options) {
|
|
|
225
294
|
const captured = capture.snapshot();
|
|
226
295
|
outputBytes += captured.retainedBytes + separatorBytes;
|
|
227
296
|
textCalls++;
|
|
228
|
-
input.
|
|
297
|
+
input.onObserve({
|
|
298
|
+
type: 'value',
|
|
299
|
+
format: handle && vm.typeof(handle) === 'object' ? 'json' : 'text',
|
|
300
|
+
value: captured.text,
|
|
301
|
+
originalBytes: captured.originalBytes
|
|
302
|
+
});
|
|
229
303
|
}
|
|
230
304
|
finally {
|
|
231
305
|
result.value.dispose();
|
|
232
306
|
}
|
|
233
307
|
return vm.undefined;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
return vm.undefined;
|
|
241
|
-
});
|
|
242
|
-
vm.setProp(vm.global, 'out', out);
|
|
243
|
-
out.dispose();
|
|
308
|
+
};
|
|
309
|
+
const observe = vm.newObject();
|
|
310
|
+
for (const kind of ['result', 'text', 'image'])
|
|
311
|
+
method(observe, kind, (handle) => observeValue(handle, kind));
|
|
312
|
+
vm.setProp(vm.global, 'observe', observe);
|
|
313
|
+
observe.dispose();
|
|
244
314
|
const stateObject = vm.newObject();
|
|
245
315
|
method(stateObject, 'get', (keyHandle) => {
|
|
246
316
|
const parsed = key(keyHandle);
|
|
@@ -384,7 +454,7 @@ export async function createCodeModeEngine(options) {
|
|
|
384
454
|
{ code: 'MAR_AGENT_TOOL_NOT_AVAILABLE' }); };
|
|
385
455
|
return target[name];
|
|
386
456
|
}});
|
|
387
|
-
for (const name of ['tools', '
|
|
457
|
+
for (const name of ['tools', 'observe', 'state', 'catalog', 'cell']) {
|
|
388
458
|
const value = globalThis[name];
|
|
389
459
|
Object.freeze(value);
|
|
390
460
|
Object.defineProperty(globalThis, name, {
|
|
@@ -394,6 +464,7 @@ export async function createCodeModeEngine(options) {
|
|
|
394
464
|
delete globalThis.__parseJson;
|
|
395
465
|
delete globalThis.__serialize;
|
|
396
466
|
delete globalThis.__text;
|
|
467
|
+
delete globalThis.__snapshot;
|
|
397
468
|
delete globalThis.__deepFreeze;
|
|
398
469
|
delete globalThis.__consumeToolCall;
|
|
399
470
|
`);
|
|
@@ -449,6 +520,9 @@ export async function createCodeModeEngine(options) {
|
|
|
449
520
|
parseJson.dispose();
|
|
450
521
|
serialize.dispose();
|
|
451
522
|
formatText.dispose();
|
|
523
|
+
snapshotFunction.dispose();
|
|
524
|
+
for (const origin of origins)
|
|
525
|
+
origin.handle.dispose();
|
|
452
526
|
deepFreeze.dispose();
|
|
453
527
|
}
|
|
454
528
|
},
|
|
@@ -528,7 +602,7 @@ function streamText(value, emit) {
|
|
|
528
602
|
emit('{');
|
|
529
603
|
let first = true;
|
|
530
604
|
const names = Object.keys(item);
|
|
531
|
-
// Special-case view_image and native MCP image objects: text omits Base64;
|
|
605
|
+
// Special-case view_image and native MCP image objects: text omits Base64; observe.image keeps pixels.
|
|
532
606
|
const imagePayload = names.includes('mimeType') &&
|
|
533
607
|
typeof item.mimeType === 'string' &&
|
|
534
608
|
item.mimeType.startsWith('image/');
|
|
@@ -542,7 +616,7 @@ function streamText(value, emit) {
|
|
|
542
616
|
write(imagePayload &&
|
|
543
617
|
(name === 'dataBase64' || (name === 'data' && item.type === 'image')) &&
|
|
544
618
|
typeof field === 'string'
|
|
545
|
-
? '[image payload omitted; use
|
|
619
|
+
? '[image payload omitted; use observe.image]'
|
|
546
620
|
: field, name);
|
|
547
621
|
}
|
|
548
622
|
emit('}');
|
|
@@ -34,10 +34,17 @@ lines.on('line', async (line) => {
|
|
|
34
34
|
onText: (value, originalBytes) => send({ kind: 'text', value, originalBytes }),
|
|
35
35
|
onImage: (value) => send({ kind: 'image', value }),
|
|
36
36
|
onYield: () => send({ kind: 'yield' }),
|
|
37
|
+
onObserve: (value) => send({ kind: 'observe', observation: value }),
|
|
37
38
|
onToolCall: (call) => new Promise((resolve, reject) => {
|
|
38
39
|
const id = ++sequence;
|
|
39
40
|
pending.set(id, { resolve, reject });
|
|
40
|
-
send({
|
|
41
|
+
send({
|
|
42
|
+
kind: 'call',
|
|
43
|
+
id,
|
|
44
|
+
operationId: call.callId,
|
|
45
|
+
name: call.name,
|
|
46
|
+
arguments: call.arguments
|
|
47
|
+
});
|
|
41
48
|
})
|
|
42
49
|
});
|
|
43
50
|
if (pending.size)
|
|
@@ -81,7 +81,9 @@ export interface MarAgentToolImageArtifact {
|
|
|
81
81
|
}
|
|
82
82
|
export type MarAgentToolArtifact = MarAgentToolImageArtifact;
|
|
83
83
|
export interface ToolExecutionResult {
|
|
84
|
+
calls?: readonly import('./code-mode/observations.js').CodeToolCallRecord[];
|
|
84
85
|
content: string;
|
|
86
|
+
observations?: readonly import('./code-mode/observations.js').CodeObservation[];
|
|
85
87
|
data?: unknown;
|
|
86
88
|
/** Runtime-only definitions used to encode a Tool Search output. */
|
|
87
89
|
toolSearchTools?: readonly ClientToolDefinition[];
|
|
@@ -93,9 +95,11 @@ export interface ToolExecutionResult {
|
|
|
93
95
|
artifacts?: readonly MarAgentToolArtifact[];
|
|
94
96
|
}
|
|
95
97
|
export interface CodeCellEventData {
|
|
98
|
+
calls?: readonly import('./code-mode/observations.js').CodeToolCallRecord[];
|
|
96
99
|
cellId: string;
|
|
97
100
|
status: 'running' | 'completed' | 'failed' | 'cancelled';
|
|
98
101
|
cached?: boolean;
|
|
102
|
+
observations?: readonly import('./code-mode/observations.js').CodeObservation[];
|
|
99
103
|
}
|
|
100
104
|
export interface ExecResult {
|
|
101
105
|
status: 'running' | 'completed' | 'cancelled';
|
|
@@ -10,6 +10,8 @@ export type ExecutedToolCall = {
|
|
|
10
10
|
call: ClientToolCall;
|
|
11
11
|
content: string;
|
|
12
12
|
images?: ToolExecutionResult['images'];
|
|
13
|
+
observations?: ToolExecutionResult['observations'];
|
|
14
|
+
calls?: ToolExecutionResult['calls'];
|
|
13
15
|
} & ({
|
|
14
16
|
failed: false;
|
|
15
17
|
output: ToolExecutionResult;
|
|
@@ -68,6 +68,8 @@ export async function executeRoundTools(calls, scope) {
|
|
|
68
68
|
call,
|
|
69
69
|
output,
|
|
70
70
|
content: modelOutput.content,
|
|
71
|
+
...(output.calls ? { calls: output.calls } : {}),
|
|
72
|
+
...(output.observations ? { observations: output.observations } : {}),
|
|
71
73
|
...(output.images?.length ? { images: output.images } : {}),
|
|
72
74
|
failed: false
|
|
73
75
|
};
|
|
@@ -81,7 +83,12 @@ export async function executeRoundTools(calls, scope) {
|
|
|
81
83
|
callId: call.callId,
|
|
82
84
|
...(parentCallId === undefined ? {} : { parentCallId }),
|
|
83
85
|
code: diagnostic.code,
|
|
84
|
-
message: diagnostic.message
|
|
86
|
+
message: diagnostic.message,
|
|
87
|
+
...(partial?.data === undefined
|
|
88
|
+
? {}
|
|
89
|
+
: {
|
|
90
|
+
data: structuredToolEventData(call.name, partial.data)
|
|
91
|
+
})
|
|
85
92
|
});
|
|
86
93
|
return {
|
|
87
94
|
call,
|
|
@@ -94,6 +101,8 @@ export async function executeRoundTools(calls, scope) {
|
|
|
94
101
|
: boundedModelToolOutput(call.name, partial.content, partial.data).content,
|
|
95
102
|
...(partial?.images?.length ? { images: partial.images } : {}),
|
|
96
103
|
failed: true,
|
|
104
|
+
...(partial?.calls ? { calls: partial.calls } : {}),
|
|
105
|
+
...(partial?.observations ? { observations: partial.observations } : {}),
|
|
97
106
|
error
|
|
98
107
|
};
|
|
99
108
|
}
|
|
@@ -14,6 +14,10 @@ export function currentExecutionImages(modelInputImages, toolImages) {
|
|
|
14
14
|
.map((image) => ({ mimeType: image.mimeType, data: Buffer.from(image.data) }));
|
|
15
15
|
}
|
|
16
16
|
export function boundedModelToolOutput(toolName, value, data) {
|
|
17
|
+
if ((toolName === 'code_exec' || toolName === 'code_exec_wait') &&
|
|
18
|
+
isRecord(data) &&
|
|
19
|
+
Array.isArray(data.observations))
|
|
20
|
+
return { content: value, truncated: data.truncated === true };
|
|
17
21
|
const execDiagnostics = (toolName === 'exec' || toolName === 'exec_wait') &&
|
|
18
22
|
isRecord(data) &&
|
|
19
23
|
typeof data.stdout === 'string' &&
|
|
@@ -36,7 +40,37 @@ export function structuredToolEventData(toolName, value) {
|
|
|
36
40
|
return {
|
|
37
41
|
cellId: value.cellId,
|
|
38
42
|
status: value.status,
|
|
39
|
-
cached: value.cached === true
|
|
43
|
+
cached: value.cached === true,
|
|
44
|
+
...(Array.isArray(value.calls)
|
|
45
|
+
? {
|
|
46
|
+
calls: value.calls.filter((entry) => isRecord(entry) &&
|
|
47
|
+
[
|
|
48
|
+
'apply_patch',
|
|
49
|
+
'todo_read',
|
|
50
|
+
'todo_write',
|
|
51
|
+
'agent_start',
|
|
52
|
+
'agent_wait',
|
|
53
|
+
'agent_message',
|
|
54
|
+
'agent_cancel'
|
|
55
|
+
].includes(String(entry.toolName)))
|
|
56
|
+
}
|
|
57
|
+
: {}),
|
|
58
|
+
...(Array.isArray(value.observations)
|
|
59
|
+
? {
|
|
60
|
+
observations: value.observations.filter((entry) => isRecord(entry) &&
|
|
61
|
+
entry.type === 'tool_result' &&
|
|
62
|
+
isRecord(entry.source) &&
|
|
63
|
+
[
|
|
64
|
+
'apply_patch',
|
|
65
|
+
'todo_read',
|
|
66
|
+
'todo_write',
|
|
67
|
+
'agent_start',
|
|
68
|
+
'agent_wait',
|
|
69
|
+
'agent_message',
|
|
70
|
+
'agent_cancel'
|
|
71
|
+
].includes(String(entry.source.toolName)))
|
|
72
|
+
}
|
|
73
|
+
: {})
|
|
40
74
|
};
|
|
41
75
|
if ((toolName === 'agent_start' ||
|
|
42
76
|
toolName === 'agent_wait' ||
|