@myagentroam/agent 0.9.140 → 0.9.142

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 (230) hide show
  1. package/dist/agent-core/host/contracts.d.ts +64 -0
  2. package/dist/{host → agent-core/host}/local-agent-host.d.ts +6 -5
  3. package/dist/{host → agent-core/host}/local-agent-host.js +4 -4
  4. package/dist/{model → agent-core/model}/anthropic-messages.js +24 -7
  5. package/dist/{model → agent-core/model}/contracts.d.ts +6 -16
  6. package/dist/{model → agent-core/model}/openai-responses.js +28 -10
  7. package/dist/agent-core/model/recovery.d.ts +4 -0
  8. package/dist/agent-core/model/recovery.js +71 -0
  9. package/dist/{model → agent-core/model}/sse.d.ts +2 -0
  10. package/dist/{model → agent-core/model}/sse.js +11 -1
  11. package/dist/{prompts → agent-core/prompts}/core.d.ts +3 -13
  12. package/dist/{prompts → agent-core/prompts}/index.d.ts +1 -1
  13. package/dist/{prompts → agent-core/prompts}/modes.d.ts +1 -1
  14. package/dist/{prompts → agent-core/prompts}/resources.d.ts +1 -1
  15. package/dist/{runtime/compact.d.ts → agent-core/runtime/compaction/history.d.ts} +2 -23
  16. package/dist/{runtime/compact.js → agent-core/runtime/compaction/history.js} +2 -92
  17. package/dist/agent-core/runtime/compaction/operation.d.ts +22 -0
  18. package/dist/agent-core/runtime/compaction/operation.js +91 -0
  19. package/dist/agent-core/runtime/context/contracts.d.ts +12 -0
  20. package/dist/{runtime/environment-context.d.ts → agent-core/runtime/context/environment.d.ts} +2 -2
  21. package/dist/{runtime/environment-context.js → agent-core/runtime/context/environment.js} +1 -1
  22. package/dist/{runtime/context-gc-runtime.d.ts → agent-core/runtime/context/gc-runtime.d.ts} +4 -4
  23. package/dist/{runtime/context-gc-runtime.js → agent-core/runtime/context/gc-runtime.js} +3 -3
  24. package/dist/{runtime/context-gc-tokens.d.ts → agent-core/runtime/context/gc-tokens.d.ts} +2 -2
  25. package/dist/{runtime/context-gc-tokens.js → agent-core/runtime/context/gc-tokens.js} +3 -3
  26. package/dist/{runtime/context-gc.d.ts → agent-core/runtime/context/gc.d.ts} +3 -3
  27. package/dist/{runtime/context-gc.js → agent-core/runtime/context/gc.js} +6 -6
  28. package/dist/{runtime → agent-core/runtime/context}/instructions.d.ts +1 -1
  29. package/dist/{runtime → agent-core/runtime/context}/instructions.js +1 -1
  30. package/dist/{runtime → agent-core/runtime/context}/normalize.d.ts +1 -1
  31. package/dist/{runtime/context-projection.d.ts → agent-core/runtime/context/projection.d.ts} +3 -3
  32. package/dist/{runtime/context-projection.js → agent-core/runtime/context/projection.js} +4 -4
  33. package/dist/agent-core/runtime/context/request-budget.d.ts +6 -0
  34. package/dist/agent-core/runtime/context/request-budget.js +22 -0
  35. package/dist/agent-core/runtime/context/restore.d.ts +15 -0
  36. package/dist/agent-core/runtime/context/restore.js +85 -0
  37. package/dist/agent-core/runtime/context/usage.d.ts +18 -0
  38. package/dist/agent-core/runtime/context/usage.js +138 -0
  39. package/dist/agent-core/runtime/context/world-state.d.ts +35 -0
  40. package/dist/agent-core/runtime/context/world-state.js +133 -0
  41. package/dist/agent-core/runtime/contracts.d.ts +37 -0
  42. package/dist/agent-core/runtime/event-projection.d.ts +5 -0
  43. package/dist/agent-core/runtime/event-projection.js +17 -0
  44. package/dist/agent-core/runtime/events.d.ts +142 -0
  45. package/dist/agent-core/runtime/execution.d.ts +50 -0
  46. package/dist/agent-core/runtime/execution.js +393 -0
  47. package/dist/{runtime → agent-core/runtime}/rollout-budget.d.ts +1 -1
  48. package/dist/agent-core/sdk/agent.js +209 -0
  49. package/dist/agent-core/sdk/model-runtime.d.ts +13 -0
  50. package/dist/agent-core/sdk/model-runtime.js +63 -0
  51. package/dist/agent-core/sdk/session-title.d.ts +3 -0
  52. package/dist/agent-core/sdk/session-title.js +42 -0
  53. package/dist/agent-core/sdk/session.d.ts +62 -0
  54. package/dist/agent-core/sdk/session.js +1147 -0
  55. package/dist/agent-core/sdk/subagent-backend.d.ts +3 -0
  56. package/dist/agent-core/sdk/subagent-backend.js +46 -0
  57. package/dist/agent-core/sdk/types.d.ts +56 -0
  58. package/dist/{session → agent-core/session}/catalog.d.ts +2 -1
  59. package/dist/{session → agent-core/session}/context-gc-checkpoint.js +1 -1
  60. package/dist/{session → agent-core/session}/jsonl-store.d.ts +2 -2
  61. package/dist/{session → agent-core/session}/jsonl-store.js +1 -1
  62. package/dist/agent-core/subagent/contracts.d.ts +91 -0
  63. package/dist/agent-core/subagent/contracts.js +1 -0
  64. package/dist/agent-core/subagent/scheduler.d.ts +117 -0
  65. package/dist/{subagent → agent-core/subagent}/session-controller.d.ts +3 -2
  66. package/dist/agent-core/tools/agent/agent-cancel.d.ts +7 -0
  67. package/dist/{tools → agent-core/tools/agent}/agent-cancel.js +1 -1
  68. package/dist/agent-core/tools/agent/agent-message.d.ts +7 -0
  69. package/dist/{tools → agent-core/tools/agent}/agent-message.js +2 -2
  70. package/dist/{tools → agent-core/tools/agent}/agent-start.d.ts +3 -3
  71. package/dist/{tools → agent-core/tools/agent}/agent-start.js +3 -3
  72. package/dist/agent-core/tools/agent/agent-wait.d.ts +7 -0
  73. package/dist/{tools → agent-core/tools/agent}/agent-wait.js +2 -2
  74. package/dist/{tools → agent-core/tools/agent}/question.d.ts +2 -2
  75. package/dist/{tools → agent-core/tools/agent}/question.js +2 -2
  76. package/dist/{tools → agent-core/tools/agent}/skill.d.ts +2 -2
  77. package/dist/{tools → agent-core/tools/agent}/skill.js +2 -2
  78. package/dist/{tools → agent-core/tools/agent}/todo-read.d.ts +2 -2
  79. package/dist/{tools → agent-core/tools/agent}/todo-write.d.ts +2 -2
  80. package/dist/{tools → agent-core/tools/agent}/todo-write.js +2 -2
  81. package/dist/{tools → agent-core/tools/agent}/todo.d.ts +2 -9
  82. package/dist/{tools → agent-core/tools/agent}/todo.js +1 -1
  83. package/dist/{tools → agent-core/tools}/code-mode/cells.d.ts +1 -1
  84. package/dist/{tools → agent-core/tools}/code-mode/dispatch.d.ts +2 -4
  85. package/dist/{tools → agent-core/tools}/code-mode/dispatch.js +5 -4
  86. package/dist/{tools → agent-core/tools}/code-mode/engine.d.ts +1 -1
  87. package/dist/agent-core/tools/code-mode/engine.js +1 -0
  88. package/dist/{tools/code-mode.d.ts → agent-core/tools/code-mode/index.d.ts} +7 -7
  89. package/dist/{tools/code-mode.js → agent-core/tools/code-mode/index.js} +15 -16
  90. package/dist/{tools → agent-core/tools}/code-mode/quickjs-engine.js +12 -2
  91. package/dist/{tools → agent-core/tools}/code-mode/state.d.ts +2 -13
  92. package/dist/agent-core/tools/code-mode/worker.d.ts +1 -0
  93. package/dist/agent-core/tools/contracts.d.ts +168 -0
  94. package/dist/agent-core/tools/contracts.js +1 -0
  95. package/dist/agent-core/tools/execution.d.ts +34 -0
  96. package/dist/agent-core/tools/execution.js +102 -0
  97. package/dist/agent-core/tools/local/apply-patch.d.ts +19 -0
  98. package/dist/{tools → agent-core/tools/local}/apply-patch.js +8 -2
  99. package/dist/{tools → agent-core/tools/local}/exec.d.ts +5 -17
  100. package/dist/{tools → agent-core/tools/local}/exec.js +4 -4
  101. package/dist/{tools → agent-core/tools/local}/read.d.ts +7 -15
  102. package/dist/{tools → agent-core/tools/local}/read.js +185 -2
  103. package/dist/{tools/local-registry.d.ts → agent-core/tools/local/registry.d.ts} +5 -5
  104. package/dist/agent-core/tools/local/registry.js +101 -0
  105. package/dist/{tools → agent-core/tools/local}/view-image.d.ts +4 -9
  106. package/dist/{tools → agent-core/tools/local}/view-image.js +2 -2
  107. package/dist/agent-core/tools/local/web-fetch.d.ts +4 -0
  108. package/dist/agent-core/tools/local/web-fetch.js +85 -0
  109. package/dist/agent-core/tools/local/web-search.d.ts +7 -0
  110. package/dist/agent-core/tools/local/web-search.js +34 -0
  111. package/dist/{tools/local-workspace-tools.js → agent-core/tools/local/workspace-tools.js} +1 -1
  112. package/dist/{sdk/mcp-catalog.d.ts → agent-core/tools/mcp/catalog.d.ts} +1 -2
  113. package/dist/{sdk/mcp-catalog.js → agent-core/tools/mcp/catalog.js} +1 -1
  114. package/dist/agent-core/tools/mcp/history.d.ts +4 -0
  115. package/dist/agent-core/tools/mcp/history.js +33 -0
  116. package/dist/{tools → agent-core/tools/mcp}/tool-search.d.ts +3 -3
  117. package/dist/{tools → agent-core/tools/mcp}/tool-search.js +4 -4
  118. package/dist/agent-core/tools/output-projection.d.ts +14 -0
  119. package/dist/agent-core/tools/output-projection.js +189 -0
  120. package/dist/{tools → agent-core/tools}/registry.d.ts +1 -1
  121. package/dist/agent-core/tools/registry.js +47 -0
  122. package/dist/{tools → agent-core/tools}/runtime-context.d.ts +8 -7
  123. package/dist/agent-core/tools/runtime-context.js +1 -0
  124. package/dist/{tools → agent-core/tools}/runtime.d.ts +1 -1
  125. package/dist/cli/codex-auth-file.d.ts +1 -1
  126. package/dist/cli/codex-auth-file.js +2 -2
  127. package/dist/cli/main.js +9 -9
  128. package/dist/index.d.ts +17 -15
  129. package/dist/index.js +8 -8
  130. package/dist/sandbox/linux/bubblewrap-sandbox-executor.d.ts +1 -1
  131. package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +1 -1
  132. package/package.json +3 -3
  133. package/dist/host/contracts.d.ts +0 -21
  134. package/dist/sdk/agent.js +0 -2524
  135. package/dist/sdk/types.d.ts +0 -349
  136. package/dist/subagent/scheduler.d.ts +0 -206
  137. package/dist/tools/agent-cancel.d.ts +0 -7
  138. package/dist/tools/agent-message.d.ts +0 -7
  139. package/dist/tools/agent-wait.d.ts +0 -7
  140. package/dist/tools/apply-patch.d.ts +0 -32
  141. package/dist/tools/local-registry.js +0 -373
  142. package/dist/tools/registry.js +0 -47
  143. package/dist/tools/web-fetch.d.ts +0 -2
  144. package/dist/tools/web-fetch.js +0 -12
  145. package/dist/tools/web-search.d.ts +0 -2
  146. package/dist/tools/web-search.js +0 -15
  147. package/dist/{error.d.ts → agent-core/error.d.ts} +0 -0
  148. package/dist/{error.js → agent-core/error.js} +0 -0
  149. package/dist/{host → agent-core/host}/contracts.js +0 -0
  150. package/dist/{host → agent-core/host}/local-process-executor.d.ts +0 -0
  151. package/dist/{host → agent-core/host}/local-process-executor.js +0 -0
  152. package/dist/{host → agent-core/host}/network.d.ts +0 -0
  153. package/dist/{host → agent-core/host}/network.js +0 -0
  154. package/dist/{model → agent-core/model}/anthropic-messages.d.ts +0 -0
  155. package/dist/{model → agent-core/model}/anthropic-thinking.d.ts +0 -0
  156. package/dist/{model → agent-core/model}/anthropic-thinking.js +0 -0
  157. package/dist/{model → agent-core/model}/catalog-file.d.ts +0 -0
  158. package/dist/{model → agent-core/model}/catalog-file.js +0 -0
  159. package/dist/{model → agent-core/model}/configuration.d.ts +0 -0
  160. package/dist/{model → agent-core/model}/configuration.js +0 -0
  161. package/dist/{model → agent-core/model}/contracts.js +0 -0
  162. package/dist/{model → agent-core/model}/http.d.ts +0 -0
  163. package/dist/{model → agent-core/model}/http.js +0 -0
  164. package/dist/{model → agent-core/model}/openai-responses.d.ts +0 -0
  165. package/dist/{model → agent-core/model}/runtime-credential.d.ts +0 -0
  166. package/dist/{model → agent-core/model}/runtime-credential.js +0 -0
  167. package/dist/{model → agent-core/model}/transport-test.d.ts +0 -0
  168. package/dist/{model → agent-core/model}/transport-test.js +0 -0
  169. package/dist/{prompts → agent-core/prompts}/compact.d.ts +0 -0
  170. package/dist/{prompts → agent-core/prompts}/compact.js +0 -0
  171. package/dist/{prompts → agent-core/prompts}/core.js +0 -0
  172. package/dist/{prompts → agent-core/prompts}/execution-budget.d.ts +0 -0
  173. package/dist/{prompts → agent-core/prompts}/execution-budget.js +0 -0
  174. package/dist/{prompts → agent-core/prompts}/index.js +0 -0
  175. package/dist/{prompts → agent-core/prompts}/modes.js +0 -0
  176. package/dist/{prompts → agent-core/prompts}/output.d.ts +0 -0
  177. package/dist/{prompts → agent-core/prompts}/output.js +0 -0
  178. package/dist/{prompts → agent-core/prompts}/resources.js +0 -0
  179. package/dist/{prompts → agent-core/prompts}/subagent.d.ts +0 -0
  180. package/dist/{prompts → agent-core/prompts}/subagent.js +0 -0
  181. package/dist/{prompts → agent-core/prompts}/title.d.ts +0 -0
  182. package/dist/{prompts → agent-core/prompts}/title.js +0 -0
  183. package/dist/{prompts → agent-core/prompts}/workflow.d.ts +0 -0
  184. package/dist/{prompts → agent-core/prompts}/workflow.js +0 -0
  185. package/dist/{runtime → agent-core/runtime}/async-queue.d.ts +0 -0
  186. package/dist/{runtime → agent-core/runtime}/async-queue.js +0 -0
  187. package/dist/{sdk/types.js → agent-core/runtime/context/contracts.js} +0 -0
  188. package/dist/{runtime → agent-core/runtime/context}/normalize.js +0 -0
  189. package/dist/{session/types.js → agent-core/runtime/contracts.js} +0 -0
  190. package/dist/{tools/code-mode/engine.js → agent-core/runtime/events.js} +0 -0
  191. package/dist/{runtime → agent-core/runtime}/execution-policy.d.ts +0 -0
  192. package/dist/{runtime → agent-core/runtime}/execution-policy.js +0 -0
  193. package/dist/{runtime → agent-core/runtime}/plan-output.d.ts +0 -0
  194. package/dist/{runtime → agent-core/runtime}/plan-output.js +0 -0
  195. package/dist/{runtime → agent-core/runtime}/rollout-budget.js +0 -0
  196. package/dist/{runtime → agent-core/runtime}/token-budget.d.ts +0 -0
  197. package/dist/{runtime → agent-core/runtime}/token-budget.js +0 -0
  198. package/dist/{sdk → agent-core/sdk}/agent.d.ts +0 -0
  199. package/dist/{tools/code-mode/worker.d.ts → agent-core/sdk/types.js} +0 -0
  200. package/dist/{session → agent-core/session}/catalog.js +0 -0
  201. package/dist/{session → agent-core/session}/context-gc-checkpoint.d.ts +0 -0
  202. package/dist/{session → agent-core/session}/context-gc-replacements.d.ts +0 -0
  203. package/dist/{session → agent-core/session}/context-gc-replacements.js +0 -0
  204. package/dist/{session → agent-core/session}/context-gc-subagent.d.ts +0 -0
  205. package/dist/{session → agent-core/session}/context-gc-subagent.js +0 -0
  206. package/dist/{session → agent-core/session}/transaction-recovery.d.ts +0 -0
  207. package/dist/{session → agent-core/session}/transaction-recovery.js +0 -0
  208. package/dist/{session → agent-core/session}/types.d.ts +0 -0
  209. package/dist/{tools/runtime-context.js → agent-core/session/types.js} +0 -0
  210. package/dist/{subagent → agent-core/subagent}/scheduler.js +0 -0
  211. package/dist/{subagent → agent-core/subagent}/session-controller.js +0 -0
  212. package/dist/{tools → agent-core/tools/agent}/todo-read.js +0 -0
  213. package/dist/{tools → agent-core/tools}/callers.d.ts +0 -0
  214. package/dist/{tools → agent-core/tools}/callers.js +0 -0
  215. package/dist/{tools → agent-core/tools}/code-mode/cells.js +1 -1
  216. /package/dist/{tools → agent-core/tools}/code-mode/quickjs-engine.d.ts +0 -0
  217. /package/dist/{tools → agent-core/tools}/code-mode/state.js +0 -0
  218. /package/dist/{tools → agent-core/tools}/code-mode/text-output.d.ts +0 -0
  219. /package/dist/{tools → agent-core/tools}/code-mode/text-output.js +0 -0
  220. /package/dist/{tools → agent-core/tools}/code-mode/worker.js +0 -0
  221. /package/dist/{tools → agent-core/tools}/execution-limits.d.ts +0 -0
  222. /package/dist/{tools → agent-core/tools}/execution-limits.js +0 -0
  223. /package/dist/{tools → agent-core/tools/local}/read-context.d.ts +0 -0
  224. /package/dist/{tools → agent-core/tools/local}/read-context.js +0 -0
  225. /package/dist/{tools/local-workspace-tools.d.ts → agent-core/tools/local/workspace-tools.d.ts} +0 -0
  226. /package/dist/{tools → agent-core/tools}/runtime.js +0 -0
  227. /package/dist/{tools → agent-core/tools}/scheduler.d.ts +0 -0
  228. /package/dist/{tools → agent-core/tools}/scheduler.js +0 -0
  229. /package/dist/{tools → agent-core/tools}/shared/path-resolver.d.ts +0 -0
  230. /package/dist/{tools → agent-core/tools}/shared/path-resolver.js +0 -0
package/dist/sdk/agent.js DELETED
@@ -1,2524 +0,0 @@
1
- import { createHash, randomUUID } from 'node:crypto';
2
- import { isDeepStrictEqual } from 'node:util';
3
- import { describeMarAgentError, MarAgentError } from '../error.js';
4
- import { CodeCellExecutionError } from '../tools/code-mode/cells.js';
5
- import { negotiateHost } from '../host/contracts.js';
6
- import { AnthropicMessagesAdapter, isAnthropicRecoverableStreamError } from '../model/anthropic-messages.js';
7
- import { marAgentReasoningEffortSchema, resolveModelDefaultReasoningEffort, usesResponsesConfigurationUpdates } from '../model/configuration.js';
8
- import { modelRetryMaxAttempts, isRetryableModelRequestError, waitForModelRetry } from '../model/http.js';
9
- import { OpenAiResponsesAdapter, isOpenAiResponsesRecoverableStreamError } from '../model/openai-responses.js';
10
- import { AsyncQueue } from '../runtime/async-queue.js';
11
- import { PlanOutputPrefix, splitPlanOutput } from '../runtime/plan-output.js';
12
- import { AGENT_EXECUTION_POLICY } from '../runtime/execution-policy.js';
13
- import { RolloutBudget, resolveRolloutBudgetOptions } from '../runtime/rollout-budget.js';
14
- import { ContextGcRuntime } from '../runtime/context-gc-runtime.js';
15
- import { buildContextProjection, providerToolCallId, providerToolName } from '../runtime/context-projection.js';
16
- import { countModelTokens, countTextLines, truncateModelText } from '../runtime/token-budget.js';
17
- import { TOOL_EXECUTION_LIMITS } from '../tools/execution-limits.js';
18
- import { DeferredMcpRegistry } from '../tools/tool-search.js';
19
- import { assertMcpToolCatalog } from './mcp-catalog.js';
20
- import { DEFAULT_COMPACTION_FOCUS, CompactionOperation, buildCompactionInput, buildCompactionMessages, buildNativeCompactionMessages, buildCompactedHistory, compactedHistoryMessages, continuationMessage } from '../runtime/compact.js';
21
- import { buildSystemPrompt, MAR_AGENT_PROMPT_VERSION } from '../prompts/index.js';
22
- import { buildEnvironmentContext } from '../prompts/core.js';
23
- import { modelOutputContinuationMessage } from '../prompts/output.js';
24
- import { retainedSessionResourcesSnapshot, retainedSessionResourcesUpdate } from '../prompts/resources.js';
25
- import { currentLocalTimeContext, environmentContextUpdate, restoreEnvironmentState } from '../runtime/environment-context.js';
26
- import { executionBudgetEnvironmentEntry } from '../prompts/execution-budget.js';
27
- import { childSubagentInstruction } from '../prompts/subagent.js';
28
- import { sessionTitlePrompt } from '../prompts/title.js';
29
- import { loadAgentInstructions, renderAgentInstructions } from '../runtime/instructions.js';
30
- import { normalizeMessagesForModel } from '../runtime/normalize.js';
31
- import { compactSummaryFromCheckpoint, replacementHistoryFromCheckpoint, JsonlSessionStore } from '../session/jsonl-store.js';
32
- import { TodoStore } from '../tools/todo.js';
33
- import { BUILTIN_TOOL_DEFINITIONS } from '../tools/registry.js';
34
- import { agentStartToolDefinitionWithModels } from '../tools/agent-start.js';
35
- import { executeRegisteredTool } from '../tools/runtime.js';
36
- import { createCodeToolDispatch } from '../tools/code-mode/dispatch.js';
37
- import { createSessionToolScheduler, scheduleToolCallBatch } from '../tools/scheduler.js';
38
- import { projectCodeModeSearchResult, projectCodeModeTools } from '../tools/code-mode.js';
39
- import { allowsDirectTool, allowsToolCaller, toolCallers } from '../tools/callers.js';
40
- import { SubagentSessionController } from '../subagent/session-controller.js';
41
- /** Retry only classified recoverable failures within the attempt budget; never retry cancellation. */
42
- async function recoverModelStream(error, retryState, signal) {
43
- if (signal.aborted ||
44
- !(isRetryableModelRequestError(error) ||
45
- isOpenAiResponsesRecoverableStreamError(error) ||
46
- isAnthropicRecoverableStreamError(error)) ||
47
- retryState.attempts >= modelRetryMaxAttempts(retryState))
48
- return false;
49
- const delayMs = retryState.retryDelayMs;
50
- delete retryState.retryDelayMs;
51
- await waitForModelRetry(typeof error.details?.retryAfter === 'string' ? error.details.retryAfter : null, retryState.attempts - 1, signal, delayMs);
52
- return true;
53
- }
54
- /** Validate models before Host negotiation and expose only built-in protocol adapters. */
55
- export async function createMarAgent(options) {
56
- if (Object.hasOwn(options, 'adapterFactory'))
57
- throw new MarAgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Custom model adapters are not supported.');
58
- return createMarAgentWithAdapter(options);
59
- }
60
- /** Package-internal seam for deterministic adapter tests; not a package export. */
61
- export async function createMarAgentWithAdapter(options) {
62
- const rolloutBudgetOptions = resolveRolloutBudgetOptions(options.rolloutBudget);
63
- const resolvedModels = resolveAgentModels(options.models);
64
- let { models, defaultModelId } = resolvedModels;
65
- let modelGeneration = 0;
66
- const { credentialProviders } = resolvedModels;
67
- const description = negotiateHost(await options.host.describe());
68
- if (description.mode !== options.mode)
69
- throw new MarAgentError('MAR_AGENT_HOST_INCOMPATIBLE', 'Host mode does not match Agent mode.');
70
- const store = await JsonlSessionStore.open(description.homeDirectory);
71
- const active = new Map();
72
- const activeExecutionAdapters = new Set();
73
- const executionResults = new Map();
74
- const sessionRuntimes = new Map();
75
- const sessionWriterOwner = {};
76
- let disposed = false;
77
- let reconfiguring = false;
78
- const createAdapter = (modelId, configuredModels = models) => {
79
- const configuration = configuredModels.get(modelId);
80
- if (!configuration)
81
- throw new MarAgentError('MAR_AGENT_MODEL_DISABLED', 'Selected model is unavailable.');
82
- return (options.adapterFactory?.(configuration) ??
83
- (configuration.protocol === 'OPENAI_RESPONSES'
84
- ? new OpenAiResponsesAdapter(configuration)
85
- : new AnthropicMessagesAdapter(configuration)));
86
- };
87
- const activeSessionIds = () => new Set([...active.keys()].map((key) => key.slice(0, key.indexOf(':'))));
88
- const activeAdaptersExcept = (sessionId) => {
89
- const activeAdapters = new Set();
90
- for (const [candidateSessionId, runtime] of sessionRuntimes)
91
- if (candidateSessionId !== sessionId && activeSessionIds().has(candidateSessionId))
92
- for (const adapter of runtime.adapters.values())
93
- activeAdapters.add(adapter);
94
- return activeAdapters;
95
- };
96
- const openSession = async (sessionId, workspace) => {
97
- const storedSession = await store.getSession(sessionId);
98
- const storedWorkspace = storedSession.workspace;
99
- const sessionSource = storedSession.source;
100
- const logicalWorkspace = options.workspaceMapping?.logicalPath ?? workspace ?? storedWorkspace ?? '';
101
- const instructionWorkspace = options.workspaceMapping?.physicalPath ?? workspace ?? storedWorkspace ?? '';
102
- const currentEnvironment = {
103
- platform: description.platform,
104
- ...(description.architecture ? { architecture: description.architecture } : {}),
105
- ...(description.shell ? { shell: description.shell } : {}),
106
- ...(description.guaranteedCommands
107
- ? { guaranteedCommands: description.guaranteedCommands }
108
- : {}),
109
- workspace: logicalWorkspace
110
- };
111
- if (!restoreEnvironmentState({ version: 1, state: currentEnvironment }))
112
- throw new MarAgentError('MAR_AGENT_HOST_INCOMPATIBLE', 'Host environment facts are invalid or exceed the context limit.');
113
- const environmentContext = buildEnvironmentContext(currentEnvironment);
114
- let sessionRuntime = sessionRuntimes.get(sessionId);
115
- if (sessionRuntime === undefined) {
116
- sessionRuntime = {
117
- sourceType: sessionSource.type,
118
- scheduleTool: createSessionToolScheduler(),
119
- adapterGeneration: modelGeneration,
120
- adapters: new Map()
121
- };
122
- sessionRuntimes.set(sessionId, sessionRuntime);
123
- }
124
- if (sessionSource.type === 'user' && sessionRuntime.subagents === undefined) {
125
- sessionRuntime.subagentsOpening ??= SubagentSessionController.open({
126
- parentSessionId: sessionId,
127
- rootSessionId: sessionId,
128
- backend: createSubagentBackend(store, storedWorkspace)
129
- });
130
- try {
131
- sessionRuntime.subagents = await sessionRuntime.subagentsOpening;
132
- }
133
- finally {
134
- delete sessionRuntime.subagentsOpening;
135
- }
136
- }
137
- const sessionAdapter = (modelId, configuredModels) => {
138
- let selected = sessionRuntime.adapters.get(modelId);
139
- if (selected === undefined) {
140
- selected = createAdapter(modelId, configuredModels);
141
- if (options.adapterFactory === undefined || selected.createTurnSession !== undefined)
142
- sessionRuntime.adapters.set(modelId, selected);
143
- }
144
- return selected;
145
- };
146
- const refreshSessionAdapters = async (generation) => {
147
- if (sessionRuntime.adapterGeneration === generation)
148
- return;
149
- const retainedByActiveSessions = activeAdaptersExcept(sessionId);
150
- await closeModelAdapters([...sessionRuntime.adapters.values()].filter((adapter) => !retainedByActiveSessions.has(adapter)));
151
- sessionRuntime.adapters.clear();
152
- sessionRuntime.adapterGeneration = generation;
153
- };
154
- const execution = (mode, input, internal = {}) => {
155
- if (disposed)
156
- throw new MarAgentError('MAR_AGENT_DISPOSED', 'Agent is disposed.');
157
- if (reconfiguring)
158
- throw new MarAgentError('MAR_AGENT_EXECUTION_ACTIVE', 'Models are being reconfigured for the next execution.');
159
- if ([...active.keys()].some((key) => key.startsWith(`${sessionId}:`)))
160
- throw new MarAgentError('MAR_AGENT_EXECUTION_ACTIVE', 'Session already has an active execution.');
161
- const executionId = randomUUID(), turnId = internal.turnId ?? randomUUID(), queue = new AsyncQueue(), controller = new AbortController();
162
- let executionEnvironment = {
163
- ...currentEnvironment,
164
- ...currentLocalTimeContext()
165
- };
166
- const executionModels = models;
167
- const executionDefaultModelId = defaultModelId;
168
- const executionModelGeneration = modelGeneration;
169
- const manualCompactionId = mode === 'compact' ? randomUUID() : undefined;
170
- active.set(`${sessionId}:${executionId}`, controller);
171
- const mailbox = [...(internal.initialMailbox ?? [])];
172
- let acceptsMailbox = sessionSource.type === 'subagent' && mode !== 'compact';
173
- let sequence = 0;
174
- let manualCompactionStarted = false;
175
- let emitTail = Promise.resolve();
176
- let exhaustedModelAttempt;
177
- // Serialize durable events before either the local stream or Host observes them.
178
- const emit = (event) => {
179
- const full = {
180
- ...event,
181
- eventId: randomUUID(),
182
- sequence: ++sequence,
183
- timestamp: new Date().toISOString(),
184
- sessionId,
185
- executionId,
186
- turnId
187
- };
188
- const operation = emitTail.then(async () => {
189
- // Transport-only progress events never become recoverable history.
190
- const persistent = eventForPersistence(full);
191
- if (persistent !== undefined)
192
- await store.append(sessionId, {
193
- type: full.type,
194
- payload: persistent,
195
- executionId,
196
- turnId
197
- });
198
- queue.push(full);
199
- if (internal.publishHostEvents !== false)
200
- await options.host.emit(full);
201
- });
202
- emitTail = operation.catch(() => undefined);
203
- return operation;
204
- };
205
- const recordModelAttempt = (purpose) => (diagnostic) => {
206
- if (purpose === 'EXECUTION' || purpose === 'COMPACTION') {
207
- if ((diagnostic.state === 'STARTED' && diagnostic.attempt === 1) ||
208
- diagnostic.state === 'SUCCEEDED')
209
- exhaustedModelAttempt = undefined;
210
- else if (diagnostic.state === 'FAILED' &&
211
- diagnostic.willRetry === false &&
212
- diagnostic.attempt > 1)
213
- exhaustedModelAttempt = {
214
- attempts: diagnostic.attempt,
215
- retries: diagnostic.attempt - 1
216
- };
217
- }
218
- const operation = emitTail.then(async () => {
219
- await store.append(sessionId, {
220
- type: 'model.attempt',
221
- payload: { purpose, ...diagnostic },
222
- executionId,
223
- turnId
224
- });
225
- });
226
- emitTail = operation.catch(() => undefined);
227
- return operation;
228
- };
229
- const createCompactionOperation = (compactionId) => new CompactionOperation(controller.signal, (progress) => {
230
- const operation = emitTail.then(async () => {
231
- await store.append(sessionId, {
232
- type: 'compact.progress',
233
- payload: { compactionId, ...progress },
234
- executionId,
235
- turnId
236
- });
237
- });
238
- emitTail = operation.catch(() => undefined);
239
- return operation;
240
- });
241
- const result = (async () => {
242
- let finalAnswer = '';
243
- const planOutputPrefixes = new Map();
244
- let manualCompaction;
245
- const rolloutBudget = internal.sharedRolloutBudget ??
246
- (rolloutBudgetOptions ? new RolloutBudget(rolloutBudgetOptions) : undefined);
247
- const ownsRolloutBudget = rolloutBudget !== undefined && internal.sharedRolloutBudget === undefined;
248
- let releaseSession;
249
- let selectedTurn;
250
- const contextGc = new ContextGcRuntime();
251
- let selectedAdapter;
252
- let titleAdapter;
253
- const executionScopedAdapters = new Set();
254
- let titleTask;
255
- let titleSettled = false;
256
- const settleTitle = async () => {
257
- if (titleSettled)
258
- return;
259
- titleSettled = true;
260
- const generatedTitle = await titleTask;
261
- if (generatedTitle)
262
- await store
263
- .updateTitle(sessionId, generatedTitle, { executionId, turnId })
264
- .catch(() => undefined);
265
- };
266
- const subagents = sessionSource.type === 'user' ? sessionRuntime.subagents : undefined;
267
- try {
268
- releaseSession = await store.acquireSessionLease(sessionId, sessionWriterOwner);
269
- await refreshSessionAdapters(executionModelGeneration);
270
- const sessionState = await store.readExecutionState(sessionId);
271
- if (!store.hasRetainedSessionLease(sessionId, sessionWriterOwner))
272
- sessionRuntime.todo = TodoStore.restore(sessionState.todo);
273
- const todo = (sessionRuntime.todo ??= TodoStore.restore(sessionState.todo));
274
- const contextRecords = sessionState.contextRecords;
275
- const contextualWorldStateGeneration = latestContextualWorldStateGeneration(contextRecords);
276
- const restoredContextualWorldState = sessionRuntime.contextualWorldState;
277
- const persistedContextualWorldState = parsePersistedContextualWorldState(await store.readContextualWorldState(sessionId));
278
- let contextualWorldState;
279
- if (restoredContextualWorldState !== undefined &&
280
- restoredContextualWorldState.generation === contextualWorldStateGeneration)
281
- contextualWorldState = restoredContextualWorldState;
282
- else if (persistedContextualWorldState !== undefined &&
283
- persistedContextualWorldState.generation === contextualWorldStateGeneration) {
284
- contextualWorldState = persistedContextualWorldState;
285
- sessionRuntime.contextualWorldState = contextualWorldState;
286
- }
287
- else {
288
- contextualWorldState = {
289
- generation: contextualWorldStateGeneration,
290
- initialized: false,
291
- instructionSnapshot: undefined,
292
- resourceSnapshot: undefined,
293
- entries: []
294
- };
295
- sessionRuntime.contextualWorldState = contextualWorldState;
296
- }
297
- const compactMayContainAgentInstructions = contextRecords.some((record) => compactSummaryFromCheckpoint(record) !== undefined);
298
- const compactMayContainRetainedResources = compactHistoryMayContainRetainedResources(contextRecords);
299
- const restored = await restoreMessages(store, sessionId, contextRecords, {
300
- includeImages: mode !== 'compact'
301
- });
302
- const messages = restored.messages;
303
- let recordedEnvironment = restored.environmentState;
304
- let projectedExecutionBudgetStage;
305
- const appendExecutionBudgetEnvironment = () => {
306
- const currentStage = rolloutBudget?.active ? rolloutBudget.stage : 'inactive';
307
- if (currentStage === projectedExecutionBudgetStage)
308
- return;
309
- const previousStage = projectedExecutionBudgetStage;
310
- projectedExecutionBudgetStage = currentStage;
311
- if ((previousStage === undefined &&
312
- (currentStage === 'normal' || currentStage === 'inactive')) ||
313
- (currentStage === 'inactive' && previousStage === 'normal'))
314
- return;
315
- const entry = executionBudgetEnvironmentEntry(currentStage === 'normal' ? 'inactive' : currentStage);
316
- messages.push(environmentContextUpdate(executionEnvironment, executionEnvironment, [entry]));
317
- };
318
- const appendMailboxMessages = async () => {
319
- let appended = false;
320
- while (mailbox.length > 0) {
321
- const pending = mailbox.splice(0);
322
- for (const { text: prompt, delivery } of pending) {
323
- await store.append(sessionId, {
324
- type: 'turn.user',
325
- payload: {
326
- prompt,
327
- inputSource: 'parent_agent',
328
- ...(delivery ? { messageId: delivery.messageId } : {})
329
- },
330
- executionId,
331
- turnId
332
- });
333
- messages.push({ role: 'user', content: prompt });
334
- delivery?.applied();
335
- appended = true;
336
- }
337
- }
338
- return appended;
339
- };
340
- const recordEnvironment = async () => {
341
- const message = environmentContextUpdate(executionEnvironment, recordedEnvironment);
342
- if (!message)
343
- return;
344
- await store.append(sessionId, {
345
- type: 'context.environment',
346
- payload: { version: 1, state: executionEnvironment },
347
- executionId,
348
- turnId
349
- });
350
- messages.push(message);
351
- recordedEnvironment = executionEnvironment;
352
- };
353
- let compactionUserMessages = [
354
- ...restored.userMessages,
355
- ...(mode === 'compact' ? [] : [input.prompt])
356
- ];
357
- const hasRestoredUserImages = messages.some((message) => message.role === 'user' && Boolean(message.images?.length));
358
- const modelInputImages = mode === 'compact'
359
- ? undefined
360
- : input.images?.map(({ mimeType, dataBase64 }) => ({
361
- mimeType,
362
- dataBase64
363
- }));
364
- const currentExecutionToolImages = [];
365
- let currentExecutionUserIndex;
366
- if (mode !== 'compact') {
367
- await recordEnvironment();
368
- const inputImageMetadata = input.images?.length
369
- ? await store.persistInputImages(sessionId, input.images)
370
- : [];
371
- // A failed sync can still leave a complete record; lease recovery decides image ownership.
372
- await store.append(sessionId, {
373
- type: 'turn.user',
374
- payload: {
375
- prompt: input.prompt,
376
- ...(internal.delivery ? { messageId: internal.delivery.messageId } : {}),
377
- ...(internal.inputSource === undefined
378
- ? {}
379
- : { inputSource: internal.inputSource }),
380
- ...(input.clientMessageId ? { clientMessageId: input.clientMessageId } : {}),
381
- ...(inputImageMetadata.length ? { images: inputImageMetadata } : {})
382
- },
383
- executionId,
384
- turnId
385
- });
386
- messages.push({ role: 'user', content: input.prompt });
387
- currentExecutionUserIndex = messages.length - 1;
388
- internal.delivery?.applied();
389
- await appendMailboxMessages();
390
- if (sessionState.automaticTitlePending) {
391
- const titleModel = [...executionModels.values()].find((model) => model.titleGeneration) ??
392
- executionModels.get(input.modelId ?? executionDefaultModelId);
393
- titleAdapter = createAdapter(titleModel.id, executionModels);
394
- activeExecutionAdapters.add(titleAdapter);
395
- titleTask = generateSessionTitle(titleAdapter, input.prompt, titleModel.defaultReasoningEffort, controller.signal, recordModelAttempt('TITLE')).catch(() => undefined);
396
- }
397
- }
398
- await emit({ type: 'execution.started', mode });
399
- if (restored.ignoredImages > 0)
400
- await store.append(sessionId, {
401
- type: 'context.warning',
402
- payload: {
403
- code: 'MAR_AGENT_INPUT_IMAGE_IGNORED',
404
- message: 'One or more corrupt historical input images were ignored.',
405
- ignoredImages: restored.ignoredImages
406
- },
407
- executionId,
408
- turnId
409
- });
410
- selectedAdapter = sessionAdapter(input.modelId ?? executionDefaultModelId, executionModels);
411
- const selectedModel = executionModels.get(input.modelId ?? executionDefaultModelId);
412
- if (options.adapterFactory !== undefined &&
413
- selectedAdapter.createTurnSession === undefined) {
414
- executionScopedAdapters.add(selectedAdapter);
415
- activeExecutionAdapters.add(selectedAdapter);
416
- }
417
- const selected = createModelTurnSession(selectedAdapter);
418
- selectedTurn = selected;
419
- let lastServerUsage = latestModelUsage(contextRecords, selectedModel.id);
420
- const compactUserTokenLimit = Math.min(20_000, Math.floor(selectedModel.contextWindowTokens / 8));
421
- const reasoningEffort = input.reasoningEffort ?? selectedModel.defaultReasoningEffort;
422
- const useEffortUpdates = usesResponsesConfigurationUpdates(selectedModel);
423
- const previousReasoningEffort = useEffortUpdates
424
- ? reasoningEffortBaseline(contextRecords, selectedModel.id)
425
- : undefined;
426
- let requestReasoningEffort = useEffortUpdates
427
- ? (previousReasoningEffort ?? reasoningEffort)
428
- : undefined;
429
- let effectiveReasoningEffort = useEffortUpdates
430
- ? previousReasoningEffort === undefined
431
- ? requestReasoningEffort
432
- : (lastConfigurationEffort(messages, selectedModel.id) ?? requestReasoningEffort)
433
- : undefined;
434
- if (mode !== 'compact' &&
435
- useEffortUpdates &&
436
- effectiveReasoningEffort !== reasoningEffort) {
437
- const update = {
438
- role: 'provider',
439
- content: '',
440
- provider: 'OPENAI_RESPONSES',
441
- providerModelId: selectedModel.id,
442
- item: { type: 'configuration_update', reasoning: { effort: reasoningEffort } }
443
- };
444
- messages.push(update);
445
- await store.append(sessionId, {
446
- type: 'model.context',
447
- payload: update,
448
- executionId,
449
- turnId
450
- });
451
- effectiveReasoningEffort = reasoningEffort;
452
- }
453
- if (mode !== 'compact' && subagents)
454
- await subagents.beginParentExecution({
455
- executionId,
456
- modelId: selectedModel.id,
457
- reasoningEffort,
458
- models: executionModels,
459
- emitTerminal: async (task) => emit({
460
- type: 'subagent.completed',
461
- agentId: task.agentId,
462
- description: task.description,
463
- status: task.status,
464
- ...(task.modelId === undefined ? {} : { modelId: task.modelId }),
465
- ...(task.modelName === undefined ? {} : { modelName: task.modelName }),
466
- ...(task.reasoningEffort === undefined
467
- ? {}
468
- : { reasoningEffort: task.reasoningEffort }),
469
- ...(task.latestMessage === undefined
470
- ? {}
471
- : { latestMessage: task.latestMessage }),
472
- ...(task.latestActivity === undefined
473
- ? {}
474
- : { latestActivity: task.latestActivity }),
475
- ...(task.errorCode === undefined ? {} : { errorCode: task.errorCode }),
476
- ...(task.errorMessage === undefined ? {} : { errorMessage: task.errorMessage })
477
- }),
478
- startTurn: async (turn) => {
479
- const child = await openSession(turn.sessionId);
480
- return child.startSubagentTurn({
481
- prompt: turn.prompt,
482
- modelId: turn.modelId,
483
- reasoningEffort: turn.reasoningEffort,
484
- role: turn.role,
485
- initialMailbox: turn.initialMailbox,
486
- turnId: turn.turnId,
487
- mcpExecutionScopeId: executionId,
488
- ...(turn.delivery ? { delivery: turn.delivery } : {}),
489
- ...(rolloutBudget === undefined ? {} : { sharedRolloutBudget: rolloutBudget })
490
- });
491
- }
492
- });
493
- if (mode !== 'compact' &&
494
- (input.images?.length || hasRestoredUserImages) &&
495
- !selectedModel.inputCapabilities.includes('IMAGE'))
496
- throw new MarAgentError('MAR_AGENT_INPUT_CAPABILITY_UNSUPPORTED', 'Selected model does not support image input.');
497
- const catalogContext = {
498
- sessionId,
499
- executionId,
500
- ...(sessionSource.type === 'subagent'
501
- ? internal.mcpExecutionScopeId === undefined
502
- ? {}
503
- : { executionScopeId: internal.mcpExecutionScopeId }
504
- : { claimExecutionScope: true })
505
- };
506
- const readMcpCatalog = async () => {
507
- if (options.host.listMcpToolCatalog) {
508
- const catalog = await options.host.listMcpToolCatalog(catalogContext);
509
- assertMcpToolCatalog(catalog);
510
- return catalog;
511
- }
512
- const definitions = (await options.host.listMcpTools?.(catalogContext)) ?? [];
513
- return {
514
- revision: createHash('sha256').update(JSON.stringify(definitions)).digest('hex'),
515
- servers: [{ id: 'legacy', name: 'External MCP' }],
516
- tools: definitions.map((definition) => ({
517
- definition,
518
- serverId: 'legacy',
519
- rawName: definition.name,
520
- sourceIdentity: `legacy:${executionId}`,
521
- definitionRevision: createHash('sha256')
522
- .update(JSON.stringify(definition))
523
- .digest('hex'),
524
- lifetime: 'execution',
525
- exposure: 'deferred'
526
- }))
527
- };
528
- };
529
- const mcpCatalog = await readMcpCatalog();
530
- const mcpRegistry = new DeferredMcpRegistry(discoveredMcpToolNames(messages));
531
- const mcpTools = mcpCatalog.tools.map((tool) => tool.definition);
532
- const extensions = (await options.host.listExecutionExtensions?.(catalogContext)) ?? {};
533
- if (extensions.tools?.some((tool) => tool.name.startsWith('mcp__')))
534
- throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Host tools cannot use the MCP namespace.');
535
- const selectedModelForTools = executionModels.get(input.modelId ?? executionDefaultModelId);
536
- const builtinTools = BUILTIN_TOOL_DEFINITIONS.map((tool) => tool.name === 'agent_start'
537
- ? agentStartToolDefinitionWithModels([...executionModels.values()].map((configuration) => ({
538
- id: configuration.id,
539
- name: configuration.name,
540
- reasoningEfforts: configuration.reasoningEfforts
541
- })))
542
- : tool);
543
- const combinedTools = [...builtinTools, ...(extensions.tools ?? []), ...mcpTools];
544
- if (new Set(combinedTools.map((tool) => tool.name)).size !== combinedTools.length)
545
- throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Tool names conflict.');
546
- const toolEligible = (tool) => {
547
- toolCallers(tool);
548
- if (sessionSource.type === 'subagent' && tool.agentScope === 'ROOT_ONLY')
549
- return false;
550
- if (sessionSource.type === 'subagent' &&
551
- (tool.name.startsWith('agent_') || tool.name === 'question'))
552
- return false;
553
- if (tool.name === 'tool_search')
554
- return mcpRegistry.hasDeferred;
555
- return tool.name === 'code_exec' || tool.name === 'code_exec_wait'
556
- ? selectedModelForTools.codeMode && description.capabilities.includes('code.execute')
557
- : tool.name === 'question'
558
- ? mode === 'plan'
559
- : tool.name === 'web_search'
560
- ? !selectedModelForTools.hostedWebSearch &&
561
- description.capabilities.includes('web.search')
562
- : tool.name === 'view_image'
563
- ? selectedModelForTools.inputCapabilities.includes('IMAGE')
564
- : tool.name !== 'web_fetch' || description.capabilities.includes('web.fetch');
565
- };
566
- mcpRegistry.update(mcpCatalog, (tool) => toolEligible(tool.definition));
567
- rebindMcpToolSearchOutputs(messages, mcpRegistry);
568
- const availableTools = combinedTools.filter(toolEligible);
569
- const instructionContext = await loadAgentInstructions({
570
- home: description.homeDirectory,
571
- workspace: instructionWorkspace,
572
- availableTools: new Set(availableTools.map((tool) => tool.name)),
573
- hostSkills: extensions.skills ?? [],
574
- ...(options.workspaceMapping === undefined
575
- ? {}
576
- : { workspaceDisplayPath: logicalWorkspace })
577
- });
578
- const instructionEntries = instructionContext?.entries ?? [];
579
- const currentInstructionSnapshot = renderAgentInstructions(instructionEntries, false);
580
- let agentInstructionInsertIndex = mode === 'compact'
581
- ? contextualInstructionInsertIndex(messages)
582
- : (currentExecutionUserIndex ?? Math.max(0, messages.length - 1));
583
- if (mode !== 'compact' &&
584
- agentInstructionInsertIndex &&
585
- messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
586
- agentInstructionInsertIndex--;
587
- const baseTools = () => [...builtinTools, ...(extensions.tools ?? [])].filter((tool) => toolEligible(tool) &&
588
- (tool.name !== 'skill' || (instructionContext?.skills.size ?? 0) > 0));
589
- let executionTools = mcpRegistry.project(baseTools());
590
- const restoreMcpDiscovery = () => {
591
- mcpRegistry.restore(discoveredMcpToolNames(messages));
592
- rebindMcpToolSearchOutputs(messages, mcpRegistry);
593
- executionTools = mcpRegistry.project(baseTools());
594
- };
595
- const loadRetainedSessionResources = async () => {
596
- const hostResources = (await options.host.listSessionResources?.(sessionId)) ?? {
597
- processes: []
598
- };
599
- return {
600
- ...(hostResources.idleTtlMs === undefined
601
- ? {}
602
- : { idleTtlMs: hostResources.idleTtlMs }),
603
- processes: hostResources.processes,
604
- ...(hostResources.codeCells ? { codeCells: hostResources.codeCells } : {}),
605
- subagents: subagents?.listSessionResources() ?? []
606
- };
607
- };
608
- let currentResourceSnapshot = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
609
- const previousContextualWorldState = contextualWorldState;
610
- contextualWorldState = updateContextualWorldState(contextualWorldState, agentInstructionInsertIndex, {
611
- instructionSnapshot: currentInstructionSnapshot,
612
- instructionContent: renderAgentInstructions(instructionEntries, compactMayContainAgentInstructions ||
613
- contextualWorldState.instructionSnapshot !== undefined),
614
- resourceSnapshot: currentResourceSnapshot,
615
- previousMayContainResources: (!contextualWorldState.initialized && compactMayContainRetainedResources) ||
616
- (options.host.listSessionResources !== undefined &&
617
- currentResourceSnapshot === undefined &&
618
- messages
619
- .slice(contextualWorldState.entries.findLast((entry) => entry.kind === 'resources')
620
- ?.index ?? 0)
621
- .some((message) => message.role === 'tool_call' &&
622
- ['exec', 'code_exec', 'code_mode', 'agent_start'].includes(message.name ?? '')))
623
- });
624
- sessionRuntime.contextualWorldState = contextualWorldState;
625
- if (!isDeepStrictEqual(previousContextualWorldState, contextualWorldState))
626
- await persistContextualWorldState(store, sessionId, contextualWorldState);
627
- const buildExecutionSystemPrompt = (availableTools = executionTools, promptMode = mode) => buildSystemPrompt({
628
- mode: promptMode,
629
- platform: description.platform,
630
- ...(description.architecture ? { architecture: description.architecture } : {}),
631
- ...(description.shell ? { shell: description.shell } : {}),
632
- ...(description.guaranteedCommands
633
- ? { guaranteedCommands: description.guaranteedCommands }
634
- : {}),
635
- workspace: logicalWorkspace,
636
- tools: availableTools
637
- .filter((tool) => !tool.name.startsWith('mcp__'))
638
- .map((tool) => tool.name),
639
- directTools: availableTools
640
- .filter((tool) => !tool.name.startsWith('mcp__') &&
641
- allowsDirectTool(tool, availableTools.some((candidate) => candidate.name === 'code_exec')))
642
- .map((tool) => tool.name),
643
- highDensityCompaction: selectedModel.highDensityCompaction,
644
- extension: [
645
- options.systemInstruction,
646
- instructionContext?.skillCatalog,
647
- sessionSource.type === 'subagent' ? childSubagentInstruction : undefined
648
- ]
649
- .filter(Boolean)
650
- .join('\n\n')
651
- });
652
- const syncMcpWorldState = async (index) => {
653
- const snapshot = mcpRegistry.navigation();
654
- if (contextualWorldState.mcpSnapshot === snapshot)
655
- return;
656
- contextualWorldState = {
657
- ...contextualWorldState,
658
- mcpSnapshot: snapshot,
659
- entries: [
660
- ...contextualWorldState.entries,
661
- {
662
- index,
663
- kind: 'mcp',
664
- content: snapshot ?? '<mcp_catalog>No deferred MCP tools remain.</mcp_catalog>'
665
- }
666
- ]
667
- };
668
- sessionRuntime.contextualWorldState = contextualWorldState;
669
- await persistContextualWorldState(store, sessionId, contextualWorldState);
670
- };
671
- if (mode !== 'compact')
672
- await syncMcpWorldState(agentInstructionInsertIndex);
673
- const initialSystemPrompt = mode === 'compact'
674
- ? buildExecutionSystemPrompt(executionTools, 'run')
675
- : buildExecutionSystemPrompt();
676
- let useManualCompactionFallback;
677
- if (mode === 'compact') {
678
- const compactSystemPrompt = initialSystemPrompt;
679
- const compactHistory = [...messages];
680
- const projectedHistory = normalizeMessagesForModel(insertContextualWorldState(compactHistory, contextualWorldState.entries), { supportsImages: false });
681
- const nativeMessages = buildNativeCompactionMessages(projectedHistory, {
682
- focus: input.prompt,
683
- highDensityCompaction: selectedModel.highDensityCompaction
684
- });
685
- const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), executionTools);
686
- const compactInputTokens = selectedModel.contextWindowTokens - compactOverheadTokens;
687
- if (compactInputTokens <= 0)
688
- throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Compaction prompt exceeds the selected model context window.');
689
- const fallbackMessages = () => buildCompactionMessages(buildCompactionInput(compactHistory, {
690
- focus: input.prompt,
691
- maxTokens: compactInputTokens
692
- }), selectedModel.highDensityCompaction);
693
- const useNative = shouldUseNativeCompaction({
694
- estimatedRequestTokens: estimatedRequestTokens(compactSystemPrompt, nativeMessages, executionTools),
695
- contextWindowTokens: selectedModel.contextWindowTokens,
696
- latestUsage: lastServerUsage,
697
- historyMessages: compactHistory,
698
- appendedMessages: nativeMessages.slice(projectedHistory.length)
699
- });
700
- messages.splice(0, messages.length, ...(useNative ? nativeMessages : fallbackMessages()));
701
- if (useNative) {
702
- let available = true;
703
- useManualCompactionFallback = () => {
704
- if (!available)
705
- return false;
706
- available = false;
707
- messages.splice(0, messages.length, ...fallbackMessages());
708
- return true;
709
- };
710
- }
711
- }
712
- await store.append(sessionId, {
713
- type: 'execution.header',
714
- payload: {
715
- promptVersion: MAR_AGENT_PROMPT_VERSION,
716
- promptHash: createHash('sha256').update(initialSystemPrompt).digest('hex'),
717
- modelId: selectedModel.id,
718
- modelName: selectedModel.name,
719
- reasoningEffort,
720
- contextWindowTokens: selectedModel.contextWindowTokens,
721
- mode
722
- },
723
- executionId,
724
- turnId
725
- });
726
- let lastServerContextTokens = lastServerUsage?.contextInputTokens;
727
- const compactContext = async () => {
728
- const current = messages.at(-1);
729
- if (!current || messages.length < 2)
730
- return false;
731
- const retainCurrent = current.role === 'user' && current.contextKind !== 'environment';
732
- let compactSummary = '';
733
- const compactSystemPrompt = buildExecutionSystemPrompt(executionTools, 'run');
734
- const compactHistory = retainCurrent ? messages.slice(0, -1) : messages;
735
- const projectedHistory = normalizeMessagesForModel(insertContextualWorldState(compactHistory, contextualWorldState.entries), { supportsImages: false });
736
- const nativeCompactMessages = buildNativeCompactionMessages(projectedHistory, {
737
- highDensityCompaction: selectedModel.highDensityCompaction
738
- });
739
- const compactOverheadTokens = estimatedRequestTokens(compactSystemPrompt, buildCompactionMessages('', selectedModel.highDensityCompaction), executionTools);
740
- const compactInputTokens = Math.max(0, selectedModel.contextWindowTokens - compactOverheadTokens);
741
- if (compactInputTokens === 0)
742
- throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Compaction prompt exceeds the selected model context window.');
743
- let usingNativeMessages = shouldUseNativeCompaction({
744
- estimatedRequestTokens: estimatedRequestTokens(compactSystemPrompt, nativeCompactMessages, executionTools),
745
- contextWindowTokens: selectedModel.contextWindowTokens,
746
- latestUsage: lastServerUsage,
747
- historyMessages: compactHistory,
748
- appendedMessages: nativeCompactMessages.slice(projectedHistory.length)
749
- });
750
- const fallbackMessages = () => buildCompactionMessages(buildCompactionInput(compactHistory, {
751
- maxTokens: Math.max(1, compactInputTokens)
752
- }), selectedModel.highDensityCompaction);
753
- let compactMessages = usingNativeMessages ? nativeCompactMessages : fallbackMessages();
754
- const compactionId = randomUUID();
755
- await emit({ type: 'context.compacting', compactionId });
756
- let latestUsage;
757
- const compactionRetryState = { attempts: 0 };
758
- const operation = createCompactionOperation(compactionId);
759
- try {
760
- while (true) {
761
- compactSummary = '';
762
- latestUsage = undefined;
763
- try {
764
- for await (const event of operation.start(selected, {
765
- retryState: compactionRetryState,
766
- system: compactSystemPrompt,
767
- messages: compactMessages,
768
- tools: executionTools,
769
- toolChoice: 'none',
770
- promptCacheKey: sessionId,
771
- reasoningEffort: requestReasoningEffort ?? reasoningEffort,
772
- onAttemptDiagnostic: recordModelAttempt('COMPACTION')
773
- })) {
774
- if (event.type === 'text.completed' &&
775
- (event.phase ?? 'final_answer') === 'final_answer')
776
- compactSummary = event.text.trim();
777
- else if (event.type === 'usage') {
778
- latestUsage = event.usage;
779
- await emit({ type: 'usage.updated', ...publicModelUsage(event.usage) });
780
- }
781
- else if (event.type === 'client_tool.call')
782
- throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Compaction returned a tool call while tools were disabled.');
783
- }
784
- break;
785
- }
786
- catch (error) {
787
- if (await recoverModelStream(error, compactionRetryState, operation.signal)) {
788
- continue;
789
- }
790
- if (isProviderContextOverflow(error) && usingNativeMessages) {
791
- usingNativeMessages = false;
792
- compactMessages = fallbackMessages();
793
- compactionRetryState.attempts = 0;
794
- continue;
795
- }
796
- if (isProviderContextOverflow(error))
797
- throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'The compaction request exceeds the model context window.', { cause: error });
798
- throw error;
799
- }
800
- }
801
- }
802
- finally {
803
- await operation.dispose();
804
- }
805
- if (!compactSummary)
806
- throw new MarAgentError('MAR_AGENT_COMPACT_FAILED', 'Automatic compact failed.');
807
- rolloutBudget?.recordUsage(latestUsage);
808
- const replacementHistory = buildCompactedHistory(compactSummary, retainCurrent ? compactionUserMessages.slice(0, -1) : compactionUserMessages, compactUserTokenLimit, retainCurrent ? current.content : undefined);
809
- const compactRecord = await store.compact(sessionId, {
810
- summary: compactSummary,
811
- replacementHistory,
812
- automatic: true,
813
- environment: executionEnvironment,
814
- modelId: selectedModel.id,
815
- contextWindowTokens: selectedModel.contextWindowTokens,
816
- ...(latestUsage === undefined ? {} : { usage: latestUsage }),
817
- executionId,
818
- turnId
819
- });
820
- contextGc.reset();
821
- messages.splice(0, messages.length, environmentContextUpdate(executionEnvironment), ...compactedHistoryMessages(replacementHistory));
822
- restoreMcpDiscovery();
823
- projectedExecutionBudgetStage = undefined;
824
- recordedEnvironment = executionEnvironment;
825
- compactionUserMessages = replacementHistory
826
- .filter((message) => message.kind === 'user_input')
827
- .map((message) => message.content);
828
- agentInstructionInsertIndex = Math.max(0, messages.length - 1);
829
- currentResourceSnapshot = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
830
- contextualWorldState = resetContextualWorldState(compactRecord.recordId, agentInstructionInsertIndex, {
831
- instructionSnapshot: currentInstructionSnapshot,
832
- instructionContent: renderAgentInstructions(instructionEntries, true),
833
- resourceSnapshot: currentResourceSnapshot,
834
- previousMayContainResources: compactSummary.includes('# Retained Session resources')
835
- });
836
- sessionRuntime.contextualWorldState = contextualWorldState;
837
- await persistContextualWorldState(store, sessionId, contextualWorldState);
838
- lastServerContextTokens = undefined;
839
- lastServerUsage = undefined;
840
- requestReasoningEffort = reasoningEffort;
841
- effectiveReasoningEffort = reasoningEffort;
842
- await emit({ type: 'context.compacted', compactionId });
843
- return true;
844
- };
845
- const ensureContextBudget = async () => {
846
- if (mode === 'compact' || lastServerContextTokens === undefined)
847
- return;
848
- const hardLimit = selectedModel.contextWindowTokens;
849
- const compactLimit = Math.floor((hardLimit * AGENT_EXECUTION_POLICY.automaticCompactionPercent) / 100);
850
- if (lastServerContextTokens < compactLimit)
851
- return;
852
- if (!(await compactContext()))
853
- throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'Provider usage reached the compaction threshold but no history can be compacted.');
854
- };
855
- let recoveringFromContextOverflow = false;
856
- let maxOutputContinuationUsed = false;
857
- let pendingOutputContinuation;
858
- const modelRetryState = { attempts: 0 };
859
- while (true) {
860
- await appendMailboxMessages();
861
- appendExecutionBudgetEnvironment();
862
- let stop;
863
- let sawTool = false;
864
- let latestUsage;
865
- let roundServerUsage;
866
- const pendingTools = [];
867
- let incompleteToolCall = false;
868
- executionTools = mcpRegistry.project(baseTools());
869
- if (mode !== 'compact')
870
- await syncMcpWorldState(messages.length);
871
- await ensureContextBudget();
872
- if (mode !== 'compact')
873
- await syncMcpWorldState(messages.length);
874
- appendExecutionBudgetEnvironment();
875
- if (mode !== 'compact') {
876
- executionEnvironment = { ...currentEnvironment, ...currentLocalTimeContext() };
877
- await recordEnvironment();
878
- }
879
- const toolsAllowed = mode !== 'compact' && (!rolloutBudget?.active || rolloutBudget.stage !== 'exhausted');
880
- const roundTools = mode === 'compact' ? executionTools : toolsAllowed ? executionTools : [];
881
- const codeModeEnabled = roundTools.some((tool) => tool.name === 'code_exec');
882
- const residentTools = mcpRegistry.projectDirect(baseTools());
883
- const requestTools = (() => {
884
- if (selectedModel.protocol !== 'OPENAI_RESPONSES' ||
885
- selectedModel.responsesEncoding === 'LITE')
886
- return projectCodeModeTools(roundTools, residentTools);
887
- const directNames = new Set(residentTools.map((tool) => tool.name));
888
- // Discovery and caller policy are independent: keep nested declarations for active tools.
889
- return projectCodeModeTools(roundTools, residentTools).filter((tool) => directNames.has(tool.name));
890
- })();
891
- const systemPrompt = mode === 'compact'
892
- ? buildExecutionSystemPrompt(roundTools, 'run')
893
- : buildExecutionSystemPrompt(roundTools);
894
- const requestContextMessages = pendingOutputContinuation === undefined
895
- ? messages
896
- : [...messages, pendingOutputContinuation];
897
- const modelMessages = mode === 'compact'
898
- ? normalizeMessagesForModel(requestContextMessages, { supportsImages: false })
899
- : normalizeMessagesForModel(insertContextualWorldState(requestContextMessages, contextualWorldState.entries), {
900
- supportsImages: selectedModel.inputCapabilities.includes('IMAGE')
901
- });
902
- const roundMcpExpectations = new Map(roundTools
903
- .filter((tool) => tool.name.startsWith('mcp__'))
904
- .map((tool) => [tool.name, mcpRegistry.expectedTool(tool.name)]));
905
- let sawModelSemanticEvent = false;
906
- let requestStartedAt;
907
- let firstModelResponseAt;
908
- let requestCompletedAt;
909
- let requestOutputTokens;
910
- const roundMessageCount = messages.length;
911
- const emitLatency = async () => {
912
- if (mode === 'compact' || firstModelResponseAt === undefined)
913
- return;
914
- await emit({
915
- type: 'model.performance',
916
- latencyMs: Math.max(0, Math.round(firstModelResponseAt - requestStartedAt))
917
- });
918
- };
919
- const emitFinalPerformance = async (completedAt) => {
920
- if (mode === 'compact' || firstModelResponseAt === undefined)
921
- return;
922
- const outputDurationMs = Math.max(0, Math.round(completedAt - firstModelResponseAt));
923
- const outputTokens = requestOutputTokens;
924
- if (outputTokens === undefined || outputDurationMs <= 0)
925
- return;
926
- await emit({
927
- type: 'model.performance',
928
- latencyMs: Math.max(0, Math.round(firstModelResponseAt - requestStartedAt)),
929
- outputTokens,
930
- outputDurationMs,
931
- tokensPerSecond: Number(((outputTokens * 1000) / outputDurationMs).toFixed(2))
932
- });
933
- };
934
- const persistToolCalls = async (calls) => {
935
- for (const call of calls) {
936
- const message = {
937
- role: 'tool_call',
938
- callId: call.callId,
939
- name: call.name,
940
- arguments: call.arguments,
941
- ...(call.inputMode ? { inputMode: call.inputMode } : {}),
942
- content: ''
943
- };
944
- messages.push(message);
945
- await store.append(sessionId, {
946
- type: 'model.context',
947
- payload: message,
948
- executionId,
949
- turnId
950
- });
951
- }
952
- };
953
- try {
954
- if (manualCompactionId !== undefined && !manualCompactionStarted) {
955
- manualCompactionStarted = true;
956
- manualCompaction = createCompactionOperation(manualCompactionId);
957
- await emit({ type: 'context.compacting', compactionId: manualCompactionId });
958
- }
959
- const startModel = manualCompaction
960
- ? (request) => manualCompaction.start(selected, request)
961
- : (request) => selected.start(request, controller.signal);
962
- requestStartedAt = performance.now();
963
- for await (const event of startModel({
964
- retryState: modelRetryState,
965
- system: systemPrompt,
966
- messages: modelMessages.map((message) => projectCodeModeSearchResult(message, codeModeEnabled)),
967
- tools: requestTools,
968
- ...(mode === 'compact'
969
- ? { toolChoice: 'none' }
970
- : toolsAllowed
971
- ? {}
972
- : { allowTools: false }),
973
- promptCacheKey: sessionId,
974
- reasoningEffort,
975
- ...(requestReasoningEffort === undefined ? {} : { requestReasoningEffort }),
976
- ...(modelInputImages?.length ? { images: modelInputImages } : {}),
977
- onAttemptDiagnostic: recordModelAttempt(mode === 'compact' ? 'COMPACTION' : 'EXECUTION')
978
- })) {
979
- const modelResponseAt = mode !== 'compact' && isModelResponseEvent(event) ? performance.now() : undefined;
980
- const isFirstModelResponse = modelResponseAt !== undefined && firstModelResponseAt === undefined;
981
- if (modelResponseAt !== undefined) {
982
- sawModelSemanticEvent = true;
983
- firstModelResponseAt ??= modelResponseAt;
984
- }
985
- if (event.type === 'response.item') {
986
- // Compaction keeps only its successful checkpoint, never intermediate native output.
987
- if (mode === 'compact')
988
- continue;
989
- const message = {
990
- role: 'provider',
991
- content: '',
992
- provider: event.provider,
993
- providerModelId: selectedModel.id,
994
- ...(event.provider === 'ANTHROPIC_MESSAGES'
995
- ? { contextKind: 'private' }
996
- : {}),
997
- item: event.item
998
- };
999
- messages.push(message);
1000
- await store.append(sessionId, {
1001
- type: 'model.context',
1002
- payload: message,
1003
- executionId,
1004
- turnId
1005
- });
1006
- }
1007
- else if (event.type === 'text.delta') {
1008
- let delta = event.delta;
1009
- if (mode === 'plan' && sessionSource.type !== 'subagent') {
1010
- let prefix = planOutputPrefixes.get(event.itemId);
1011
- if (!prefix) {
1012
- prefix = new PlanOutputPrefix();
1013
- planOutputPrefixes.set(event.itemId, prefix);
1014
- }
1015
- delta = prefix.push(delta);
1016
- }
1017
- if (mode !== 'compact' && delta) {
1018
- await emit({
1019
- type: 'message.delta',
1020
- itemId: event.itemId,
1021
- phase: event.phase,
1022
- delta
1023
- });
1024
- }
1025
- }
1026
- else if (event.type === 'text.completed') {
1027
- const phase = event.phase ?? 'final_answer';
1028
- if (phase === 'final_answer')
1029
- finalAnswer = event.text;
1030
- planOutputPrefixes.delete(event.itemId);
1031
- if (mode !== 'compact') {
1032
- if (mode === 'plan' && sessionSource.type !== 'subagent') {
1033
- const parts = splitPlanOutput(event.text);
1034
- for (const [index, part] of parts.entries()) {
1035
- const itemId = index === 0 ? event.itemId : `${event.itemId}:plan-part:${index}`;
1036
- if (part.kind === 'plan')
1037
- await emit({ type: 'plan.completed', itemId, text: part.text });
1038
- else {
1039
- await emit({
1040
- type: 'message.completed',
1041
- itemId,
1042
- phase,
1043
- text: part.text,
1044
- modelId: selectedModel.id,
1045
- modelName: selectedModel.name
1046
- });
1047
- }
1048
- }
1049
- }
1050
- else {
1051
- await emit({
1052
- type: 'message.completed',
1053
- itemId: event.itemId,
1054
- phase,
1055
- text: event.text,
1056
- modelId: selectedModel.id,
1057
- modelName: selectedModel.name
1058
- });
1059
- }
1060
- }
1061
- messages.push({ role: 'assistant', content: event.text });
1062
- }
1063
- else if (event.type === 'usage') {
1064
- latestUsage = event.usage;
1065
- const outputTokens = typeof event.usage.outputTokens === 'number' &&
1066
- Number.isSafeInteger(event.usage.outputTokens) &&
1067
- event.usage.outputTokens >= 0
1068
- ? event.usage.outputTokens
1069
- : undefined;
1070
- if (outputTokens !== undefined) {
1071
- requestOutputTokens = outputTokens;
1072
- }
1073
- const reportedContext = event.usage.contextInputTokens ?? event.usage.inputTokens;
1074
- if (reportedContext !== undefined &&
1075
- Number.isSafeInteger(reportedContext) &&
1076
- reportedContext >= 0) {
1077
- lastServerContextTokens = reportedContext;
1078
- roundServerUsage = {
1079
- contextInputTokens: reportedContext,
1080
- outputTokens: typeof event.usage.outputTokens === 'number' &&
1081
- Number.isSafeInteger(event.usage.outputTokens) &&
1082
- event.usage.outputTokens >= 0
1083
- ? event.usage.outputTokens
1084
- : undefined
1085
- };
1086
- }
1087
- await emit({ type: 'usage.updated', ...publicModelUsage(event.usage) });
1088
- }
1089
- else if (event.type === 'reasoning.summary') {
1090
- if (mode !== 'compact')
1091
- await emit({ type: 'reasoning.summary', text: event.text });
1092
- }
1093
- else if (event.type === 'hosted_tool') {
1094
- if (mode !== 'compact')
1095
- await emit({
1096
- type: 'hosted_tool.updated',
1097
- itemId: event.itemId,
1098
- kind: event.kind,
1099
- status: event.status,
1100
- summary: event.summary
1101
- });
1102
- }
1103
- else if (event.type === 'citation') {
1104
- if (mode !== 'compact')
1105
- await emit({
1106
- type: 'citation.added',
1107
- id: event.citation.id,
1108
- url: event.citation.url,
1109
- ...(event.citation.title ? { title: event.citation.title } : {})
1110
- });
1111
- }
1112
- else if (event.type === 'client_tool.call') {
1113
- if (mode === 'compact')
1114
- throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Compaction returned a tool call while tools were disabled.');
1115
- sawTool = true;
1116
- pendingTools.push(event);
1117
- }
1118
- else if (event.type === 'completed') {
1119
- stop = event.reason;
1120
- requestCompletedAt = performance.now();
1121
- }
1122
- if (isFirstModelResponse)
1123
- await emitLatency();
1124
- }
1125
- if (mode !== 'compact' && firstModelResponseAt !== undefined) {
1126
- const completedAt = requestCompletedAt ?? performance.now();
1127
- await emitFinalPerformance(completedAt);
1128
- }
1129
- pendingOutputContinuation = undefined;
1130
- incompleteToolCall = pendingTools.some((call) => call.argumentError !== undefined);
1131
- if (!(stop === 'max_output' && incompleteToolCall)) {
1132
- await persistToolCalls(pendingTools);
1133
- }
1134
- else
1135
- pendingTools.splice(0, pendingTools.length);
1136
- modelRetryState.attempts = 0;
1137
- }
1138
- catch (error) {
1139
- planOutputPrefixes.clear();
1140
- if (await recoverModelStream(error, modelRetryState, manualCompaction?.signal ?? controller.signal)) {
1141
- finalAnswer = '';
1142
- if (mode === 'compact')
1143
- messages.splice(roundMessageCount);
1144
- if (pendingTools.some((call) => call.argumentError !== undefined))
1145
- pendingTools.splice(0, pendingTools.length);
1146
- else if (pendingTools.length > 0)
1147
- await persistToolCalls(pendingTools);
1148
- if (pendingTools.length === 0)
1149
- continue;
1150
- }
1151
- else if (mode === 'compact' &&
1152
- !sawModelSemanticEvent &&
1153
- isProviderContextOverflow(error) &&
1154
- useManualCompactionFallback?.()) {
1155
- modelRetryState.attempts = 0;
1156
- finalAnswer = '';
1157
- continue;
1158
- }
1159
- else if (mode !== 'compact' &&
1160
- !sawModelSemanticEvent &&
1161
- isProviderContextOverflow(error)) {
1162
- if (!recoveringFromContextOverflow && (await compactContext())) {
1163
- modelRetryState.attempts = 0;
1164
- recoveringFromContextOverflow = true;
1165
- continue;
1166
- }
1167
- throw new MarAgentError('MAR_AGENT_CONTEXT_LIMIT', 'The model rejected the compacted context as too large.', { cause: error });
1168
- }
1169
- else
1170
- throw error;
1171
- }
1172
- recoveringFromContextOverflow = false;
1173
- if (roundServerUsage !== undefined)
1174
- lastServerUsage = {
1175
- ...roundServerUsage,
1176
- historyMessages: [...messages]
1177
- };
1178
- if (pendingTools.length === 0 && mailbox.length > 0) {
1179
- await appendMailboxMessages();
1180
- finalAnswer = '';
1181
- continue;
1182
- }
1183
- const executeCall = async (call, signal = controller.signal, caller = 'direct', parentCallId) => {
1184
- await emit({
1185
- type: 'tool.started',
1186
- toolName: call.name,
1187
- callId: call.callId,
1188
- ...(parentCallId === undefined ? {} : { parentCallId }),
1189
- input: call.arguments
1190
- });
1191
- try {
1192
- if (call.argumentError)
1193
- throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', call.argumentError);
1194
- if (!roundTools.some((tool) => tool.name === call.name && allowsToolCaller(tool, caller)))
1195
- throw new MarAgentError('MAR_AGENT_TOOL_NOT_AVAILABLE', 'Tool is unavailable in this execution.');
1196
- const stateSnapshot = call.name === 'code_exec' ? await store.readCodeModeState(sessionId) : [];
1197
- const runTool = () => {
1198
- signal.throwIfAborted();
1199
- return executeRegisteredTool(call.name, call.arguments, call.callId, {
1200
- host: options.host,
1201
- todo,
1202
- ...(roundMcpExpectations.get(call.name)
1203
- ? { expectedMcpTool: roundMcpExpectations.get(call.name) }
1204
- : {}),
1205
- searchMcpTools: (arguments_, searchSignal) => mcpRegistry.expose(arguments_, {
1206
- signal: searchSignal,
1207
- base: baseTools(),
1208
- window: selectedModel.contextWindowTokens,
1209
- system: systemPrompt
1210
- }),
1211
- ...(subagents === undefined ? {} : { subagents }),
1212
- mode,
1213
- sessionId,
1214
- executionId,
1215
- signal,
1216
- codeMode: {
1217
- retainSession: () => store.retainSessionLease(sessionId, sessionWriterOwner),
1218
- onSettled: async (result) => {
1219
- await store.append(sessionId, {
1220
- type: 'code.cell.completed',
1221
- payload: { ...result, callId: call.callId },
1222
- executionId,
1223
- turnId
1224
- });
1225
- },
1226
- supportsImages: selectedModel.inputCapabilities.includes('IMAGE'),
1227
- tools: roundTools.filter((tool) => allowsToolCaller(tool, 'programmatic')),
1228
- state: stateSnapshot,
1229
- commitState: (mutations) => store.commitCodeModeState(sessionId, mutations, {
1230
- executionId,
1231
- turnId,
1232
- expectedState: stateSnapshot
1233
- }),
1234
- invoke: createCodeToolDispatch({
1235
- context: {
1236
- host: options.host,
1237
- todo,
1238
- ...(subagents ? { subagents: subagents.captureExecution() } : {}),
1239
- mode,
1240
- sessionId,
1241
- executionId,
1242
- emit,
1243
- skills: instructionContext?.skills ?? new Map(),
1244
- recentImages: currentExecutionImages(modelInputImages, currentExecutionToolImages)
1245
- },
1246
- tools: roundTools,
1247
- expectations: roundMcpExpectations,
1248
- parentCallId: call.callId,
1249
- schedule: sessionRuntime.scheduleTool,
1250
- emit,
1251
- projectData: structuredToolEventData
1252
- })
1253
- },
1254
- emit,
1255
- skills: instructionContext?.skills ?? new Map(),
1256
- ...(extensions.tools?.some((tool) => tool.name === call.name)
1257
- ? {
1258
- recentImages: currentExecutionImages(modelInputImages, currentExecutionToolImages)
1259
- }
1260
- : {})
1261
- });
1262
- };
1263
- const output = call.name === 'code_exec' || call.name === 'code_exec_wait'
1264
- ? await runTool()
1265
- : await sessionRuntime.scheduleTool(roundTools.find((tool) => tool.name === call.name), runTool);
1266
- const modelOutput = boundedModelToolOutput(call.name, output.content, output.data);
1267
- const eventSummary = output.content.slice(0, AGENT_EXECUTION_POLICY.toolEventSummaryCharacters);
1268
- const structuredData = structuredToolEventData(call.name, output.data);
1269
- const artifacts = structuredToolArtifacts(output.artifacts);
1270
- await emit({
1271
- type: 'tool.completed',
1272
- toolName: call.name,
1273
- callId: call.callId,
1274
- ...(parentCallId === undefined ? {} : { parentCallId }),
1275
- summary: eventSummary,
1276
- truncated: output.truncated === true ||
1277
- modelOutput.truncated ||
1278
- eventSummary.length < output.content.length,
1279
- ...(artifacts === undefined ? {} : { artifacts }),
1280
- ...(structuredData === undefined ? {} : { data: structuredData })
1281
- });
1282
- return {
1283
- call,
1284
- output,
1285
- content: modelOutput.content,
1286
- ...(output.images?.length ? { images: output.images } : {}),
1287
- failed: false
1288
- };
1289
- }
1290
- catch (error) {
1291
- const diagnostic = describeMarAgentError(error, 'MAR_AGENT_HOST_OPERATION_FAILED');
1292
- const partial = error instanceof CodeCellExecutionError ? error.result : undefined;
1293
- await emit({
1294
- type: 'tool.failed',
1295
- toolName: call.name,
1296
- callId: call.callId,
1297
- ...(parentCallId === undefined ? {} : { parentCallId }),
1298
- code: diagnostic.code,
1299
- message: diagnostic.message
1300
- });
1301
- return {
1302
- call,
1303
- content: partial === undefined
1304
- ? JSON.stringify({
1305
- error: diagnostic.code,
1306
- message: diagnostic.message,
1307
- ...(error instanceof MarAgentError && error.retryable
1308
- ? { retryable: true }
1309
- : {})
1310
- })
1311
- : boundedModelToolOutput(call.name, partial.content, partial.data).content,
1312
- ...(partial?.images?.length ? { images: partial.images } : {}),
1313
- failed: true,
1314
- error
1315
- };
1316
- }
1317
- };
1318
- const toolResults = await scheduleToolCallBatch(pendingTools, roundTools, executeCall);
1319
- for (const result of toolResults) {
1320
- if ('images' in result && result.images?.length)
1321
- currentExecutionToolImages.push(...result.images.map((image) => ({
1322
- mimeType: image.mimeType,
1323
- data: Buffer.from(image.dataBase64, 'base64')
1324
- })));
1325
- const toolSearchTools = result.call.name === 'tool_search' && !result.failed
1326
- ? result.output.toolSearchTools
1327
- : undefined;
1328
- const message = {
1329
- role: 'tool',
1330
- callId: result.call.callId,
1331
- content: result.content,
1332
- ...(toolSearchTools === undefined ? {} : { toolSearchTools }),
1333
- ...('images' in result && result.images?.length ? { images: result.images } : {})
1334
- };
1335
- messages.push(message);
1336
- await store.append(sessionId, {
1337
- type: 'model.context',
1338
- payload: {
1339
- role: message.role,
1340
- callId: message.callId,
1341
- content: message.content,
1342
- ...(message.toolSearchTools === undefined
1343
- ? {}
1344
- : { toolSearchTools: message.toolSearchTools })
1345
- },
1346
- executionId,
1347
- turnId
1348
- });
1349
- }
1350
- if (sawTool) {
1351
- modelRetryState.attempts = 0;
1352
- if (sessionSource.type === 'subagent')
1353
- finalAnswer = '';
1354
- }
1355
- rolloutBudget?.recordUsage(latestUsage);
1356
- if (mode !== 'compact' && stop === 'max_output') {
1357
- const needsContinuation = !sawTool || incompleteToolCall;
1358
- if (needsContinuation) {
1359
- if (maxOutputContinuationUsed ||
1360
- (rolloutBudget?.active && rolloutBudget.stage === 'exhausted'))
1361
- throw new MarAgentError('MAR_AGENT_MODEL_OUTPUT_INCOMPLETE', 'Model output reached its limit before completing.');
1362
- maxOutputContinuationUsed = true;
1363
- pendingOutputContinuation = {
1364
- role: 'user',
1365
- content: modelOutputContinuationMessage
1366
- };
1367
- finalAnswer = '';
1368
- continue;
1369
- }
1370
- maxOutputContinuationUsed = false;
1371
- }
1372
- else
1373
- maxOutputContinuationUsed = false;
1374
- if (mode !== 'compact' &&
1375
- sessionSource.type !== 'subagent' &&
1376
- latestUsage !== undefined) {
1377
- // Only a committed GC checkpoint may replace active context and invalidate continuation.
1378
- const committed = await contextGc.run({
1379
- store,
1380
- sessionId,
1381
- system: systemPrompt,
1382
- tools: roundTools,
1383
- modelId: selectedModel.id,
1384
- contextWindowTokens: selectedModel.contextWindowTokens,
1385
- usage: latestUsage,
1386
- modelProtocol: selectedModel.protocol,
1387
- responsesConfigurationUpdates: usesResponsesConfigurationUpdates(selectedModel),
1388
- workspace: logicalWorkspace,
1389
- platform: description.platform,
1390
- getActiveProcessIds: async () => {
1391
- const resources = await options.host
1392
- .listSessionResources?.(sessionId)
1393
- .catch(() => undefined);
1394
- return new Set((resources?.processes ?? [])
1395
- .filter((process) => process.status === 'running')
1396
- .map((process) => process.processId));
1397
- }
1398
- });
1399
- if (committed) {
1400
- selected.resetContinuation?.();
1401
- const refreshed = await restoreMessages(store, sessionId, committed.records, {
1402
- includeImages: true,
1403
- currentTurnId: turnId,
1404
- projection: committed.projection
1405
- });
1406
- messages.splice(0, messages.length, ...refreshed.messages);
1407
- restoreMcpDiscovery();
1408
- projectedExecutionBudgetStage = undefined;
1409
- recordedEnvironment = refreshed.environmentState;
1410
- compactionUserMessages = [...refreshed.userMessages];
1411
- agentInstructionInsertIndex =
1412
- refreshed.currentTurnUserIndex ?? Math.max(0, messages.length - 1);
1413
- if (agentInstructionInsertIndex &&
1414
- messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
1415
- agentInstructionInsertIndex--;
1416
- currentResourceSnapshot = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
1417
- contextualWorldState = resetContextualWorldState(latestContextualWorldStateGeneration(committed.records), agentInstructionInsertIndex, {
1418
- instructionSnapshot: currentInstructionSnapshot,
1419
- instructionContent: renderAgentInstructions(instructionEntries, committed.records.some((record) => compactSummaryFromCheckpoint(record) !== undefined)),
1420
- resourceSnapshot: currentResourceSnapshot,
1421
- previousMayContainResources: compactHistoryMayContainRetainedResources(committed.records)
1422
- });
1423
- sessionRuntime.contextualWorldState = contextualWorldState;
1424
- await persistContextualWorldState(store, sessionId, contextualWorldState);
1425
- }
1426
- }
1427
- if (controller.signal.aborted) {
1428
- const aborted = toolResults.find((result) => result.failed)?.error;
1429
- throw (aborted ??
1430
- new MarAgentError('MAR_AGENT_EXECUTION_CANCELLED', 'Execution cancelled.'));
1431
- }
1432
- if (mailbox.length > 0) {
1433
- await appendMailboxMessages();
1434
- finalAnswer = '';
1435
- continue;
1436
- }
1437
- if (stop === 'completed' && !sawTool) {
1438
- acceptsMailbox = false;
1439
- const compactSummary = mode === 'compact' ? finalAnswer.trim() : '';
1440
- if (mode === 'compact' && !compactSummary)
1441
- throw new MarAgentError('MAR_AGENT_COMPACT_FAILED', 'Compaction returned an empty handoff.');
1442
- if (mode !== 'compact' && !finalAnswer.trim())
1443
- throw new MarAgentError(sessionSource.type === 'subagent'
1444
- ? 'MAR_AGENT_SUBAGENT_FAILED'
1445
- : 'MAR_AGENT_MODEL_OUTPUT_INCOMPLETE', sessionSource.type === 'subagent'
1446
- ? 'Subagent completed without a final summary.'
1447
- : 'Model completed without an answer or tool request.');
1448
- if (manualCompaction) {
1449
- manualCompaction.signal.throwIfAborted();
1450
- await manualCompaction.dispose();
1451
- }
1452
- if (mode === 'compact')
1453
- await store.compact(sessionId, {
1454
- summary: compactSummary,
1455
- replacementHistory: buildCompactedHistory(compactSummary, compactionUserMessages, compactUserTokenLimit),
1456
- modelId: selectedModel.id,
1457
- contextWindowTokens: selectedModel.contextWindowTokens,
1458
- ...(latestUsage === undefined ? {} : { usage: latestUsage }),
1459
- executionId,
1460
- turnId
1461
- });
1462
- if (mode === 'compact' && manualCompactionId !== undefined)
1463
- await emit({ type: 'context.compacted', compactionId: manualCompactionId });
1464
- await settleTitle();
1465
- subagents?.endParentExecution(executionId);
1466
- await emit({ type: 'execution.completed' });
1467
- return {
1468
- executionId,
1469
- status: 'completed',
1470
- finalAnswer: mode === 'compact' ? '' : finalAnswer
1471
- };
1472
- }
1473
- if (!sawTool && stop !== 'pause')
1474
- throw new MarAgentError('MAR_AGENT_MODEL_OUTPUT_INCOMPLETE', 'Model did not complete or request a tool.');
1475
- }
1476
- }
1477
- catch (error) {
1478
- acceptsMailbox = false;
1479
- await settleTitle();
1480
- if (controller.signal.aborted) {
1481
- subagents?.endParentExecution(executionId);
1482
- await emit({ type: 'execution.cancelled' });
1483
- return {
1484
- executionId,
1485
- status: 'cancelled',
1486
- finalAnswer: mode === 'compact' ? '' : finalAnswer
1487
- };
1488
- }
1489
- const diagnostic = modelAttemptFailureDiagnostic(describeMarAgentError(error, 'MAR_AGENT_EXECUTION_FAILED'), exhaustedModelAttempt);
1490
- subagents?.endParentExecution(executionId);
1491
- await emit({ type: 'execution.failed', ...diagnostic });
1492
- return {
1493
- executionId,
1494
- status: 'failed',
1495
- finalAnswer: mode === 'compact' ? '' : finalAnswer,
1496
- errorCode: diagnostic.code,
1497
- errorMessage: diagnostic.message
1498
- };
1499
- }
1500
- finally {
1501
- acceptsMailbox = false;
1502
- if (ownsRolloutBudget)
1503
- rolloutBudget.discard();
1504
- await manualCompaction?.dispose();
1505
- await selectedTurn?.close().catch(() => undefined);
1506
- if (titleAdapter !== undefined && titleAdapter !== selectedAdapter)
1507
- executionScopedAdapters.add(titleAdapter);
1508
- try {
1509
- await closeModelAdapters(executionScopedAdapters);
1510
- }
1511
- catch {
1512
- // Execution cleanup is best effort.
1513
- }
1514
- finally {
1515
- for (const adapter of executionScopedAdapters)
1516
- activeExecutionAdapters.delete(adapter);
1517
- }
1518
- subagents?.endParentExecution(executionId);
1519
- await releaseSession?.();
1520
- active.delete(`${sessionId}:${executionId}`);
1521
- queue.close();
1522
- }
1523
- })();
1524
- const executionKey = `${sessionId}:${executionId}`;
1525
- executionResults.set(executionKey, result);
1526
- void result.then(() => executionResults.delete(executionKey), () => executionResults.delete(executionKey));
1527
- return {
1528
- executionId,
1529
- turnId,
1530
- [Symbol.asyncIterator]: () => queue[Symbol.asyncIterator](),
1531
- result: () => {
1532
- queue.discardIfUnobserved();
1533
- return result;
1534
- },
1535
- cancel: () => controller.abort(new MarAgentError('MAR_AGENT_CANCELLED', 'Execution was cancelled.')),
1536
- enqueueMessage: (message, delivery) => {
1537
- if (!acceptsMailbox || controller.signal.aborted)
1538
- return false;
1539
- mailbox.push({ text: message, ...(delivery ? { delivery } : {}) });
1540
- return true;
1541
- }
1542
- };
1543
- };
1544
- return {
1545
- id: sessionId,
1546
- run: (input) => execution('run', input),
1547
- plan: (input) => execution('plan', input),
1548
- compact: (input = {}) => execution('compact', {
1549
- prompt: input.prompt ?? DEFAULT_COMPACTION_FOCUS,
1550
- ...input
1551
- }),
1552
- cancel: async (executionId) => active.get(`${sessionId}:${executionId}`)?.abort(),
1553
- startSubagentTurn: (turn) => execution(turn.role === 'explore' ? 'plan' : 'run', {
1554
- prompt: turn.prompt,
1555
- modelId: turn.modelId,
1556
- reasoningEffort: turn.reasoningEffort
1557
- }, {
1558
- publishHostEvents: false,
1559
- inputSource: 'parent_agent',
1560
- initialMailbox: turn.initialMailbox,
1561
- turnId: turn.turnId,
1562
- mcpExecutionScopeId: turn.mcpExecutionScopeId,
1563
- ...(turn.delivery ? { delivery: turn.delivery } : {}),
1564
- ...(turn.sharedRolloutBudget === undefined
1565
- ? {}
1566
- : { sharedRolloutBudget: turn.sharedRolloutBudget })
1567
- })
1568
- };
1569
- };
1570
- return {
1571
- createSession: async (input) => openSession((await store.createSession(input)).id, input.workspace),
1572
- openSession: (id) => openSession(id),
1573
- listSessions: (input) => store.list(input),
1574
- clearSession: async (id) => {
1575
- const runtime = sessionRuntimes.get(id);
1576
- if ([...active.keys()].some((key) => key.startsWith(`${id}:`)))
1577
- throw new MarAgentError('MAR_AGENT_EXECUTION_ACTIVE', 'A session cannot be cleared while an execution is active.');
1578
- await runtime?.subagents?.dispose();
1579
- await closeModelAdapters(runtime === undefined ? [] : [...runtime.adapters.values()]);
1580
- sessionRuntimes.delete(id);
1581
- await options.host.disposeSessionResources?.(id);
1582
- await store.clearSession(id);
1583
- },
1584
- reconfigureModels: async (input) => {
1585
- if (disposed)
1586
- throw new MarAgentError('MAR_AGENT_DISPOSED', 'Agent is disposed.');
1587
- if (reconfiguring)
1588
- throw new MarAgentError('MAR_AGENT_EXECUTION_ACTIVE', 'Models are already being reconfigured for the next execution.');
1589
- const next = resolveAgentModels(input.models, input.defaultModelId);
1590
- const activeSessions = activeSessionIds();
1591
- if ([...activeSessions].some((sessionId) => sessionRuntimes.get(sessionId)?.sourceType !== 'subagent'))
1592
- throw new MarAgentError('MAR_AGENT_EXECUTION_ACTIVE', 'Models cannot be reconfigured while a main execution is active.');
1593
- const activeAdapters = new Set(activeExecutionAdapters);
1594
- for (const [sessionId, runtime] of sessionRuntimes)
1595
- if (activeSessions.has(sessionId))
1596
- for (const adapter of runtime.adapters.values())
1597
- activeAdapters.add(adapter);
1598
- const inactiveRuntimes = [...sessionRuntimes].flatMap(([sessionId, runtime]) => activeSessions.has(sessionId) ? [] : [runtime]);
1599
- reconfiguring = true;
1600
- try {
1601
- const reconfigurationCommits = new Map();
1602
- for (const runtime of inactiveRuntimes)
1603
- for (const [modelId, adapter] of runtime.adapters) {
1604
- if (activeAdapters.has(adapter))
1605
- continue;
1606
- const configuration = next.models.get(modelId);
1607
- const commit = configuration === undefined ? undefined : adapter.prepareReconfigure?.(configuration);
1608
- if (commit !== undefined)
1609
- reconfigurationCommits.set(adapter, commit);
1610
- }
1611
- const retainedAdapters = new Set(reconfigurationCommits.keys());
1612
- const adapters = [
1613
- ...new Set(inactiveRuntimes.flatMap((runtime) => [...runtime.adapters.values()]))
1614
- ].filter((adapter) => !activeAdapters.has(adapter) && !retainedAdapters.has(adapter));
1615
- for (const runtime of inactiveRuntimes) {
1616
- for (const [modelId, adapter] of runtime.adapters)
1617
- if (!retainedAdapters.has(adapter))
1618
- runtime.adapters.delete(modelId);
1619
- }
1620
- await closeModelAdapters(adapters);
1621
- if (disposed)
1622
- throw new MarAgentError('MAR_AGENT_DISPOSED', 'Agent is disposed.');
1623
- for (const commit of reconfigurationCommits.values())
1624
- commit();
1625
- models = next.models;
1626
- defaultModelId = next.defaultModelId;
1627
- modelGeneration++;
1628
- for (const provider of next.credentialProviders)
1629
- credentialProviders.add(provider);
1630
- for (const runtime of inactiveRuntimes) {
1631
- runtime.adapterGeneration = modelGeneration;
1632
- }
1633
- const liveProviders = new Set(next.credentialProviders);
1634
- for (const adapter of activeExecutionAdapters)
1635
- addModelCredentialProviders(liveProviders, adapter.configuration);
1636
- for (const runtime of sessionRuntimes.values())
1637
- for (const adapter of runtime.adapters.values())
1638
- addModelCredentialProviders(liveProviders, adapter.configuration);
1639
- const obsoleteProviders = [...credentialProviders].filter((provider) => !liveProviders.has(provider));
1640
- const providerCloseResults = await Promise.allSettled(obsoleteProviders.map((provider) => provider.close()));
1641
- for (const [index, result] of providerCloseResults.entries())
1642
- if (result.status === 'fulfilled')
1643
- credentialProviders.delete(obsoleteProviders[index]);
1644
- }
1645
- finally {
1646
- reconfiguring = false;
1647
- }
1648
- },
1649
- deleteSession: async (id) => {
1650
- const sessionIds = await store.sessionTree(id);
1651
- const sessionIdSet = new Set(sessionIds);
1652
- const executionKeys = [...active.keys()].filter((key) => sessionIdSet.has(key.slice(0, key.indexOf(':'))));
1653
- for (const key of executionKeys)
1654
- active.get(key)?.abort();
1655
- const runtimes = sessionIds.flatMap((sessionId) => {
1656
- const runtime = sessionRuntimes.get(sessionId);
1657
- sessionRuntimes.delete(sessionId);
1658
- return runtime === undefined ? [] : [runtime];
1659
- });
1660
- await Promise.all(runtimes.map((runtime) => runtime.subagents?.dispose()));
1661
- await Promise.allSettled(executionKeys.flatMap((key) => {
1662
- const result = executionResults.get(key);
1663
- return result === undefined ? [] : [result];
1664
- }));
1665
- await closeModelAdapters(runtimes.flatMap((runtime) => [...runtime.adapters.values()]));
1666
- await Promise.all(sessionIds.map((sessionId) => options.host.disposeSessionResources?.(sessionId)));
1667
- await store.delete(id);
1668
- },
1669
- dispose: async () => {
1670
- if (disposed)
1671
- return;
1672
- disposed = true;
1673
- for (const controller of active.values())
1674
- controller.abort();
1675
- await Promise.all([...sessionRuntimes.values()].map((runtime) => runtime.subagents?.dispose().catch(() => undefined)));
1676
- await Promise.allSettled([...executionResults.values()]);
1677
- await closeModelAdapters([...sessionRuntimes.values()].flatMap((runtime) => [...runtime.adapters.values()]));
1678
- let disposalError;
1679
- const providerResults = await Promise.allSettled([...credentialProviders].map((provider) => provider.close()));
1680
- disposalError = providerResults.find((result) => result.status === 'rejected')?.reason;
1681
- try {
1682
- await options.host.dispose();
1683
- }
1684
- catch (error) {
1685
- disposalError ??= error;
1686
- }
1687
- try {
1688
- await store.close();
1689
- }
1690
- catch (error) {
1691
- disposalError ??= error;
1692
- }
1693
- if (disposalError !== undefined)
1694
- throw disposalError;
1695
- }
1696
- };
1697
- }
1698
- function createSubagentBackend(store, workspace) {
1699
- return {
1700
- createSession: async (input) => {
1701
- const session = await store.createSession({
1702
- workspace,
1703
- title: input.title,
1704
- source: input.source
1705
- });
1706
- return { id: session.id };
1707
- },
1708
- deleteSession: async (sessionId) => {
1709
- await store.delete(sessionId);
1710
- },
1711
- listChildren: async (parentSessionId) => {
1712
- const sessions = await store.listSubagentChildren(parentSessionId);
1713
- return Promise.all(sessions.map(async (session) => ({
1714
- id: session.id,
1715
- createdAt: session.createdAt,
1716
- lastActivityAt: session.lastActivityAt,
1717
- source: session.source,
1718
- records: await store.read(session.id)
1719
- })));
1720
- },
1721
- loadChild: async (parentSessionId, childSessionId) => {
1722
- let session;
1723
- try {
1724
- session = await store.getSession(childSessionId);
1725
- }
1726
- catch (error) {
1727
- if (error instanceof MarAgentError && error.code === 'MAR_AGENT_SESSION_NOT_FOUND')
1728
- return undefined;
1729
- throw error;
1730
- }
1731
- if (session.source.type !== 'subagent' || session.source.parentSessionId !== parentSessionId)
1732
- return undefined;
1733
- return {
1734
- id: session.id,
1735
- createdAt: session.createdAt,
1736
- lastActivityAt: session.lastActivityAt,
1737
- source: session.source,
1738
- records: await store.read(session.id)
1739
- };
1740
- }
1741
- };
1742
- }
1743
- function resolveAgentModels(configurations, requestedDefaultModelId) {
1744
- const configuredModels = configurations.map((model) => ({
1745
- ...model,
1746
- defaultReasoningEffort: resolveModelDefaultReasoningEffort(model)
1747
- }));
1748
- const credentialProviders = new Set();
1749
- for (const model of configuredModels) {
1750
- if ((model.apiKey === undefined) === (model.credentialProvider === undefined))
1751
- throw new MarAgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Exactly one model credential source is required.');
1752
- if (model.responsesEncoding !== undefined &&
1753
- (model.protocol !== 'OPENAI_RESPONSES' ||
1754
- !['STANDARD', 'LITE'].includes(model.responsesEncoding)))
1755
- throw new MarAgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Responses encoding is invalid.');
1756
- if (model.fastMode === true && model.protocol !== 'OPENAI_RESPONSES')
1757
- throw new MarAgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Fast mode requires Responses.');
1758
- if (model.serviceTier !== undefined &&
1759
- (model.protocol !== 'OPENAI_RESPONSES' ||
1760
- !['auto', 'default', 'flex', 'priority', 'fast', 'ultrafast'].includes(model.serviceTier)))
1761
- throw new MarAgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Responses service tier is invalid.');
1762
- if (model.credentialProvider !== undefined)
1763
- credentialProviders.add(model.credentialProvider);
1764
- }
1765
- const models = new Map(configuredModels.filter((model) => model.enabled).map((model) => [model.id, model]));
1766
- if (models.size === 0)
1767
- throw new MarAgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'At least one enabled model is required.');
1768
- const defaultModelId = requestedDefaultModelId ?? models.keys().next().value;
1769
- if (!models.has(defaultModelId))
1770
- throw new MarAgentError('MAR_AGENT_MODEL_CONFIG_INVALID', 'Default model is unavailable.');
1771
- return { models, defaultModelId, credentialProviders };
1772
- }
1773
- function createModelTurnSession(adapter) {
1774
- const turn = adapter.createTurnSession?.() ?? {
1775
- start: (request, signal) => adapter.start(request, signal),
1776
- close: async () => undefined
1777
- };
1778
- return {
1779
- async *start(request, signal) {
1780
- const before = request.retryState?.attempts;
1781
- try {
1782
- yield* turn.start(request, signal);
1783
- }
1784
- finally {
1785
- // Custom adapters may not participate in the shared attempt accounting.
1786
- if (request.retryState && request.retryState.attempts === before)
1787
- request.retryState.attempts++;
1788
- }
1789
- },
1790
- resetContinuation: () => turn.resetContinuation?.(),
1791
- close: () => turn.close()
1792
- };
1793
- }
1794
- function modelAttemptFailureDiagnostic(diagnostic, summary) {
1795
- if (summary === undefined)
1796
- return diagnostic;
1797
- const prefix = `Model request failed after ${summary.attempts} attempts (${summary.retries} retries). `;
1798
- const availableCharacters = Math.max(0, 1_000 - [...prefix].length);
1799
- return {
1800
- code: diagnostic.code,
1801
- message: `${prefix}${[...diagnostic.message].slice(0, availableCharacters).join('')}`
1802
- };
1803
- }
1804
- async function closeModelAdapters(adapters) {
1805
- const results = await Promise.allSettled([...new Set(adapters)].flatMap((adapter) => adapter.close === undefined ? [] : [adapter.close()]));
1806
- const failure = results.find((result) => result.status === 'rejected');
1807
- if (failure !== undefined)
1808
- throw failure.reason;
1809
- }
1810
- function addModelCredentialProviders(target, configuration) {
1811
- if (configuration.credentialProvider !== undefined)
1812
- target.add(configuration.credentialProvider);
1813
- }
1814
- function truncateUtf8(value, maximumBytes) {
1815
- const bytes = Buffer.from(value);
1816
- if (bytes.length <= maximumBytes)
1817
- return value;
1818
- let end = maximumBytes;
1819
- while (end > 0 && (bytes[end] & 0xc0) === 0x80)
1820
- end--;
1821
- return bytes.subarray(0, end).toString('utf8');
1822
- }
1823
- function publicModelUsage(usage) {
1824
- const { rolloutBudgetUnits, ...publicUsage } = usage;
1825
- void rolloutBudgetUnits;
1826
- return publicUsage;
1827
- }
1828
- async function generateSessionTitle(adapter, prompt, reasoningEffort, signal, onAttemptDiagnostic) {
1829
- let titleInput = '';
1830
- let characters = 0;
1831
- for (const character of prompt) {
1832
- if (characters++ >= 1000)
1833
- break;
1834
- titleInput += character;
1835
- }
1836
- let output = '';
1837
- let completed = false;
1838
- const turn = createModelTurnSession(adapter);
1839
- try {
1840
- for await (const event of turn.start({
1841
- retryState: { attempts: 0, maxAttempts: 1 },
1842
- system: sessionTitlePrompt,
1843
- messages: [{ role: 'user', content: titleInput }],
1844
- tools: [],
1845
- reasoningEffort,
1846
- onAttemptDiagnostic
1847
- }, signal)) {
1848
- if (event.type === 'text.completed')
1849
- output = event.text;
1850
- else if (event.type === 'completed')
1851
- completed = event.reason === 'completed';
1852
- }
1853
- }
1854
- finally {
1855
- await turn.close();
1856
- }
1857
- if (!completed)
1858
- return undefined;
1859
- const line = output
1860
- .replace(/<think>[\s\S]*?<\/think>\s*/giu, '')
1861
- .split(/\r?\n/u)
1862
- .map((value) => value.trim())
1863
- .find(Boolean);
1864
- if (!line)
1865
- return undefined;
1866
- return [...line].slice(0, 50).join('');
1867
- }
1868
- async function restoreMessages(store, sessionId, records, options) {
1869
- const messages = [];
1870
- const modelProjection = new Map((options.projection ??
1871
- buildContextProjection(records, (await store.getSession(sessionId)).workspace)).entries.map((entry) => [entry.record.recordId, entry.message]));
1872
- let userMessages = [];
1873
- let ignoredImages = 0;
1874
- let currentTurnUserIndex;
1875
- let environmentState;
1876
- for (const record of records) {
1877
- const payload = record.payload;
1878
- const compactSummary = compactSummaryFromCheckpoint(record);
1879
- if (record.type === 'turn.user' && typeof payload.prompt === 'string') {
1880
- userMessages.push(payload.prompt);
1881
- let images = [];
1882
- if (options.includeImages)
1883
- try {
1884
- images = await store.readInputImages(sessionId, payload.images);
1885
- }
1886
- catch (error) {
1887
- if (!(error instanceof MarAgentError) || error.code !== 'MAR_AGENT_SESSION_CORRUPT')
1888
- throw error;
1889
- ignoredImages += Array.isArray(payload.images) ? payload.images.length : 1;
1890
- }
1891
- if (record.turnId === options.currentTurnId && currentTurnUserIndex === undefined)
1892
- currentTurnUserIndex = messages.length;
1893
- messages.push({
1894
- role: 'user',
1895
- content: payload.prompt,
1896
- ...(images.length ? { images } : {})
1897
- });
1898
- }
1899
- else if ((record.type === 'message.completed' || record.type === 'plan.completed') &&
1900
- typeof payload.text === 'string')
1901
- messages.push({ role: 'assistant', content: payload.text });
1902
- else if (compactSummary !== undefined) {
1903
- environmentState = restoreEnvironmentState({ version: 1, state: payload.environment });
1904
- if (environmentState)
1905
- messages.push(environmentContextUpdate(environmentState));
1906
- const history = replacementHistoryFromCheckpoint(record);
1907
- userMessages =
1908
- history
1909
- ?.filter((message) => message.kind === 'user_input')
1910
- .map((message) => message.content) ?? [];
1911
- messages.push(...(history
1912
- ? compactedHistoryMessages(history)
1913
- : [{ role: 'user', content: continuationMessage(compactSummary) }]));
1914
- }
1915
- else if (record.type === 'context.environment') {
1916
- const current = restoreEnvironmentState(payload);
1917
- if (current) {
1918
- const message = environmentContextUpdate(current, environmentState);
1919
- if (message)
1920
- messages.push(message);
1921
- environmentState = current;
1922
- }
1923
- }
1924
- else if (record.type === 'model.context') {
1925
- const message = modelProjection.get(record.recordId);
1926
- if (message)
1927
- messages.push(message);
1928
- }
1929
- }
1930
- const completedToolCalls = new Set(messages.filter((message) => message.role === 'tool').map((message) => message.callId));
1931
- return {
1932
- environmentState,
1933
- userMessages,
1934
- messages: messages.filter((message) => {
1935
- if (message.role === 'tool_call')
1936
- return completedToolCalls.has(message.callId);
1937
- if (message.role !== 'provider' || message.provider !== 'OPENAI_RESPONSES')
1938
- return true;
1939
- const item = message.item;
1940
- if (item.type !== 'function_call' && item.type !== 'custom_tool_call')
1941
- return true;
1942
- return typeof item.call_id !== 'string' || completedToolCalls.has(item.call_id);
1943
- }),
1944
- ignoredImages,
1945
- currentTurnUserIndex
1946
- };
1947
- }
1948
- function currentExecutionImages(modelInputImages, toolImages) {
1949
- return [
1950
- ...(modelInputImages ?? []).map((image) => ({
1951
- mimeType: image.mimeType,
1952
- data: Buffer.from(image.dataBase64, 'base64')
1953
- })),
1954
- ...toolImages
1955
- ]
1956
- .slice(-5)
1957
- .map((image) => ({ mimeType: image.mimeType, data: Buffer.from(image.data) }));
1958
- }
1959
- function latestModelUsage(records, selectedModelId) {
1960
- let usage;
1961
- const toolCallIds = new Set();
1962
- // readContext starts at compact.completed, whose execution header is before the boundary.
1963
- let activeModelId = selectedModelId;
1964
- for (const record of records) {
1965
- const payload = record.payload;
1966
- if (compactSummaryFromCheckpoint(record) !== undefined) {
1967
- usage = undefined;
1968
- toolCallIds.clear();
1969
- activeModelId = modelIdFromPayload(payload);
1970
- continue;
1971
- }
1972
- if (record.type === 'execution.header') {
1973
- activeModelId = modelIdFromPayload(payload);
1974
- usage = undefined;
1975
- toolCallIds.clear();
1976
- continue;
1977
- }
1978
- if (record.type === 'model.context' && payload.role === 'tool_call') {
1979
- if (typeof payload.callId === 'string' && payload.callId.length > 0)
1980
- toolCallIds.add(payload.callId);
1981
- else
1982
- usage = undefined;
1983
- }
1984
- if (usage !== undefined) {
1985
- if (record.type === 'model.context') {
1986
- const role = payload.role;
1987
- if (role === 'tool') {
1988
- if (typeof payload.callId !== 'string' || !toolCallIds.has(payload.callId))
1989
- usage = undefined;
1990
- else
1991
- usage = {
1992
- ...usage,
1993
- appendedMessages: [
1994
- ...(usage.appendedMessages ?? []),
1995
- payload
1996
- ]
1997
- };
1998
- }
1999
- else if (role !== 'tool_call' && role !== 'assistant' && role !== 'provider')
2000
- usage = undefined;
2001
- }
2002
- else if (invalidatesUsageCalibration(record.type, payload.type))
2003
- usage = undefined;
2004
- }
2005
- if (payload.type === 'usage.updated' && activeModelId === selectedModelId) {
2006
- const value = payload.contextInputTokens ?? payload.inputTokens;
2007
- if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0)
2008
- usage = {
2009
- contextInputTokens: value,
2010
- outputTokens: typeof payload.outputTokens === 'number' &&
2011
- Number.isSafeInteger(payload.outputTokens) &&
2012
- payload.outputTokens >= 0
2013
- ? payload.outputTokens
2014
- : undefined
2015
- };
2016
- }
2017
- }
2018
- return usage;
2019
- }
2020
- function reasoningEffortBaseline(records, modelId) {
2021
- let baseline;
2022
- for (let index = records.length - 1; index >= 0; index--) {
2023
- const record = records[index];
2024
- if (compactSummaryFromCheckpoint(record) !== undefined)
2025
- break;
2026
- if (record.type !== 'execution.header')
2027
- continue;
2028
- const payload = record.payload;
2029
- if (payload.modelId !== modelId)
2030
- break;
2031
- const effort = marAgentReasoningEffortSchema.safeParse(payload.reasoningEffort);
2032
- if (effort.success)
2033
- baseline = effort.data;
2034
- }
2035
- return baseline;
2036
- }
2037
- function lastConfigurationEffort(messages, modelId) {
2038
- for (let index = messages.length - 1; index >= 0; index--) {
2039
- const message = messages[index];
2040
- if (message.role !== 'provider' ||
2041
- message.provider !== 'OPENAI_RESPONSES' ||
2042
- message.providerModelId !== modelId ||
2043
- message.item?.type !== 'configuration_update')
2044
- continue;
2045
- const reasoning = message.item.reasoning;
2046
- const effort = marAgentReasoningEffortSchema.safeParse(reasoning?.effort);
2047
- if (effort.success)
2048
- return effort.data;
2049
- }
2050
- return undefined;
2051
- }
2052
- function shouldUseNativeCompaction(input) {
2053
- if (input.estimatedRequestTokens <= input.contextWindowTokens)
2054
- return true;
2055
- if (input.latestUsage === undefined || input.latestUsage.outputTokens === undefined)
2056
- return false;
2057
- const historyDelta = appendOnlyUsageDelta(input.latestUsage, input.historyMessages);
2058
- if (historyDelta === undefined)
2059
- return false;
2060
- const appendedTokens = countModelTokens(stableJson([...historyDelta, ...input.appendedMessages]));
2061
- return (input.latestUsage.contextInputTokens + input.latestUsage.outputTokens + appendedTokens <=
2062
- input.contextWindowTokens);
2063
- }
2064
- function appendOnlyUsageDelta(usage, historyMessages) {
2065
- if (usage.historyMessages === undefined)
2066
- return usage.appendedMessages ?? [];
2067
- if (historyMessages.length < usage.historyMessages.length)
2068
- return undefined;
2069
- for (let index = 0; index < usage.historyMessages.length; index++)
2070
- if (!isDeepStrictEqual(usage.historyMessages[index], historyMessages[index]))
2071
- return undefined;
2072
- const appended = historyMessages.slice(usage.historyMessages.length);
2073
- const toolCallIds = new Set(usage.historyMessages
2074
- .filter((message) => message.role === 'tool_call')
2075
- .map((message) => message.callId));
2076
- return appended.every((message) => (message.role === 'tool' && toolCallIds.has(message.callId)) ||
2077
- (message.role === 'user' && message.contextKind === 'environment'))
2078
- ? appended
2079
- : undefined;
2080
- }
2081
- function invalidatesUsageCalibration(recordType, payloadType) {
2082
- if (recordType === 'turn.user' ||
2083
- recordType === 'context.environment' ||
2084
- recordType === 'context.gc.completed')
2085
- return true;
2086
- return payloadType === 'message.completed';
2087
- }
2088
- const PROVIDER_CONTEXT_OVERFLOW_CODES = new Set([
2089
- 'mar_agent_context_limit',
2090
- 'context_length_exceeded',
2091
- 'context_overflow',
2092
- 'context_window_exceeded',
2093
- 'prompt_too_long',
2094
- 'request_too_large',
2095
- 'string_above_max_length'
2096
- ]);
2097
- const PROVIDER_CONTEXT_OVERFLOW_PATTERNS = [
2098
- /prompt is too long/iu,
2099
- /request_too_large/iu,
2100
- /input is too long for requested model/iu,
2101
- /exceeds (?:the )?(?:model'?s )?(?:maximum )?context (?:length|window)/iu,
2102
- /input token count.*exceeds the maximum/iu,
2103
- /tokens in request more than max tokens allowed/iu,
2104
- /maximum prompt length is \d+/iu,
2105
- /reduce the length of the messages/iu,
2106
- /maximum context length is \d+ tokens/iu,
2107
- /exceeds (?:the )?maximum allowed input length/iu,
2108
- /greater than the context length/iu,
2109
- /context window exceeds limit/iu,
2110
- /exceeded model token limit/iu,
2111
- /context[_ ]length[_ ]exceeded/iu,
2112
- /request entity too large/iu,
2113
- /input length.*exceeds.*context length/iu,
2114
- /prompt too long; exceeded (?:max )?context length/iu,
2115
- /model_context_window_exceeded/iu,
2116
- /too many tokens/iu,
2117
- /token limit exceeded/iu
2118
- ];
2119
- const PROVIDER_CONTEXT_OVERFLOW_EXCLUSIONS = [
2120
- /^(?:[^:]+: )?(?:throttling error|service unavailable):/iu,
2121
- /rate limit/iu,
2122
- /too many requests/iu
2123
- ];
2124
- function isProviderContextOverflow(error) {
2125
- if (!(error instanceof MarAgentError))
2126
- return false;
2127
- const code = error.code.trim().toLowerCase();
2128
- if (PROVIDER_CONTEXT_OVERFLOW_CODES.has(code))
2129
- return true;
2130
- if (error.details?.status === 413)
2131
- return true;
2132
- const message = error.message;
2133
- // K3-compatible gateways report context overflow as HTTP 401 instead of a
2134
- // standard provider error code. Keep this exception scoped to that exact shape.
2135
- if (error.details?.status === 401 && /supports only [\d,.]+[km]? context/iu.test(message))
2136
- return true;
2137
- if (code !== 'invalid_request_error' && code !== 'mar_agent_model_protocol_error')
2138
- return false;
2139
- return (!PROVIDER_CONTEXT_OVERFLOW_EXCLUSIONS.some((pattern) => pattern.test(message)) &&
2140
- PROVIDER_CONTEXT_OVERFLOW_PATTERNS.some((pattern) => pattern.test(message)));
2141
- }
2142
- function modelIdFromPayload(payload, key = 'modelId') {
2143
- const value = payload[key];
2144
- return typeof value === 'string' && value.length > 0 ? value : undefined;
2145
- }
2146
- function insertContextualWorldState(messages, entries) {
2147
- const result = [...messages];
2148
- let offset = 0;
2149
- for (const entry of entries) {
2150
- const insertionIndex = Math.max(0, Math.min(entry.index + offset, result.length));
2151
- result.splice(insertionIndex, 0, { role: 'user', content: entry.content });
2152
- offset++;
2153
- }
2154
- return result;
2155
- }
2156
- function latestContextualWorldStateGeneration(records) {
2157
- let generation;
2158
- for (const record of records)
2159
- if (record.type === 'context.gc.completed' ||
2160
- compactSummaryFromCheckpoint(record) !== undefined)
2161
- generation = record.recordId;
2162
- return generation;
2163
- }
2164
- const MAX_CONTEXTUAL_WORLD_STATE_ENTRIES = 1_024;
2165
- const MAX_CONTEXTUAL_WORLD_STATE_CONTENT_BYTES = 8 * 1024 * 1024;
2166
- function parsePersistedContextualWorldState(payload) {
2167
- if (payload === null || typeof payload !== 'object' || Array.isArray(payload))
2168
- return undefined;
2169
- const record = payload;
2170
- if (record.version !== 1)
2171
- return undefined;
2172
- const generation = record.generation === null
2173
- ? undefined
2174
- : typeof record.generation === 'string' && record.generation.length <= 128
2175
- ? record.generation
2176
- : null;
2177
- if (generation === null)
2178
- return undefined;
2179
- const instructionSnapshot = nullableWorldStateString(record.instructionSnapshot);
2180
- const resourceSnapshot = nullableWorldStateString(record.resourceSnapshot);
2181
- if (instructionSnapshot === null || resourceSnapshot === null || !Array.isArray(record.entries))
2182
- return undefined;
2183
- if (record.entries.length > MAX_CONTEXTUAL_WORLD_STATE_ENTRIES)
2184
- return undefined;
2185
- const entries = [];
2186
- let contentBytes = 0;
2187
- for (const value of record.entries) {
2188
- if (value === null || typeof value !== 'object' || Array.isArray(value))
2189
- return undefined;
2190
- const entry = value;
2191
- if (!Number.isSafeInteger(entry.index) ||
2192
- entry.index < 0 ||
2193
- (entry.kind !== 'instructions' && entry.kind !== 'resources' && entry.kind !== 'mcp') ||
2194
- typeof entry.content !== 'string' ||
2195
- entry.content.length === 0)
2196
- return undefined;
2197
- contentBytes += Buffer.byteLength(entry.content);
2198
- if (contentBytes > MAX_CONTEXTUAL_WORLD_STATE_CONTENT_BYTES)
2199
- return undefined;
2200
- entries.push({
2201
- index: entry.index,
2202
- kind: entry.kind,
2203
- content: entry.content
2204
- });
2205
- }
2206
- return {
2207
- generation,
2208
- initialized: true,
2209
- instructionSnapshot,
2210
- resourceSnapshot,
2211
- mcpSnapshot: typeof record.mcpSnapshot === 'string' ? record.mcpSnapshot : undefined,
2212
- entries
2213
- };
2214
- }
2215
- function nullableWorldStateString(value) {
2216
- if (value === null)
2217
- return undefined;
2218
- return typeof value === 'string' &&
2219
- Buffer.byteLength(value) <= MAX_CONTEXTUAL_WORLD_STATE_CONTENT_BYTES
2220
- ? value
2221
- : null;
2222
- }
2223
- async function persistContextualWorldState(store, sessionId, state) {
2224
- await store.writeContextualWorldState(sessionId, {
2225
- version: 1,
2226
- generation: state.generation ?? null,
2227
- instructionSnapshot: state.instructionSnapshot ?? null,
2228
- resourceSnapshot: state.resourceSnapshot ?? null,
2229
- mcpSnapshot: state.mcpSnapshot ?? null,
2230
- entries: state.entries
2231
- });
2232
- }
2233
- function compactHistoryMayContainRetainedResources(records) {
2234
- return records.some((record) => compactSummaryFromCheckpoint(record)?.includes('# Retained Session resources'));
2235
- }
2236
- function updateContextualWorldState(state, index, current) {
2237
- const entries = [...state.entries];
2238
- if (!state.initialized || state.instructionSnapshot !== current.instructionSnapshot) {
2239
- if (current.instructionContent !== undefined)
2240
- entries.push({ index, kind: 'instructions', content: current.instructionContent });
2241
- }
2242
- const resourceContent = retainedSessionResourcesUpdate(current.resourceSnapshot, state.initialized ? state.resourceSnapshot : undefined, current.previousMayContainResources);
2243
- if (resourceContent !== undefined)
2244
- entries.push({ index, kind: 'resources', content: resourceContent });
2245
- return {
2246
- generation: state.generation,
2247
- mcpSnapshot: state.mcpSnapshot,
2248
- initialized: true,
2249
- instructionSnapshot: current.instructionSnapshot,
2250
- resourceSnapshot: current.resourceSnapshot,
2251
- entries
2252
- };
2253
- }
2254
- function resetContextualWorldState(generation, index, current) {
2255
- const entries = [];
2256
- if (current.instructionContent !== undefined)
2257
- entries.push({ index, kind: 'instructions', content: current.instructionContent });
2258
- const resourceContent = retainedSessionResourcesUpdate(current.resourceSnapshot, undefined, current.previousMayContainResources);
2259
- if (resourceContent !== undefined)
2260
- entries.push({ index, kind: 'resources', content: resourceContent });
2261
- return {
2262
- generation,
2263
- initialized: true,
2264
- instructionSnapshot: current.instructionSnapshot,
2265
- resourceSnapshot: current.resourceSnapshot,
2266
- entries
2267
- };
2268
- }
2269
- function contextualInstructionInsertIndex(messages) {
2270
- let index = messages.findLastIndex((message) => message.role === 'user' && message.contextKind !== 'environment');
2271
- if (index < 0)
2272
- return 0;
2273
- if (index > 0 && messages[index - 1]?.contextKind === 'environment')
2274
- index--;
2275
- return index;
2276
- }
2277
- function estimatedRequestTokens(system, messages, tools, images) {
2278
- const transientImageCount = messages.reduce((total, message) => total + (message.images?.length ?? 0), images?.length ?? 0);
2279
- const textMessages = messages.map((message) => {
2280
- const textMessage = { ...message };
2281
- delete textMessage.images;
2282
- return textMessage;
2283
- });
2284
- return (countModelTokens(`${system}${stableJson(textMessages)}${stableJson(tools)}`) +
2285
- transientImageCount * AGENT_EXECUTION_POLICY.estimatedTokensPerImage);
2286
- }
2287
- function boundedModelToolOutput(toolName, value, data) {
2288
- const execDiagnostics = (toolName === 'exec' || toolName === 'exec_wait') &&
2289
- isRecord(data) &&
2290
- typeof data.stdout === 'string' &&
2291
- typeof data.stderr === 'string'
2292
- ? {
2293
- originalTokenCount: countModelTokens(data.stdout) + countModelTokens(data.stderr),
2294
- totalLines: countTextLines(data.stdout) + countTextLines(data.stderr)
2295
- }
2296
- : undefined;
2297
- const result = truncateModelText(value, AGENT_EXECUTION_POLICY.modelToolOutputTokens, 'tool output', execDiagnostics);
2298
- return { content: result.content, truncated: result.truncated };
2299
- }
2300
- function stableJson(value) {
2301
- if (Array.isArray(value))
2302
- return `[${value.map(stableJson).join(',')}]`;
2303
- if (value !== null && typeof value === 'object')
2304
- return `{${Object.entries(value)
2305
- .sort(([left], [right]) => left.localeCompare(right))
2306
- .map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`)
2307
- .join(',')}}`;
2308
- return JSON.stringify(value) ?? 'undefined';
2309
- }
2310
- function discoveredMcpToolNames(messages) {
2311
- const calls = toolSearchCallIds(messages);
2312
- const names = new Set();
2313
- for (const message of messages)
2314
- for (const tool of calls.has(message.callId ?? '') ? (message.toolSearchTools ?? []) : [])
2315
- if (tool.name.startsWith('mcp__'))
2316
- names.add(tool.name);
2317
- return [...names];
2318
- }
2319
- function rebindMcpToolSearchOutputs(messages, registry) {
2320
- const calls = toolSearchCallIds(messages);
2321
- for (const message of messages) {
2322
- if (!message.toolSearchTools)
2323
- continue;
2324
- if (!calls.has(message.callId ?? '')) {
2325
- delete message.toolSearchTools;
2326
- continue;
2327
- }
2328
- const definitions = registry.restoredDefinitions(message.toolSearchTools.map((tool) => tool.name));
2329
- message.toolSearchTools = definitions;
2330
- }
2331
- }
2332
- function toolSearchCallIds(messages) {
2333
- return new Set(messages
2334
- .filter((message) => (message.role === 'tool_call' &&
2335
- message.name === 'tool_search' &&
2336
- typeof message.callId === 'string') ||
2337
- (message.role === 'provider' &&
2338
- providerToolName(message) === 'tool_search' &&
2339
- providerToolCallId(message) !== undefined))
2340
- .map((message) => message.role === 'provider' ? providerToolCallId(message) : message.callId));
2341
- }
2342
- /** Project only known, validated tool result shapes into public structured events. */
2343
- function structuredToolEventData(toolName, value) {
2344
- if (!isRecord(value))
2345
- return undefined;
2346
- if ((toolName === 'code_exec' || toolName === 'code_exec_wait') &&
2347
- typeof value.cellId === 'string' &&
2348
- ['running', 'completed', 'failed', 'cancelled'].includes(String(value.status)))
2349
- return {
2350
- cellId: value.cellId,
2351
- status: value.status,
2352
- cached: value.cached === true
2353
- };
2354
- if ((toolName === 'agent_start' ||
2355
- toolName === 'agent_wait' ||
2356
- toolName === 'agent_message' ||
2357
- toolName === 'agent_cancel') &&
2358
- isSubagentTaskSnapshot(value))
2359
- return value;
2360
- if (toolName === 'read' &&
2361
- value.mode === 'batch' &&
2362
- Array.isArray(value.results) &&
2363
- typeof value.truncated === 'boolean' &&
2364
- value.results.every((result) => isRecord(result) &&
2365
- typeof result.path === 'string' &&
2366
- (result.startLine === undefined || isNonnegativeInteger(result.startLine)) &&
2367
- (result.endLine === undefined || isNonnegativeInteger(result.endLine)) &&
2368
- (result.error === undefined ||
2369
- (isRecord(result.error) &&
2370
- typeof result.error.code === 'string' &&
2371
- typeof result.error.message === 'string')))) {
2372
- const result = value;
2373
- return {
2374
- mode: 'batch',
2375
- truncated: result.truncated,
2376
- results: result.results.map((item) => ({
2377
- path: item.path,
2378
- ...('startLine' in item && item.startLine !== undefined
2379
- ? { startLine: item.startLine }
2380
- : {}),
2381
- ...('endLine' in item && item.endLine !== undefined ? { endLine: item.endLine } : {}),
2382
- ...('error' in item ? { error: { ...item.error } } : {})
2383
- }))
2384
- };
2385
- }
2386
- if (toolName === 'view_image' &&
2387
- typeof value.path === 'string' &&
2388
- (value.mimeType === 'image/png' ||
2389
- value.mimeType === 'image/jpeg' ||
2390
- value.mimeType === 'image/webp') &&
2391
- isNonnegativeInteger(value.bytes) &&
2392
- typeof value.sha256 === 'string' &&
2393
- /^[a-f0-9]{64}$/u.test(value.sha256))
2394
- return value;
2395
- if ((toolName === 'exec' || toolName === 'exec_wait') &&
2396
- (value.status === 'running' || value.status === 'completed' || value.status === 'cancelled') &&
2397
- typeof value.stdout === 'string' &&
2398
- typeof value.stderr === 'string' &&
2399
- typeof value.truncated === 'boolean' &&
2400
- (value.processId === undefined || typeof value.processId === 'string') &&
2401
- (value.exitCode === undefined ||
2402
- value.exitCode === null ||
2403
- (typeof value.exitCode === 'number' && Number.isInteger(value.exitCode))) &&
2404
- isNonnegativeInteger(value.stdoutBytes) &&
2405
- isNonnegativeInteger(value.stderrBytes) &&
2406
- isNonnegativeInteger(value.stdoutDroppedBytes) &&
2407
- isNonnegativeInteger(value.stderrDroppedBytes))
2408
- return value;
2409
- if (toolName === 'apply_patch' &&
2410
- Array.isArray(value.files) &&
2411
- value.files.every((file) => isRecord(file) &&
2412
- typeof file.path === 'string' &&
2413
- file.path.length > 0 &&
2414
- (file.targetPath === undefined || typeof file.targetPath === 'string') &&
2415
- (file.operation === 'added' ||
2416
- file.operation === 'modified' ||
2417
- file.operation === 'deleted' ||
2418
- file.operation === 'moved') &&
2419
- isNonnegativeInteger(file.addedLines) &&
2420
- isNonnegativeInteger(file.deletedLines)) &&
2421
- isRecord(value.totals) &&
2422
- isNonnegativeInteger(value.totals.files) &&
2423
- isNonnegativeInteger(value.totals.addedLines) &&
2424
- isNonnegativeInteger(value.totals.deletedLines))
2425
- return value;
2426
- return undefined;
2427
- }
2428
- function structuredToolArtifacts(value) {
2429
- if (value === undefined)
2430
- return undefined;
2431
- if (value.length !== 1 ||
2432
- value.some((artifact) => artifact.kind !== 'image' ||
2433
- artifact.mimeType !== 'image/png' ||
2434
- typeof artifact.name !== 'string' ||
2435
- artifact.name.length === 0 ||
2436
- typeof artifact.fileName !== 'string' ||
2437
- !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}\.png$/u.test(artifact.fileName) ||
2438
- !isNonnegativeInteger(artifact.byteSize) ||
2439
- typeof artifact.sha256 !== 'string' ||
2440
- !/^[a-f0-9]{64}$/u.test(artifact.sha256)))
2441
- throw new MarAgentError('MAR_AGENT_TOOL_OUTPUT_INVALID', 'Tool image artifact is invalid.');
2442
- return value.map((artifact) => ({ ...artifact }));
2443
- }
2444
- /** Keep streaming deltas live-only; exec output has one durable copy in model context records. */
2445
- function eventForPersistence(event) {
2446
- if (event.type === 'message.delta' ||
2447
- event.type === 'context.compacting' ||
2448
- event.type === 'model.performance' ||
2449
- event.type === 'message.performance')
2450
- return undefined;
2451
- if (event.type !== 'tool.completed' || event.toolName !== 'exec' || !event.data)
2452
- return event;
2453
- const metadata = Object.fromEntries(Object.entries(event.data).filter(([key]) => key !== 'stdout' && key !== 'stderr'));
2454
- return { ...event, data: metadata };
2455
- }
2456
- function isModelResponseEvent(event) {
2457
- if (event.type === 'text.delta')
2458
- return event.delta.length > 0;
2459
- if (event.type === 'text.completed' || event.type === 'reasoning.summary')
2460
- return event.text.length > 0;
2461
- return (event.type === 'response.item' ||
2462
- event.type === 'client_tool.call' ||
2463
- event.type === 'hosted_tool' ||
2464
- event.type === 'citation');
2465
- }
2466
- function isRecord(value) {
2467
- return value !== null && typeof value === 'object' && !Array.isArray(value);
2468
- }
2469
- function isSubagentTaskSnapshot(value) {
2470
- return (typeof value.agentId === 'string' &&
2471
- value.agentId.length > 0 &&
2472
- value.agentId.length <= 128 &&
2473
- typeof value.description === 'string' &&
2474
- value.description.length > 0 &&
2475
- value.description.length <= TOOL_EXECUTION_LIMITS.subagentDescriptionMaxCharacters &&
2476
- (value.status === 'queued' ||
2477
- value.status === 'running' ||
2478
- value.status === 'completed' ||
2479
- value.status === 'failed' ||
2480
- value.status === 'cancelled' ||
2481
- value.status === 'interrupted') &&
2482
- (value.modelId === undefined || typeof value.modelId === 'string') &&
2483
- (value.modelName === undefined || typeof value.modelName === 'string') &&
2484
- (value.reasoningEffort === undefined ||
2485
- (typeof value.reasoningEffort === 'string' &&
2486
- ['low', 'medium', 'high', 'xhigh', 'max', 'ultra'].includes(value.reasoningEffort))) &&
2487
- (value.waitOutcome === undefined ||
2488
- value.waitOutcome === 'snapshot' ||
2489
- value.waitOutcome === 'message' ||
2490
- value.waitOutcome === 'completion' ||
2491
- value.waitOutcome === 'timeout') &&
2492
- (value.latestMessage === undefined ||
2493
- (isRecord(value.latestMessage) &&
2494
- typeof value.latestMessage.itemId === 'string' &&
2495
- typeof value.latestMessage.turnId === 'string' &&
2496
- (value.latestMessage.phase === 'commentary' ||
2497
- value.latestMessage.phase === 'final_answer') &&
2498
- typeof value.latestMessage.text === 'string' &&
2499
- typeof value.latestMessage.timestamp === 'string' &&
2500
- typeof value.latestMessage.truncated === 'boolean')) &&
2501
- (value.summary === undefined || typeof value.summary === 'string') &&
2502
- (value.changedFiles === undefined ||
2503
- (Array.isArray(value.changedFiles) &&
2504
- value.changedFiles.every((path) => typeof path === 'string'))) &&
2505
- (value.verification === undefined ||
2506
- (Array.isArray(value.verification) &&
2507
- value.verification.every((item) => typeof item === 'string'))) &&
2508
- (value.evidence === undefined ||
2509
- (Array.isArray(value.evidence) &&
2510
- value.evidence.every((item) => isRecord(item) &&
2511
- typeof item.path === 'string' &&
2512
- (item.startLine === undefined ||
2513
- (typeof item.startLine === 'number' &&
2514
- Number.isInteger(item.startLine) &&
2515
- item.startLine >= 1)) &&
2516
- (item.endLine === undefined ||
2517
- (typeof item.endLine === 'number' &&
2518
- Number.isInteger(item.endLine) &&
2519
- item.endLine >= 1))))) &&
2520
- (value.errorCode === undefined || typeof value.errorCode === 'string'));
2521
- }
2522
- function isNonnegativeInteger(value) {
2523
- return typeof value === 'number' && Number.isInteger(value) && value >= 0;
2524
- }