@myagentroam/agent 0.9.141 → 0.9.142
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-core/host/contracts.d.ts +64 -0
- package/dist/{host → agent-core/host}/local-agent-host.d.ts +6 -5
- package/dist/{host → agent-core/host}/local-agent-host.js +4 -4
- package/dist/{model → agent-core/model}/contracts.d.ts +2 -16
- package/dist/agent-core/model/recovery.d.ts +4 -0
- package/dist/agent-core/model/recovery.js +71 -0
- package/dist/{prompts → agent-core/prompts}/core.d.ts +3 -13
- package/dist/{prompts → agent-core/prompts}/index.d.ts +1 -1
- package/dist/{prompts → agent-core/prompts}/modes.d.ts +1 -1
- package/dist/{prompts → agent-core/prompts}/resources.d.ts +1 -1
- package/dist/{runtime/compact.d.ts → agent-core/runtime/compaction/history.d.ts} +2 -23
- package/dist/{runtime/compact.js → agent-core/runtime/compaction/history.js} +2 -92
- package/dist/agent-core/runtime/compaction/operation.d.ts +22 -0
- package/dist/agent-core/runtime/compaction/operation.js +91 -0
- package/dist/agent-core/runtime/context/contracts.d.ts +12 -0
- package/dist/{runtime/environment-context.d.ts → agent-core/runtime/context/environment.d.ts} +2 -2
- package/dist/{runtime/environment-context.js → agent-core/runtime/context/environment.js} +1 -1
- package/dist/{runtime/context-gc-runtime.d.ts → agent-core/runtime/context/gc-runtime.d.ts} +4 -4
- package/dist/{runtime/context-gc-runtime.js → agent-core/runtime/context/gc-runtime.js} +3 -3
- package/dist/{runtime/context-gc-tokens.d.ts → agent-core/runtime/context/gc-tokens.d.ts} +2 -2
- package/dist/{runtime/context-gc-tokens.js → agent-core/runtime/context/gc-tokens.js} +3 -3
- package/dist/{runtime/context-gc.d.ts → agent-core/runtime/context/gc.d.ts} +3 -3
- package/dist/{runtime/context-gc.js → agent-core/runtime/context/gc.js} +6 -6
- package/dist/{runtime → agent-core/runtime/context}/instructions.d.ts +1 -1
- package/dist/{runtime → agent-core/runtime/context}/instructions.js +1 -1
- package/dist/{runtime → agent-core/runtime/context}/normalize.d.ts +1 -1
- package/dist/{runtime/context-projection.d.ts → agent-core/runtime/context/projection.d.ts} +3 -3
- package/dist/{runtime/context-projection.js → agent-core/runtime/context/projection.js} +4 -4
- package/dist/agent-core/runtime/context/request-budget.d.ts +6 -0
- package/dist/agent-core/runtime/context/request-budget.js +22 -0
- package/dist/agent-core/runtime/context/restore.d.ts +15 -0
- package/dist/agent-core/runtime/context/restore.js +85 -0
- package/dist/agent-core/runtime/context/usage.d.ts +18 -0
- package/dist/agent-core/runtime/context/usage.js +138 -0
- package/dist/agent-core/runtime/context/world-state.d.ts +35 -0
- package/dist/agent-core/runtime/context/world-state.js +133 -0
- package/dist/agent-core/runtime/contracts.d.ts +37 -0
- package/dist/agent-core/runtime/event-projection.d.ts +5 -0
- package/dist/agent-core/runtime/event-projection.js +17 -0
- package/dist/agent-core/runtime/events.d.ts +142 -0
- package/dist/agent-core/runtime/execution.d.ts +50 -0
- package/dist/agent-core/runtime/execution.js +393 -0
- package/dist/{runtime → agent-core/runtime}/rollout-budget.d.ts +1 -1
- package/dist/agent-core/sdk/agent.js +209 -0
- package/dist/agent-core/sdk/model-runtime.d.ts +13 -0
- package/dist/agent-core/sdk/model-runtime.js +63 -0
- package/dist/agent-core/sdk/session-title.d.ts +3 -0
- package/dist/agent-core/sdk/session-title.js +42 -0
- package/dist/agent-core/sdk/session.d.ts +62 -0
- package/dist/agent-core/sdk/session.js +1147 -0
- package/dist/agent-core/sdk/subagent-backend.d.ts +3 -0
- package/dist/agent-core/sdk/subagent-backend.js +46 -0
- package/dist/agent-core/sdk/types.d.ts +56 -0
- package/dist/{session → agent-core/session}/catalog.d.ts +2 -1
- package/dist/{session → agent-core/session}/context-gc-checkpoint.js +1 -1
- package/dist/{session → agent-core/session}/jsonl-store.d.ts +2 -2
- package/dist/{session → agent-core/session}/jsonl-store.js +1 -1
- package/dist/agent-core/subagent/contracts.d.ts +91 -0
- package/dist/agent-core/subagent/contracts.js +1 -0
- package/dist/agent-core/subagent/scheduler.d.ts +117 -0
- package/dist/{subagent → agent-core/subagent}/session-controller.d.ts +3 -2
- package/dist/agent-core/tools/agent/agent-cancel.d.ts +7 -0
- package/dist/{tools → agent-core/tools/agent}/agent-cancel.js +1 -1
- package/dist/agent-core/tools/agent/agent-message.d.ts +7 -0
- package/dist/{tools → agent-core/tools/agent}/agent-message.js +2 -2
- package/dist/{tools → agent-core/tools/agent}/agent-start.d.ts +3 -3
- package/dist/{tools → agent-core/tools/agent}/agent-start.js +3 -3
- package/dist/agent-core/tools/agent/agent-wait.d.ts +7 -0
- package/dist/{tools → agent-core/tools/agent}/agent-wait.js +2 -2
- package/dist/{tools → agent-core/tools/agent}/question.d.ts +2 -2
- package/dist/{tools → agent-core/tools/agent}/question.js +2 -2
- package/dist/{tools → agent-core/tools/agent}/skill.d.ts +2 -2
- package/dist/{tools → agent-core/tools/agent}/skill.js +2 -2
- package/dist/{tools → agent-core/tools/agent}/todo-read.d.ts +2 -2
- package/dist/{tools → agent-core/tools/agent}/todo-write.d.ts +2 -2
- package/dist/{tools → agent-core/tools/agent}/todo-write.js +2 -2
- package/dist/{tools → agent-core/tools/agent}/todo.d.ts +2 -9
- package/dist/{tools → agent-core/tools/agent}/todo.js +1 -1
- package/dist/{tools → agent-core/tools}/code-mode/cells.d.ts +1 -1
- package/dist/{tools → agent-core/tools}/code-mode/dispatch.d.ts +2 -4
- package/dist/{tools → agent-core/tools}/code-mode/dispatch.js +5 -4
- package/dist/{tools → agent-core/tools}/code-mode/engine.d.ts +1 -1
- package/dist/agent-core/tools/code-mode/engine.js +1 -0
- package/dist/{tools/code-mode.d.ts → agent-core/tools/code-mode/index.d.ts} +7 -7
- package/dist/{tools/code-mode.js → agent-core/tools/code-mode/index.js} +14 -15
- package/dist/{tools → agent-core/tools}/code-mode/state.d.ts +2 -13
- package/dist/agent-core/tools/code-mode/worker.d.ts +1 -0
- package/dist/agent-core/tools/contracts.d.ts +168 -0
- package/dist/agent-core/tools/contracts.js +1 -0
- package/dist/agent-core/tools/execution.d.ts +34 -0
- package/dist/agent-core/tools/execution.js +102 -0
- package/dist/agent-core/tools/local/apply-patch.d.ts +19 -0
- package/dist/{tools → agent-core/tools/local}/apply-patch.js +8 -2
- package/dist/{tools → agent-core/tools/local}/exec.d.ts +5 -17
- package/dist/{tools → agent-core/tools/local}/exec.js +4 -4
- package/dist/{tools → agent-core/tools/local}/read.d.ts +7 -15
- package/dist/{tools → agent-core/tools/local}/read.js +185 -2
- package/dist/{tools/local-registry.d.ts → agent-core/tools/local/registry.d.ts} +5 -5
- package/dist/agent-core/tools/local/registry.js +101 -0
- package/dist/{tools → agent-core/tools/local}/view-image.d.ts +4 -9
- package/dist/{tools → agent-core/tools/local}/view-image.js +2 -2
- package/dist/agent-core/tools/local/web-fetch.d.ts +4 -0
- package/dist/agent-core/tools/local/web-fetch.js +85 -0
- package/dist/agent-core/tools/local/web-search.d.ts +7 -0
- package/dist/agent-core/tools/local/web-search.js +34 -0
- package/dist/{tools/local-workspace-tools.js → agent-core/tools/local/workspace-tools.js} +1 -1
- package/dist/{sdk/mcp-catalog.d.ts → agent-core/tools/mcp/catalog.d.ts} +1 -2
- package/dist/{sdk/mcp-catalog.js → agent-core/tools/mcp/catalog.js} +1 -1
- package/dist/agent-core/tools/mcp/history.d.ts +4 -0
- package/dist/agent-core/tools/mcp/history.js +33 -0
- package/dist/{tools → agent-core/tools/mcp}/tool-search.d.ts +3 -3
- package/dist/{tools → agent-core/tools/mcp}/tool-search.js +4 -4
- package/dist/agent-core/tools/output-projection.d.ts +14 -0
- package/dist/agent-core/tools/output-projection.js +189 -0
- package/dist/{tools → agent-core/tools}/registry.d.ts +1 -1
- package/dist/agent-core/tools/registry.js +47 -0
- package/dist/{tools → agent-core/tools}/runtime-context.d.ts +8 -7
- package/dist/agent-core/tools/runtime-context.js +1 -0
- package/dist/{tools → agent-core/tools}/runtime.d.ts +1 -1
- package/dist/cli/codex-auth-file.d.ts +1 -1
- package/dist/cli/codex-auth-file.js +2 -2
- package/dist/cli/main.js +9 -9
- package/dist/index.d.ts +17 -15
- package/dist/index.js +8 -8
- package/dist/sandbox/linux/bubblewrap-sandbox-executor.d.ts +1 -1
- package/dist/sandbox/linux/bubblewrap-sandbox-executor.js +1 -1
- package/package.json +3 -3
- package/dist/host/contracts.d.ts +0 -21
- package/dist/sdk/agent.js +0 -2548
- package/dist/sdk/types.d.ts +0 -349
- package/dist/subagent/scheduler.d.ts +0 -206
- package/dist/tools/agent-cancel.d.ts +0 -7
- package/dist/tools/agent-message.d.ts +0 -7
- package/dist/tools/agent-wait.d.ts +0 -7
- package/dist/tools/apply-patch.d.ts +0 -32
- package/dist/tools/local-registry.js +0 -373
- package/dist/tools/registry.js +0 -47
- package/dist/tools/web-fetch.d.ts +0 -2
- package/dist/tools/web-fetch.js +0 -12
- package/dist/tools/web-search.d.ts +0 -2
- package/dist/tools/web-search.js +0 -15
- package/dist/{error.d.ts → agent-core/error.d.ts} +0 -0
- package/dist/{error.js → agent-core/error.js} +0 -0
- package/dist/{host → agent-core/host}/contracts.js +0 -0
- package/dist/{host → agent-core/host}/local-process-executor.d.ts +0 -0
- package/dist/{host → agent-core/host}/local-process-executor.js +0 -0
- package/dist/{host → agent-core/host}/network.d.ts +0 -0
- package/dist/{host → agent-core/host}/network.js +0 -0
- package/dist/{model → agent-core/model}/anthropic-messages.d.ts +0 -0
- package/dist/{model → agent-core/model}/anthropic-messages.js +0 -0
- package/dist/{model → agent-core/model}/anthropic-thinking.d.ts +0 -0
- package/dist/{model → agent-core/model}/anthropic-thinking.js +0 -0
- package/dist/{model → agent-core/model}/catalog-file.d.ts +0 -0
- package/dist/{model → agent-core/model}/catalog-file.js +0 -0
- package/dist/{model → agent-core/model}/configuration.d.ts +0 -0
- package/dist/{model → agent-core/model}/configuration.js +0 -0
- package/dist/{model → agent-core/model}/contracts.js +0 -0
- package/dist/{model → agent-core/model}/http.d.ts +0 -0
- package/dist/{model → agent-core/model}/http.js +0 -0
- package/dist/{model → agent-core/model}/openai-responses.d.ts +0 -0
- package/dist/{model → agent-core/model}/openai-responses.js +0 -0
- package/dist/{model → agent-core/model}/runtime-credential.d.ts +0 -0
- package/dist/{model → agent-core/model}/runtime-credential.js +0 -0
- package/dist/{model → agent-core/model}/sse.d.ts +0 -0
- package/dist/{model → agent-core/model}/sse.js +0 -0
- package/dist/{model → agent-core/model}/transport-test.d.ts +0 -0
- package/dist/{model → agent-core/model}/transport-test.js +0 -0
- package/dist/{prompts → agent-core/prompts}/compact.d.ts +0 -0
- package/dist/{prompts → agent-core/prompts}/compact.js +0 -0
- package/dist/{prompts → agent-core/prompts}/core.js +0 -0
- package/dist/{prompts → agent-core/prompts}/execution-budget.d.ts +0 -0
- package/dist/{prompts → agent-core/prompts}/execution-budget.js +0 -0
- package/dist/{prompts → agent-core/prompts}/index.js +0 -0
- package/dist/{prompts → agent-core/prompts}/modes.js +0 -0
- package/dist/{prompts → agent-core/prompts}/output.d.ts +0 -0
- package/dist/{prompts → agent-core/prompts}/output.js +0 -0
- package/dist/{prompts → agent-core/prompts}/resources.js +0 -0
- package/dist/{prompts → agent-core/prompts}/subagent.d.ts +0 -0
- package/dist/{prompts → agent-core/prompts}/subagent.js +0 -0
- package/dist/{prompts → agent-core/prompts}/title.d.ts +0 -0
- package/dist/{prompts → agent-core/prompts}/title.js +0 -0
- package/dist/{prompts → agent-core/prompts}/workflow.d.ts +0 -0
- package/dist/{prompts → agent-core/prompts}/workflow.js +0 -0
- package/dist/{runtime → agent-core/runtime}/async-queue.d.ts +0 -0
- package/dist/{runtime → agent-core/runtime}/async-queue.js +0 -0
- package/dist/{sdk/types.js → agent-core/runtime/context/contracts.js} +0 -0
- package/dist/{runtime → agent-core/runtime/context}/normalize.js +0 -0
- package/dist/{session/types.js → agent-core/runtime/contracts.js} +0 -0
- package/dist/{tools/code-mode/engine.js → agent-core/runtime/events.js} +0 -0
- package/dist/{runtime → agent-core/runtime}/execution-policy.d.ts +0 -0
- package/dist/{runtime → agent-core/runtime}/execution-policy.js +0 -0
- package/dist/{runtime → agent-core/runtime}/plan-output.d.ts +0 -0
- package/dist/{runtime → agent-core/runtime}/plan-output.js +0 -0
- package/dist/{runtime → agent-core/runtime}/rollout-budget.js +0 -0
- package/dist/{runtime → agent-core/runtime}/token-budget.d.ts +0 -0
- package/dist/{runtime → agent-core/runtime}/token-budget.js +0 -0
- package/dist/{sdk → agent-core/sdk}/agent.d.ts +0 -0
- package/dist/{tools/code-mode/worker.d.ts → agent-core/sdk/types.js} +0 -0
- package/dist/{session → agent-core/session}/catalog.js +0 -0
- package/dist/{session → agent-core/session}/context-gc-checkpoint.d.ts +0 -0
- package/dist/{session → agent-core/session}/context-gc-replacements.d.ts +0 -0
- package/dist/{session → agent-core/session}/context-gc-replacements.js +0 -0
- package/dist/{session → agent-core/session}/context-gc-subagent.d.ts +0 -0
- package/dist/{session → agent-core/session}/context-gc-subagent.js +0 -0
- package/dist/{session → agent-core/session}/transaction-recovery.d.ts +0 -0
- package/dist/{session → agent-core/session}/transaction-recovery.js +0 -0
- package/dist/{session → agent-core/session}/types.d.ts +0 -0
- package/dist/{tools/runtime-context.js → agent-core/session/types.js} +0 -0
- package/dist/{subagent → agent-core/subagent}/scheduler.js +0 -0
- package/dist/{subagent → agent-core/subagent}/session-controller.js +0 -0
- package/dist/{tools → agent-core/tools/agent}/todo-read.js +0 -0
- package/dist/{tools → agent-core/tools}/callers.d.ts +0 -0
- package/dist/{tools → agent-core/tools}/callers.js +0 -0
- package/dist/{tools → agent-core/tools}/code-mode/cells.js +1 -1
- /package/dist/{tools → agent-core/tools}/code-mode/quickjs-engine.d.ts +0 -0
- /package/dist/{tools → agent-core/tools}/code-mode/quickjs-engine.js +0 -0
- /package/dist/{tools → agent-core/tools}/code-mode/state.js +0 -0
- /package/dist/{tools → agent-core/tools}/code-mode/text-output.d.ts +0 -0
- /package/dist/{tools → agent-core/tools}/code-mode/text-output.js +0 -0
- /package/dist/{tools → agent-core/tools}/code-mode/worker.js +0 -0
- /package/dist/{tools → agent-core/tools}/execution-limits.d.ts +0 -0
- /package/dist/{tools → agent-core/tools}/execution-limits.js +0 -0
- /package/dist/{tools → agent-core/tools/local}/read-context.d.ts +0 -0
- /package/dist/{tools → agent-core/tools/local}/read-context.js +0 -0
- /package/dist/{tools/local-workspace-tools.d.ts → agent-core/tools/local/workspace-tools.d.ts} +0 -0
- /package/dist/{tools → agent-core/tools}/runtime.js +0 -0
- /package/dist/{tools → agent-core/tools}/scheduler.d.ts +0 -0
- /package/dist/{tools → agent-core/tools}/scheduler.js +0 -0
- /package/dist/{tools → agent-core/tools}/shared/path-resolver.d.ts +0 -0
- /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
|
-
}
|