@myagentroam/agent 0.9.135 → 0.9.136

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 (69) 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/workflow.js +2 -0
  14. package/dist/runtime/context-gc-runtime.d.ts +1 -0
  15. package/dist/runtime/context-gc-runtime.js +3 -0
  16. package/dist/runtime/context-gc-tokens.d.ts +1 -0
  17. package/dist/runtime/context-gc-tokens.js +1 -1
  18. package/dist/runtime/context-gc.d.ts +1 -0
  19. package/dist/runtime/context-gc.js +85 -8
  20. package/dist/runtime/context-projection.js +19 -2
  21. package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +29 -2
  22. package/dist/sdk/agent.js +208 -25
  23. package/dist/sdk/mcp-catalog.d.ts +13 -0
  24. package/dist/sdk/mcp-catalog.js +219 -0
  25. package/dist/sdk/types.d.ts +50 -1
  26. package/dist/session/context-gc-checkpoint.d.ts +7 -1
  27. package/dist/session/context-gc-checkpoint.js +28 -2
  28. package/dist/session/context-gc-replacements.d.ts +7 -0
  29. package/dist/session/context-gc-replacements.js +26 -0
  30. package/dist/session/jsonl-store.d.ts +6 -0
  31. package/dist/session/jsonl-store.js +92 -54
  32. package/dist/subagent/scheduler.d.ts +3 -3
  33. package/dist/subagent/session-controller.d.ts +1 -1
  34. package/dist/tools/agent-cancel.js +1 -0
  35. package/dist/tools/agent-message.js +1 -0
  36. package/dist/tools/agent-start.js +1 -0
  37. package/dist/tools/agent-wait.js +1 -0
  38. package/dist/tools/apply-patch.js +2 -1
  39. package/dist/tools/callers.d.ts +4 -0
  40. package/dist/tools/callers.js +18 -0
  41. package/dist/tools/code-mode/engine.d.ts +43 -0
  42. package/dist/tools/code-mode/engine.js +1 -0
  43. package/dist/tools/code-mode/quickjs-engine.d.ts +2 -0
  44. package/dist/tools/code-mode/quickjs-engine.js +454 -0
  45. package/dist/tools/code-mode/state.d.ts +20 -0
  46. package/dist/tools/code-mode/state.js +82 -0
  47. package/dist/tools/code-mode/worker.d.ts +1 -0
  48. package/dist/tools/code-mode/worker.js +65 -0
  49. package/dist/tools/code-mode.d.ts +5 -3
  50. package/dist/tools/code-mode.js +129 -120
  51. package/dist/tools/exec.js +1 -0
  52. package/dist/tools/execution-limits.d.ts +8 -0
  53. package/dist/tools/execution-limits.js +8 -0
  54. package/dist/tools/local-registry.d.ts +2 -0
  55. package/dist/tools/local-registry.js +1 -1
  56. package/dist/tools/question.js +1 -0
  57. package/dist/tools/read.js +1 -0
  58. package/dist/tools/registry.js +3 -0
  59. package/dist/tools/runtime-context.d.ts +2 -0
  60. package/dist/tools/runtime.js +3 -1
  61. package/dist/tools/skill.js +1 -0
  62. package/dist/tools/todo-read.js +1 -0
  63. package/dist/tools/todo-write.js +1 -0
  64. package/dist/tools/tool-search.d.ts +43 -0
  65. package/dist/tools/tool-search.js +278 -0
  66. package/dist/tools/view-image.js +1 -0
  67. package/dist/tools/web-fetch.js +1 -0
  68. package/dist/tools/web-search.js +1 -0
  69. package/package.json +3 -1
package/dist/sdk/agent.js CHANGED
@@ -3,7 +3,7 @@ import { isDeepStrictEqual } from 'node:util';
3
3
  import { describeMarAgentError, MarAgentError } from '../error.js';
4
4
  import { negotiateHost } from '../host/contracts.js';
5
5
  import { AnthropicMessagesAdapter, isAnthropicRecoverableStreamError } from '../model/anthropic-messages.js';
6
- import { marAgentReasoningEffortSchema, resolveModelDefaultReasoningEffort } from '../model/configuration.js';
6
+ import { marAgentReasoningEffortSchema, resolveModelDefaultReasoningEffort, usesResponsesConfigurationUpdates } from '../model/configuration.js';
7
7
  import { modelRetryMaxAttempts, isRetryableModelRequestError, waitForModelRetry } from '../model/http.js';
8
8
  import { OpenAiResponsesAdapter, isOpenAiResponsesRecoverableStreamError } from '../model/openai-responses.js';
9
9
  import { AsyncQueue } from '../runtime/async-queue.js';
@@ -11,9 +11,11 @@ import { PlanOutputPrefix, splitPlanOutput } from '../runtime/plan-output.js';
11
11
  import { AGENT_EXECUTION_POLICY } from '../runtime/execution-policy.js';
12
12
  import { RolloutBudget, resolveRolloutBudgetOptions } from '../runtime/rollout-budget.js';
13
13
  import { ContextGcRuntime } from '../runtime/context-gc-runtime.js';
14
- import { buildContextProjection } from '../runtime/context-projection.js';
14
+ import { buildContextProjection, providerToolCallId, providerToolName } from '../runtime/context-projection.js';
15
15
  import { countModelTokens, countTextLines, truncateModelText } from '../runtime/token-budget.js';
16
16
  import { TOOL_EXECUTION_LIMITS } from '../tools/execution-limits.js';
17
+ import { DeferredMcpRegistry } from '../tools/tool-search.js';
18
+ import { assertMcpToolCatalog } from './mcp-catalog.js';
17
19
  import { DEFAULT_COMPACTION_FOCUS, CompactionOperation, buildCompactionInput, buildCompactionMessages, buildNativeCompactionMessages, buildCompactedHistory, compactedHistoryMessages, continuationMessage } from '../runtime/compact.js';
18
20
  import { buildSystemPrompt, MAR_AGENT_PROMPT_VERSION } from '../prompts/index.js';
19
21
  import { buildEnvironmentContext } from '../prompts/core.js';
@@ -31,7 +33,8 @@ import { BUILTIN_TOOL_DEFINITIONS } from '../tools/registry.js';
31
33
  import { agentStartToolDefinitionWithModels } from '../tools/agent-start.js';
32
34
  import { executeRegisteredTool } from '../tools/runtime.js';
33
35
  import { scheduleToolCallBatch } from '../tools/scheduler.js';
34
- import { withCodeModeResultTypes } from '../tools/code-mode.js';
36
+ import { projectCodeModeTools } from '../tools/code-mode.js';
37
+ import { allowsToolCaller, toolCallers } from '../tools/callers.js';
35
38
  import { SubagentSessionController } from '../subagent/session-controller.js';
36
39
  /** Retry only classified recoverable failures within the attempt budget; never retry cancellation. */
37
40
  async function recoverModelStream(error, retryState, signal) {
@@ -261,6 +264,7 @@ export async function createMarAgentWithAdapter(options) {
261
264
  releaseSession = await store.acquireSessionLease(sessionId);
262
265
  await refreshSessionAdapters(executionModelGeneration);
263
266
  const sessionState = await store.readExecutionState(sessionId);
267
+ let codeModeState = await store.readCodeModeState(sessionId);
264
268
  const todo = TodoStore.restore(sessionState.todo);
265
269
  const contextRecords = sessionState.contextRecords;
266
270
  const contextualWorldStateGeneration = latestContextualWorldStateGeneration(contextRecords);
@@ -410,9 +414,7 @@ export async function createMarAgentWithAdapter(options) {
410
414
  let lastServerUsage = latestModelUsage(contextRecords, selectedModel.id);
411
415
  const compactUserTokenLimit = Math.min(20_000, Math.floor(selectedModel.contextWindowTokens / 8));
412
416
  const reasoningEffort = input.reasoningEffort ?? selectedModel.defaultReasoningEffort;
413
- const useEffortUpdates = selectedModel.protocol === 'OPENAI_RESPONSES' &&
414
- selectedModel.responsesEncoding !== 'LITE' &&
415
- selectedModel.codexCredential === true;
417
+ const useEffortUpdates = usesResponsesConfigurationUpdates(selectedModel);
416
418
  const previousReasoningEffort = useEffortUpdates
417
419
  ? reasoningEffortBaseline(contextRecords, selectedModel.id)
418
420
  : undefined;
@@ -496,7 +498,32 @@ export async function createMarAgentWithAdapter(options) {
496
498
  : { executionScopeId: internal.mcpExecutionScopeId }
497
499
  : { claimExecutionScope: true })
498
500
  };
499
- const mcpTools = (await options.host.listMcpTools?.(catalogContext)) ?? [];
501
+ const readMcpCatalog = async () => {
502
+ if (options.host.listMcpToolCatalog) {
503
+ const catalog = await options.host.listMcpToolCatalog(catalogContext);
504
+ assertMcpToolCatalog(catalog);
505
+ return catalog;
506
+ }
507
+ const definitions = (await options.host.listMcpTools?.(catalogContext)) ?? [];
508
+ return {
509
+ revision: createHash('sha256').update(JSON.stringify(definitions)).digest('hex'),
510
+ servers: [{ id: 'legacy', name: 'External MCP' }],
511
+ tools: definitions.map((definition) => ({
512
+ definition,
513
+ serverId: 'legacy',
514
+ rawName: definition.name,
515
+ sourceIdentity: `legacy:${executionId}`,
516
+ definitionRevision: createHash('sha256')
517
+ .update(JSON.stringify(definition))
518
+ .digest('hex'),
519
+ lifetime: 'execution',
520
+ exposure: 'deferred'
521
+ }))
522
+ };
523
+ };
524
+ const mcpCatalog = await readMcpCatalog();
525
+ const mcpRegistry = new DeferredMcpRegistry(discoveredMcpToolNames(messages));
526
+ const mcpTools = mcpCatalog.tools.map((tool) => tool.definition);
500
527
  const extensions = (await options.host.listExecutionExtensions?.(catalogContext)) ?? {};
501
528
  if (extensions.tools?.some((tool) => tool.name.startsWith('mcp__')))
502
529
  throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Host tools cannot use the MCP namespace.');
@@ -511,12 +538,15 @@ export async function createMarAgentWithAdapter(options) {
511
538
  const combinedTools = [...builtinTools, ...(extensions.tools ?? []), ...mcpTools];
512
539
  if (new Set(combinedTools.map((tool) => tool.name)).size !== combinedTools.length)
513
540
  throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Tool names conflict.');
514
- const availableTools = combinedTools.filter((tool) => {
541
+ const toolEligible = (tool) => {
542
+ toolCallers(tool);
515
543
  if (sessionSource.type === 'subagent' && tool.agentScope === 'ROOT_ONLY')
516
544
  return false;
517
545
  if (sessionSource.type === 'subagent' &&
518
546
  (tool.name.startsWith('agent_') || tool.name === 'question'))
519
547
  return false;
548
+ if (tool.name === 'tool_search')
549
+ return mcpRegistry.hasDeferred;
520
550
  return tool.name === 'code_mode'
521
551
  ? selectedModelForTools.codeMode && description.capabilities.includes('code.execute')
522
552
  : tool.name === 'question'
@@ -527,7 +557,10 @@ export async function createMarAgentWithAdapter(options) {
527
557
  : tool.name === 'view_image'
528
558
  ? selectedModelForTools.inputCapabilities.includes('IMAGE')
529
559
  : tool.name !== 'web_fetch' || description.capabilities.includes('web.fetch');
530
- });
560
+ };
561
+ mcpRegistry.update(mcpCatalog, (tool) => toolEligible(tool.definition));
562
+ rebindMcpToolSearchOutputs(messages, mcpRegistry);
563
+ const availableTools = combinedTools.filter(toolEligible);
531
564
  const instructionContext = await loadAgentInstructions({
532
565
  home: description.homeDirectory,
533
566
  workspace: instructionWorkspace,
@@ -546,8 +579,14 @@ export async function createMarAgentWithAdapter(options) {
546
579
  agentInstructionInsertIndex &&
547
580
  messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
548
581
  agentInstructionInsertIndex--;
549
- const tools = withCodeModeResultTypes(availableTools.filter((tool) => tool.name !== 'skill' || (instructionContext?.skills.size ?? 0) > 0));
550
- const executionTools = tools;
582
+ const baseTools = () => [...builtinTools, ...(extensions.tools ?? [])].filter((tool) => toolEligible(tool) &&
583
+ (tool.name !== 'skill' || (instructionContext?.skills.size ?? 0) > 0));
584
+ let executionTools = mcpRegistry.project(baseTools());
585
+ const restoreMcpDiscovery = () => {
586
+ mcpRegistry.restore(discoveredMcpToolNames(messages));
587
+ rebindMcpToolSearchOutputs(messages, mcpRegistry);
588
+ executionTools = mcpRegistry.project(baseTools());
589
+ };
551
590
  const loadRetainedSessionResources = async () => {
552
591
  const hostResources = (await options.host.listSessionResources?.(sessionId)) ?? {
553
592
  processes: []
@@ -581,7 +620,9 @@ export async function createMarAgentWithAdapter(options) {
581
620
  ? { guaranteedCommands: description.guaranteedCommands }
582
621
  : {}),
583
622
  workspace: logicalWorkspace,
584
- tools: availableTools.map((tool) => tool.name),
623
+ tools: availableTools
624
+ .filter((tool) => !tool.name.startsWith('mcp__'))
625
+ .map((tool) => tool.name),
585
626
  highDensityCompaction: selectedModel.highDensityCompaction,
586
627
  extension: [
587
628
  options.systemInstruction,
@@ -591,6 +632,27 @@ export async function createMarAgentWithAdapter(options) {
591
632
  .filter(Boolean)
592
633
  .join('\n\n')
593
634
  });
635
+ const syncMcpWorldState = async (index) => {
636
+ const snapshot = mcpRegistry.navigation();
637
+ if (contextualWorldState.mcpSnapshot === snapshot)
638
+ return;
639
+ contextualWorldState = {
640
+ ...contextualWorldState,
641
+ mcpSnapshot: snapshot,
642
+ entries: [
643
+ ...contextualWorldState.entries,
644
+ {
645
+ index,
646
+ kind: 'mcp',
647
+ content: snapshot ?? '<mcp_catalog>No deferred MCP tools remain.</mcp_catalog>'
648
+ }
649
+ ]
650
+ };
651
+ sessionRuntime.contextualWorldState = contextualWorldState;
652
+ await persistContextualWorldState(store, sessionId, contextualWorldState);
653
+ };
654
+ if (mode !== 'compact')
655
+ await syncMcpWorldState(agentInstructionInsertIndex);
594
656
  const initialSystemPrompt = mode === 'compact'
595
657
  ? buildExecutionSystemPrompt(executionTools, 'run')
596
658
  : buildExecutionSystemPrompt();
@@ -740,6 +802,7 @@ export async function createMarAgentWithAdapter(options) {
740
802
  });
741
803
  contextGc.reset();
742
804
  messages.splice(0, messages.length, environmentContextUpdate(executionEnvironment), ...compactedHistoryMessages(replacementHistory));
805
+ restoreMcpDiscovery();
743
806
  projectedExecutionBudgetStage = undefined;
744
807
  recordedEnvironment = executionEnvironment;
745
808
  compactionUserMessages = replacementHistory
@@ -785,7 +848,12 @@ export async function createMarAgentWithAdapter(options) {
785
848
  let roundServerUsage;
786
849
  const pendingTools = [];
787
850
  let incompleteToolCall = false;
851
+ executionTools = mcpRegistry.project(baseTools());
852
+ if (mode !== 'compact')
853
+ await syncMcpWorldState(messages.length);
788
854
  await ensureContextBudget();
855
+ if (mode !== 'compact')
856
+ await syncMcpWorldState(messages.length);
789
857
  appendExecutionBudgetEnvironment();
790
858
  if (mode !== 'compact') {
791
859
  executionEnvironment = { ...currentEnvironment, ...currentLocalTimeContext() };
@@ -793,6 +861,14 @@ export async function createMarAgentWithAdapter(options) {
793
861
  }
794
862
  const toolsAllowed = mode !== 'compact' && (!rolloutBudget?.active || rolloutBudget.stage !== 'exhausted');
795
863
  const roundTools = mode === 'compact' ? executionTools : toolsAllowed ? executionTools : [];
864
+ const requestTools = (() => {
865
+ if (selectedModel.protocol !== 'OPENAI_RESPONSES' ||
866
+ selectedModel.responsesEncoding === 'LITE')
867
+ return projectCodeModeTools(roundTools);
868
+ const directNames = new Set(mcpRegistry.projectDirect(baseTools()).map((tool) => tool.name));
869
+ // Discovery and caller policy are independent: keep nested declarations for active tools.
870
+ return projectCodeModeTools(roundTools).filter((tool) => directNames.has(tool.name));
871
+ })();
796
872
  const systemPrompt = mode === 'compact'
797
873
  ? buildExecutionSystemPrompt(roundTools, 'run')
798
874
  : buildExecutionSystemPrompt(roundTools);
@@ -804,7 +880,14 @@ export async function createMarAgentWithAdapter(options) {
804
880
  : normalizeMessagesForModel(insertContextualWorldState(requestContextMessages, contextualWorldState.entries), {
805
881
  supportsImages: selectedModel.inputCapabilities.includes('IMAGE')
806
882
  });
883
+ const roundMcpExpectations = new Map(roundTools
884
+ .filter((tool) => tool.name.startsWith('mcp__'))
885
+ .map((tool) => [tool.name, mcpRegistry.expectedTool(tool.name)]));
807
886
  let sawModelSemanticEvent = false;
887
+ let requestStartedAt;
888
+ let firstVisibleOutputAt;
889
+ let requestCompletedAt;
890
+ let requestOutputTokens;
808
891
  const roundMessageCount = messages.length;
809
892
  const persistToolCalls = async (calls) => {
810
893
  for (const call of calls) {
@@ -834,11 +917,17 @@ export async function createMarAgentWithAdapter(options) {
834
917
  const startModel = manualCompaction
835
918
  ? (request) => manualCompaction.start(selected, request)
836
919
  : (request) => selected.start(request, controller.signal);
920
+ requestStartedAt = performance.now();
837
921
  for await (const event of startModel({
838
922
  retryState: modelRetryState,
839
923
  system: systemPrompt,
840
- messages: modelMessages,
841
- tools: roundTools,
924
+ messages: modelMessages.map((message) => message.toolSearchTools
925
+ ? {
926
+ ...message,
927
+ toolSearchTools: message.toolSearchTools.filter((tool) => allowsToolCaller(tool, 'direct'))
928
+ }
929
+ : message),
930
+ tools: requestTools,
842
931
  ...(mode === 'compact'
843
932
  ? { toolChoice: 'none' }
844
933
  : toolsAllowed
@@ -884,13 +973,15 @@ export async function createMarAgentWithAdapter(options) {
884
973
  }
885
974
  delta = prefix.push(delta);
886
975
  }
887
- if (mode !== 'compact' && delta)
976
+ if (mode !== 'compact' && delta) {
977
+ firstVisibleOutputAt ??= performance.now();
888
978
  await emit({
889
979
  type: 'message.delta',
890
980
  itemId: event.itemId,
891
981
  phase: event.phase,
892
982
  delta
893
983
  });
984
+ }
894
985
  }
895
986
  else if (event.type === 'text.completed') {
896
987
  const phase = event.phase ?? 'final_answer';
@@ -898,13 +989,15 @@ export async function createMarAgentWithAdapter(options) {
898
989
  finalAnswer = event.text;
899
990
  planOutputPrefixes.delete(event.itemId);
900
991
  if (mode !== 'compact') {
992
+ if (event.text.length > 0)
993
+ firstVisibleOutputAt ??= performance.now();
901
994
  if (mode === 'plan' && sessionSource.type !== 'subagent') {
902
995
  const parts = splitPlanOutput(event.text);
903
996
  for (const [index, part] of parts.entries()) {
904
997
  const itemId = index === 0 ? event.itemId : `${event.itemId}:plan-part:${index}`;
905
998
  if (part.kind === 'plan')
906
999
  await emit({ type: 'plan.completed', itemId, text: part.text });
907
- else
1000
+ else {
908
1001
  await emit({
909
1002
  type: 'message.completed',
910
1003
  itemId,
@@ -913,9 +1006,10 @@ export async function createMarAgentWithAdapter(options) {
913
1006
  modelId: selectedModel.id,
914
1007
  modelName: selectedModel.name
915
1008
  });
1009
+ }
916
1010
  }
917
1011
  }
918
- else
1012
+ else {
919
1013
  await emit({
920
1014
  type: 'message.completed',
921
1015
  itemId: event.itemId,
@@ -924,11 +1018,18 @@ export async function createMarAgentWithAdapter(options) {
924
1018
  modelId: selectedModel.id,
925
1019
  modelName: selectedModel.name
926
1020
  });
1021
+ }
927
1022
  }
928
1023
  messages.push({ role: 'assistant', content: event.text });
929
1024
  }
930
1025
  else if (event.type === 'usage') {
931
1026
  latestUsage = event.usage;
1027
+ requestOutputTokens =
1028
+ typeof event.usage.outputTokens === 'number' &&
1029
+ Number.isSafeInteger(event.usage.outputTokens) &&
1030
+ event.usage.outputTokens >= 0
1031
+ ? event.usage.outputTokens
1032
+ : undefined;
932
1033
  const reportedContext = event.usage.contextInputTokens ?? event.usage.inputTokens;
933
1034
  if (reportedContext !== undefined &&
934
1035
  Number.isSafeInteger(reportedContext) &&
@@ -974,8 +1075,26 @@ export async function createMarAgentWithAdapter(options) {
974
1075
  sawTool = true;
975
1076
  pendingTools.push(event);
976
1077
  }
977
- else if (event.type === 'completed')
1078
+ else if (event.type === 'completed') {
978
1079
  stop = event.reason;
1080
+ requestCompletedAt = performance.now();
1081
+ }
1082
+ }
1083
+ if (mode !== 'compact' && firstVisibleOutputAt !== undefined) {
1084
+ const completedAt = requestCompletedAt ?? performance.now();
1085
+ const ttftMs = Math.max(0, Math.round(firstVisibleOutputAt - requestStartedAt));
1086
+ const outputDurationMs = Math.max(0, Math.round(completedAt - firstVisibleOutputAt));
1087
+ const outputTokens = requestOutputTokens;
1088
+ const tokensPerSecond = outputTokens !== undefined && outputDurationMs > 0
1089
+ ? Number(((outputTokens * 1000) / outputDurationMs).toFixed(2))
1090
+ : undefined;
1091
+ await emit({
1092
+ type: 'model.performance',
1093
+ ttftMs,
1094
+ ...(outputTokens === undefined ? {} : { outputTokens }),
1095
+ outputDurationMs,
1096
+ ...(tokensPerSecond === undefined ? {} : { tokensPerSecond })
1097
+ });
979
1098
  }
980
1099
  pendingOutputContinuation = undefined;
981
1100
  incompleteToolCall = pendingTools.some((call) => call.argumentError !== undefined);
@@ -1031,7 +1150,7 @@ export async function createMarAgentWithAdapter(options) {
1031
1150
  finalAnswer = '';
1032
1151
  continue;
1033
1152
  }
1034
- const executeCall = async (call, signal = controller.signal) => {
1153
+ const executeCall = async (call, signal = controller.signal, caller = 'direct') => {
1035
1154
  await emit({
1036
1155
  type: 'tool.started',
1037
1156
  toolName: call.name,
@@ -1041,11 +1160,20 @@ export async function createMarAgentWithAdapter(options) {
1041
1160
  try {
1042
1161
  if (call.argumentError)
1043
1162
  throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', call.argumentError);
1044
- if (!availableTools.some((tool) => tool.name === call.name))
1163
+ if (!roundTools.some((tool) => tool.name === call.name && allowsToolCaller(tool, caller)))
1045
1164
  throw new MarAgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Tool is unavailable in this execution.');
1046
1165
  const output = await executeRegisteredTool(call.name, call.arguments, call.callId, {
1047
1166
  host: options.host,
1048
1167
  todo,
1168
+ ...(roundMcpExpectations.get(call.name)
1169
+ ? { expectedMcpTool: roundMcpExpectations.get(call.name) }
1170
+ : {}),
1171
+ searchMcpTools: (arguments_, searchSignal) => mcpRegistry.expose(arguments_, {
1172
+ signal: searchSignal,
1173
+ base: baseTools(),
1174
+ window: selectedModel.contextWindowTokens,
1175
+ system: systemPrompt
1176
+ }),
1049
1177
  ...(subagents === undefined ? {} : { subagents }),
1050
1178
  mode,
1051
1179
  sessionId,
@@ -1053,14 +1181,22 @@ export async function createMarAgentWithAdapter(options) {
1053
1181
  signal,
1054
1182
  codeMode: {
1055
1183
  supportsImages: selectedModel.inputCapabilities.includes('IMAGE'),
1056
- tools: roundTools,
1184
+ tools: roundTools.filter((tool) => allowsToolCaller(tool, 'programmatic')),
1185
+ state: codeModeState,
1186
+ commitState: async (mutations) => {
1187
+ codeModeState = await store.commitCodeModeState(sessionId, mutations, {
1188
+ executionId,
1189
+ turnId
1190
+ });
1191
+ return codeModeState;
1192
+ },
1057
1193
  invoke: async (nested, nestedSignal) => {
1058
1194
  const result = await executeCall({
1059
1195
  type: 'client_tool.call',
1060
1196
  name: nested.name,
1061
1197
  arguments: nested.arguments,
1062
1198
  callId: nested.callId ?? randomUUID()
1063
- }, nestedSignal);
1199
+ }, nestedSignal, 'programmatic');
1064
1200
  if (result.failed)
1065
1201
  throw result.error;
1066
1202
  return { ...result.output, content: result.content };
@@ -1127,10 +1263,14 @@ export async function createMarAgentWithAdapter(options) {
1127
1263
  mimeType: image.mimeType,
1128
1264
  data: Buffer.from(image.dataBase64, 'base64')
1129
1265
  })));
1266
+ const toolSearchTools = result.call.name === 'tool_search' && !result.failed
1267
+ ? result.output.toolSearchTools
1268
+ : undefined;
1130
1269
  const message = {
1131
1270
  role: 'tool',
1132
1271
  callId: result.call.callId,
1133
1272
  content: result.content,
1273
+ ...(toolSearchTools === undefined ? {} : { toolSearchTools }),
1134
1274
  ...('images' in result && result.images?.length ? { images: result.images } : {})
1135
1275
  };
1136
1276
  messages.push(message);
@@ -1139,7 +1279,10 @@ export async function createMarAgentWithAdapter(options) {
1139
1279
  payload: {
1140
1280
  role: message.role,
1141
1281
  callId: message.callId,
1142
- content: message.content
1282
+ content: message.content,
1283
+ ...(message.toolSearchTools === undefined
1284
+ ? {}
1285
+ : { toolSearchTools: message.toolSearchTools })
1143
1286
  },
1144
1287
  executionId,
1145
1288
  turnId
@@ -1182,6 +1325,7 @@ export async function createMarAgentWithAdapter(options) {
1182
1325
  contextWindowTokens: selectedModel.contextWindowTokens,
1183
1326
  usage: latestUsage,
1184
1327
  modelProtocol: selectedModel.protocol,
1328
+ responsesConfigurationUpdates: usesResponsesConfigurationUpdates(selectedModel),
1185
1329
  workspace: logicalWorkspace,
1186
1330
  platform: description.platform,
1187
1331
  getActiveProcessIds: async () => {
@@ -1201,6 +1345,7 @@ export async function createMarAgentWithAdapter(options) {
1201
1345
  projection: committed.projection
1202
1346
  });
1203
1347
  messages.splice(0, messages.length, ...refreshed.messages);
1348
+ restoreMcpDiscovery();
1204
1349
  projectedExecutionBudgetStage = undefined;
1205
1350
  recordedEnvironment = refreshed.environmentState;
1206
1351
  compactionUserMessages = [...refreshed.userMessages];
@@ -1986,7 +2131,7 @@ function parsePersistedContextualWorldState(payload) {
1986
2131
  const entry = value;
1987
2132
  if (!Number.isSafeInteger(entry.index) ||
1988
2133
  entry.index < 0 ||
1989
- (entry.kind !== 'instructions' && entry.kind !== 'resources') ||
2134
+ (entry.kind !== 'instructions' && entry.kind !== 'resources' && entry.kind !== 'mcp') ||
1990
2135
  typeof entry.content !== 'string' ||
1991
2136
  entry.content.length === 0)
1992
2137
  return undefined;
@@ -2004,6 +2149,7 @@ function parsePersistedContextualWorldState(payload) {
2004
2149
  initialized: true,
2005
2150
  instructionSnapshot,
2006
2151
  resourceSnapshot,
2152
+ mcpSnapshot: typeof record.mcpSnapshot === 'string' ? record.mcpSnapshot : undefined,
2007
2153
  entries
2008
2154
  };
2009
2155
  }
@@ -2021,6 +2167,7 @@ async function persistContextualWorldState(store, sessionId, state) {
2021
2167
  generation: state.generation ?? null,
2022
2168
  instructionSnapshot: state.instructionSnapshot ?? null,
2023
2169
  resourceSnapshot: state.resourceSnapshot ?? null,
2170
+ mcpSnapshot: state.mcpSnapshot ?? null,
2024
2171
  entries: state.entries
2025
2172
  });
2026
2173
  }
@@ -2038,6 +2185,7 @@ function updateContextualWorldState(state, index, current) {
2038
2185
  entries.push({ index, kind: 'resources', content: resourceContent });
2039
2186
  return {
2040
2187
  generation: state.generation,
2188
+ mcpSnapshot: state.mcpSnapshot,
2041
2189
  initialized: true,
2042
2190
  instructionSnapshot: current.instructionSnapshot,
2043
2191
  resourceSnapshot: current.resourceSnapshot,
@@ -2100,6 +2248,38 @@ function stableJson(value) {
2100
2248
  .join(',')}}`;
2101
2249
  return JSON.stringify(value) ?? 'undefined';
2102
2250
  }
2251
+ function discoveredMcpToolNames(messages) {
2252
+ const calls = toolSearchCallIds(messages);
2253
+ const names = new Set();
2254
+ for (const message of messages)
2255
+ for (const tool of calls.has(message.callId ?? '') ? (message.toolSearchTools ?? []) : [])
2256
+ if (tool.name.startsWith('mcp__'))
2257
+ names.add(tool.name);
2258
+ return [...names];
2259
+ }
2260
+ function rebindMcpToolSearchOutputs(messages, registry) {
2261
+ const calls = toolSearchCallIds(messages);
2262
+ for (const message of messages) {
2263
+ if (!message.toolSearchTools)
2264
+ continue;
2265
+ if (!calls.has(message.callId ?? '')) {
2266
+ delete message.toolSearchTools;
2267
+ continue;
2268
+ }
2269
+ const definitions = registry.restoredDefinitions(message.toolSearchTools.map((tool) => tool.name));
2270
+ message.toolSearchTools = definitions;
2271
+ }
2272
+ }
2273
+ function toolSearchCallIds(messages) {
2274
+ return new Set(messages
2275
+ .filter((message) => (message.role === 'tool_call' &&
2276
+ message.name === 'tool_search' &&
2277
+ typeof message.callId === 'string') ||
2278
+ (message.role === 'provider' &&
2279
+ providerToolName(message) === 'tool_search' &&
2280
+ providerToolCallId(message) !== undefined))
2281
+ .map((message) => message.role === 'provider' ? providerToolCallId(message) : message.callId));
2282
+ }
2103
2283
  /** Project only known, validated tool result shapes into public structured events. */
2104
2284
  function structuredToolEventData(toolName, value) {
2105
2285
  if (!isRecord(value))
@@ -2196,7 +2376,10 @@ function structuredToolArtifacts(value) {
2196
2376
  }
2197
2377
  /** Keep streaming deltas live-only; exec output has one durable copy in model context records. */
2198
2378
  function eventForPersistence(event) {
2199
- if (event.type === 'message.delta' || event.type === 'context.compacting')
2379
+ if (event.type === 'message.delta' ||
2380
+ event.type === 'context.compacting' ||
2381
+ event.type === 'model.performance' ||
2382
+ event.type === 'message.performance')
2200
2383
  return undefined;
2201
2384
  if (event.type !== 'tool.completed' || event.toolName !== 'exec' || !event.data)
2202
2385
  return event;
@@ -0,0 +1,13 @@
1
+ import type { ClientToolDefinition } from '../model/contracts.js';
2
+ import type { McpToolCatalog } from './types.js';
3
+ export declare const MCP_DEFINITION_LIMITS: Readonly<{
4
+ depth: 64;
5
+ nodes: 32768;
6
+ definitionBytes: number;
7
+ catalogBytes: number;
8
+ tools: 10000;
9
+ servers: 256;
10
+ }>;
11
+ /** Shared Host and Node publication boundary, not a provider-specific conversion. */
12
+ export declare function assertMcpToolDefinition(value: unknown): asserts value is ClientToolDefinition;
13
+ export declare function assertMcpToolCatalog(value: unknown): asserts value is McpToolCatalog;