@myagentroam/agent 0.9.141 → 0.9.143

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