@myagentroam/agent 0.9.135 → 0.9.137

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 (72) hide show
  1. package/dist/cli/main.js +1 -1
  2. package/dist/host/local-agent-host.d.ts +9 -7
  3. package/dist/host/local-agent-host.js +77 -5
  4. package/dist/host/local-process-executor.d.ts +2 -0
  5. package/dist/index.d.ts +4 -2
  6. package/dist/index.js +1 -0
  7. package/dist/model/catalog-file.d.ts +2 -0
  8. package/dist/model/configuration.d.ts +5 -0
  9. package/dist/model/configuration.js +8 -0
  10. package/dist/model/contracts.d.ts +7 -0
  11. package/dist/model/openai-responses.d.ts +1 -1
  12. package/dist/model/openai-responses.js +85 -20
  13. package/dist/prompts/index.d.ts +2 -1
  14. package/dist/prompts/index.js +2 -2
  15. package/dist/prompts/workflow.d.ts +1 -1
  16. package/dist/prompts/workflow.js +9 -2
  17. package/dist/runtime/context-gc-runtime.d.ts +1 -0
  18. package/dist/runtime/context-gc-runtime.js +3 -0
  19. package/dist/runtime/context-gc-tokens.d.ts +1 -0
  20. package/dist/runtime/context-gc-tokens.js +1 -1
  21. package/dist/runtime/context-gc.d.ts +1 -0
  22. package/dist/runtime/context-gc.js +85 -8
  23. package/dist/runtime/context-projection.js +19 -2
  24. package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +29 -2
  25. package/dist/sdk/agent.js +249 -26
  26. package/dist/sdk/mcp-catalog.d.ts +13 -0
  27. package/dist/sdk/mcp-catalog.js +219 -0
  28. package/dist/sdk/types.d.ts +53 -1
  29. package/dist/session/context-gc-checkpoint.d.ts +7 -1
  30. package/dist/session/context-gc-checkpoint.js +28 -2
  31. package/dist/session/context-gc-replacements.d.ts +7 -0
  32. package/dist/session/context-gc-replacements.js +26 -0
  33. package/dist/session/jsonl-store.d.ts +6 -0
  34. package/dist/session/jsonl-store.js +92 -54
  35. package/dist/subagent/scheduler.d.ts +3 -3
  36. package/dist/subagent/session-controller.d.ts +1 -1
  37. package/dist/tools/agent-cancel.js +1 -0
  38. package/dist/tools/agent-message.js +1 -0
  39. package/dist/tools/agent-start.js +1 -0
  40. package/dist/tools/agent-wait.js +1 -0
  41. package/dist/tools/apply-patch.js +2 -1
  42. package/dist/tools/callers.d.ts +6 -0
  43. package/dist/tools/callers.js +23 -0
  44. package/dist/tools/code-mode/engine.d.ts +43 -0
  45. package/dist/tools/code-mode/engine.js +1 -0
  46. package/dist/tools/code-mode/quickjs-engine.d.ts +2 -0
  47. package/dist/tools/code-mode/quickjs-engine.js +454 -0
  48. package/dist/tools/code-mode/state.d.ts +20 -0
  49. package/dist/tools/code-mode/state.js +82 -0
  50. package/dist/tools/code-mode/worker.d.ts +1 -0
  51. package/dist/tools/code-mode/worker.js +65 -0
  52. package/dist/tools/code-mode.d.ts +8 -4
  53. package/dist/tools/code-mode.js +154 -120
  54. package/dist/tools/exec.js +1 -0
  55. package/dist/tools/execution-limits.d.ts +8 -0
  56. package/dist/tools/execution-limits.js +8 -0
  57. package/dist/tools/local-registry.d.ts +2 -0
  58. package/dist/tools/local-registry.js +1 -1
  59. package/dist/tools/question.js +1 -0
  60. package/dist/tools/read.js +1 -0
  61. package/dist/tools/registry.js +3 -0
  62. package/dist/tools/runtime-context.d.ts +2 -0
  63. package/dist/tools/runtime.js +3 -1
  64. package/dist/tools/skill.js +1 -0
  65. package/dist/tools/todo-read.js +1 -0
  66. package/dist/tools/todo-write.js +1 -0
  67. package/dist/tools/tool-search.d.ts +43 -0
  68. package/dist/tools/tool-search.js +284 -0
  69. package/dist/tools/view-image.js +1 -0
  70. package/dist/tools/web-fetch.js +1 -0
  71. package/dist/tools/web-search.js +1 -0
  72. package/package.json +3 -1
@@ -5,6 +5,7 @@ import { basename, join } from 'node:path';
5
5
  import { setTimeout as delay } from 'node:timers/promises';
6
6
  import { isDeepStrictEqual } from 'node:util';
7
7
  import { MarAgentError } from '../error.js';
8
+ import { applyCodeModeStateMutations, parseCodeModeStateMutations, restoreCodeModeState } from '../tools/code-mode/state.js';
8
9
  import { todoItemsFromSnapshot } from '../tools/todo.js';
9
10
  import { contextGcBaseFromRecords, contextGcBaseSequence, contextGcMinimumUsageGrowth, contextGcSourceProjectionHash, latestValidContextGcCheckpoint, normalizeContextGcReplacements, parseContextGcCheckpoint, sameContextGcBase, validateContextGcCheckpoint } from './context-gc-checkpoint.js';
10
11
  import { beginSessionStorageTransaction, isUnjournaledTransactionFile, recoverSessionStorageTransaction } from './transaction-recovery.js';
@@ -521,20 +522,31 @@ export class JsonlSessionStore {
521
522
  this.#shared.pendingSessions.set(target.id, target);
522
523
  let discard = false;
523
524
  try {
524
- await this.append(target.id, {
525
- type: 'session.created',
526
- payload: {
527
- title: target.title,
528
- workspace: target.workspace,
529
- automaticTitle: false,
530
- source: target.source
525
+ const copied = [
526
+ {
527
+ schemaVersion: 1,
528
+ recordId: randomUUID(),
529
+ sequence: 1,
530
+ timestamp: createdAt,
531
+ sessionId: target.id,
532
+ type: 'session.created',
533
+ payload: {
534
+ title: target.title,
535
+ workspace: target.workspace,
536
+ automaticTitle: false,
537
+ source: target.source
538
+ }
531
539
  }
532
- });
540
+ ];
533
541
  const retained = input.boundaryTurnId === null ? [] : records.slice(1, boundaryIndex + 1);
534
542
  for (const record of retained) {
535
543
  if (record.type === 'context.gc.completed' || record.type === 'responses.continuation')
536
544
  continue;
537
- await this.append(target.id, {
545
+ copied.push({
546
+ schemaVersion: 1,
547
+ recordId: randomUUID(),
548
+ sequence: copied.length + 1,
549
+ sessionId: target.id,
538
550
  type: record.type,
539
551
  payload: forkedPayload(record.type, record.payload, sourceSessionId, target.id, input.title),
540
552
  ...(record.executionId === undefined ? {} : { executionId: record.executionId }),
@@ -542,6 +554,15 @@ export class JsonlSessionStore {
542
554
  timestamp: record.timestamp
543
555
  });
544
556
  }
557
+ // A fork is unpublished until every segment and referenced image is durable.
558
+ const staged = await stageSessionSegments(this.sessionDirectory(target.id), copied, 'fork');
559
+ for (const file of staged.files)
560
+ await rename(file.temporaryPath, file.path);
561
+ target.segments = staged.segments;
562
+ target.nextSequence = copied.length + 1;
563
+ this.#shared.segments.set(target.id, staged.segments.length);
564
+ for (const record of copied)
565
+ this.#applyRecordToResumeIndex(target, record);
545
566
  await copyGeneratedImages(this.sessionDirectory(sourceSessionId), this.sessionDirectory(target.id), generatedImageFileNames(retained));
546
567
  await copyInputImages(this.sessionDirectory(sourceSessionId), this.sessionDirectory(target.id), inputImageReferencesFromRecords(retained));
547
568
  target.lastActivityAt = target.createdAt;
@@ -621,7 +642,7 @@ export class JsonlSessionStore {
621
642
  const retained = records
622
643
  .slice(0, boundaryIndex + 1)
623
644
  .filter((record) => record.type !== 'responses.continuation');
624
- const staged = await stageRollbackSegments(this.sessionDirectory(sessionId), retained);
645
+ const staged = await stageSessionSegments(this.sessionDirectory(sessionId), retained);
625
646
  const transactionId = randomUUID();
626
647
  const targetNextSequence = (retained.at(-1)?.sequence ?? 0) + 1;
627
648
  const contextualWorldStatePath = join(this.sessionDirectory(sessionId), CONTEXTUAL_WORLD_STATE_FILE);
@@ -765,7 +786,7 @@ export class JsonlSessionStore {
765
786
  }
766
787
  }
767
788
  ];
768
- const staged = await stageRollbackSegments(this.sessionDirectory(sessionId), retained);
789
+ const staged = await stageSessionSegments(this.sessionDirectory(sessionId), retained);
769
790
  const transactionId = randomUUID();
770
791
  const contextualWorldStatePath = join(this.sessionDirectory(sessionId), CONTEXTUAL_WORLD_STATE_FILE);
771
792
  const contextualWorldStateBackup = `${contextualWorldStatePath}.2.${transactionId}.clear-backup`;
@@ -966,6 +987,22 @@ export class JsonlSessionStore {
966
987
  throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.', { cause });
967
988
  }
968
989
  }
990
+ async readCodeModeState(sessionId) {
991
+ return restoreCodeModeState(await this.read(sessionId));
992
+ }
993
+ async commitCodeModeState(sessionId, mutations, input) {
994
+ const parsed = parseCodeModeStateMutations(mutations);
995
+ if (parsed.length === 0)
996
+ return this.readCodeModeState(sessionId);
997
+ const state = applyCodeModeStateMutations(await this.readCodeModeState(sessionId), parsed);
998
+ await this.append(sessionId, {
999
+ type: 'code.state.updated',
1000
+ payload: { mutations: parsed },
1001
+ executionId: input.executionId,
1002
+ turnId: input.turnId
1003
+ });
1004
+ return state;
1005
+ }
969
1006
  async readExecutionState(sessionId) {
970
1007
  this.#assertOpen();
971
1008
  const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
@@ -1637,55 +1674,56 @@ function generatedImageFileNames(records) {
1637
1674
  }
1638
1675
  return [...names];
1639
1676
  }
1640
- async function stageRollbackSegments(sessionDirectory, records) {
1641
- const groups = [];
1642
- for (const record of records) {
1643
- const line = `${JSON.stringify(record)}\n`;
1644
- const bytes = Buffer.byteLength(line);
1645
- let group = groups.at(-1);
1646
- if (group !== undefined && group.bytes > 0 && group.bytes + bytes > MAX_SEGMENT_BYTES)
1647
- group = undefined;
1648
- if (group === undefined) {
1649
- group = {
1650
- lines: [],
1651
- bytes: 0,
1652
- firstSequence: record.sequence,
1653
- lastSequence: record.sequence
1654
- };
1655
- groups.push(group);
1656
- }
1657
- group.lines.push(line);
1658
- group.bytes += bytes;
1659
- group.lastSequence = record.sequence;
1660
- }
1661
- if (groups.length === 0)
1677
+ async function stageSessionSegments(sessionDirectory, records, operation = 'rollback') {
1678
+ let group;
1679
+ if (records.length === 0)
1662
1680
  throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session history is corrupt.');
1663
1681
  const transactionId = randomUUID();
1664
1682
  const files = [];
1683
+ const segments = [];
1684
+ const flush = async () => {
1685
+ if (group === undefined)
1686
+ return;
1687
+ const number = segments.length + 1;
1688
+ const path = join(sessionDirectory, `events-${String(number).padStart(6, '0')}.jsonl`);
1689
+ const temporaryPath = `${path}.${transactionId}.${operation}-tmp`;
1690
+ const handle = await open(temporaryPath, 'wx', 0o600);
1691
+ files.push({ path, temporaryPath });
1692
+ try {
1693
+ await handle.writeFile(group.lines.join(''));
1694
+ await handle.sync();
1695
+ }
1696
+ finally {
1697
+ await handle.close();
1698
+ }
1699
+ segments.push({
1700
+ number,
1701
+ firstSequence: group.firstSequence,
1702
+ lastSequence: group.lastSequence,
1703
+ bytes: group.bytes
1704
+ });
1705
+ group = undefined;
1706
+ };
1665
1707
  try {
1666
- for (const [index, group] of groups.entries()) {
1667
- const number = index + 1;
1668
- const path = join(sessionDirectory, `events-${String(number).padStart(6, '0')}.jsonl`);
1669
- const temporaryPath = `${path}.${transactionId}.rollback-tmp`;
1670
- const handle = await open(temporaryPath, 'wx', 0o600);
1671
- try {
1672
- await handle.writeFile(group.lines.join(''));
1673
- await handle.sync();
1674
- }
1675
- finally {
1676
- await handle.close();
1708
+ for (const record of records) {
1709
+ const line = `${JSON.stringify(record)}\n`;
1710
+ const bytes = Buffer.byteLength(line);
1711
+ if (group !== undefined && group.bytes > 0 && group.bytes + bytes > MAX_SEGMENT_BYTES)
1712
+ await flush();
1713
+ if (group === undefined) {
1714
+ group = {
1715
+ lines: [],
1716
+ bytes: 0,
1717
+ firstSequence: record.sequence,
1718
+ lastSequence: record.sequence
1719
+ };
1677
1720
  }
1678
- files.push({ path, temporaryPath });
1721
+ group.lines.push(line);
1722
+ group.bytes += bytes;
1723
+ group.lastSequence = record.sequence;
1679
1724
  }
1680
- return {
1681
- files,
1682
- segments: groups.map((group, index) => ({
1683
- number: index + 1,
1684
- firstSequence: group.firstSequence,
1685
- lastSequence: group.lastSequence,
1686
- bytes: group.bytes
1687
- }))
1688
- };
1725
+ await flush();
1726
+ return { files, segments };
1689
1727
  }
1690
1728
  catch (error) {
1691
1729
  await Promise.all(files.map((item) => rm(item.temporaryPath, { force: true }))).catch(() => undefined);
@@ -125,7 +125,7 @@ export declare class SubagentScheduler implements SubagentController {
125
125
  turnId?: string;
126
126
  deliveryStatus?: "accepted" | "applied" | "cancelled" | "failed";
127
127
  } | {
128
- waitOutcome: "timeout" | "completion";
128
+ waitOutcome: "completion" | "timeout";
129
129
  agentId: string;
130
130
  description: string;
131
131
  status: "queued" | "running" | "completed" | "failed" | "cancelled" | "interrupted";
@@ -172,7 +172,7 @@ export declare class SubagentScheduler implements SubagentController {
172
172
  turnId?: string;
173
173
  deliveryStatus?: "accepted" | "applied" | "cancelled" | "failed";
174
174
  } | {
175
- waitOutcome: "timeout" | "completion";
175
+ waitOutcome: "completion" | "timeout";
176
176
  agentId: string;
177
177
  description: string;
178
178
  status: "queued" | "running" | "completed" | "failed" | "cancelled" | "interrupted";
@@ -199,7 +199,7 @@ export declare class SubagentScheduler implements SubagentController {
199
199
  message(_agentId: string, _message: string): Promise<SubagentTaskSnapshot>;
200
200
  listSessionResources(): {
201
201
  agentId: string;
202
- status: "completed" | "cancelled" | "running" | "failed" | "queued";
202
+ status: "completed" | "cancelled" | "running" | "queued" | "failed";
203
203
  description: string;
204
204
  }[];
205
205
  dispose(): Promise<void>;
@@ -91,7 +91,7 @@ export declare class SubagentSessionController implements SubagentController {
91
91
  cancel(agentId: string): Promise<SubagentTaskSnapshot>;
92
92
  listSessionResources(): {
93
93
  agentId: string;
94
- status: "completed" | "cancelled" | "running" | "failed" | "queued" | "interrupted";
94
+ status: "completed" | "cancelled" | "running" | "queued" | "failed" | "interrupted";
95
95
  description: string;
96
96
  }[];
97
97
  dispose(): Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import { MarAgentError } from '../error.js';
2
2
  export const agentCancelToolDefinition = {
3
3
  name: 'agent_cancel',
4
+ allowedCallers: ['direct', 'programmatic'],
4
5
  parallelSafety: 'serial',
5
6
  description: 'Idempotently cancel a child agent using the agentId returned by agent_start. For an exec processId, use exec with action:"cancel" instead. Waits for a running task to reach cancelled and returns its agentId and description.',
6
7
  inputSchema: {
@@ -2,6 +2,7 @@ import { MarAgentError } from '../error.js';
2
2
  import { TOOL_EXECUTION_LIMITS } from './execution-limits.js';
3
3
  export const agentMessageToolDefinition = {
4
4
  name: 'agent_message',
5
+ allowedCallers: ['direct', 'programmatic'],
5
6
  parallelSafety: 'serial',
6
7
  description: 'Send a real user message to an existing child agent. Default delivery=append inserts guidance into a running child turn at the next safe boundary after the current model response or complete tool batch. Explicit delivery=replace immediately requests cancellation, waits for the old turn to close, then starts a new turn in the same Session. When idle, either mode starts a new turn using persisted history. Returns messageId, turnId, deliveryStatus, current status, and model metadata without repeating the child’s previous latestMessage, latestActivity, summary, or verification evidence. accepted means queued in memory; applied means persisted in model context, not that the guidance has been executed. Use agent_wait for subsequent progress or completion. Does not wait for the new task to complete or roll back existing side effects.',
7
8
  inputSchema: {
@@ -3,6 +3,7 @@ import { marAgentReasoningEffortSchema } from '../model/configuration.js';
3
3
  import { TOOL_EXECUTION_LIMITS } from './execution-limits.js';
4
4
  export const agentStartToolDefinition = {
5
5
  name: 'agent_start',
6
+ allowedCallers: ['direct', 'programmatic'],
6
7
  parallelSafety: 'serial',
7
8
  description: 'Start one bounded, non-recursive child agent with self-contained context. Use when an independent investigation would consume substantial main-context space or when safe parallel work has real value; keep small or tightly coupled work in the main context. All agents share the filesystem, so avoid conflicting writes. The optional reasoningEffort overrides the parent effort and must be supported by the selected model; omit it to inherit. Returns agentId for agent_wait, agent_message, or agent_cancel, description, queued/running/completed/failed/cancelled status, bounded summary, changedFiles, verification, and read evidence; background returns immediately while foreground waits for a terminal result.',
8
9
  inputSchema: {
@@ -2,6 +2,7 @@ import { MarAgentError } from '../error.js';
2
2
  import { TOOL_EXECUTION_LIMITS } from './execution-limits.js';
3
3
  export const agentWaitToolDefinition = {
4
4
  name: 'agent_wait',
5
+ allowedCallers: ['direct', 'programmatic'],
5
6
  parallelSafety: 'serial',
6
7
  description: 'Read or wait a bounded time for a child agent result. 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 with action:"poll" instead. 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. Every result includes 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.',
7
8
  inputSchema: {
@@ -6,8 +6,9 @@ import { MarAgentError } from '../error.js';
6
6
  import { TOOL_EXECUTION_LIMITS } from './execution-limits.js';
7
7
  export const applyPatchToolDefinition = {
8
8
  name: 'apply_patch',
9
+ allowedCallers: ['direct', 'programmatic'],
9
10
  parallelSafety: 'serial',
10
- description: `Preferred for precise, reviewable local edits. Do not batch all task files merely to reduce tool calls. Group only a few tightly coupled edits whose combined patch is quick to generate and review; split broad or independent work into successive bounded patches before composing them. Submit a raw *** Begin Patch / *** End Patch patch. Supports Add File, Update File, Delete File, and Move to. Add File may replace an existing regular file, and Move to may replace an existing regular destination; duplicate operations on the same resolved path remain invalid. Update hunks accept @@ or @@ <change context>; context-only hunks may also be used as ordered anchors before a later changing hunk. Context is searched forward with Codex-style exact, whitespace-tolerant, then common Unicode punctuation-tolerant matching. Syntax and resolved paths are validated before writing, then file units are committed in order. Each file unit is atomic, and a Move keeps its source and destination atomic. On MAR_AGENT_PATCH_PARTIAL, earlier files remain applied; the error lists applied, failed, and pending paths, so retry only failed and pending paths. Existing file permissions, line endings, and final-newline state are preserved. Returns changed file paths and operations with per-file and total line counts when all units succeed. Oversized patches are rejected without advertising defensive limits as recommended patch sizes. A first-unit MAR_AGENT_PATCH_CONFLICT applies nothing; hunk match conflicts identify the affected path and hunk so you can reread only that file before retrying.`,
11
+ description: `Preferred for precise, reviewable local edits. Do not batch all task files merely to reduce tool calls. Group only a few tightly coupled edits whose combined patch is quick to generate and review; split broad or independent work into successive bounded patches before composing them. Submit a raw *** Begin Patch / *** End Patch patch. Supports Add File, Update File, Delete File, and Move to. Add File may replace an existing regular file, and Move to may replace an existing regular destination; duplicate operations on the same resolved path remain invalid. Update hunks accept @@ or @@ <change context>; context-only hunks may also be used as ordered anchors before a later changing hunk. Context is searched forward with Codex-style exact, whitespace-tolerant, then common Unicode punctuation-tolerant matching. Syntax and resolved paths are validated before writing, then file units are committed in order. Each file unit is atomic, and a Move keeps its source and destination atomic. On MAR_AGENT_PATCH_PARTIAL, earlier files remain applied; the error lists applied, failed, and pending paths, so retry only failed and pending paths. Existing file permissions, line endings, and final-newline state are preserved. Returns changed file paths and operations with per-file and total line counts when all units succeed. In Code Mode, the Promise resolves on success and rejects with code/message on failure; the returned envelope has data.files and data.totals, not an ok or success flag. Oversized patches are rejected without advertising defensive limits as recommended patch sizes. A first-unit MAR_AGENT_PATCH_CONFLICT applies nothing; hunk match conflicts identify the affected path and hunk so you can reread only that file before retrying.`,
11
12
  inputMode: 'freeform',
12
13
  outputSchema: {
13
14
  type: 'object',
@@ -0,0 +1,6 @@
1
+ import type { ClientToolDefinition, ToolCaller } from '../model/contracts.js';
2
+ /** Normalize trusted Host policy independently of discovery, Agent scope and parallel safety. */
3
+ export declare function toolCallers(tool: ClientToolDefinition): readonly ToolCaller[];
4
+ export declare function allowsToolCaller(tool: ClientToolDefinition, caller: ToolCaller): boolean;
5
+ /** Code Mode Only changes model exposure, never the Host's underlying caller permissions. */
6
+ export declare function allowsDirectTool(tool: ClientToolDefinition, codeModeEnabled: boolean): boolean;
@@ -0,0 +1,23 @@
1
+ import { MarAgentError } from '../error.js';
2
+ /** Normalize trusted Host policy independently of discovery, Agent scope and parallel safety. */
3
+ export function toolCallers(tool) {
4
+ const value = tool.allowedCallers;
5
+ if (value === undefined)
6
+ return tool.name.startsWith('mcp__') ? ['direct', 'programmatic'] : ['direct'];
7
+ if (!Array.isArray(value) ||
8
+ value.length === 0 ||
9
+ value.some((caller) => caller !== 'direct' && caller !== 'programmatic'))
10
+ throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Tool caller policy is invalid.');
11
+ return ['direct', 'programmatic'].filter((caller) => value.includes(caller));
12
+ }
13
+ export function allowsToolCaller(tool, caller) {
14
+ // Control tools cannot be widened by a Host override or historical definition.
15
+ if (caller === 'programmatic' && ['code_mode', 'tool_search', 'question'].includes(tool.name))
16
+ return false;
17
+ return toolCallers(tool).includes(caller);
18
+ }
19
+ /** Code Mode Only changes model exposure, never the Host's underlying caller permissions. */
20
+ export function allowsDirectTool(tool, codeModeEnabled) {
21
+ return (allowsToolCaller(tool, 'direct') &&
22
+ (!codeModeEnabled || !allowsToolCaller(tool, 'programmatic')));
23
+ }
@@ -0,0 +1,43 @@
1
+ import type { ToolCall, ToolExecutionResult } from '../../sdk/types.js';
2
+ import type { CodeModeStateEntry, CodeModeStateMutation } from './state.js';
3
+ export interface CodeModeCatalogEntry {
4
+ readonly name: string;
5
+ readonly description: string;
6
+ readonly inputSchema: unknown;
7
+ readonly inputMode: 'json' | 'freeform';
8
+ }
9
+ /** A trusted, replaceable worker-side engine; no engine handles cross this interface. */
10
+ export interface CodeModeEngine {
11
+ execute(input: {
12
+ code: string;
13
+ tools: readonly string[];
14
+ catalog: readonly CodeModeCatalogEntry[];
15
+ state: readonly CodeModeStateEntry[];
16
+ signal: AbortSignal;
17
+ limits: {
18
+ memoryBytes: number;
19
+ stackBytes: number;
20
+ timeoutMs: number;
21
+ outputBytes: number;
22
+ stateEntries: number;
23
+ stateMutations: number;
24
+ stateKeyBytes: number;
25
+ stateValueBytes: number;
26
+ stateTotalBytes: number;
27
+ sleepMs: number;
28
+ };
29
+ onToolCall(call: ToolCall): Promise<ToolExecutionResult>;
30
+ onText(value: string): void;
31
+ onImage(value: unknown): void;
32
+ }): Promise<{
33
+ readonly stateMutations: readonly CodeModeStateMutation[];
34
+ }>;
35
+ dispose(): Promise<void>;
36
+ }
37
+ /** Only the Local Host may supply this descriptor; it is never part of model tool input. */
38
+ export interface CodeModeEngineFactory {
39
+ readonly moduleUrl: string;
40
+ readonly runtimeDirectories: readonly string[];
41
+ readonly options?: Readonly<Record<string, unknown>>;
42
+ }
43
+ export type CreateCodeModeEngine = (options: Readonly<Record<string, unknown>>) => Promise<CodeModeEngine>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ /** @returns {Promise<import('./engine.js').CodeModeEngine>} */
2
+ export function createCodeModeEngine(options: any): Promise<import("./engine.js").CodeModeEngine>;