@myagentroam/agent 0.10.43 → 0.10.45

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.
@@ -1,6 +1,6 @@
1
1
  import type { ExecutionMode } from '../runtime/contracts.js';
2
2
  import { type CorePromptPart } from '../extensions/prompt-composition.js';
3
- export declare const MAR_AGENT_PROMPT_VERSION = "1.65";
3
+ export declare const MAR_AGENT_PROMPT_VERSION = "1.66";
4
4
  interface SystemPromptInput {
5
5
  mode: ExecutionMode;
6
6
  platform: string;
@@ -5,7 +5,7 @@ import { modePrompt } from './modes.js';
5
5
  import { outputStylePrompt } from './output.js';
6
6
  import { subagentPrompt } from './subagent.js';
7
7
  import { interactionPrompt, longRunningPrompt, safetyPrompt, toolUsagePrompt, workflowPrompt } from './workflow.js';
8
- export const MAR_AGENT_PROMPT_VERSION = '1.65';
8
+ export const MAR_AGENT_PROMPT_VERSION = '1.66';
9
9
  export function buildSystemPrompt(input) {
10
10
  return renderPromptSections(buildSystemPromptSections(input));
11
11
  }
@@ -1,5 +1,6 @@
1
1
  export const subagentPrompt = [
2
2
  '# Subagents',
3
+ 'Unread child messages and terminal results are supplied before the next model request.',
3
4
  'Call agent_start, agent_wait, agent_message and agent_cancel directly, never inside code_exec; they are absent from the cell tools namespace.',
4
5
  'Delegate bounded, self-contained tasks when independent progress or context savings materially helps. Keep simple or tightly coupled work local; do not split work merely to use agents.',
5
6
  'All agents share the filesystem. Give each child an objective, necessary context, expected output, and an exclusive write scope or a read-only boundary; avoid conflicting writes.',
@@ -30,6 +30,7 @@ import { MAR_AGENT_PROMPT_VERSION, buildSystemPromptSections } from '../prompts/
30
30
  import { renderPromptSections } from '../extensions/prompt-composition.js';
31
31
  import { retainedSessionResourcesSnapshot } from '../prompts/resources.js';
32
32
  import { childSubagentInstruction } from '../prompts/subagent.js';
33
+ import { renderSubagentUpdate } from '../subagent/model-update.js';
33
34
  import { AsyncQueue } from '../runtime/async-queue.js';
34
35
  import { CompactionOperation } from '../runtime/compaction/operation.js';
35
36
  import { CompactCoordinator } from '../runtime/compaction/coordinator.js';
@@ -1516,8 +1517,29 @@ export function createSessionOpener(services) {
1516
1517
  });
1517
1518
  messageSources.set(message, stored);
1518
1519
  }),
1519
- prepareRound: async function prepareRound(pendingOutputContinuation) {
1520
+ prepareRound: async function prepareRound(pendingOutputContinuation, subagentUpdatesCollected = false) {
1520
1521
  await appendMailboxMessages();
1522
+ if (!subagentUpdatesCollected && subagents)
1523
+ await withRecordProjection(async () => {
1524
+ controller.signal.throwIfAborted();
1525
+ for (const update of subagents.collectModelUpdates()) {
1526
+ controller.signal.throwIfAborted();
1527
+ const message = {
1528
+ role: 'user',
1529
+ content: renderSubagentUpdate(update.snapshot, update.truncated),
1530
+ contextKind: 'contextual'
1531
+ };
1532
+ const stored = await store.append(sessionId, {
1533
+ type: 'model.context',
1534
+ payload: message,
1535
+ executionId,
1536
+ turnId
1537
+ }, { signal: controller.signal });
1538
+ messages.push(message);
1539
+ messageSources.set(message, stored);
1540
+ update.acknowledge();
1541
+ }
1542
+ });
1521
1543
  updateMcpRegistry();
1522
1544
  executionTools = mcpRegistry.project(baseTools());
1523
1545
  await syncMcpWorldState(messages.length);
@@ -1557,7 +1579,6 @@ export function createSessionOpener(services) {
1557
1579
  : {}),
1558
1580
  messages: modelMessages,
1559
1581
  tools: requestTools,
1560
- ...(permissionPolicy.mode === 'auto' ? { allowHostedTools: false } : {}),
1561
1582
  promptCacheKey: sessionId,
1562
1583
  reasoningEffort,
1563
1584
  ...(requestReasoningEffort === undefined ? {} : { requestReasoningEffort }),
@@ -1587,7 +1608,7 @@ export function createSessionOpener(services) {
1587
1608
  if (lateCoreChange || lateAdopted) {
1588
1609
  // Middleware may admit a blocking join after the initial checkpoint.
1589
1610
  // Never sample the candidate assembled against the replaced context.
1590
- return prepareRound(pendingOutputContinuation);
1611
+ return prepareRound(pendingOutputContinuation, true);
1591
1612
  }
1592
1613
  return { request, toolScope };
1593
1614
  },
@@ -0,0 +1,2 @@
1
+ import type { SubagentTaskSnapshot } from './contracts.js';
2
+ export declare function renderSubagentUpdate(snapshot: SubagentTaskSnapshot, truncated?: boolean): string;
@@ -0,0 +1,85 @@
1
+ import { escapeMarkupText, markupAttributes } from '../prompts/markup.js';
2
+ import { TOOL_EXECUTION_LIMITS } from '../tools/execution-limits.js';
3
+ export function renderSubagentUpdate(snapshot, truncated = false) {
4
+ const attributes = (values) => markupAttributes(Object.fromEntries(Object.entries(values).map(([key, value]) => {
5
+ if (value === undefined)
6
+ return [key, undefined];
7
+ let bounded = '';
8
+ let count = 0;
9
+ for (const point of String(value)) {
10
+ if (count++ === 256) {
11
+ truncated = true;
12
+ break;
13
+ }
14
+ bounded += point;
15
+ }
16
+ return [key, bounded];
17
+ })));
18
+ const root = '<mar_subagent_update' +
19
+ attributes({
20
+ agentId: snapshot.agentId,
21
+ status: snapshot.status,
22
+ modelId: snapshot.modelId,
23
+ modelName: snapshot.modelName,
24
+ reasoningEffort: snapshot.reasoningEffort,
25
+ messageId: snapshot.messageId,
26
+ turnId: snapshot.turnId,
27
+ deliveryStatus: snapshot.deliveryStatus
28
+ });
29
+ const close = '</mar_subagent_update>';
30
+ let remaining = TOOL_EXECUTION_LIMITS.subagentUpdateMaxBytes -
31
+ Buffer.byteLength(root + ' truncated="false">\n' + close);
32
+ const lines = [];
33
+ const field = (name, value, metadata = {}, limit = remaining) => {
34
+ let attrs = attributes(metadata);
35
+ const overhead = Buffer.byteLength(' <' + name + attrs + '></' + name + '>\n');
36
+ if (overhead > remaining) {
37
+ truncated = true;
38
+ return;
39
+ }
40
+ let budget = Math.min(limit, remaining - overhead);
41
+ let text = '';
42
+ let clipped = false;
43
+ for (const point of value) {
44
+ const encoded = escapeMarkupText(point);
45
+ const bytes = Buffer.byteLength(encoded);
46
+ if (bytes > budget) {
47
+ clipped = true;
48
+ break;
49
+ }
50
+ text += encoded;
51
+ budget -= bytes;
52
+ }
53
+ if (clipped) {
54
+ truncated = true;
55
+ if ('truncated' in metadata)
56
+ attrs = attributes({ ...metadata, truncated: true });
57
+ }
58
+ const opening = ' <' + name + attrs;
59
+ const line = text === '' ? opening + ' />\n' : opening + '>' + text + '</' + name + '>\n';
60
+ remaining -= Buffer.byteLength(line);
61
+ lines.push(line);
62
+ };
63
+ // Preserve failure/permission facts before spending the remaining budget on result text.
64
+ if (snapshot.errorCode !== undefined || snapshot.errorMessage !== undefined)
65
+ field('error', snapshot.errorMessage ?? '', { code: snapshot.errorCode }, 1024);
66
+ if (snapshot.permissionStop)
67
+ field('permission_stop', snapshot.permissionStop.reason, { action: snapshot.permissionStop.action, ...snapshot.permissionStop.source }, 1024);
68
+ field('description', snapshot.description, {}, 1024);
69
+ if (snapshot.latestMessage) {
70
+ const { text, ...metadata } = snapshot.latestMessage;
71
+ truncated ||= metadata.truncated;
72
+ field('message', text, metadata, TOOL_EXECUTION_LIMITS.subagentSummaryMaxBytes);
73
+ }
74
+ if (snapshot.summary !== undefined)
75
+ field('summary', snapshot.summary);
76
+ for (const path of snapshot.changedFiles ?? [])
77
+ field('changed_file', path);
78
+ for (const text of snapshot.verification ?? [])
79
+ field('verification', text);
80
+ for (const { path, startLine, endLine } of snapshot.evidence ?? [])
81
+ field('evidence', path, { startLine, endLine });
82
+ if (snapshot.latestActivity)
83
+ field('activity', '', snapshot.latestActivity);
84
+ return root + ' truncated="' + String(truncated) + '">\n' + lines.join('') + close;
85
+ }
@@ -107,6 +107,11 @@ export declare class SubagentSessionController implements SubagentController {
107
107
  withOperation<T>(operation: SubagentParentOperation, run: () => T): T;
108
108
  inspect(agentId: string): Promise<SubagentTaskSnapshot>;
109
109
  inspectAll(): readonly SubagentTaskSnapshot[];
110
+ collectModelUpdates(): readonly {
111
+ snapshot: SubagentTaskSnapshot;
112
+ truncated: boolean;
113
+ acknowledge(): void;
114
+ }[];
110
115
  cancelOwned(ownerInstanceId: string): Promise<void>;
111
116
  endParentExecution(executionId: string): void;
112
117
  parentExecution(executionId: string): SubagentParentExecution | undefined;
@@ -86,6 +86,41 @@ export class SubagentSessionController {
86
86
  throw new MarAgentError('MAR_AGENT_DISPOSED', 'Subagent controller is disposed.');
87
87
  return [...this.#agents.values()].map((state) => this.#snapshot(state, { full: true }));
88
88
  }
89
+ collectModelUpdates() {
90
+ return [...this.#agents.values()]
91
+ .filter((state) => state.messageRevision > state.observedMessageRevision ||
92
+ (isTerminalState(state) &&
93
+ state.terminalRevision !== undefined &&
94
+ state.terminalRevision > state.observedTerminalRevision))
95
+ .sort((left, right) => left.resourceRevision - right.resourceRevision)
96
+ .slice(0, TOOL_EXECUTION_LIMITS.agentWaitMaxAgents)
97
+ .map((state) => {
98
+ const messageRevision = state.messageRevision;
99
+ const terminalRevision = isTerminalState(state) ? state.terminalRevision : undefined;
100
+ const includeTerminalDetails = terminalRevision !== undefined && terminalRevision > state.observedTerminalRevision;
101
+ const snapshot = this.#snapshot(state, {
102
+ includeLatestMessage: messageRevision > state.observedMessageRevision,
103
+ includeTerminalDetails,
104
+ full: false
105
+ });
106
+ return {
107
+ snapshot,
108
+ truncated: snapshot.latestMessage?.truncated === true ||
109
+ (includeTerminalDetails &&
110
+ (snapshot.summary !== state.summary ||
111
+ (snapshot.changedFiles?.length ?? 0) < state.changedFiles.length ||
112
+ (snapshot.evidence?.length ?? 0) < state.evidence.length ||
113
+ (snapshot.verification?.length ?? 0) < state.verification.length ||
114
+ (snapshot.verification?.some((value, index) => value !== state.verification[index]) ??
115
+ false))),
116
+ acknowledge: () => {
117
+ state.observedMessageRevision = Math.max(state.observedMessageRevision, messageRevision);
118
+ if (includeTerminalDetails)
119
+ state.observedTerminalRevision = Math.max(state.observedTerminalRevision, terminalRevision);
120
+ }
121
+ };
122
+ });
123
+ }
89
124
  async cancelOwned(ownerInstanceId) {
90
125
  for (const pending of this.#pendingDeliveries)
91
126
  if (pending.ownerInstanceId === ownerInstanceId)
@@ -548,7 +583,7 @@ export class SubagentSessionController {
548
583
  if (index >= 0)
549
584
  this.#queue.splice(index, 1);
550
585
  state.status = 'cancelled';
551
- this.#touchResource(state);
586
+ this.#touchResource(state, true);
552
587
  this.#closeReceipt(state);
553
588
  state.terminalPending = true;
554
589
  await this.#publishTerminal(state);
@@ -731,7 +766,7 @@ export class SubagentSessionController {
731
766
  state.errorMessage = result.errorMessage ?? 'Subagent execution failed.';
732
767
  }
733
768
  state.terminalPending = true;
734
- this.#touchResource(state);
769
+ this.#touchResource(state, true);
735
770
  this.#closeReceipt(state);
736
771
  await this.#publishTerminal(state);
737
772
  }
@@ -767,10 +802,12 @@ export class SubagentSessionController {
767
802
  state.errorCode = diagnostic.code;
768
803
  state.errorMessage = diagnostic.message;
769
804
  state.terminalPending = true;
770
- this.#touchResource(state);
805
+ this.#touchResource(state, true);
771
806
  }
772
- #touchResource(state) {
807
+ #touchResource(state, terminal = false) {
773
808
  state.resourceRevision = ++this.#resourceRevision;
809
+ if (terminal)
810
+ state.terminalRevision = state.resourceRevision;
774
811
  }
775
812
  #waitForAnyCompletion(candidates, waitMs, signal) {
776
813
  return waitForCompletionNotification(this.#completionWaiters, (state) => candidates.includes(state), waitMs, signal);
@@ -5,7 +5,7 @@ export const agentWaitToolDefinition = {
5
5
  name: 'agent_wait',
6
6
  allowedCallers: ['direct'],
7
7
  parallelSafety: 'serial',
8
- description: 'Read or wait a bounded time for a child agent result. Wait only when the next local step depends on the child result; otherwise continue useful non-overlapping work. waitFor defaults to "completion"; use "message" only when a fresh public commentary/final message would change the next decision. Omit agentId to wait for whichever live child returns first, matching Codex-style untargeted waiting and avoiding UUID transcription; provide the exact agentId returned by agent_start only when observing a specific child. If no child is live, untargeted waiting reads the most recently created child. For an exec processId, use exec_wait instead (tools.exec_wait in Code Mode). Omitting waitMs waits up to 30 seconds by default; waitMs=0 returns the current snapshot immediately. Positive waits range up to 5 minutes, and positive values below 10 seconds are raised to 10 seconds. When blocked on a live child result, prefer one 300000 ms completion wait rather than repeated short waits. Use agentIds with mode any (default) or all to wait for an explicit non-empty set of up to 8 distinct child IDs; never combine agentId and agentIds or use mode without agentIds. mode and waitFor are independent. A group uses one deadline and returns mode, waitFor, waitOutcome, agents in input order, and pendingAgentIds. Failed, cancelled and interrupted turns also satisfy completion; terminal state satisfies message waits too. Group waitMs=0 returns only snapshots without consuming messages. Single-target and untargeted results include waitOutcome as snapshot, message, completion, or timeout plus the real agentId and description. A message, timeout with unread progress, or first terminal completion result includes the newly observed latest public commentary/final message; snapshots and repeated observations omit previously observed message text. latestActivity can still identify a public message or bounded tool started/completed/failed status. A queued or running result is not completion. A message wait consumes only the latest unread public message in the current Session runtime; tool activity does not wake it, and parent cancellation preserves unread progress for a later Execution. Do not use message waits as heartbeat checks or request progress merely to confirm that a child is still running. First terminal results include bounded summary, changedFiles, verification, and read evidence with path/line ranges; use that evidence to avoid repeating the full exploration, while independently checking consequential edits and claims.',
8
+ description: 'Read or wait a bounded time for a child agent result. Unread child messages and terminal results are supplied automatically before the next model request. Wait only when the next local step depends on the child result; otherwise continue useful non-overlapping work. waitFor defaults to "completion"; use "message" only when a fresh public commentary/final message would change the next decision. Omit agentId to wait for whichever live child returns first, matching Codex-style untargeted waiting and avoiding UUID transcription; provide the exact agentId returned by agent_start only when observing a specific child. If no child is live, untargeted waiting reads the most recently created child. For an exec processId, use exec_wait instead (tools.exec_wait in Code Mode). Omitting waitMs waits up to 30 seconds by default; waitMs=0 returns the current snapshot immediately. Positive waits range up to 5 minutes, and positive values below 10 seconds are raised to 10 seconds. When blocked on a live child result, prefer one 300000 ms completion wait rather than repeated short waits. Use agentIds with mode any (default) or all to wait for an explicit non-empty set of up to 8 distinct child IDs; never combine agentId and agentIds or use mode without agentIds. mode and waitFor are independent. A group uses one deadline and returns mode, waitFor, waitOutcome, agents in input order, and pendingAgentIds. Failed, cancelled and interrupted turns also satisfy completion; terminal state satisfies message waits too. Group waitMs=0 returns only snapshots without consuming messages. Single-target and untargeted results include waitOutcome as snapshot, message, completion, or timeout plus the real agentId and description. A message, timeout with unread progress, or first terminal completion result includes the newly observed latest public commentary/final message; snapshots and repeated observations omit previously observed message text. latestActivity can still identify a public message or bounded tool started/completed/failed status. A queued or running result is not completion. A message wait consumes only the latest unread public message in the current Session runtime; tool activity does not wake it, and parent cancellation preserves unread progress for a later Execution. Do not use message waits as heartbeat checks or request progress merely to confirm that a child is still running. First terminal results include bounded summary, changedFiles, verification, and read evidence with path/line ranges; use that evidence to avoid repeating the full exploration, while independently checking consequential edits and claims.',
9
9
  inputSchema: {
10
10
  type: 'object',
11
11
  properties: {
@@ -32,7 +32,7 @@ export const codeModeToolDefinition = {
32
32
  - Use only standard ECMAScript built-ins and the explicitly documented tools, observe, state, catalog and cell namespaces. Do not use Node.js, browser or Web APIs. Module imports, filesystem and network APIs are unavailable. Obtain external capabilities only through available tools. Use tools.exec to start managed processes when permitted; use tools.exec_wait afterward to wait, write stdin or cancel.
33
33
 
34
34
  Tool calls:
35
- - tools.NAME(input) returns a Promise. Use the current catalog input schema: raw text only when inputMode is freeform; use the declared JSON form when inputMode is json. For parameterless tools prefer tools.NAME({}); tools.NAME() is accepted only for explicitly closed empty-object schemas.
35
+ - tools.NAME(input) returns a Promise. Pass a JavaScript value matching the current catalog inputSchema. For parameterless tools prefer tools.NAME({}); tools.NAME() is accepted only for explicitly closed empty-object schemas.
36
36
  - Results use the MAR envelope: { content?: string, data?: unknown, images?: Array<{mimeType:string,dataBase64:string}>, truncated?: boolean }. Redundant JSON content is omitted when it exactly renders data; independent text remains in content. Prefer data for programmatic results: result.data.results[0].text for read source text and result.data.exitCode/result.data.stdout for exec. content, when present, is rendered text, not raw file bytes. Use only fields documented by that tool; do not invent success or ok flags.
37
37
  - Tool errors reject with code/message and may be caught, except nested Auto deny, which skips only that call and resolves with data.error/data.message. A failed cell preserves already emitted output alongside its status and error; completed tool effects are not rolled back. Inspect it before retrying completed work. Await every call; unawaited calls fail the cell. Use Promise.all for independent calls and sequential await for dependencies. Runtime preserves safe/serial barriers and Host checks.
38
38
  - skill, tool_search, question, code_exec, code_exec_wait and direct-only tools cannot be called here. Load Skills directly and read their instructions before the next tool call. Use direct tool_search before a later code cell for deferred tools; the current cell's tools do not change.
@@ -41,7 +41,7 @@ Built-in namespaces:
41
41
  - observe.result(result): void registers an unmodified complete tools.NAME result or caught tool error as a trusted tool_result, using the original text and all images without repeating program data. Non-original, copied or modified values are rejected. This selects evidence for the next model response, not a log and not a new model call. Prefer observe.result for inspecting tool results.
42
42
  - observe.text(value): void registers text/JSON values. observe.image(value): void registers one image value when image input is supported. Both accept original tool results for compatibility and use the same complete tool_result observation path; they do not select part of an original result. Extracted single images, copies and computed summaries are value observations, never trusted tool results. Observations are returned in registration order. Only explicitly observed evidence reaches the model; registration does not prove inspection. Sources and types are runtime-assigned. No out or callable observe(value) alias exists.
43
43
  - state.get(key), state.has(key), state.set(key, value), state.delete(key) access bounded session JSON state. Mutations commit atomically only when the cell succeeds; failed or cancelled cells discard them.
44
- - catalog.tools() returns a read-only snapshot of the tools callable in this cell, including name, description, inputSchema and inputMode. It does not discover or authorize additional tools.
44
+ - catalog.tools() returns a read-only snapshot of the tools callable in this cell, including name, description and inputSchema. It does not discover or authorize additional tools.
45
45
  - await cell.sleep(ms) performs a cancellable delay up to ${limits.codeSleepMaxMs} ms.
46
46
  - await cell.yield_control() returns accumulated output to the model immediately while this cell continues running.
47
47
  - Optionally start source with // @options: {"yieldMs": ${limits.codeDefaultYieldMs}}. yieldMs defaults to ${limits.codeDefaultYieldMs} ms and controls this call's wait window (0..${limits.codeMaxYieldMs}), not nested tool timeouts or wait windows. A running result includes cellId; use direct code_exec_wait for new output, completion or termination.
@@ -63,7 +63,7 @@ Inspect before a dependent edit; successful await needs no invented success flag
63
63
  const inspected = await tools.read({ reads: [{ path: "source.txt" }] });
64
64
  const sourceText = inspected.data.results[0].text;
65
65
  if (!sourceText?.includes("original=1")) throw new Error("Unexpected source");
66
- const patched = await tools.apply_patch("*** Begin Patch\\n*** Update File: source.txt\\n@@\\n original=1\\n+updated=2\\n*** End Patch");
66
+ const patched = await tools.apply_patch({ patch: "*** Begin Patch\\n*** Update File: source.txt\\n@@\\n original=1\\n+updated=2\\n*** End Patch" });
67
67
  observe.result(patched);
68
68
  \`\`\``,
69
69
  inputMode: 'freeform',
@@ -102,6 +102,13 @@ export const codeExecWaitToolDefinition = {
102
102
  allowedCallers: ['direct'],
103
103
  parallelSafety: 'safe'
104
104
  };
105
+ function projectProgrammaticTool(tool) {
106
+ return {
107
+ name: tool.name,
108
+ description: `Call only as tools.${tool.name} inside code_exec, never as a direct model tool.\nPass a JavaScript value matching inputSchema.\n${tool.description}`,
109
+ inputSchema: stableToolSchema(tool.inputSchema)
110
+ };
111
+ }
105
112
  /** Deferred definitions travel in search results, not in the stable Code Mode description. */
106
113
  export function projectCodeModeTools(tools, residentTools = tools) {
107
114
  const codeModeEnabled = tools.some((tool) => tool.name === 'code_exec' && allowsToolCaller(tool, 'direct'));
@@ -113,12 +120,7 @@ export function projectCodeModeTools(tools, residentTools = tools) {
113
120
  const programmatic = orderToolDefinitions(residentTools.filter((tool) => allowsToolCaller(tool, 'programmatic')));
114
121
  const directory = JSON.stringify({
115
122
  callableNames: programmatic.map((tool) => tool.name),
116
- programmaticOnly: programmatic.map((tool) => ({
117
- name: tool.name,
118
- description: tool.description,
119
- inputSchema: stableToolSchema(tool.inputSchema),
120
- inputMode: tool.inputMode ?? 'json'
121
- }))
123
+ programmaticOnly: programmatic.map(projectProgrammaticTool)
122
124
  });
123
125
  if (Buffer.byteLength(directory) > 32 * 1024)
124
126
  throw new MarAgentError('MAR_AGENT_TOOL_EXPOSURE_LIMIT', 'Programmatic tool directory is too large.');
@@ -141,12 +143,14 @@ export function projectCodeModeSearchResult(message, codeModeEnabled) {
141
143
  .map(stableToolDefinition)
142
144
  };
143
145
  const tools = message.toolSearchTools.map((tool) => ({
144
- name: tool.name,
145
- description: allowsToolCaller(tool, 'programmatic')
146
- ? `Call only as tools.${tool.name} inside code_exec, never as a direct model tool.\n${tool.description}`
147
- : tool.description,
148
- inputSchema: stableToolSchema(tool.inputSchema),
149
- inputMode: tool.inputMode ?? 'json',
146
+ ...(allowsToolCaller(tool, 'programmatic')
147
+ ? projectProgrammaticTool(tool)
148
+ : {
149
+ name: tool.name,
150
+ description: tool.description,
151
+ inputSchema: stableToolSchema(tool.inputSchema),
152
+ inputMode: tool.inputMode ?? 'json'
153
+ }),
150
154
  caller: allowsToolCaller(tool, 'programmatic') ? 'programmatic' : 'direct'
151
155
  }));
152
156
  return {
@@ -631,9 +635,7 @@ export class CodeModeTool {
631
635
  catalog: context
632
636
  .codeMode.tools.filter((tool) => available.has(tool.name))
633
637
  .map((tool) => ({
634
- name: tool.name,
635
- description: tool.description,
636
- inputSchema: tool.inputSchema,
638
+ ...projectProgrammaticTool(tool),
637
639
  ...(tool.parameterless ? { parameterless: true } : {}),
638
640
  inputMode: tool.inputMode ?? 'json'
639
641
  })),
@@ -451,7 +451,11 @@ export async function createCodeModeEngine(options) {
451
451
  stateObject.dispose();
452
452
  const catalog = vm.newObject();
453
453
  method(catalog, 'tools', () => {
454
- const value = parse(input.catalog);
454
+ const value = parse(input.catalog.map(({ name, description, inputSchema }) => ({
455
+ name,
456
+ description,
457
+ inputSchema
458
+ })));
455
459
  const frozen = callGuest(deepFreeze, vm.undefined, value);
456
460
  value.dispose();
457
461
  if (frozen.error)
@@ -52,6 +52,7 @@ export declare const TOOL_EXECUTION_LIMITS: Readonly<{
52
52
  subagentMessageMaxBytes: 131072;
53
53
  subagentReasoningEffortMaxCharacters: 64;
54
54
  subagentSummaryMaxBytes: 16384;
55
+ subagentUpdateMaxBytes: 32768;
55
56
  subagentRunningLatestMessageMaxCharacters: 2000;
56
57
  subagentEvidenceMaxItems: 64;
57
58
  subagentVerificationMaxItems: 16;
@@ -52,6 +52,7 @@ export const TOOL_EXECUTION_LIMITS = Object.freeze({
52
52
  subagentMessageMaxBytes: 131_072,
53
53
  subagentReasoningEffortMaxCharacters: 64,
54
54
  subagentSummaryMaxBytes: 16_384,
55
+ subagentUpdateMaxBytes: 32_768,
55
56
  subagentRunningLatestMessageMaxCharacters: 2_000,
56
57
  subagentEvidenceMaxItems: 64,
57
58
  subagentVerificationMaxItems: 16,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/agent",
3
- "version": "0.10.43",
3
+ "version": "0.10.45",
4
4
  "description": "Embeddable MAR coding agent SDK and CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",