@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.
Files changed (28) hide show
  1. package/dist/agent-core/model/anthropic-messages.js +24 -13
  2. package/dist/agent-core/model/contracts.d.ts +4 -0
  3. package/dist/agent-core/model/openai-responses.js +16 -9
  4. package/dist/agent-core/runtime/context/gc-tokens.js +4 -2
  5. package/dist/agent-core/runtime/context/gc.js +146 -13
  6. package/dist/agent-core/runtime/context/projection.js +31 -0
  7. package/dist/agent-core/runtime/events.d.ts +1 -0
  8. package/dist/agent-core/runtime/execution.js +4 -0
  9. package/dist/agent-core/session/context-gc-checkpoint.d.ts +3 -0
  10. package/dist/agent-core/session/context-gc-checkpoint.js +121 -22
  11. package/dist/agent-core/session/context-gc-observations.d.ts +38 -0
  12. package/dist/agent-core/session/context-gc-observations.js +150 -0
  13. package/dist/agent-core/session/context-gc-replacements.d.ts +1 -1
  14. package/dist/agent-core/session/context-gc-replacements.js +6 -2
  15. package/dist/agent-core/tools/code-mode/cells.d.ts +3 -0
  16. package/dist/agent-core/tools/code-mode/cells.js +59 -5
  17. package/dist/agent-core/tools/code-mode/dispatch.js +7 -4
  18. package/dist/agent-core/tools/code-mode/engine.d.ts +12 -0
  19. package/dist/agent-core/tools/code-mode/index.js +176 -13
  20. package/dist/agent-core/tools/code-mode/observations.d.ts +103 -0
  21. package/dist/agent-core/tools/code-mode/observations.js +195 -0
  22. package/dist/agent-core/tools/code-mode/quickjs-engine.js +91 -17
  23. package/dist/agent-core/tools/code-mode/worker.js +8 -1
  24. package/dist/agent-core/tools/contracts.d.ts +4 -0
  25. package/dist/agent-core/tools/execution.d.ts +2 -0
  26. package/dist/agent-core/tools/execution.js +10 -1
  27. package/dist/agent-core/tools/output-projection.js +35 -1
  28. 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 out = vm.newObject();
202
- method(out, 'text', (handle) => {
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.onText(captured.text, captured.originalBytes);
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
- method(out, 'image', (handle) => {
236
- const parsed = jsonValue(handle ?? vm.undefined);
237
- if (parsed.error)
238
- return parsed;
239
- input.onImage(parsed.value);
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', 'out', 'state', 'catalog', 'cell']) {
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; out.image keeps pixels.
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 out.image]'
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({ kind: 'call', id, name: call.name, arguments: call.arguments });
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' ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/agent",
3
- "version": "0.9.142",
3
+ "version": "0.9.143",
4
4
  "description": "Embeddable MAR coding agent SDK and CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",