@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
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,8 +33,10 @@ 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 { projectCodeModeSearchResult, projectCodeModeTools } from '../tools/code-mode.js';
37
+ import { allowsDirectTool, allowsToolCaller, toolCallers } from '../tools/callers.js';
35
38
  import { SubagentSessionController } from '../subagent/session-controller.js';
39
+ const MODEL_PERFORMANCE_MIN_INTERVAL_MS = 1_000;
36
40
  /** Retry only classified recoverable failures within the attempt budget; never retry cancellation. */
37
41
  async function recoverModelStream(error, retryState, signal) {
38
42
  if (signal.aborted ||
@@ -261,6 +265,7 @@ export async function createMarAgentWithAdapter(options) {
261
265
  releaseSession = await store.acquireSessionLease(sessionId);
262
266
  await refreshSessionAdapters(executionModelGeneration);
263
267
  const sessionState = await store.readExecutionState(sessionId);
268
+ let codeModeState = await store.readCodeModeState(sessionId);
264
269
  const todo = TodoStore.restore(sessionState.todo);
265
270
  const contextRecords = sessionState.contextRecords;
266
271
  const contextualWorldStateGeneration = latestContextualWorldStateGeneration(contextRecords);
@@ -410,9 +415,7 @@ export async function createMarAgentWithAdapter(options) {
410
415
  let lastServerUsage = latestModelUsage(contextRecords, selectedModel.id);
411
416
  const compactUserTokenLimit = Math.min(20_000, Math.floor(selectedModel.contextWindowTokens / 8));
412
417
  const reasoningEffort = input.reasoningEffort ?? selectedModel.defaultReasoningEffort;
413
- const useEffortUpdates = selectedModel.protocol === 'OPENAI_RESPONSES' &&
414
- selectedModel.responsesEncoding !== 'LITE' &&
415
- selectedModel.codexCredential === true;
418
+ const useEffortUpdates = usesResponsesConfigurationUpdates(selectedModel);
416
419
  const previousReasoningEffort = useEffortUpdates
417
420
  ? reasoningEffortBaseline(contextRecords, selectedModel.id)
418
421
  : undefined;
@@ -496,7 +499,32 @@ export async function createMarAgentWithAdapter(options) {
496
499
  : { executionScopeId: internal.mcpExecutionScopeId }
497
500
  : { claimExecutionScope: true })
498
501
  };
499
- const mcpTools = (await options.host.listMcpTools?.(catalogContext)) ?? [];
502
+ const readMcpCatalog = async () => {
503
+ if (options.host.listMcpToolCatalog) {
504
+ const catalog = await options.host.listMcpToolCatalog(catalogContext);
505
+ assertMcpToolCatalog(catalog);
506
+ return catalog;
507
+ }
508
+ const definitions = (await options.host.listMcpTools?.(catalogContext)) ?? [];
509
+ return {
510
+ revision: createHash('sha256').update(JSON.stringify(definitions)).digest('hex'),
511
+ servers: [{ id: 'legacy', name: 'External MCP' }],
512
+ tools: definitions.map((definition) => ({
513
+ definition,
514
+ serverId: 'legacy',
515
+ rawName: definition.name,
516
+ sourceIdentity: `legacy:${executionId}`,
517
+ definitionRevision: createHash('sha256')
518
+ .update(JSON.stringify(definition))
519
+ .digest('hex'),
520
+ lifetime: 'execution',
521
+ exposure: 'deferred'
522
+ }))
523
+ };
524
+ };
525
+ const mcpCatalog = await readMcpCatalog();
526
+ const mcpRegistry = new DeferredMcpRegistry(discoveredMcpToolNames(messages));
527
+ const mcpTools = mcpCatalog.tools.map((tool) => tool.definition);
500
528
  const extensions = (await options.host.listExecutionExtensions?.(catalogContext)) ?? {};
501
529
  if (extensions.tools?.some((tool) => tool.name.startsWith('mcp__')))
502
530
  throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Host tools cannot use the MCP namespace.');
@@ -511,12 +539,15 @@ export async function createMarAgentWithAdapter(options) {
511
539
  const combinedTools = [...builtinTools, ...(extensions.tools ?? []), ...mcpTools];
512
540
  if (new Set(combinedTools.map((tool) => tool.name)).size !== combinedTools.length)
513
541
  throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Tool names conflict.');
514
- const availableTools = combinedTools.filter((tool) => {
542
+ const toolEligible = (tool) => {
543
+ toolCallers(tool);
515
544
  if (sessionSource.type === 'subagent' && tool.agentScope === 'ROOT_ONLY')
516
545
  return false;
517
546
  if (sessionSource.type === 'subagent' &&
518
547
  (tool.name.startsWith('agent_') || tool.name === 'question'))
519
548
  return false;
549
+ if (tool.name === 'tool_search')
550
+ return mcpRegistry.hasDeferred;
520
551
  return tool.name === 'code_mode'
521
552
  ? selectedModelForTools.codeMode && description.capabilities.includes('code.execute')
522
553
  : tool.name === 'question'
@@ -527,7 +558,10 @@ export async function createMarAgentWithAdapter(options) {
527
558
  : tool.name === 'view_image'
528
559
  ? selectedModelForTools.inputCapabilities.includes('IMAGE')
529
560
  : tool.name !== 'web_fetch' || description.capabilities.includes('web.fetch');
530
- });
561
+ };
562
+ mcpRegistry.update(mcpCatalog, (tool) => toolEligible(tool.definition));
563
+ rebindMcpToolSearchOutputs(messages, mcpRegistry);
564
+ const availableTools = combinedTools.filter(toolEligible);
531
565
  const instructionContext = await loadAgentInstructions({
532
566
  home: description.homeDirectory,
533
567
  workspace: instructionWorkspace,
@@ -546,8 +580,14 @@ export async function createMarAgentWithAdapter(options) {
546
580
  agentInstructionInsertIndex &&
547
581
  messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
548
582
  agentInstructionInsertIndex--;
549
- const tools = withCodeModeResultTypes(availableTools.filter((tool) => tool.name !== 'skill' || (instructionContext?.skills.size ?? 0) > 0));
550
- const executionTools = tools;
583
+ const baseTools = () => [...builtinTools, ...(extensions.tools ?? [])].filter((tool) => toolEligible(tool) &&
584
+ (tool.name !== 'skill' || (instructionContext?.skills.size ?? 0) > 0));
585
+ let executionTools = mcpRegistry.project(baseTools());
586
+ const restoreMcpDiscovery = () => {
587
+ mcpRegistry.restore(discoveredMcpToolNames(messages));
588
+ rebindMcpToolSearchOutputs(messages, mcpRegistry);
589
+ executionTools = mcpRegistry.project(baseTools());
590
+ };
551
591
  const loadRetainedSessionResources = async () => {
552
592
  const hostResources = (await options.host.listSessionResources?.(sessionId)) ?? {
553
593
  processes: []
@@ -581,7 +621,13 @@ export async function createMarAgentWithAdapter(options) {
581
621
  ? { guaranteedCommands: description.guaranteedCommands }
582
622
  : {}),
583
623
  workspace: logicalWorkspace,
584
- tools: availableTools.map((tool) => tool.name),
624
+ tools: availableTools
625
+ .filter((tool) => !tool.name.startsWith('mcp__'))
626
+ .map((tool) => tool.name),
627
+ directTools: availableTools
628
+ .filter((tool) => !tool.name.startsWith('mcp__') &&
629
+ allowsDirectTool(tool, availableTools.some((candidate) => candidate.name === 'code_mode')))
630
+ .map((tool) => tool.name),
585
631
  highDensityCompaction: selectedModel.highDensityCompaction,
586
632
  extension: [
587
633
  options.systemInstruction,
@@ -591,6 +637,27 @@ export async function createMarAgentWithAdapter(options) {
591
637
  .filter(Boolean)
592
638
  .join('\n\n')
593
639
  });
640
+ const syncMcpWorldState = async (index) => {
641
+ const snapshot = mcpRegistry.navigation();
642
+ if (contextualWorldState.mcpSnapshot === snapshot)
643
+ return;
644
+ contextualWorldState = {
645
+ ...contextualWorldState,
646
+ mcpSnapshot: snapshot,
647
+ entries: [
648
+ ...contextualWorldState.entries,
649
+ {
650
+ index,
651
+ kind: 'mcp',
652
+ content: snapshot ?? '<mcp_catalog>No deferred MCP tools remain.</mcp_catalog>'
653
+ }
654
+ ]
655
+ };
656
+ sessionRuntime.contextualWorldState = contextualWorldState;
657
+ await persistContextualWorldState(store, sessionId, contextualWorldState);
658
+ };
659
+ if (mode !== 'compact')
660
+ await syncMcpWorldState(agentInstructionInsertIndex);
594
661
  const initialSystemPrompt = mode === 'compact'
595
662
  ? buildExecutionSystemPrompt(executionTools, 'run')
596
663
  : buildExecutionSystemPrompt();
@@ -740,6 +807,7 @@ export async function createMarAgentWithAdapter(options) {
740
807
  });
741
808
  contextGc.reset();
742
809
  messages.splice(0, messages.length, environmentContextUpdate(executionEnvironment), ...compactedHistoryMessages(replacementHistory));
810
+ restoreMcpDiscovery();
743
811
  projectedExecutionBudgetStage = undefined;
744
812
  recordedEnvironment = executionEnvironment;
745
813
  compactionUserMessages = replacementHistory
@@ -785,7 +853,12 @@ export async function createMarAgentWithAdapter(options) {
785
853
  let roundServerUsage;
786
854
  const pendingTools = [];
787
855
  let incompleteToolCall = false;
856
+ executionTools = mcpRegistry.project(baseTools());
857
+ if (mode !== 'compact')
858
+ await syncMcpWorldState(messages.length);
788
859
  await ensureContextBudget();
860
+ if (mode !== 'compact')
861
+ await syncMcpWorldState(messages.length);
789
862
  appendExecutionBudgetEnvironment();
790
863
  if (mode !== 'compact') {
791
864
  executionEnvironment = { ...currentEnvironment, ...currentLocalTimeContext() };
@@ -793,6 +866,16 @@ export async function createMarAgentWithAdapter(options) {
793
866
  }
794
867
  const toolsAllowed = mode !== 'compact' && (!rolloutBudget?.active || rolloutBudget.stage !== 'exhausted');
795
868
  const roundTools = mode === 'compact' ? executionTools : toolsAllowed ? executionTools : [];
869
+ const codeModeEnabled = roundTools.some((tool) => tool.name === 'code_mode');
870
+ const residentTools = mcpRegistry.projectDirect(baseTools());
871
+ const requestTools = (() => {
872
+ if (selectedModel.protocol !== 'OPENAI_RESPONSES' ||
873
+ selectedModel.responsesEncoding === 'LITE')
874
+ return projectCodeModeTools(roundTools, residentTools);
875
+ const directNames = new Set(residentTools.map((tool) => tool.name));
876
+ // Discovery and caller policy are independent: keep nested declarations for active tools.
877
+ return projectCodeModeTools(roundTools, residentTools).filter((tool) => directNames.has(tool.name));
878
+ })();
796
879
  const systemPrompt = mode === 'compact'
797
880
  ? buildExecutionSystemPrompt(roundTools, 'run')
798
881
  : buildExecutionSystemPrompt(roundTools);
@@ -804,8 +887,47 @@ export async function createMarAgentWithAdapter(options) {
804
887
  : normalizeMessagesForModel(insertContextualWorldState(requestContextMessages, contextualWorldState.entries), {
805
888
  supportsImages: selectedModel.inputCapabilities.includes('IMAGE')
806
889
  });
890
+ const roundMcpExpectations = new Map(roundTools
891
+ .filter((tool) => tool.name.startsWith('mcp__'))
892
+ .map((tool) => [tool.name, mcpRegistry.expectedTool(tool.name)]));
807
893
  let sawModelSemanticEvent = false;
894
+ let requestStartedAt;
895
+ let firstModelResponseAt;
896
+ let requestCompletedAt;
897
+ let requestOutputTokens;
898
+ let outputTokenRevision = 0;
899
+ let lastPerformanceEmittedAt;
900
+ let lastPublishedOutputTokenRevision = 0;
901
+ let performanceEmitted = false;
808
902
  const roundMessageCount = messages.length;
903
+ const emitPerformance = async (sampledAt, force = false) => {
904
+ if (mode === 'compact' || firstModelResponseAt === undefined)
905
+ return;
906
+ const outputDurationMs = Math.max(0, Math.round(sampledAt - firstModelResponseAt));
907
+ const outputTokens = requestOutputTokens;
908
+ const tokensPerSecond = outputTokens !== undefined && outputDurationMs > 0
909
+ ? Number(((outputTokens * 1000) / outputDurationMs).toFixed(2))
910
+ : undefined;
911
+ if (performanceEmitted &&
912
+ (tokensPerSecond === undefined ||
913
+ outputTokenRevision === lastPublishedOutputTokenRevision))
914
+ return;
915
+ if (!force &&
916
+ lastPerformanceEmittedAt !== undefined &&
917
+ sampledAt - lastPerformanceEmittedAt < MODEL_PERFORMANCE_MIN_INTERVAL_MS)
918
+ return;
919
+ await emit({
920
+ type: 'model.performance',
921
+ latencyMs: Math.max(0, Math.round(firstModelResponseAt - requestStartedAt)),
922
+ ...(outputTokens === undefined ? {} : { outputTokens }),
923
+ outputDurationMs,
924
+ ...(tokensPerSecond === undefined ? {} : { tokensPerSecond })
925
+ });
926
+ performanceEmitted = true;
927
+ lastPerformanceEmittedAt = sampledAt;
928
+ if (tokensPerSecond !== undefined)
929
+ lastPublishedOutputTokenRevision = outputTokenRevision;
930
+ };
809
931
  const persistToolCalls = async (calls) => {
810
932
  for (const call of calls) {
811
933
  const message = {
@@ -834,11 +956,12 @@ export async function createMarAgentWithAdapter(options) {
834
956
  const startModel = manualCompaction
835
957
  ? (request) => manualCompaction.start(selected, request)
836
958
  : (request) => selected.start(request, controller.signal);
959
+ requestStartedAt = performance.now();
837
960
  for await (const event of startModel({
838
961
  retryState: modelRetryState,
839
962
  system: systemPrompt,
840
- messages: modelMessages,
841
- tools: roundTools,
963
+ messages: modelMessages.map((message) => projectCodeModeSearchResult(message, codeModeEnabled)),
964
+ tools: requestTools,
842
965
  ...(mode === 'compact'
843
966
  ? { toolChoice: 'none' }
844
967
  : toolsAllowed
@@ -850,8 +973,11 @@ export async function createMarAgentWithAdapter(options) {
850
973
  ...(modelInputImages?.length ? { images: modelInputImages } : {}),
851
974
  onAttemptDiagnostic: recordModelAttempt(mode === 'compact' ? 'COMPACTION' : 'EXECUTION')
852
975
  })) {
853
- if (event.type !== 'response.started' && event.type !== 'usage')
976
+ const modelResponseAt = mode !== 'compact' && isModelResponseEvent(event) ? performance.now() : undefined;
977
+ if (modelResponseAt !== undefined) {
854
978
  sawModelSemanticEvent = true;
979
+ firstModelResponseAt ??= modelResponseAt;
980
+ }
855
981
  if (event.type === 'response.item') {
856
982
  // Compaction keeps only its successful checkpoint, never intermediate native output.
857
983
  if (mode === 'compact')
@@ -884,13 +1010,14 @@ export async function createMarAgentWithAdapter(options) {
884
1010
  }
885
1011
  delta = prefix.push(delta);
886
1012
  }
887
- if (mode !== 'compact' && delta)
1013
+ if (mode !== 'compact' && delta) {
888
1014
  await emit({
889
1015
  type: 'message.delta',
890
1016
  itemId: event.itemId,
891
1017
  phase: event.phase,
892
1018
  delta
893
1019
  });
1020
+ }
894
1021
  }
895
1022
  else if (event.type === 'text.completed') {
896
1023
  const phase = event.phase ?? 'final_answer';
@@ -904,7 +1031,7 @@ export async function createMarAgentWithAdapter(options) {
904
1031
  const itemId = index === 0 ? event.itemId : `${event.itemId}:plan-part:${index}`;
905
1032
  if (part.kind === 'plan')
906
1033
  await emit({ type: 'plan.completed', itemId, text: part.text });
907
- else
1034
+ else {
908
1035
  await emit({
909
1036
  type: 'message.completed',
910
1037
  itemId,
@@ -913,9 +1040,10 @@ export async function createMarAgentWithAdapter(options) {
913
1040
  modelId: selectedModel.id,
914
1041
  modelName: selectedModel.name
915
1042
  });
1043
+ }
916
1044
  }
917
1045
  }
918
- else
1046
+ else {
919
1047
  await emit({
920
1048
  type: 'message.completed',
921
1049
  itemId: event.itemId,
@@ -924,11 +1052,21 @@ export async function createMarAgentWithAdapter(options) {
924
1052
  modelId: selectedModel.id,
925
1053
  modelName: selectedModel.name
926
1054
  });
1055
+ }
927
1056
  }
928
1057
  messages.push({ role: 'assistant', content: event.text });
929
1058
  }
930
1059
  else if (event.type === 'usage') {
931
1060
  latestUsage = event.usage;
1061
+ const outputTokens = typeof event.usage.outputTokens === 'number' &&
1062
+ Number.isSafeInteger(event.usage.outputTokens) &&
1063
+ event.usage.outputTokens >= 0
1064
+ ? event.usage.outputTokens
1065
+ : undefined;
1066
+ if (outputTokens !== undefined) {
1067
+ requestOutputTokens = outputTokens;
1068
+ outputTokenRevision++;
1069
+ }
932
1070
  const reportedContext = event.usage.contextInputTokens ?? event.usage.inputTokens;
933
1071
  if (reportedContext !== undefined &&
934
1072
  Number.isSafeInteger(reportedContext) &&
@@ -974,8 +1112,16 @@ export async function createMarAgentWithAdapter(options) {
974
1112
  sawTool = true;
975
1113
  pendingTools.push(event);
976
1114
  }
977
- else if (event.type === 'completed')
1115
+ else if (event.type === 'completed') {
978
1116
  stop = event.reason;
1117
+ requestCompletedAt = performance.now();
1118
+ }
1119
+ if (modelResponseAt !== undefined)
1120
+ await emitPerformance(modelResponseAt);
1121
+ }
1122
+ if (mode !== 'compact' && firstModelResponseAt !== undefined) {
1123
+ const completedAt = requestCompletedAt ?? performance.now();
1124
+ await emitPerformance(completedAt, true);
979
1125
  }
980
1126
  pendingOutputContinuation = undefined;
981
1127
  incompleteToolCall = pendingTools.some((call) => call.argumentError !== undefined);
@@ -1031,21 +1177,31 @@ export async function createMarAgentWithAdapter(options) {
1031
1177
  finalAnswer = '';
1032
1178
  continue;
1033
1179
  }
1034
- const executeCall = async (call, signal = controller.signal) => {
1180
+ const executeCall = async (call, signal = controller.signal, caller = 'direct', parentCallId) => {
1035
1181
  await emit({
1036
1182
  type: 'tool.started',
1037
1183
  toolName: call.name,
1038
1184
  callId: call.callId,
1185
+ ...(parentCallId === undefined ? {} : { parentCallId }),
1039
1186
  input: call.arguments
1040
1187
  });
1041
1188
  try {
1042
1189
  if (call.argumentError)
1043
1190
  throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', call.argumentError);
1044
- if (!availableTools.some((tool) => tool.name === call.name))
1191
+ if (!roundTools.some((tool) => tool.name === call.name && allowsToolCaller(tool, caller)))
1045
1192
  throw new MarAgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Tool is unavailable in this execution.');
1046
1193
  const output = await executeRegisteredTool(call.name, call.arguments, call.callId, {
1047
1194
  host: options.host,
1048
1195
  todo,
1196
+ ...(roundMcpExpectations.get(call.name)
1197
+ ? { expectedMcpTool: roundMcpExpectations.get(call.name) }
1198
+ : {}),
1199
+ searchMcpTools: (arguments_, searchSignal) => mcpRegistry.expose(arguments_, {
1200
+ signal: searchSignal,
1201
+ base: baseTools(),
1202
+ window: selectedModel.contextWindowTokens,
1203
+ system: systemPrompt
1204
+ }),
1049
1205
  ...(subagents === undefined ? {} : { subagents }),
1050
1206
  mode,
1051
1207
  sessionId,
@@ -1053,14 +1209,22 @@ export async function createMarAgentWithAdapter(options) {
1053
1209
  signal,
1054
1210
  codeMode: {
1055
1211
  supportsImages: selectedModel.inputCapabilities.includes('IMAGE'),
1056
- tools: roundTools,
1212
+ tools: roundTools.filter((tool) => allowsToolCaller(tool, 'programmatic')),
1213
+ state: codeModeState,
1214
+ commitState: async (mutations) => {
1215
+ codeModeState = await store.commitCodeModeState(sessionId, mutations, {
1216
+ executionId,
1217
+ turnId
1218
+ });
1219
+ return codeModeState;
1220
+ },
1057
1221
  invoke: async (nested, nestedSignal) => {
1058
1222
  const result = await executeCall({
1059
1223
  type: 'client_tool.call',
1060
1224
  name: nested.name,
1061
1225
  arguments: nested.arguments,
1062
1226
  callId: nested.callId ?? randomUUID()
1063
- }, nestedSignal);
1227
+ }, nestedSignal, 'programmatic', call.callId);
1064
1228
  if (result.failed)
1065
1229
  throw result.error;
1066
1230
  return { ...result.output, content: result.content };
@@ -1082,6 +1246,7 @@ export async function createMarAgentWithAdapter(options) {
1082
1246
  type: 'tool.completed',
1083
1247
  toolName: call.name,
1084
1248
  callId: call.callId,
1249
+ ...(parentCallId === undefined ? {} : { parentCallId }),
1085
1250
  summary: eventSummary,
1086
1251
  truncated: output.truncated === true ||
1087
1252
  modelOutput.truncated ||
@@ -1103,6 +1268,7 @@ export async function createMarAgentWithAdapter(options) {
1103
1268
  type: 'tool.failed',
1104
1269
  toolName: call.name,
1105
1270
  callId: call.callId,
1271
+ ...(parentCallId === undefined ? {} : { parentCallId }),
1106
1272
  code: diagnostic.code,
1107
1273
  message: diagnostic.message
1108
1274
  });
@@ -1127,10 +1293,14 @@ export async function createMarAgentWithAdapter(options) {
1127
1293
  mimeType: image.mimeType,
1128
1294
  data: Buffer.from(image.dataBase64, 'base64')
1129
1295
  })));
1296
+ const toolSearchTools = result.call.name === 'tool_search' && !result.failed
1297
+ ? result.output.toolSearchTools
1298
+ : undefined;
1130
1299
  const message = {
1131
1300
  role: 'tool',
1132
1301
  callId: result.call.callId,
1133
1302
  content: result.content,
1303
+ ...(toolSearchTools === undefined ? {} : { toolSearchTools }),
1134
1304
  ...('images' in result && result.images?.length ? { images: result.images } : {})
1135
1305
  };
1136
1306
  messages.push(message);
@@ -1139,7 +1309,10 @@ export async function createMarAgentWithAdapter(options) {
1139
1309
  payload: {
1140
1310
  role: message.role,
1141
1311
  callId: message.callId,
1142
- content: message.content
1312
+ content: message.content,
1313
+ ...(message.toolSearchTools === undefined
1314
+ ? {}
1315
+ : { toolSearchTools: message.toolSearchTools })
1143
1316
  },
1144
1317
  executionId,
1145
1318
  turnId
@@ -1182,6 +1355,7 @@ export async function createMarAgentWithAdapter(options) {
1182
1355
  contextWindowTokens: selectedModel.contextWindowTokens,
1183
1356
  usage: latestUsage,
1184
1357
  modelProtocol: selectedModel.protocol,
1358
+ responsesConfigurationUpdates: usesResponsesConfigurationUpdates(selectedModel),
1185
1359
  workspace: logicalWorkspace,
1186
1360
  platform: description.platform,
1187
1361
  getActiveProcessIds: async () => {
@@ -1201,6 +1375,7 @@ export async function createMarAgentWithAdapter(options) {
1201
1375
  projection: committed.projection
1202
1376
  });
1203
1377
  messages.splice(0, messages.length, ...refreshed.messages);
1378
+ restoreMcpDiscovery();
1204
1379
  projectedExecutionBudgetStage = undefined;
1205
1380
  recordedEnvironment = refreshed.environmentState;
1206
1381
  compactionUserMessages = [...refreshed.userMessages];
@@ -1986,7 +2161,7 @@ function parsePersistedContextualWorldState(payload) {
1986
2161
  const entry = value;
1987
2162
  if (!Number.isSafeInteger(entry.index) ||
1988
2163
  entry.index < 0 ||
1989
- (entry.kind !== 'instructions' && entry.kind !== 'resources') ||
2164
+ (entry.kind !== 'instructions' && entry.kind !== 'resources' && entry.kind !== 'mcp') ||
1990
2165
  typeof entry.content !== 'string' ||
1991
2166
  entry.content.length === 0)
1992
2167
  return undefined;
@@ -2004,6 +2179,7 @@ function parsePersistedContextualWorldState(payload) {
2004
2179
  initialized: true,
2005
2180
  instructionSnapshot,
2006
2181
  resourceSnapshot,
2182
+ mcpSnapshot: typeof record.mcpSnapshot === 'string' ? record.mcpSnapshot : undefined,
2007
2183
  entries
2008
2184
  };
2009
2185
  }
@@ -2021,6 +2197,7 @@ async function persistContextualWorldState(store, sessionId, state) {
2021
2197
  generation: state.generation ?? null,
2022
2198
  instructionSnapshot: state.instructionSnapshot ?? null,
2023
2199
  resourceSnapshot: state.resourceSnapshot ?? null,
2200
+ mcpSnapshot: state.mcpSnapshot ?? null,
2024
2201
  entries: state.entries
2025
2202
  });
2026
2203
  }
@@ -2038,6 +2215,7 @@ function updateContextualWorldState(state, index, current) {
2038
2215
  entries.push({ index, kind: 'resources', content: resourceContent });
2039
2216
  return {
2040
2217
  generation: state.generation,
2218
+ mcpSnapshot: state.mcpSnapshot,
2041
2219
  initialized: true,
2042
2220
  instructionSnapshot: current.instructionSnapshot,
2043
2221
  resourceSnapshot: current.resourceSnapshot,
@@ -2100,6 +2278,38 @@ function stableJson(value) {
2100
2278
  .join(',')}}`;
2101
2279
  return JSON.stringify(value) ?? 'undefined';
2102
2280
  }
2281
+ function discoveredMcpToolNames(messages) {
2282
+ const calls = toolSearchCallIds(messages);
2283
+ const names = new Set();
2284
+ for (const message of messages)
2285
+ for (const tool of calls.has(message.callId ?? '') ? (message.toolSearchTools ?? []) : [])
2286
+ if (tool.name.startsWith('mcp__'))
2287
+ names.add(tool.name);
2288
+ return [...names];
2289
+ }
2290
+ function rebindMcpToolSearchOutputs(messages, registry) {
2291
+ const calls = toolSearchCallIds(messages);
2292
+ for (const message of messages) {
2293
+ if (!message.toolSearchTools)
2294
+ continue;
2295
+ if (!calls.has(message.callId ?? '')) {
2296
+ delete message.toolSearchTools;
2297
+ continue;
2298
+ }
2299
+ const definitions = registry.restoredDefinitions(message.toolSearchTools.map((tool) => tool.name));
2300
+ message.toolSearchTools = definitions;
2301
+ }
2302
+ }
2303
+ function toolSearchCallIds(messages) {
2304
+ return new Set(messages
2305
+ .filter((message) => (message.role === 'tool_call' &&
2306
+ message.name === 'tool_search' &&
2307
+ typeof message.callId === 'string') ||
2308
+ (message.role === 'provider' &&
2309
+ providerToolName(message) === 'tool_search' &&
2310
+ providerToolCallId(message) !== undefined))
2311
+ .map((message) => message.role === 'provider' ? providerToolCallId(message) : message.callId));
2312
+ }
2103
2313
  /** Project only known, validated tool result shapes into public structured events. */
2104
2314
  function structuredToolEventData(toolName, value) {
2105
2315
  if (!isRecord(value))
@@ -2196,13 +2406,26 @@ function structuredToolArtifacts(value) {
2196
2406
  }
2197
2407
  /** Keep streaming deltas live-only; exec output has one durable copy in model context records. */
2198
2408
  function eventForPersistence(event) {
2199
- if (event.type === 'message.delta' || event.type === 'context.compacting')
2409
+ if (event.type === 'message.delta' ||
2410
+ event.type === 'context.compacting' ||
2411
+ event.type === 'model.performance' ||
2412
+ event.type === 'message.performance')
2200
2413
  return undefined;
2201
2414
  if (event.type !== 'tool.completed' || event.toolName !== 'exec' || !event.data)
2202
2415
  return event;
2203
2416
  const metadata = Object.fromEntries(Object.entries(event.data).filter(([key]) => key !== 'stdout' && key !== 'stderr'));
2204
2417
  return { ...event, data: metadata };
2205
2418
  }
2419
+ function isModelResponseEvent(event) {
2420
+ if (event.type === 'text.delta')
2421
+ return event.delta.length > 0;
2422
+ if (event.type === 'text.completed' || event.type === 'reasoning.summary')
2423
+ return event.text.length > 0;
2424
+ return (event.type === 'response.item' ||
2425
+ event.type === 'client_tool.call' ||
2426
+ event.type === 'hosted_tool' ||
2427
+ event.type === 'citation');
2428
+ }
2206
2429
  function isRecord(value) {
2207
2430
  return value !== null && typeof value === 'object' && !Array.isArray(value);
2208
2431
  }
@@ -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;