@myagentroam/agent 0.11.43 → 0.11.44

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.
@@ -3,7 +3,7 @@ import type { ToolExecutionContext, ToolExecutionResult } from '../contracts.js'
3
3
  import type { CodeObservation, CodeToolCallRecord } from '../../session/code-observation-contract.js';
4
4
  export interface CellOutput {
5
5
  call(value: CodeToolCallRecord): void;
6
- observe(value: CodeObservation): void;
6
+ observe(value: CodeObservation, display?: CodeObservation, displayHeadTruncated?: boolean): void;
7
7
  text(value: string, originalBytes?: number): void;
8
8
  image(value: NonNullable<ToolExecutionResult['images']>[number]): void;
9
9
  yield(): void;
@@ -6,6 +6,7 @@ import { TOOL_EXECUTION_LIMITS as limits } from '../execution-limits.js';
6
6
  import { waitForToolCondition, waitMetadata } from '../wait.js';
7
7
  import { BoundedTextOutput, renderCapturedText } from './text-output.js';
8
8
  import { boundObservations, newObservationId } from './observations.js';
9
+ import { publicCodeObservations } from '../output-projection.js';
9
10
  import { selectObservationImages, visibleObservation } from '../../session/code-observation-projection.js';
10
11
  /** Keeps a failed tool call observable without discarding earlier explicit output. */
11
12
  export class CodeCellExecutionError extends AgentError {
@@ -214,7 +215,18 @@ export class CodeCells {
214
215
  releaseSession = await context.codeMode?.retainSession?.();
215
216
  return run(source, { ...context, signal: controller.signal, waitInterrupt: undefined }, {
216
217
  call: (value) => cell.calls.set(value.callId, value),
217
- observe: (value) => cell.observations.push(value),
218
+ observe: (value, display = value, displayHeadTruncated = false) => {
219
+ cell.observations.push(value);
220
+ const observations = [...(cell.display?.observations ?? []), display];
221
+ cell.display = publicCodeObservations(observations, {
222
+ cellId: cell.cellId,
223
+ status: cell.status,
224
+ observations,
225
+ ...(cell.display?.outputHeadTruncated || displayHeadTruncated
226
+ ? { outputHeadTruncated: true }
227
+ : {})
228
+ });
229
+ },
218
230
  text: (text, originalBytes) => cell.text.append(text, originalBytes),
219
231
  image: (image) => cell.images.push(image),
220
232
  yield: () => {
@@ -359,7 +371,14 @@ export class CodeCells {
359
371
  observations: visible.observations.map(visibleObservation)
360
372
  }),
361
373
  data,
362
- eventData: { ...data, executionSource: cell.source },
374
+ eventData: {
375
+ ...data,
376
+ ...cell.display,
377
+ status: cell.status,
378
+ cached: true,
379
+ ...wait,
380
+ executionSource: cell.source
381
+ },
363
382
  truncated: data.truncated
364
383
  };
365
384
  }
@@ -430,7 +449,12 @@ export class CodeCells {
430
449
  calls: [...cell.calls.values()],
431
450
  observations: remapped,
432
451
  data,
433
- eventData: { ...data, executionSource: cell.source },
452
+ eventData: {
453
+ ...data,
454
+ ...cell.display,
455
+ status: cell.status,
456
+ executionSource: cell.source
457
+ },
434
458
  truncated: cell.truncated || structured.truncated,
435
459
  ...(visibleImages.length ? { images: visibleImages } : {})
436
460
  };
@@ -49,6 +49,9 @@ export function createCodeToolDispatch(input) {
49
49
  const { eventData: _eventData, ...guestResult } = result;
50
50
  return {
51
51
  ...guestResult,
52
+ ...((call.name === 'exec' || call.name === 'exec_wait') && data !== undefined
53
+ ? { eventData: { ...data } }
54
+ : {}),
52
55
  content: bounded.content,
53
56
  truncated: result.truncated === true || bounded.truncated
54
57
  };
@@ -41,6 +41,8 @@ export interface CodeModeEngine {
41
41
  format: 'text' | 'json';
42
42
  value: string;
43
43
  originalBytes: number;
44
+ /** Public display tail, independent of the model capture budget. */
45
+ displayValue?: string;
44
46
  } | {
45
47
  type: 'image';
46
48
  value: unknown;
@@ -300,7 +300,7 @@ export class CodeModeTool {
300
300
  const images = [];
301
301
  const observations = [];
302
302
  let observationBytes = 0;
303
- const appendObservation = (value, originalBytes) => {
303
+ const appendObservation = (value, originalBytes, display) => {
304
304
  const capture = new BoundedTextOutput(Math.max(0, limits.codeMaxOutputBytes - observationBytes), '');
305
305
  const body = value.type === 'tool_result' ? value.result.content : value.value;
306
306
  capture.append(body);
@@ -323,7 +323,10 @@ export class CodeModeTool {
323
323
  truncated: value.truncated === true || bounded.droppedBytes > 0
324
324
  };
325
325
  observations.push(observation);
326
- output?.observe(observation);
326
+ output?.observe(observation, display ?? value, display !== undefined &&
327
+ (display.type === 'tool_result'
328
+ ? display.result.truncated === true
329
+ : display.truncated === true));
327
330
  };
328
331
  const toolResults = new Map();
329
332
  let transportBytes = 0;
@@ -389,6 +392,7 @@ export class CodeModeTool {
389
392
  return index;
390
393
  };
391
394
  let observation;
395
+ let displayObservation;
392
396
  if (value.type === 'tool_result' && typeof value.operationId === 'string') {
393
397
  const original = toolResults.get(value.operationId);
394
398
  if (!original)
@@ -423,6 +427,23 @@ export class CodeModeTool {
423
427
  ...(indices?.length ? { imageIndices: indices } : {})
424
428
  }
425
429
  };
430
+ const display = original.result.eventData?.displayOutput;
431
+ if ((original.source.toolName === 'exec' || original.source.toolName === 'exec_wait') &&
432
+ display !== null &&
433
+ typeof display === 'object' &&
434
+ 'stdout' in display &&
435
+ 'stderr' in display &&
436
+ typeof display.stdout === 'string' &&
437
+ typeof display.stderr === 'string')
438
+ displayObservation = {
439
+ ...observation,
440
+ result: {
441
+ content: '',
442
+ data: { stdout: display.stdout, stderr: display.stderr },
443
+ truncated: ('stdoutDroppedBytes' in display && Number(display.stdoutDroppedBytes) > 0) ||
444
+ ('stderrDroppedBytes' in display && Number(display.stderrDroppedBytes) > 0)
445
+ }
446
+ };
426
447
  }
427
448
  else if (value.type === 'image') {
428
449
  observation = {
@@ -444,12 +465,22 @@ export class CodeModeTool {
444
465
  truncated: typeof value.originalBytes === 'number' &&
445
466
  value.originalBytes > Buffer.byteLength(value.value)
446
467
  };
468
+ if (typeof value.displayValue === 'string') {
469
+ if (Buffer.byteLength(value.displayValue) > 10_000)
470
+ throw new AgentError('MAR_AGENT_CODE_PROTOCOL_ERROR', 'Code Mode sent oversized display text.');
471
+ displayObservation = {
472
+ ...observation,
473
+ value: value.displayValue,
474
+ truncated: typeof value.originalBytes === 'number' &&
475
+ value.originalBytes > Buffer.byteLength(value.displayValue)
476
+ };
477
+ }
447
478
  }
448
479
  else
449
480
  throw new AgentError('MAR_AGENT_CODE_PROTOCOL_ERROR', 'Invalid observation frame.');
450
481
  appendObservation(observation, value.type === 'value' && typeof value.originalBytes === 'number'
451
482
  ? value.originalBytes
452
- : undefined);
483
+ : undefined, displayObservation);
453
484
  }
454
485
  else if (frame.kind === 'text' && typeof frame.value === 'string') {
455
486
  const originalBytes = frame.originalBytes ?? Buffer.byteLength(frame.value);
@@ -527,10 +558,11 @@ export class CodeModeTool {
527
558
  : undefined
528
559
  }
529
560
  });
530
- const { content, ...structured } = output;
561
+ const { eventData: _eventData, ...guestOutput } = output;
562
+ const { content, ...structured } = guestOutput;
531
563
  const projected = output.data !== undefined && content === JSON.stringify(output.data)
532
564
  ? structured
533
- : output;
565
+ : guestOutput;
534
566
  await write({ id, ok: true, output: projected });
535
567
  }
536
568
  catch (error) {
@@ -354,6 +354,7 @@ export async function createCodeModeEngine(options) {
354
354
  }
355
355
  const separatorBytes = textCalls ? 1 : 0;
356
356
  const capture = new BoundedTextOutput(Math.max(0, input.limits.outputBytes - outputBytes - separatorBytes), '');
357
+ const display = new BoundedTextOutput(10_000, '', 'tail');
357
358
  const emit = vm.newFunction('textChunk', (chunk) => {
358
359
  if (!chunk || vm.typeof(chunk) !== 'string')
359
360
  return {
@@ -366,7 +367,9 @@ export async function createCodeModeEngine(options) {
366
367
  return {
367
368
  error: guestError('MAR_AGENT_TOOL_OUTPUT_LIMIT', 'Text serialization chunk limit exceeded.')
368
369
  };
369
- capture.append(vm.getString(chunk));
370
+ const text = vm.getString(chunk);
371
+ capture.append(text);
372
+ display.append(text);
370
373
  return vm.undefined;
371
374
  });
372
375
  const result = callGuest(formatText, vm.undefined, handle ?? vm.undefined, emit);
@@ -381,6 +384,7 @@ export async function createCodeModeEngine(options) {
381
384
  type: 'value',
382
385
  format: handle && vm.typeof(handle) === 'object' ? 'json' : 'text',
383
386
  value: captured.text,
387
+ displayValue: display.snapshot().text,
384
388
  originalBytes: captured.originalBytes
385
389
  });
386
390
  }
@@ -1,9 +1,10 @@
1
1
  export function renderCapturedText(snapshot: any, imageCount?: number): string;
2
2
  /** Bounded UTF-8 capture shared by trusted engines, the bridge and retained cells. */
3
3
  export class BoundedTextOutput {
4
- constructor(maximumBytes: any, separator?: string);
4
+ constructor(maximumBytes: any, separator?: string, retention?: string);
5
5
  maximumBytes: any;
6
6
  separator: string;
7
+ retention: string;
7
8
  append(value: any, originalBytes?: number): void;
8
9
  snapshot(): {
9
10
  text: string;
@@ -6,9 +6,10 @@ export class BoundedTextOutput {
6
6
  #retained = 0;
7
7
  #hasValue = false;
8
8
  #sealed = false;
9
- constructor(maximumBytes, separator = '\n') {
9
+ constructor(maximumBytes, separator = '\n', retention = 'head') {
10
10
  this.maximumBytes = maximumBytes;
11
11
  this.separator = separator;
12
+ this.retention = retention;
12
13
  }
13
14
  append(value, originalBytes = Buffer.byteLength(value)) {
14
15
  const separator = this.#hasValue ? this.separator : '';
@@ -16,10 +17,22 @@ export class BoundedTextOutput {
16
17
  this.#original += Buffer.byteLength(separator) + originalBytes;
17
18
  this.#capture(separator);
18
19
  this.#capture(value);
19
- if (originalBytes > Buffer.byteLength(value))
20
+ if (this.retention === 'head' && originalBytes > Buffer.byteLength(value))
20
21
  this.#sealed = true;
21
22
  }
22
23
  #capture(value) {
24
+ if (this.retention === 'tail') {
25
+ let start = Math.max(0, value.length - this.maximumBytes);
26
+ if (start > 0 && /[\uDC00-\uDFFF]/u.test(value[start]))
27
+ start++;
28
+ const bytes = Buffer.from(this.#chunks.join('') + value.slice(start));
29
+ let offset = Math.max(0, bytes.length - this.maximumBytes);
30
+ while (offset < bytes.length && (bytes[offset] & 0xc0) === 0x80)
31
+ offset++;
32
+ this.#chunks = [bytes.subarray(offset).toString('utf8')];
33
+ this.#retained = bytes.length - offset;
34
+ return;
35
+ }
23
36
  const remaining = Math.max(0, this.maximumBytes - this.#used);
24
37
  if (this.#sealed || !value.length)
25
38
  return;
@@ -12,6 +12,8 @@ export declare function boundedModelToolOutput(toolName: string, value: string,
12
12
  };
13
13
  /** The opted-in allowlist fails closed and never mutates the invocation arguments. */
14
14
  export declare function publicToolEventInput(value: unknown, fields: readonly string[]): Readonly<Record<string, unknown>>;
15
+ /** Explicit observations are public, but their event projection must remain bounded. */
16
+ export declare function publicCodeObservations(values: unknown, data: CodeCellEventData): CodeCellEventData;
15
17
  /** Project only known, validated tool result shapes into public structured events. */
16
18
  export declare function structuredToolEventData(toolName: string, value: unknown): CodeCellEventData | ExecResult | ApplyPatchResult | ReadEventData | ViewImageResult | SleepResult | PublicToolMetadata | undefined;
17
19
  /** Partial patch facts are allowlisted; arbitrary error details never become public event data. */
@@ -68,7 +68,7 @@ export function publicToolEventInput(value, fields) {
68
68
  }
69
69
  }
70
70
  /** Explicit observations are public, but their event projection must remain bounded. */
71
- function publicCodeObservations(values, data) {
71
+ export function publicCodeObservations(values, data) {
72
72
  if (!Array.isArray(values))
73
73
  return data;
74
74
  const retained = new Set(data.observations ?? []);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/agent",
3
- "version": "0.11.43",
3
+ "version": "0.11.44",
4
4
  "description": "Embeddable MAR coding agent SDK and CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",