@myagentroam/agent 0.9.142 → 0.9.144

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 (31) 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/async-queue.js +7 -5
  5. package/dist/agent-core/runtime/context/gc-tokens.js +4 -2
  6. package/dist/agent-core/runtime/context/gc.js +147 -14
  7. package/dist/agent-core/runtime/context/projection.js +32 -1
  8. package/dist/agent-core/runtime/events.d.ts +1 -0
  9. package/dist/agent-core/runtime/execution.js +4 -0
  10. package/dist/agent-core/sdk/session.js +1 -1
  11. package/dist/agent-core/session/context-gc-checkpoint.d.ts +4 -1
  12. package/dist/agent-core/session/context-gc-checkpoint.js +123 -26
  13. package/dist/agent-core/session/context-gc-observations.d.ts +38 -0
  14. package/dist/agent-core/session/context-gc-observations.js +148 -0
  15. package/dist/agent-core/session/context-gc-replacements.d.ts +1 -1
  16. package/dist/agent-core/session/context-gc-replacements.js +6 -4
  17. package/dist/agent-core/tools/callers.js +1 -1
  18. package/dist/agent-core/tools/code-mode/cells.d.ts +3 -0
  19. package/dist/agent-core/tools/code-mode/cells.js +62 -6
  20. package/dist/agent-core/tools/code-mode/dispatch.js +7 -4
  21. package/dist/agent-core/tools/code-mode/engine.d.ts +12 -0
  22. package/dist/agent-core/tools/code-mode/index.js +183 -14
  23. package/dist/agent-core/tools/code-mode/observations.d.ts +103 -0
  24. package/dist/agent-core/tools/code-mode/observations.js +195 -0
  25. package/dist/agent-core/tools/code-mode/quickjs-engine.js +121 -20
  26. package/dist/agent-core/tools/code-mode/worker.js +8 -1
  27. package/dist/agent-core/tools/contracts.d.ts +4 -0
  28. package/dist/agent-core/tools/execution.d.ts +2 -0
  29. package/dist/agent-core/tools/execution.js +10 -1
  30. package/dist/agent-core/tools/output-projection.js +35 -1
  31. package/package.json +1 -1
@@ -0,0 +1,195 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { truncateModelText, countModelTokens } from '../../runtime/token-budget.js';
3
+ export function parseCodeCalls(value) {
4
+ if (!Array.isArray(value) || value.length > 128)
5
+ return undefined;
6
+ const ids = new Set();
7
+ for (const item of value) {
8
+ if (!item ||
9
+ typeof item !== 'object' ||
10
+ typeof item.callId !== 'string' ||
11
+ !item.callId ||
12
+ ids.has(item.callId) ||
13
+ typeof item.toolName !== 'string' ||
14
+ !['running', 'completed', 'failed'].includes(item.status))
15
+ return undefined;
16
+ if (item.result !== undefined && (!item.result || typeof item.result.content !== 'string'))
17
+ return undefined;
18
+ ids.add(item.callId);
19
+ }
20
+ return value;
21
+ }
22
+ export function observationContent(value) {
23
+ return value.type === 'tool_result' ? value.result.content : value.value;
24
+ }
25
+ export function observationImages(value) {
26
+ return value.type === 'tool_result'
27
+ ? (value.result.imageIndices ?? [])
28
+ : (value.imageIndices ?? []);
29
+ }
30
+ export function selectObservationImages(values, images, offset = 0) {
31
+ const selected = [];
32
+ const indices = new Map();
33
+ const observations = values.map((value) => {
34
+ const remap = observationImages(value).flatMap((index) => {
35
+ const image = images[index - offset];
36
+ if (!image)
37
+ return [];
38
+ let next = indices.get(index);
39
+ if (next === undefined) {
40
+ next = selected.length;
41
+ indices.set(index, next);
42
+ selected.push(image);
43
+ }
44
+ return [next];
45
+ });
46
+ return value.type === 'tool_result'
47
+ ? { ...value, result: { ...value.result, imageIndices: remap } }
48
+ : { ...value, imageIndices: remap };
49
+ });
50
+ return { observations, images: selected };
51
+ }
52
+ /** Only the visible projection crosses into model text; program data and inputs stay private. */
53
+ export function visibleObservation(value) {
54
+ return value.type === 'tool_result'
55
+ ? {
56
+ type: value.type,
57
+ toolName: value.source.toolName,
58
+ outcome: value.outcome,
59
+ content: value.result.content,
60
+ ...(value.result.truncated ? { truncated: true } : {})
61
+ }
62
+ : {
63
+ type: value.type,
64
+ format: value.format,
65
+ value: value.value,
66
+ ...(value.truncated ? { truncated: true } : {})
67
+ };
68
+ }
69
+ export function renderObservations(values) {
70
+ return JSON.stringify(values.map(visibleObservation));
71
+ }
72
+ export function renderObservationResult(values, original) {
73
+ try {
74
+ const envelope = JSON.parse(original);
75
+ if (envelope && !Array.isArray(envelope) && typeof envelope === 'object')
76
+ return JSON.stringify({ ...envelope, observations: values.map(visibleObservation) });
77
+ }
78
+ catch { }
79
+ return renderObservations(values);
80
+ }
81
+ /** Truncate each body before encoding, never a JSON array or its trusted identity. */
82
+ export function boundObservations(values, maximumTokens) {
83
+ let remaining = Math.max(0, maximumTokens - 2);
84
+ let truncated = false;
85
+ const observations = [];
86
+ const selected = [];
87
+ let overheadBudget = 0;
88
+ for (const original of values) {
89
+ const empty = original.type === 'tool_result'
90
+ ? { ...original, result: { ...original.result, content: '', truncated: true } }
91
+ : { ...original, value: '', truncated: true };
92
+ const overhead = countModelTokens(JSON.stringify(visibleObservation(empty))) + 8;
93
+ if (overheadBudget + overhead > remaining) {
94
+ truncated = true;
95
+ break;
96
+ }
97
+ selected.push({ original, overhead });
98
+ overheadBudget += overhead;
99
+ }
100
+ for (const { original, overhead } of selected) {
101
+ let bodyBudget = Math.max(0, remaining - overheadBudget - 12);
102
+ let value;
103
+ let bodyTruncated = false;
104
+ for (;;) {
105
+ const bounded = truncateModelText(observationContent(original), bodyBudget);
106
+ bodyTruncated ||= bounded.truncated;
107
+ value =
108
+ original.type === 'tool_result'
109
+ ? {
110
+ ...original,
111
+ result: {
112
+ ...original.result,
113
+ content: bounded.content,
114
+ truncated: original.result.truncated === true || bodyTruncated
115
+ }
116
+ }
117
+ : {
118
+ ...original,
119
+ value: bounded.content,
120
+ truncated: original.truncated === true || bodyTruncated
121
+ };
122
+ if (countModelTokens(JSON.stringify(visibleObservation(value))) + 1 <=
123
+ remaining - (overheadBudget - overhead) ||
124
+ bodyBudget === 0)
125
+ break;
126
+ bodyBudget = Math.floor(bodyBudget / 2);
127
+ }
128
+ overheadBudget -= overhead;
129
+ observations.push(value);
130
+ remaining -= countModelTokens(JSON.stringify(visibleObservation(value))) + 1;
131
+ truncated ||=
132
+ bodyTruncated ||
133
+ (original.type === 'tool_result'
134
+ ? original.result.truncated === true
135
+ : original.truncated === true);
136
+ }
137
+ return { observations, content: renderObservations(observations), truncated };
138
+ }
139
+ export function newObservationId() {
140
+ return randomUUID();
141
+ }
142
+ export function observationModelParts(message) {
143
+ if (!message.observations || !message.images?.length)
144
+ return undefined;
145
+ const prefix = [];
146
+ try {
147
+ const parsed = JSON.parse(message.content ?? '');
148
+ if (parsed && !Array.isArray(parsed) && typeof parsed === 'object') {
149
+ const { observations: _observations, ...metadata } = parsed;
150
+ prefix.push({ type: 'text', text: JSON.stringify(metadata) });
151
+ }
152
+ }
153
+ catch { }
154
+ return [
155
+ ...prefix,
156
+ ...message.observations.flatMap((value) => {
157
+ const parts = [{ type: 'text', text: JSON.stringify(visibleObservation(value)) }];
158
+ for (const index of observationImages(value)) {
159
+ const image = message.images?.[index];
160
+ if (image)
161
+ parts.push({ type: 'image', image });
162
+ }
163
+ return parts;
164
+ })
165
+ ];
166
+ }
167
+ export function parseObservations(value) {
168
+ if (!Array.isArray(value) || value.length > 4096)
169
+ return undefined;
170
+ const ids = new Set();
171
+ for (const item of value) {
172
+ if (!item || typeof item !== 'object' || typeof item.id !== 'string' || ids.has(item.id))
173
+ return undefined;
174
+ ids.add(item.id);
175
+ if (item.type === 'tool_result') {
176
+ if (!item.source ||
177
+ typeof item.source.callId !== 'string' ||
178
+ typeof item.source.toolName !== 'string' ||
179
+ !['completed', 'failed'].includes(item.outcome) ||
180
+ !item.result ||
181
+ typeof item.result.content !== 'string')
182
+ return undefined;
183
+ }
184
+ else if (item.type !== 'value' ||
185
+ !['text', 'json', 'image'].includes(item.format) ||
186
+ typeof item.value !== 'string')
187
+ return undefined;
188
+ const indices = item.type === 'tool_result' ? item.result.imageIndices : item.imageIndices;
189
+ if (indices !== undefined &&
190
+ (!Array.isArray(indices) ||
191
+ indices.some((i) => !Number.isSafeInteger(i) || Number(i) < 0 || Number(i) >= 5)))
192
+ return undefined;
193
+ }
194
+ return value;
195
+ }
@@ -29,18 +29,22 @@ 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;
35
36
  };
36
- const fail = (handle) => {
37
+ const fail = (handle, locateSource = false) => {
37
38
  const error = vm.dump(handle);
38
39
  handle.dispose();
39
40
  if (input.signal.aborted)
40
41
  throw input.signal.reason;
41
42
  if (Date.now() >= deadline)
42
43
  throw diagnostic('MAR_AGENT_CODE_TIMEOUT', 'JavaScript execution exceeded its deadline.');
43
- const message = String(error?.message ?? error).slice(0, 4096);
44
+ let message = String(error?.message ?? error).slice(0, 4096);
45
+ const location = locateSource ? compilerSourceLocation(error, input.code) : '';
46
+ if (location)
47
+ message = message.slice(0, 4096 - location.length) + location;
44
48
  const code = message.includes('out of memory')
45
49
  ? 'MAR_AGENT_CODE_MEMORY_LIMIT'
46
50
  : typeof error?.code === 'string'
@@ -72,6 +76,28 @@ export async function createCodeModeEngine(options) {
72
76
  });
73
77
  };
74
78
  globalThis.__text = ${streamText.toString()};
79
+ globalThis.__snapshot = (() => {
80
+ const keys = Reflect.ownKeys, descriptor = Object.getOwnPropertyDescriptor;
81
+ const create = Object.create, stringify = JSON.stringify;
82
+ return (value, errorSnapshot) => {
83
+ const seen = create(null); let depth = 0;
84
+ function walk(item) {
85
+ if (item === null || typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean') return stringify(item);
86
+ if (typeof item !== 'object') throw new Error('Not an original JSON object.');
87
+ for (let i = 0; i < depth; i++) if (seen[i] === item) throw new Error('Cyclic object.');
88
+ seen[depth++] = item;
89
+ const names = keys(item); let text = '{';
90
+ for (let i = 0; i < names.length; i++) {
91
+ const name = names[i], field = descriptor(item, name);
92
+ if (errorSnapshot && depth === 1 && name !== 'code' && name !== 'message' && name !== 'name') continue;
93
+ if (typeof name !== 'string' || !field || !('value' in field)) throw new Error('Accessor object.');
94
+ text += stringify(name) + ':' + Number(field.enumerable) + Number(field.writable) + Number(field.configurable) + walk(field.value) + ';';
95
+ }
96
+ depth--; return text + '}';
97
+ }
98
+ try { return walk(value); } catch { return ''; }
99
+ };
100
+ })();
75
101
  globalThis.__deepFreeze = function deepFreeze(value) {
76
102
  if (value && typeof value === 'object' && !Object.isFrozen(value)) {
77
103
  for (const item of Object.values(value)) deepFreeze(item);
@@ -86,6 +112,20 @@ export async function createCodeModeEngine(options) {
86
112
  const parseJson = vm.getProp(vm.global, '__parseJson');
87
113
  const serialize = vm.getProp(vm.global, '__serialize');
88
114
  const formatText = vm.getProp(vm.global, '__text');
115
+ const snapshotFunction = vm.getProp(vm.global, '__snapshot');
116
+ const snapshot = (handle, errorSnapshot = false) => {
117
+ const value = vm.callFunction(snapshotFunction, vm.undefined, handle, errorSnapshot ? vm.true : vm.false);
118
+ if (value.error) {
119
+ value.error.dispose();
120
+ return '';
121
+ }
122
+ try {
123
+ return vm.getString(value.value);
124
+ }
125
+ finally {
126
+ value.value.dispose();
127
+ }
128
+ };
89
129
  const deepFreeze = vm.getProp(vm.global, '__deepFreeze');
90
130
  const parse = (value) => {
91
131
  const handle = vm.newString(JSON.stringify(value));
@@ -142,11 +182,17 @@ export async function createCodeModeEngine(options) {
142
182
  const deferred = vm.newPromise();
143
183
  deferreds.add(deferred);
144
184
  const job = Promise.resolve()
145
- .then(() => input.onToolCall({ name, arguments: parsed.value }))
185
+ .then(() => input.onToolCall({ name, arguments: parsed.value, callId: String(operationId) }))
146
186
  .then((output) => {
147
187
  if (disposed || input.signal.aborted)
148
188
  return;
149
189
  const handle = parse(output);
190
+ origins.push({
191
+ handle: handle.dup(),
192
+ snapshot: snapshot(handle),
193
+ errorSnapshot: false,
194
+ operationId: String(operationId)
195
+ });
150
196
  try {
151
197
  deferred.resolve(handle);
152
198
  }
@@ -163,6 +209,12 @@ export async function createCodeModeEngine(options) {
163
209
  const code = vm.newString(String(error?.code ?? 'MAR_AGENT_HOST_OPERATION_FAILED'));
164
210
  vm.setProp(handle, 'code', code);
165
211
  code.dispose();
212
+ origins.push({
213
+ handle: handle.dup(),
214
+ snapshot: snapshot(handle, true),
215
+ errorSnapshot: true,
216
+ operationId: String(operationId)
217
+ });
166
218
  try {
167
219
  deferred.reject(handle);
168
220
  }
@@ -198,8 +250,28 @@ export async function createCodeModeEngine(options) {
198
250
  });
199
251
  vm.setProp(vm.global, '__consumeToolCall', consumeToolCall);
200
252
  consumeToolCall.dispose();
201
- const out = vm.newObject();
202
- method(out, 'text', (handle) => {
253
+ const observeValue = (handle, kind) => {
254
+ for (const origin of origins) {
255
+ if (handle &&
256
+ vm.eq(handle, origin.handle) &&
257
+ origin.snapshot &&
258
+ snapshot(handle, origin.errorSnapshot) === origin.snapshot) {
259
+ input.onObserve({ type: 'tool_result', operationId: origin.operationId });
260
+ return vm.undefined;
261
+ }
262
+ }
263
+ if (kind === 'result')
264
+ return {
265
+ error: guestError('MAR_AGENT_TOOL_INPUT_INVALID', 'observe.result requires an unmodified original tool result or error.')
266
+ };
267
+ // A standalone image is a value, never an original tool-result observation.
268
+ if (kind === 'image') {
269
+ const parsed = jsonValue(handle);
270
+ if (parsed.error)
271
+ return parsed;
272
+ input.onObserve({ type: 'image', value: parsed.value });
273
+ return vm.undefined;
274
+ }
203
275
  const separatorBytes = textCalls ? 1 : 0;
204
276
  const capture = new BoundedTextOutput(Math.max(0, input.limits.outputBytes - outputBytes - separatorBytes), '');
205
277
  const emit = vm.newFunction('textChunk', (chunk) => {
@@ -225,22 +297,23 @@ export async function createCodeModeEngine(options) {
225
297
  const captured = capture.snapshot();
226
298
  outputBytes += captured.retainedBytes + separatorBytes;
227
299
  textCalls++;
228
- input.onText(captured.text, captured.originalBytes);
300
+ input.onObserve({
301
+ type: 'value',
302
+ format: handle && vm.typeof(handle) === 'object' ? 'json' : 'text',
303
+ value: captured.text,
304
+ originalBytes: captured.originalBytes
305
+ });
229
306
  }
230
307
  finally {
231
308
  result.value.dispose();
232
309
  }
233
310
  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();
311
+ };
312
+ const observe = vm.newObject();
313
+ for (const kind of ['result', 'text', 'image'])
314
+ method(observe, kind, (handle) => observeValue(handle, kind));
315
+ vm.setProp(vm.global, 'observe', observe);
316
+ observe.dispose();
244
317
  const stateObject = vm.newObject();
245
318
  method(stateObject, 'get', (keyHandle) => {
246
319
  const parsed = key(keyHandle);
@@ -384,7 +457,7 @@ export async function createCodeModeEngine(options) {
384
457
  { code: 'MAR_AGENT_TOOL_NOT_AVAILABLE' }); };
385
458
  return target[name];
386
459
  }});
387
- for (const name of ['tools', 'out', 'state', 'catalog', 'cell']) {
460
+ for (const name of ['tools', 'observe', 'state', 'catalog', 'cell']) {
388
461
  const value = globalThis[name];
389
462
  Object.freeze(value);
390
463
  Object.defineProperty(globalThis, name, {
@@ -394,6 +467,7 @@ export async function createCodeModeEngine(options) {
394
467
  delete globalThis.__parseJson;
395
468
  delete globalThis.__serialize;
396
469
  delete globalThis.__text;
470
+ delete globalThis.__snapshot;
397
471
  delete globalThis.__deepFreeze;
398
472
  delete globalThis.__consumeToolCall;
399
473
  `);
@@ -405,7 +479,7 @@ export async function createCodeModeEngine(options) {
405
479
  try {
406
480
  const evaluated = vm.evalCode(`(async () => {\n${input.code}\n})()`, 'code-mode.js');
407
481
  if (evaluated.error)
408
- fail(evaluated.error);
482
+ fail(evaluated.error, true);
409
483
  main = evaluated.value;
410
484
  for (;;) {
411
485
  input.signal.throwIfAborted();
@@ -449,6 +523,9 @@ export async function createCodeModeEngine(options) {
449
523
  parseJson.dispose();
450
524
  serialize.dispose();
451
525
  formatText.dispose();
526
+ snapshotFunction.dispose();
527
+ for (const origin of origins)
528
+ origin.handle.dispose();
452
529
  deepFreeze.dispose();
453
530
  }
454
531
  },
@@ -528,7 +605,7 @@ function streamText(value, emit) {
528
605
  emit('{');
529
606
  let first = true;
530
607
  const names = Object.keys(item);
531
- // Special-case view_image and native MCP image objects: text omits Base64; out.image keeps pixels.
608
+ // Special-case view_image and native MCP image objects: text omits Base64; observe.image keeps pixels.
532
609
  const imagePayload = names.includes('mimeType') &&
533
610
  typeof item.mimeType === 'string' &&
534
611
  item.mimeType.startsWith('image/');
@@ -542,7 +619,7 @@ function streamText(value, emit) {
542
619
  write(imagePayload &&
543
620
  (name === 'dataBase64' || (name === 'data' && item.type === 'image')) &&
544
621
  typeof field === 'string'
545
- ? '[image payload omitted; use out.image]'
622
+ ? '[image payload omitted; use observe.image]'
546
623
  : field, name);
547
624
  }
548
625
  emit('}');
@@ -551,6 +628,30 @@ function streamText(value, emit) {
551
628
  };
552
629
  write(value, '');
553
630
  }
631
+ function compilerSourceLocation(error, source) {
632
+ if (error?.name !== 'SyntaxError' ||
633
+ error.fileName !== 'code-mode.js' ||
634
+ !Number.isSafeInteger(error.lineNumber) ||
635
+ error.lineNumber <= 1)
636
+ return '';
637
+ // QuickJS counts LF lines and Unicode code-point columns; the wrapper adds one LF line.
638
+ const lines = source.split('\n');
639
+ const line = error.lineNumber - 1;
640
+ if (line > lines.length)
641
+ return ` (code-mode.js:${source.split(/\r\n|[\r\n\u2028\u2029]/u).length}, end of input)`;
642
+ const column = Number(typeof error.stack === 'string'
643
+ ? error.stack.match(/^\s*at code-mode\.js:\d+:(\d+)(?:\n|$)/u)?.[1]
644
+ : undefined);
645
+ if (!Number.isSafeInteger(column) || column < 1)
646
+ return '';
647
+ const prefix = [
648
+ ...lines.slice(0, line - 1),
649
+ Array.from(lines[line - 1])
650
+ .slice(0, column - 1)
651
+ .join('')
652
+ ].join('\n');
653
+ return ` (code-mode.js:${prefix.split(/\r\n|[\r\n\u2028\u2029]/u).length})`;
654
+ }
554
655
  function diagnostic(code, message) {
555
656
  return Object.assign(new Error(message), { code });
556
657
  }
@@ -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.144",
4
4
  "description": "Embeddable MAR coding agent SDK and CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",