@sema-agent/core 7.5.0 → 7.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/core/auto-mode.d.ts +9 -0
  3. package/dist/core/auto-mode.js +11 -0
  4. package/dist/core/checkpoint-store.js +5 -1
  5. package/dist/core/runner/checkpoint-scope.d.ts +32 -0
  6. package/dist/core/runner/checkpoint-scope.js +4 -0
  7. package/dist/core/runner/contracts.d.ts +1878 -0
  8. package/dist/core/runner/contracts.js +1 -0
  9. package/dist/core/runner/denial-limit-arms.d.ts +1 -1
  10. package/dist/core/runner/derived-route-fallback.d.ts +34 -0
  11. package/dist/core/runner/derived-route-fallback.js +16 -0
  12. package/dist/core/runner/prepare-acquire-reconcile.d.ts +1 -1
  13. package/dist/core/runner/prepare-caps-and-workflow.d.ts +170 -0
  14. package/dist/core/runner/prepare-caps-and-workflow.js +255 -0
  15. package/dist/core/runner/prepare-config-doors.d.ts +2 -10
  16. package/dist/core/runner/prepare-defer-classify.d.ts +86 -0
  17. package/dist/core/runner/prepare-defer-classify.js +107 -0
  18. package/dist/core/runner/prepare-delegation-surface.d.ts +104 -0
  19. package/dist/core/runner/prepare-delegation-surface.js +144 -0
  20. package/dist/core/runner/prepare-execution-env.d.ts +54 -0
  21. package/dist/core/runner/prepare-execution-env.js +86 -0
  22. package/dist/core/runner/prepare-file-history.d.ts +95 -0
  23. package/dist/core/runner/prepare-file-history.js +383 -0
  24. package/dist/core/runner/prepare-hands-readface.d.ts +5 -7
  25. package/dist/core/runner/prepare-hands-readface.js +1 -1
  26. package/dist/core/runner/prepare-inherited-gate.d.ts +268 -0
  27. package/dist/core/runner/prepare-inherited-gate.js +266 -0
  28. package/dist/core/runner/prepare-listings.d.ts +77 -0
  29. package/dist/core/runner/prepare-listings.js +76 -0
  30. package/dist/core/runner/prepare-lsp.d.ts +55 -0
  31. package/dist/core/runner/prepare-lsp.js +27 -0
  32. package/dist/core/runner/prepare-memory.d.ts +1 -1
  33. package/dist/core/runner/prepare-offload-wrappers.d.ts +62 -0
  34. package/dist/core/runner/prepare-offload-wrappers.js +45 -0
  35. package/dist/core/runner/prepare-project-context.d.ts +131 -0
  36. package/dist/core/runner/prepare-project-context.js +150 -0
  37. package/dist/core/runner/prepare-prompt-inputs.d.ts +138 -0
  38. package/dist/core/runner/prepare-prompt-inputs.js +141 -0
  39. package/dist/core/runner/prepare-protocol-tools.d.ts +91 -0
  40. package/dist/core/runner/prepare-protocol-tools.js +182 -0
  41. package/dist/core/runner/prepare-question-face.d.ts +119 -0
  42. package/dist/core/runner/prepare-question-face.js +83 -0
  43. package/dist/core/runner/prepare-run-refs.d.ts +89 -0
  44. package/dist/core/runner/prepare-run-refs.js +39 -0
  45. package/dist/core/runner/prepare-safety-scan.d.ts +3 -2
  46. package/dist/core/runner/prepare-task.d.ts +11 -1846
  47. package/dist/core/runner/prepare-task.js +83 -2366
  48. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +111 -0
  49. package/dist/core/runner/prepare-tool-disclosure-mount.js +219 -0
  50. package/dist/core/runner/prepare-wiring-manifest.d.ts +184 -0
  51. package/dist/core/runner/prepare-wiring-manifest.js +240 -0
  52. package/dist/core/runner/prepare-workspace-restore.d.ts +1 -27
  53. package/dist/core/runner/prepare-workspace-restore.js +1 -22
  54. package/dist/core/runner/rollback-stack.d.ts +32 -0
  55. package/dist/core/runner/rollback-stack.js +30 -0
  56. package/dist/core/runner/workspace-path.d.ts +33 -0
  57. package/dist/core/runner/workspace-path.js +22 -0
  58. package/dist/core/tool-policy.d.ts +16 -0
  59. package/dist/core/tool-policy.js +3 -0
  60. package/dist/core/types.d.ts +2 -2
  61. package/dist/core/write-protect.js +3 -2
  62. package/package.json +6 -2
@@ -1,73 +1,78 @@
1
1
  import { createHash, randomBytes } from "node:crypto";
2
2
  import { realpathSync } from "node:fs";
3
3
  import { resolve as resolveFsPath } from "node:path";
4
- import { AgentHarness, DEFAULT_CHARS_PER_TOKEN, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, estimateContextTokens, isSyntheticApiErrorMessage, readCompactionActiveTools, summaryOutputBudgetTokens } from "../../internal/harness.js";
4
+ import { AgentHarness, DEFAULT_CHARS_PER_TOKEN, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, estimateContextTokens, summaryOutputBudgetTokens } from "../../internal/harness.js";
5
5
  const PROMPT_HASH_SALT = randomBytes(16);
6
6
  import { sanitizeCompactionSettings } from "../auto-compaction.js";
7
7
  import { projectStaleToolResults, resolveStaleToolResultOffload } from "./compaction-call-options.js";
8
- import { createAutoModeDecider, createAutoModeDenialTracker } from "../auto-mode.js";
9
- import { gateAskCarry, inheritedAskCarry, attachRebuiltDenialTrackers, createDenialLimitStop, headlessDenyAtFold, headlessDenyAtRecheck, judgeInheritedClassifier, settleDenialLimitFallback } from "./denial-limit-arms.js";
10
- import { autoModeArmingRecipeOf } from "../auto-mode-arming.js";
11
- import { buildAutoModePrompt, renderAutoModeAction, renderAutoModeWindow } from "../auto-mode-prompt.js";
12
- import { resolveTaskModel } from "../roles.js";
8
+ import { gateAskCarry, inheritedAskCarry, headlessDenyAtFold, headlessDenyAtRecheck, judgeInheritedClassifier, settleDenialLimitFallback } from "./denial-limit-arms.js";
13
9
  import { primaryActivityArg } from "../arg-summary.js";
14
- import { materializeMcpTools } from "../mcp.js";
15
- import { materializeA2aTools } from "../a2a.js";
16
- import { Type } from "typebox";
17
10
  import { Value } from "typebox/value";
18
11
  import { uuidv7 } from "../../engine/session/uuid.js";
19
12
  import { brainToRuntime } from "../runtime.js";
20
13
  import { hasSessionFork } from "../session.js";
21
- import { createSubagentWorktreeHelper, forkGovernanceDenial, resolveDelegationEntryCaps } from "../../agents/subagent.js";
22
- import { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME } from "../../agents/agent-transcript-tool.js";
23
- import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
24
- import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
25
- import { askApproverIdentity, carriesBidiControls, checkToolPolicyProjection, combinePolicies, constraintChainDigest, constraintChainEntryOfLayer, isApprovalSettledBy, isAskDenyResolution, screenApproverAttribution, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
14
+ import { SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
15
+ import { askApproverIdentity, isLiveApproverSeat, carriesBidiControls, checkToolPolicyProjection, combinePolicies, constraintChainDigest, constraintChainEntryOfLayer, isApprovalSettledBy, isAskDenyResolution, screenApproverAttribution, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
26
16
  import { suggestRulesForCommand } from "../permission-rule-model.js";
27
17
  import { PERSISTED_RULE_TOOL, inheritedAskRuleEvidence, createPermissionRuleLanes } from "./prepare-permission-rules.js";
28
- import { ActiveSkillScope, createActiveSkillScopePolicy } from "./active-skill-scope.js";
29
- import { CHANGED_FILES_MTIME_EPS_MS, fenceMcpServerInstructions, renderAgentListingDelta } from "./turn-attachments.js";
18
+ import { createActiveSkillScopePolicy } from "./active-skill-scope.js";
19
+ import { CHANGED_FILES_MTIME_EPS_MS } from "./turn-attachments.js";
30
20
  import { inlineUntrusted } from "../untrusted-text.js";
31
21
  import { loadAnnounceOnceLedger, serializeAnnouncedListings } from "./prepare-announce-once.js";
32
22
  import { isValidReminderMark, mintReminderMark, reminderMarkDeclaration } from "../reminder-mint.js";
33
23
  import { policyAskClassOf } from "../ask-class.js";
34
24
  import { emitTrace } from "../trace.js";
35
25
  import { createSessionRulePolicy, PATH_CONFINABLE_WRITE_TOOLS } from "./session-rule-policy.js";
36
- import { askCarryRowMembers, cloneObserverInput, createHookEnvCapabilities, createPreToolUseConstraintPolicy, formatHookFeedback, mintHookInvocationIdentity, persistedRuleMandateOf, resolveHookTimeoutMs, runHookSeat, hookSeatExpiredError, runToolGate } from "../hooks.js";
26
+ import { askCarryRowMembers, cloneObserverInput, formatHookFeedback, persistedRuleMandateOf, resolveHookTimeoutMs, runHookSeat, hookSeatExpiredError, runToolGate } from "../hooks.js";
37
27
  import { createWriteProtectionCheck } from "../write-protect.js";
38
28
  import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-detector.js";
39
29
  import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
40
- import { adjudicateDerivedRoute, fallbackToPrimaryNotice, sameRouteIdentity } from "../../brain/route-adjudicator.js";
41
30
  import { STALL_CONNECT_MS, STALL_FIRST_TOKEN_MS, STALL_IDLE_MS, withBrainCallGuardrail } from "../../brain/timeout.js";
42
- import { defineTool, isDefineToolProduct } from "../tools.js";
43
31
  import { RETIRED_TOOL_NAMES } from "../tool-name-aliases.js";
44
32
  import { protocolOf } from "../protocol-table.js";
45
33
  import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "../permission-rules.js";
46
- import { pathToUri } from "../lsp-protocol.js";
47
- import { DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, createOffloadPersist, firstPartyOffloadPolicy, InMemoryToolResultStore, RunnerSharedToolResultStore, ScopedToolResultStore, isVolatileOffloadStore, OFFLOAD_TOOL_NAME, createReadToolResultTool, withToolResultOffload, } from "../tool-result-store.js";
48
- import { OUTPUT_TOOL_NAME, REPORT_FINDINGS_TOOL_NAME, SKILL_CONTENT_MAX_CHARS, SKILL_TOOL_NAME, createOutputTool, createReportBlockedTool, createReportFindingsTool, createSkillTool, normalizeSkills } from "./synthetic-tools.js";
34
+ import { createOffloadPersist, InMemoryToolResultStore, ScopedToolResultStore, isVolatileOffloadStore, OFFLOAD_TOOL_NAME, } from "../tool-result-store.js";
35
+ import { OUTPUT_TOOL_NAME, createOutputTool } from "./synthetic-tools.js";
49
36
  import { compileOutputSchema } from "./strict-output-schema.js";
50
- import { TOOL_SEARCH_NAME, buildDeferredRegistry, classifyDeferred, extractDiscoveredToolNames, createPlaceholderTool, createToolSearchTool, staticSchemaRenderable, } from "./tool-disclosure.js";
51
- import { createSharedMemoryTools } from "../shared-memory/tools.js";
52
- import { SHARED_MEMORY_TOOL_NAMES } from "../shared-memory/types.js";
53
37
  import { MEMORY_ENGINE_TOOL_NAMES } from "../memory-engine/tools.js";
54
38
  import { classifyToolContentOrigin, contentOriginPollutes, delegationCallIsExternal } from "../memory-engine/content-origin.js";
55
39
  import { narrowContentSafety, readCardAttestation } from "../memory-engine/delegation-provenance.js";
56
40
  import { recordSyncUnattestable, sessionSettlements } from "../memory-engine/delegation-settlement.js";
57
- import { composeMemoryBlock } from "../memory.js";
58
- import { COMPLIANCE_CAPABILITIES, WEB_FETCH_TOOL_NAME, complianceCallDenial, resolveComplianceDenies } from "../compliance.js";
41
+ import { complianceCallDenial } from "../compliance.js";
59
42
  import { foldAdmissionFreeze } from "../memory-admission.js";
60
43
  import { prepareMemory } from "./prepare-memory.js";
44
+ import { prepareDeferClassify } from "./prepare-defer-classify.js";
45
+ import { prepareListings } from "./prepare-listings.js";
46
+ import { preparePromptInputs } from "./prepare-prompt-inputs.js";
47
+ import { hasConversationContent, prepareProjectContext } from "./prepare-project-context.js";
61
48
  import { limitConfigError, prepareConfigDoors } from "./prepare-config-doors.js";
62
49
  import { NAMESPACED_NAME_SHAPES, prepareSafetyScan } from "./prepare-safety-scan.js";
63
50
  import { prepareAcquireReconcile } from "./prepare-acquire-reconcile.js";
64
51
  import { prepareHandsMount, resolveHandsLessReadFace } from "./prepare-hands-readface.js";
65
- import { createEditedFilesLedger } from "./edited-files-ledger.js";
66
52
  import { prepareWorkspaceRestore, remoteEnvFailureNote, restoreWorkspaceWithRetry } from "./prepare-workspace-restore.js";
67
- import { defaultPromptProvider, buildEnvironmentContext, formatLocalDate, isValidTimeZone, PROJECT_CONTEXT_FRAMING } from "../../prompts/default.js";
68
- import { applyGitFrameGuard, probeGitStatusLane } from "./git-status-frame.js";
53
+ import { prepareOffloadWrappers } from "./prepare-offload-wrappers.js";
54
+ import { prepareFileHistory } from "./prepare-file-history.js";
55
+ import { prepareExecutionEnv } from "./prepare-execution-env.js";
56
+ import { prepareRunRefs } from "./prepare-run-refs.js";
57
+ import { prepareInheritedGate } from "./prepare-inherited-gate.js";
58
+ import { prepareCapsAndWorkflow } from "./prepare-caps-and-workflow.js";
59
+ import { prepareDelegationSurface } from "./prepare-delegation-surface.js";
60
+ import { prepareToolDisclosureMount } from "./prepare-tool-disclosure-mount.js";
61
+ import { prepareWiringManifest } from "./prepare-wiring-manifest.js";
62
+ import { checkpointScopeOf } from "./checkpoint-scope.js";
63
+ export { DEFAULT_IRREVERSIBLE_SCOPE, checkpointScopeOf } from "./checkpoint-scope.js";
64
+ export { mcpManifestEntries } from "./prepare-wiring-manifest.js";
65
+ import { derivedRouteFallsBack } from "./derived-route-fallback.js";
66
+ import { prepareProtocolTools } from "./prepare-protocol-tools.js";
67
+ import { CONTENT_ASK_BINDING_CAP, prepareQuestionFace } from "./prepare-question-face.js";
68
+ import { prepareLsp } from "./prepare-lsp.js";
69
+ import { createRollbackStack } from "./rollback-stack.js";
70
+ export { __resetMaterializeEnvAnnouncements } from "./prepare-tool-disclosure-mount.js";
71
+ export { fileHistoryFilesystemIdentity, resolveFileHistoryScope } from "./prepare-file-history.js";
72
+ import { defaultPromptProvider, buildEnvironmentContext } from "../../prompts/default.js";
73
+ import { applyGitFrameGuard } from "./git-status-frame.js";
69
74
  import { assemblePrompt } from "../../prompt-assembly/assemble.js";
70
- import { auditToolCollisions, getToolContract, projectToolManifest } from "../../prompt-assembly/tool-catalog.js";
75
+ import { auditToolCollisions, projectToolManifest } from "../../prompt-assembly/tool-catalog.js";
71
76
  import { resolveEpochAgainstBundled } from "../../prompt-assembly/epoch.js";
72
77
  import { artifactDeclarations } from "../../prompt-assembly/artifact.js";
73
78
  import { buildTurnPromptSnapshot } from "../../prompt-assembly/turn-snapshot.js";
@@ -76,56 +81,22 @@ import { clearStaleToolResults, createClearedProjectionLedger, dropEmptyFailureA
76
81
  import { capAggregateToolResults } from "../tool-result-budget.js";
77
82
  import { capAggregateMediaBytes, AGGREGATE_MEDIA_BUDGET_BYTES } from "../media-byte-cap.js";
78
83
  import { dropOrphanToolResults, guardBudget, insertTrimNotice, trimToBudget } from "../context-guard.js";
79
- import { StubExecutionEnv } from "../stub-env.js";
80
- import { hasDestroy, isIsolated, isRemoteExecutionEnv, isSuspendable, missingRestoreSurface, RemoteExecutionError } from "../remote-env.js";
84
+ import { isIsolated, isRemoteExecutionEnv, isSuspendable, RemoteExecutionError } from "../remote-env.js";
81
85
  import { settleTeardownLeg } from "./teardown-bounded.js";
82
86
  import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.js";
83
- import { createTaskOutputTool, createTaskStopTool, defaultTaskRegistry } from "../task-registry.js";
84
- import { createMonitorTool } from "../../tools/monitor.js";
85
- import { createWorktreeTools } from "../../tools/worktree.js";
86
- import { announcePeerLaneMount, bindPeerLaneDrain, listAgentsMountable, mountListAgents, peerLaneSendMessageSeats } from "../../agents/peer-session-drain.js";
87
- import { CROSS_SESSION_CLASSIFIER_RULE } from "../../agents/cross-session-envelope.js";
88
- import { NodeExecutionEnv } from "../../engine/execution-env/node-execution-env.js";
89
- import { applyCompactionToReadFileState, isReadDedupStubResult, FULL_SHELL_CONTRACT_ID, HAND_TOOL_EFFECTS } from "../../tools/fs/index.js";
87
+ import { defaultTaskRegistry } from "../task-registry.js";
88
+ import { announcePeerLaneMount } from "../../agents/peer-session-drain.js";
89
+ import { applyCompactionToReadFileState, isReadDedupStubResult } from "../../tools/fs/index.js";
90
90
  import { decodeTextBytes } from "../../tools/fs/encoding.js";
91
- import { ASK_USER_QUESTION_TOOL_NAME, createAskUserQuestionTool, classifyQuestionOutcome, isLiveQuestionFace, validateAskQuestions, } from "../ask-question.js";
91
+ import { ASK_USER_QUESTION_TOOL_NAME, classifyQuestionOutcome, validateAskQuestions, } from "../ask-question.js";
92
92
  import { createSchedulerTools } from "../../tools/scheduler-tools.js";
93
- import { createPresentPlanTool, createEnterPlanModeTool, PRESENT_PLAN_TOOL_NAME } from "../present-plan-tool.js";
94
- import { isSelfOrchestrationActive, selfOrchestrationFailClosedReason } from "../../orchestration/workflow-script-runner.js";
95
- import { createRunWorkflowTool, RUN_WORKFLOW_TOOL_NAME } from "../../orchestration/run-workflow-tool.js";
96
- import { resolveWorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
97
- import { createLspTool, gitCheckIgnoreFilter, resolveLspPath } from "../lsp.js";
93
+ import { PRESENT_PLAN_TOOL_NAME } from "../present-plan-tool.js";
94
+ import { selfOrchestrationFailClosedReason } from "../../orchestration/workflow-script-runner.js";
98
95
  import { fileArgPath, resolveKey } from "../../tools/fs/safety.js";
99
96
  import { BINDING_CHECKPOINT_VERSION, mintCheckpointId, mintCheckpointToken, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, FACE_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, buildRiskDescriptor, debitLedger, encodeAtFidelity, remainingBudgetMicroUsd, resolveCheckpointStore, resolveDeclaredFidelity, samePlainValue, } from "../checkpoint-store.js";
100
97
  import { boundInputHashOf } from "../canonical-json.js";
101
- import { countElicitOptIns, deriveWiringManifest, resolveAskSeamForm, resolveDeclaredDurability, resolveElicitSeam, resolveQuestionSeam, resolveSubagentTranscriptTier } from "../wiring-manifest.js";
102
98
  import { durableParkGapFor } from "../park-selfcheck.js";
103
- import { GLOBAL_USAGE_KEY, usageRetryAfterMs, windowsGovernCost } from "../usage-window-store.js";
104
99
  import { deliverEngineNotice } from "../types.js";
105
- import { resolveTrackKey } from "../file-history-store.js";
106
- let announcedMaterializeEnvBySink = new WeakMap();
107
- const announcedMaterializeEnvConsole = new Set();
108
- function materializeEnvLedger(onNotice) {
109
- if (typeof onNotice !== "function")
110
- return announcedMaterializeEnvConsole;
111
- let lines = announcedMaterializeEnvBySink.get(onNotice);
112
- if (lines === undefined) {
113
- lines = new Set();
114
- announcedMaterializeEnvBySink.set(onNotice, lines);
115
- }
116
- return lines;
117
- }
118
- export function __resetMaterializeEnvAnnouncements() {
119
- announcedMaterializeEnvBySink = new WeakMap();
120
- announcedMaterializeEnvConsole.clear();
121
- }
122
- function emitMaterializeEnvNotice(onNotice, message, detail) {
123
- const ledger = materializeEnvLedger(onNotice);
124
- if (ledger.has(message))
125
- return;
126
- ledger.add(message);
127
- deliverEngineNotice(onNotice, { code: "config.materialize_env_discarded", message, detail });
128
- }
129
100
  function warnCompactionWindowHazard(tracer, spec, model, compModel, hostTaskId) {
130
101
  if (compModel === undefined)
131
102
  return;
@@ -215,24 +186,6 @@ function toolModelGateManifest(gate) {
215
186
  }
216
187
  return firstClass === undefined ? undefined : { class: firstClass, removed, restore: TOOL_MODEL_GATE_RESTORE_SENTENCE };
217
188
  }
218
- export function mcpManifestEntries(entries, statuses) {
219
- return (entries ?? []).map((entry) => {
220
- const status = statuses.find((s) => s.name === entry.name);
221
- const source = typeof entry.source === "string" && entry.source.length > 0 ? { source: inlineUntrusted(entry.source, 80) } : {};
222
- if (status === undefined)
223
- return { name: entry.name, ...source, status: "skipped", toolCount: 0 };
224
- if (status.status === "connected")
225
- return { name: entry.name, ...source, status: "connected", toolCount: status.toolNames?.length ?? 0 };
226
- return {
227
- name: entry.name,
228
- ...source,
229
- status: "failed",
230
- ...(status.errorCode !== undefined ? { errorCode: status.errorCode } : {}),
231
- ...(status.error !== undefined ? { error: inlineUntrusted(status.error, 240) } : {}),
232
- toolCount: 0,
233
- };
234
- });
235
- }
236
189
  function announceDeclaredMcpContentClasses(args) {
237
190
  for (const entry of args.entries ?? []) {
238
191
  const declaredClass = entry.contentOrigin;
@@ -263,65 +216,9 @@ function announceDeclaredMcpContentClasses(args) {
263
216
  }
264
217
  export { __resetReadFaceClampAnnouncement } from "./prepare-hands-readface.js";
265
218
  const DEFAULT_MAX_SUSPENDS = 5;
266
- const ULTRA_REASONING_TIERS = new Set(["xhigh", "max"]);
267
219
  const DEFAULT_RESOURCE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
268
220
  export const ENV_LIFETIME_SUSPEND_MARGIN_MS = 60_000;
269
221
  export const USAGE_WINDOW_REAP_MARGIN_MS = 60 * 60 * 1000;
270
- function buildUsageGovernance(windows, deps, principal, sessionId) {
271
- if (windows === undefined || windows.length === 0)
272
- return undefined;
273
- const store = deps.usageWindowStore;
274
- if (store === undefined) {
275
- deps.onError?.(new Error("RunnerDeps.usageWindows is set but INACTIVE: no `usageWindowStore` is wired, so cross-task usage cannot be counted and no window can be enforced. Wire InMemoryUsageWindowStore (process-local) or FileUsageWindowStore (restart-surviving)."), { phase: "config", sessionId });
276
- return undefined;
277
- }
278
- const key = principal || GLOBAL_USAGE_KEY;
279
- const governsCost = windowsGovernCost(windows);
280
- let chargedTokens = 0;
281
- let chargedCostMicroUsd = 0;
282
- let announcedCostGap = false;
283
- let recordedUnpricedGap = false;
284
- return {
285
- key,
286
- governsCost,
287
- async check(now) {
288
- const readings = await store.read(key, windows, now);
289
- if (!announcedCostGap && readings.some((r) => r.costUnknown === true)) {
290
- announcedCostGap = true;
291
- deps.onError?.(new Error(`the usage window for ledger key ${JSON.stringify(key)} holds spend that nothing could price, so its maxCostUsd ceiling is being evaluated against a LOWER BOUND until that charge ages out of the window. Price every model this deployment can reach (including degrade targets and the compaction model).`), { phase: "config", sessionId });
292
- }
293
- return usageRetryAfterMs(readings, windows);
294
- },
295
- async commit(cumulativeTokens, cumulativeCostMicroUsd, now) {
296
- if (!governsCost) {
297
- const delta = cumulativeTokens - chargedTokens;
298
- if (delta <= 0)
299
- return;
300
- await store.charge(key, delta, now, windows);
301
- chargedTokens = cumulativeTokens;
302
- return;
303
- }
304
- if (cumulativeCostMicroUsd === undefined) {
305
- const tokenDelta = cumulativeTokens - chargedTokens;
306
- if (tokenDelta > 0 || !recordedUnpricedGap) {
307
- await store.charge(key, Math.max(0, tokenDelta), now, windows, null);
308
- chargedTokens = Math.max(chargedTokens, cumulativeTokens);
309
- recordedUnpricedGap = true;
310
- }
311
- const e = new Error("a deployment usage window declares maxCostUsd, but this run's spend has NO cost figure (RB-368 unpriced: a model served without a RunnerDeps.pricing entry or a Model.cost declaration). Refused rather than charged a fabricated 0 — the money ceiling would have silently stopped applying. Price every model this run can reach, or drop maxCostUsd from the window.");
312
- e.code = "config.usage_window_unpriced";
313
- throw e;
314
- }
315
- const tokenDelta = cumulativeTokens - chargedTokens;
316
- const costDelta = cumulativeCostMicroUsd - chargedCostMicroUsd;
317
- if (tokenDelta <= 0 && costDelta <= 0)
318
- return;
319
- await store.charge(key, Math.max(0, tokenDelta), now, windows, Math.max(0, costDelta));
320
- chargedTokens = cumulativeTokens;
321
- chargedCostMicroUsd = cumulativeCostMicroUsd;
322
- },
323
- };
324
- }
325
222
  export function resolveEnvLifetimeExpiry(env, observedAt) {
326
223
  const lifetimeMs = env.lifetimeMs;
327
224
  if (lifetimeMs === undefined)
@@ -338,22 +235,14 @@ export function resolveEnvLifetimeExpiry(env, observedAt) {
338
235
  return { unanchored: true };
339
236
  return { expiresAt: anchor + lifetimeMs };
340
237
  }
341
- function isLiveApproverSeat(onAsk) {
342
- return typeof onAsk === "function";
343
- }
344
238
  const DEFAULT_UNATTENDED_APPROVAL_TTL_MS = 30 * 24 * 60 * 60 * 1000;
345
239
  function sanitizedTtlMs(ttlMs) {
346
240
  if (ttlMs === undefined)
347
241
  return undefined;
348
242
  return Number.isFinite(ttlMs) && ttlMs > 0 ? ttlMs : DEFAULT_RESOURCE_TTL_MS;
349
243
  }
350
- const ungatedWarnedShapes = new WeakMap();
351
244
  const advisedPolicyNames = new WeakMap();
352
245
  const ADVISED_KEYS_CAP = 64;
353
- export const DEFAULT_IRREVERSIBLE_SCOPE = "irreversible";
354
- export function checkpointScopeOf(spec) {
355
- return spec.durableApproval?.scope || spec.principal || DEFAULT_IRREVERSIBLE_SCOPE;
356
- }
357
246
  class ParkRefusal extends Error {
358
247
  constructor(message, options) {
359
248
  super(message, options);
@@ -362,7 +251,7 @@ class ParkRefusal extends Error {
362
251
  }
363
252
  export { resolveCheckpointStore } from "../checkpoint-store.js";
364
253
  export { isFableFamilyModelId, resolveAttachmentsConfig, resolveModelPromptTraits, resolveTaskLimits } from "./prepare-config-doors.js";
365
- export { rebaseWorkspacePath, rebaseWorkspacePathAcross } from "./prepare-workspace-restore.js";
254
+ export { rebaseWorkspacePath, rebaseWorkspacePathAcross } from "./workspace-path.js";
366
255
  function buildMicroCompactState(deps, model, sessionId, runId, offloadStore, knob) {
367
256
  const triggerWindow = resolveTriggerWindow(model);
368
257
  if (triggerWindow.clamped) {
@@ -473,226 +362,6 @@ export async function runGuardChain(args) {
473
362
  const t = applyGuardTrim(working, anchoredWorking);
474
363
  return { working, trimmed: t.trimmed, trimDroppedMessages: t.dropped };
475
364
  }
476
- function restoredAbsPathsOf(restored, root) {
477
- const out = [];
478
- for (const key of [...restored.filesChanged, ...restored.failed.map((f) => f.path)]) {
479
- const abs = resolveTrackKey(root, key);
480
- if (abs.ok)
481
- out.push(abs.abs);
482
- }
483
- return out;
484
- }
485
- function resolveWiredFileHistoryStore(candidate) {
486
- if (candidate === undefined)
487
- return undefined;
488
- const verbs = ["trackEdit", "annulTrack", "snapshot", "restore", "canRestore", "reap", "adoptScope"];
489
- const verbOf = (v) => {
490
- try {
491
- return candidate[v];
492
- }
493
- catch {
494
- return undefined;
495
- }
496
- };
497
- const probeable = (typeof candidate === "object" && candidate !== null) || typeof candidate === "function";
498
- const missing = probeable ? verbs.filter((v) => typeof verbOf(v) !== "function") : [...verbs];
499
- if (missing.length > 0) {
500
- const shape = candidate === null ? "null" : probeable ? "a value that is not a FileHistoryStore" : `a ${typeof candidate}`;
501
- const e = new Error(`RunnerDeps.fileHistoryStore was wired with ${shape} — missing contract verbs: ${missing.join(", ")}. Refusing prepare rather than half-wiring the rewind seat (a present-but-unusable store makes every Edit/Write/NotebookEdit fail its first-touch backup while restore requests report "no store wired"). Omit the key to run without file history.`);
502
- e.code = "config.invalid_file_history_store";
503
- throw e;
504
- }
505
- return candidate;
506
- }
507
- const LEGACY_PROBE_TIMEOUT_MS = 2_000;
508
- const LEGACY_PROBE_TIMED_OUT = Symbol("legacy-rewind-probe-timeout");
509
- async function legacyRewindEpochTail(deps, sessionId, entryId, form) {
510
- const probe = deps.legacyRewindBoundaryProbe;
511
- let verdict;
512
- if (probe !== undefined) {
513
- try {
514
- let timer;
515
- const timeout = new Promise((resolve) => {
516
- timer = setTimeout(() => resolve(LEGACY_PROBE_TIMED_OUT), LEGACY_PROBE_TIMEOUT_MS);
517
- });
518
- try {
519
- verdict = await Promise.race([Promise.resolve(probe({ sessionId, entryId })), timeout]);
520
- }
521
- finally {
522
- clearTimeout(timer);
523
- }
524
- if (verdict === LEGACY_PROBE_TIMED_OUT) {
525
- verdict = undefined;
526
- try {
527
- deps.onError?.(new Error(`RunnerDeps.legacyRewindBoundaryProbe did not answer within ${LEGACY_PROBE_TIMEOUT_MS}ms while explaining a rewind refusal — the refusal stands and names both possibilities`), { phase: "rewind", sessionId });
528
- }
529
- catch {
530
- }
531
- }
532
- }
533
- catch (err) {
534
- verdict = undefined;
535
- try {
536
- deps.onError?.(new Error(`RunnerDeps.legacyRewindBoundaryProbe threw while explaining a rewind refusal (${err.message}) — the refusal stands and names both possibilities`), { phase: "rewind", sessionId });
537
- }
538
- catch {
539
- }
540
- }
541
- if (verdict !== undefined && typeof verdict !== "boolean") {
542
- try {
543
- deps.onError?.(new Error(`RunnerDeps.legacyRewindBoundaryProbe answered ${JSON.stringify(String(verdict))} for entry "${entryId}" — only true/false/undefined are meaningful, so the refusal names both possibilities instead of trusting it`), { phase: "rewind", sessionId });
544
- }
545
- catch {
546
- }
547
- verdict = undefined;
548
- }
549
- }
550
- if (verdict === true) {
551
- return `This deployment's legacy-epoch probe reports that entry "${entryId}" DOES carry a boundary from the retired whole-tree snapshot era, which this engine's per-edited-file seat does not restore — the two epochs are not interchangeable (a whole-tree manifest carries no tracked-set provenance, so replaying it here would converge files this session never edited). Restore it with the whole-tree-era tooling this deployment kept, or re-run the turns under the current engine.`;
552
- }
553
- if (verdict === false) {
554
- return `This deployment's legacy-epoch probe reports no whole-tree-era boundary for entry "${entryId}" either, so the boundary simply does not exist: its turns ran without a RunnerDeps.fileHistoryStore wired, or it was reaped.`;
555
- }
556
- return form === "keyed"
557
- ? `Boundaries are keyed by a turn's INITIAL entry and exist only for turns that ran with a RunnerDeps.fileHistoryStore wired; a boundary from the retired whole-tree snapshot era is not restorable by this seat, and reaped boundaries have none.`
558
- : `Either the boundary does not exist (its turns ran without a RunnerDeps.fileHistoryStore, or it was reaped), or it was produced in the retired whole-tree snapshot era, which this engine's per-edited-file seat does not restore.`;
559
- }
560
- const FILE_HISTORY_BOUNDARY_TIMEOUT_MS = 30_000;
561
- const FILE_HISTORY_CARRY_MEMO = new WeakMap();
562
- async function adoptForkedFileHistory(store, session, sessionId, onError) {
563
- const memo = FILE_HISTORY_CARRY_MEMO.get(store) ?? { settled: new Set(), disclosed: new Set(), inFlight: new Map() };
564
- FILE_HISTORY_CARRY_MEMO.set(store, memo);
565
- const discloseOnce = (err) => {
566
- if (memo.disclosed.has(sessionId))
567
- return;
568
- memo.disclosed.add(sessionId);
569
- onError?.(err, { phase: "rewind", sessionId });
570
- };
571
- try {
572
- if (memo.settled.has(sessionId))
573
- return;
574
- const forkedFrom = (await session.getMetadata()).forkedFrom;
575
- if (forkedFrom === undefined || forkedFrom === sessionId)
576
- return;
577
- const shared = memo.inFlight.get(sessionId);
578
- if (shared !== undefined)
579
- return await shared;
580
- if (memo.settled.has(sessionId))
581
- return;
582
- const attempt = store.adoptScope(forkedFrom, sessionId).then((carried) => {
583
- if (carried.ok || carried.error.code === "conflict") {
584
- memo.settled.add(sessionId);
585
- return;
586
- }
587
- discloseOnce(new Error(`file-history fork carry from session "${forkedFrom}" failed (${carried.error.code}): ${carried.error.message} — this forked session starts with an EMPTY rewind history, so boundaries recorded before the fork are not restorable from it (the run itself proceeds; the carry is retried on this session's next turn while its scope is still empty)`));
588
- }, (err) => discloseOnce(err));
589
- memo.inFlight.set(sessionId, attempt);
590
- try {
591
- await attempt;
592
- }
593
- finally {
594
- memo.inFlight.delete(sessionId);
595
- }
596
- }
597
- catch (err) {
598
- try {
599
- discloseOnce(err);
600
- }
601
- catch {
602
- }
603
- }
604
- }
605
- function createFileHistoryBoundarySeat(opts) {
606
- let inFlight;
607
- let begun = false;
608
- return {
609
- begin(entryId) {
610
- if (begun)
611
- return;
612
- begun = true;
613
- const ac = new AbortController();
614
- let timer;
615
- const timeout = new Promise((resolve) => {
616
- timer = setTimeout(() => {
617
- ac.abort();
618
- resolve({ ok: false });
619
- }, FILE_HISTORY_BOUNDARY_TIMEOUT_MS);
620
- });
621
- let work;
622
- try {
623
- work = opts.store.snapshot(opts.sessionId, entryId, opts.env, opts.root, ac.signal);
624
- }
625
- catch (err) {
626
- work = Promise.resolve({ ok: false, error: { code: "snapshot_failed", message: `history store threw synchronously: ${err.message}` } });
627
- }
628
- work.catch(() => { });
629
- inFlight = Promise.race([
630
- work.then((r) => (r.ok ? undefined : `boundary capture failed (${r.error.code}): ${r.error.message}`)),
631
- timeout.then(() => `boundary capture timed out after ${FILE_HISTORY_BOUNDARY_TIMEOUT_MS / 1000}s (fenced — the attempt can never publish)`),
632
- ])
633
- .then((failure) => {
634
- if (failure !== undefined) {
635
- try {
636
- opts.onError?.(new Error(`file-history ${failure} — this turn has no boundary and cannot be rewound to (the turn itself proceeds)`), { phase: "rewind", sessionId: opts.sessionId });
637
- }
638
- catch {
639
- }
640
- }
641
- })
642
- .catch((err) => {
643
- try {
644
- opts.onError?.(err, { phase: "rewind", sessionId: opts.sessionId });
645
- }
646
- catch {
647
- }
648
- })
649
- .finally(() => clearTimeout(timer));
650
- },
651
- async settle() {
652
- if (inFlight !== undefined)
653
- await inFlight;
654
- },
655
- };
656
- }
657
- async function resolveFileHistoryCoordinates(enabled, env, taskRoot, lineage, sessionId) {
658
- if (!enabled)
659
- return { historyRoot: taskRoot, historyScope: sessionId, historyFs: "" };
660
- const canonRoot = await env.canonicalPath(taskRoot);
661
- const historyRoot = canonRoot.ok ? canonRoot.value : taskRoot;
662
- const historyFs = fileHistoryFilesystemIdentity(env);
663
- return { historyRoot, historyScope: resolveFileHistoryScope(lineage, historyRoot, historyFs, sessionId), historyFs };
664
- }
665
- export function resolveFileHistoryScope(lineage, historyRoot, historyFs, sessionId) {
666
- if (lineage === undefined || lineage.scope === "" || lineage.root !== historyRoot || lineage.fs !== historyFs)
667
- return sessionId;
668
- return lineage.scope;
669
- }
670
- const envFilesystemTokens = new WeakMap();
671
- let envFilesystemSeq = 0;
672
- export function fileHistoryFilesystemIdentity(env) {
673
- if (isRemoteExecutionEnv(env)) {
674
- try {
675
- const h = env.workspaceHandle();
676
- return JSON.stringify(["remote", h.provider, h.sandboxId, h.deviceId !== undefined && h.deviceId !== "" ? h.deviceId : null]);
677
- }
678
- catch {
679
- }
680
- }
681
- else if (env.hostLocalPaths === true || (env.hostLocalPaths === undefined && env instanceof NodeExecutionEnv)) {
682
- return "host";
683
- }
684
- let token = envFilesystemTokens.get(env);
685
- if (token === undefined) {
686
- token = `instance:${++envFilesystemSeq}`;
687
- envFilesystemTokens.set(env, token);
688
- }
689
- return token;
690
- }
691
- function childScopeRewindRefusal(rootScope, target) {
692
- const e = new Error(`rewind-files: this run is a delegated child recording its edits into the ROOT session "${rootScope}"'s file history, so restoring files to entry "${target}" is the root session's action (rewindFilesTo / resumeAt+restoreFiles on that session), not the child's — the files were NOT rewound`);
693
- e.code = "rewind.child_scope_unsupported";
694
- return e;
695
- }
696
365
  export function gatedCallIdOf(p) {
697
366
  if (p.suspendRef.token !== undefined)
698
367
  return p.suspendRef.gatedCallId;
@@ -717,9 +386,6 @@ function publishCommittedSuspend(refs, token, gate, scope, remoteHandle, checkpo
717
386
  ref.restoreMode = remoteHandle.restoreMode === "park_only" ? "park_only" : "snapshot";
718
387
  ref.scope = scope;
719
388
  }
720
- function hasConversationContent(branch) {
721
- return branch.some((e) => e.type === "message" || e.type === "custom_message" || e.type === "compaction");
722
- }
723
389
  export function effectiveDelegationFacts(internals, seedIsDelegatedChild) {
724
390
  const isDelegatedChild = internals?.isDelegatedChild !== undefined ? internals.isDelegatedChild === true : seedIsDelegatedChild === true;
725
391
  return { isDelegatedChild, isNonForkChild: isDelegatedChild && internals?.insideFork !== true };
@@ -906,19 +572,6 @@ function createDenyObserverNotifier(hooks, hookTimeoutMs, signal, report) {
906
572
  }
907
573
  };
908
574
  }
909
- function mcpRevocationWiring(deps, runId) {
910
- if (deps.mcpRevocations === undefined)
911
- return undefined;
912
- const ledger = deps.mcpRevocations;
913
- return {
914
- isRevoked: (name) => ledger.isRevoked(name),
915
- onProbeFailure: (e) => deliverEngineNotice(deps.onNotice, {
916
- code: "mcp.revocation_probe_failed",
917
- message: `the mcpRevocations.isRevoked probe threw — MCP dispatch fails OPEN (no server treated as revoked) until the probe recovers: ${e instanceof Error ? e.message : String(e)}`,
918
- detail: { message: e instanceof Error ? e.message : String(e), runId },
919
- }),
920
- };
921
- }
922
575
  async function forgetQuietly(sessions, sessionId) {
923
576
  try {
924
577
  if (sessions.forget)
@@ -1065,13 +718,6 @@ function makeRuleOffersOf(cfg) {
1065
718
  : { ruleOffersAbsence: "lane_cannot_speak" };
1066
719
  };
1067
720
  }
1068
- function cwdConflictsRestoreError(requestedCwd) {
1069
- const e = new Error(`RunInternals.requestedCwd ("${requestedCwd}") cannot be combined with a checkpoint workspace restore — ` +
1070
- `the restored workspace's own mount path is authoritative for the task root, so a requested cwd on this leg ` +
1071
- `would be ignored (or worse, probed against an unrestored environment). Drop requestedCwd on resume legs.`);
1072
- e.code = "config.cwd_conflicts_restore";
1073
- return e;
1074
- }
1075
721
  async function adoptReminderMark(session, sessionId, seedMark, spawnMark, onError) {
1076
722
  const sessionReminderMark = await (async () => {
1077
723
  try {
@@ -1099,28 +745,6 @@ async function adoptReminderMark(session, sessionId, seedMark, spawnMark, onErro
1099
745
  }
1100
746
  return { reminderMark, reminderDisclosureCounts: {} };
1101
747
  }
1102
- async function derivedRouteFallsBack(args) {
1103
- try {
1104
- if (sameRouteIdentity(args.derived, args.primary))
1105
- return false;
1106
- const verdict = await adjudicateDerivedRoute({ brain: args.brain, model: args.derived, getApiKeyAndHeaders: args.getApiKeyAndHeaders });
1107
- if (verdict === undefined || verdict.ok)
1108
- return false;
1109
- deliverEngineNotice(args.onNotice, fallbackToPrimaryNotice({ seat: args.seat, from: args.derived.id, to: args.primary.id, verdict, ...(args.sessionId !== undefined ? { sessionId: args.sessionId } : {}), ...(args.runId !== undefined ? { runId: args.runId } : {}) }));
1110
- return true;
1111
- }
1112
- catch {
1113
- return false;
1114
- }
1115
- }
1116
- function mintPlacementRootSessionId(placementRootResolved) {
1117
- if (placementRootResolved === "") {
1118
- const e = new Error("placement root resolved EMPTY (internals.placementRoot / internals.rootSessionId carries an empty string) — an empty fixed point cannot key a placement lookup; fix the spawning lane instead of defaulting around it.");
1119
- e.code = "config.placement_root_invalid";
1120
- throw e;
1121
- }
1122
- return placementRootResolved;
1123
- }
1124
748
  function restorePlacementRoot(internals, seedPlacementRoot) {
1125
749
  if (internals?.placementRoot !== undefined)
1126
750
  return internals;
@@ -1135,21 +759,6 @@ export function placementValueOrAbsent(value) {
1135
759
  function stampPlacementRootSessionId(placementRootResolved) {
1136
760
  return placementValueOrAbsent(placementRootResolved);
1137
761
  }
1138
- function explicitlyDeferredMemoryTrio(mounted, roster, deferNames) {
1139
- return mounted ? MEMORY_ENGINE_TOOL_NAMES.filter((n) => roster.some((t) => t.name === n) && (deferNames ?? []).includes(n)) : [];
1140
- }
1141
- function memoryGroupRetractionSet(builtinDeferPairNames, engineTrioInPlay) {
1142
- return new Set([...builtinDeferPairNames, ...(engineTrioInPlay ? MEMORY_ENGINE_TOOL_NAMES : [])]);
1143
- }
1144
- function assembleParentCaptureState(o, i, ctl, ancestors) {
1145
- const build = (optedOut, indeterminate) => ({
1146
- optedOut: optedOut === true,
1147
- indeterminate: indeterminate === true,
1148
- ...(ctl !== undefined ? { controlDir: ctl } : {}),
1149
- ancestors,
1150
- });
1151
- return o instanceof Promise || i instanceof Promise ? Promise.all([o, i]).then(([ov, iv]) => build(ov, iv)) : build(o, i);
1152
- }
1153
762
  function refuseRequireExistingWithoutSession(spec) {
1154
763
  if (spec.requireExistingSession && !spec.sessionId) {
1155
764
  const e = new Error(`requireExistingSession requires a sessionId — cannot require an existing session without one (design/114 Phase3)`);
@@ -1157,31 +766,6 @@ function refuseRequireExistingWithoutSession(spec) {
1157
766
  throw e;
1158
767
  }
1159
768
  }
1160
- function screenAutoModeCap(caps, onError, sessionId) {
1161
- if (caps === undefined)
1162
- return { caps, faulted: false };
1163
- if (typeof caps !== "object" || caps === null || Array.isArray(caps)) {
1164
- onError?.(new Error(`runtimeCapsResolver returned a non-record value (${JSON.stringify(caps)}) — read as a resolver fault: every per-principal capability is DENIED for this run (auto mode is not armed; asks flow the original chain). Return a RuntimeCaps object or undefined.`), { phase: "config", sessionId });
1165
- return { caps: { allowWorkflows: false, allowFork: false, autoMode: false }, faulted: true };
1166
- }
1167
- const record = caps;
1168
- if (record.autoMode === undefined || typeof record.autoMode === "boolean")
1169
- return { caps: record, faulted: false };
1170
- onError?.(new Error(`runtimeCapsResolver returned a non-boolean autoMode (${JSON.stringify(record.autoMode)}) — read as a resolver fault: auto mode is DENIED for this run (the classifier is not armed; asks flow the original chain). Return true, false, or omit the key.`), { phase: "config", sessionId });
1171
- return { caps: { ...record, autoMode: false }, faulted: true };
1172
- }
1173
- function mintPersistedArming(am, laneRule, classifierSystemPrompt, onError, sessionId) {
1174
- const arming = autoModeArmingRecipeOf(laneRule ? { ...am, crossSessionMessagesRule: true } : am, {
1175
- promptDigest: `apv1:${createHash("sha256").update(classifierSystemPrompt).digest("hex")}`,
1176
- });
1177
- if (arming === undefined) {
1178
- onError?.(new Error("RunnerDeps.autoMode.persistArming is set but the auto-mode face did not canonicalize into an arming recipe " +
1179
- "(a rule list carrying a non-string, or a non-finite/out-of-range timeoutMs / failureThreshold / window bound) — " +
1180
- "NO arming recipe is recorded on this run's parked constraint chain, and a cross-process redemption keeps the " +
1181
- "conservative behavior (the ancestor classifier answers unavailable and the inherited ask flows to a human)"), { phase: "config", sessionId });
1182
- }
1183
- return arming;
1184
- }
1185
769
  function autoModeLatchHealthy(d) {
1186
770
  try {
1187
771
  return d.breakerOpen() !== true && typeof d.consecutiveFailures === "function" && d.consecutiveFailures() === 0;
@@ -1190,15 +774,6 @@ function autoModeLatchHealthy(d) {
1190
774
  return false;
1191
775
  }
1192
776
  }
1193
- function autoModeArmReasonOf(intent, facePresent, capsAutoMode, capsFaulted) {
1194
- if (!intent)
1195
- return "no_intent";
1196
- if (!facePresent)
1197
- return "no_face";
1198
- if (capsAutoMode === false)
1199
- return capsFaulted ? "resolver_fault" : "denied";
1200
- return "armed";
1201
- }
1202
777
  function validateInputFaultNotice(onError, sessionId) {
1203
778
  return ({ toolName, toolCallId, error }) => onError?.(new Error(`tool input pre-validation (validateInput) threw for ${toolName} (call ${toolCallId}) — read as no verdict; the call proceeded to the gate: ${error instanceof Error ? error.message : String(error)}`), { phase: "hook", sessionId });
1204
779
  }
@@ -1210,50 +785,6 @@ function refuseInvalidAutoModeSeat(spec) {
1210
785
  }
1211
786
  return spec.autoModeRequested === true;
1212
787
  }
1213
- function announceDurableGateUnavailable(args) {
1214
- if (!args.forceDurableGate || args.storeWired)
1215
- return;
1216
- const cause = args.taskStoreNull ? "task_store_null" : "no_deployment_store";
1217
- const inheritedContentMandate = (args.parentConstraints ?? []).some((pc) => pc.contentMandate === true);
1218
- const liveQuestionFace = args.liveQuestionFace && !inheritedContentMandate;
1219
- deliverEngineNotice(args.onNotice, {
1220
- code: "config.durable_gate_unavailable",
1221
- message: `Durable approval gate mandated but unavailable: the runtime entitlement forceDurableGate is in force for this ` +
1222
- `principal, but ${cause === "task_store_null" ? "this task disabled the checkpoint store (checkpointStore: null)" : "this deployment wired no checkpoint store"}, ` +
1223
- `so no interactive ask of this task can be parked for a durable answer. Asks resolve on the live chain instead ` +
1224
- `(${args.liveApprover ? "a live approver answers permission asks in-stream" : "permission asks are denied fail-closed — no live approver is wired"}; ` +
1225
- `${liveQuestionFace ? "a live question face answers AskUserQuestion in-stream" : args.liveQuestionFace ? "AskUserQuestion is withheld from the live face by an inherited durable mandate and refused" : "AskUserQuestion has no live face and is refused"}) ` +
1226
- `and NO durable approval record is written. ${cause === "task_store_null" ? "Run this task with its store, or withdraw the entitlement for principals whose runs are machine-started." : "Wire a checkpoint store on this deployment, or withdraw the entitlement for principals served here."}`,
1227
- detail: {
1228
- sessionId: args.sessionId,
1229
- runId: args.runId,
1230
- ...(args.principal ? { principal: args.principal } : {}),
1231
- cause,
1232
- liveApprover: args.liveApprover,
1233
- liveQuestionFace,
1234
- },
1235
- });
1236
- }
1237
- function buildTier3ReviveSpawn(delegationForRevive, enrichSpecToolCtx) {
1238
- if (delegationForRevive === undefined)
1239
- return undefined;
1240
- return async (req) => {
1241
- const out = await delegationForRevive.execute({
1242
- description: req.row.description ?? "revived teammate",
1243
- prompt: req.prompt,
1244
- run_in_background: true,
1245
- ...(req.row.agentType !== undefined ? { subagent_type: req.row.agentType } : {}),
1246
- ...(req.row.name !== undefined ? { name: req.row.name } : {}),
1247
- ...(req.row.model !== undefined ? { model: req.row.model } : {}),
1248
- }, enrichSpecToolCtx({ toolCallId: `rv-${randomBytes(8).toString("hex")}`, reviveClaim: { row: req.row, rev: req.rev, ...(req.peerSeed !== undefined ? { peerSeed: req.peerSeed } : {}) } }));
1249
- const o = (typeof out === "string" ? { content: out } : out);
1250
- return {
1251
- content: typeof o.content === "string" ? o.content : JSON.stringify(o.content),
1252
- ...(o.isError === true ? { isError: true } : {}),
1253
- ...(o.details !== undefined ? { details: o.details } : {}),
1254
- };
1255
- };
1256
- }
1257
788
  export async function prepareTask(spec, deps, sessions, resume, internals, runnerSelf, runIdSink) {
1258
789
  const runId = uuidv7();
1259
790
  if (runIdSink !== undefined)
@@ -1305,112 +836,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1305
836
  e.code = "config.extra_body_output_cap";
1306
837
  throw e;
1307
838
  }
1308
- const nestedStats = { tokens: 0, turns: 0, tasks: 0, costMicroUsd: 0, anyUnpriced: false };
1309
- if (resume) {
1310
- nestedStats.tokens = resume.seed.nestedStats.tokens;
1311
- nestedStats.turns = resume.seed.nestedStats.turns;
1312
- nestedStats.tasks = resume.seed.nestedStats.tasks;
1313
- nestedStats.costMicroUsd = resume.seed.nestedStats.costMicroUsd;
1314
- nestedStats.anyUnpriced = resume.seed.nestedStats.anyUnpriced ?? true;
1315
- }
1316
- const reportUsage = (u) => {
1317
- nestedStats.tokens += u.tokens;
1318
- nestedStats.turns += u.turns;
1319
- nestedStats.tasks += u.tasks;
1320
- nestedStats.costMicroUsd += u.costMicroUsd ?? 0;
1321
- if (u.costMicroUsd === undefined)
1322
- nestedStats.anyUnpriced = true;
1323
- };
1324
- const explicitGlobalThreshold = spec.toolResultThresholdChars ?? deps.toolResultThresholdChars;
1325
- const offloadThreshold = explicitGlobalThreshold ?? DEFAULT_TOOL_RESULT_THRESHOLD_CHARS;
1326
- const offloadEnabled = Number.isFinite(offloadThreshold) && offloadThreshold > 0;
1327
- const rawOffloadStore = offloadEnabled ? (deps.toolResultStore ?? new InMemoryToolResultStore()) : undefined;
1328
- const offloadStore = rawOffloadStore instanceof RunnerSharedToolResultStore
1329
- ? new ScopedToolResultStore(rawOffloadStore, offloadScope)
1330
- : rawOffloadStore;
1331
- const offloadReachableToolsRef = {};
1332
- const maybeOffload = (tool, perTool) => {
1333
- if (!offloadStore || perTool?.offload === false)
1334
- return tool;
1335
- return withToolResultOffload(tool, offloadStore, perTool?.offloadThresholdChars ?? offloadThreshold, sessionId, () => offloadReachableToolsRef.current?.());
1336
- };
1337
- const firstPartyOffload = (tool) => {
1338
- const policy = firstPartyOffloadPolicy(tool.name);
1339
- if (policy.offload === false)
1340
- return maybeOffload(tool, policy);
1341
- return maybeOffload(tool, explicitGlobalThreshold === undefined ? policy : undefined);
1342
- };
1343
- const remoteToolOffload = (tool) => {
1344
- if (explicitGlobalThreshold !== undefined)
1345
- return maybeOffload(tool);
1346
- return maybeOffload(tool, { offloadThresholdChars: tool.mcpMaxResultSizeChars ?? 50_000 });
1347
- };
839
+ const { nestedStats, reportUsage, offloadStore, offloadReachableToolsRef, maybeOffload, firstPartyOffload, remoteToolOffload } = prepareOffloadWrappers({ spec, deps, resume, offloadScope, sessionId });
1348
840
  if (offloadStore && (spec.tools ?? []).some((t) => t.name === OFFLOAD_TOOL_NAME)) {
1349
841
  const e = new Error(`Tool name "${OFFLOAD_TOOL_NAME}" is reserved when large-result offload is enabled.`);
1350
842
  e.code = "config.reserved_tool_name";
1351
843
  await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (reserved-name leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
1352
844
  throw e;
1353
845
  }
1354
- let ownedEnv;
1355
- let ownedEnvObservedAt;
1356
- let mcp;
1357
- let a2a;
1358
- if (internals?.requestedCwd !== undefined && deps.executionEnvFactory === undefined) {
1359
- await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (cwd-unsupported leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
1360
- const e = new Error(`Agent cwd "${internals.requestedCwd}" cannot take effect: this deployment has no executionEnvFactory (a static execution environment cannot be re-rooted per agent). Drop the cwd parameter or deploy a factory.`);
1361
- e.code = "config.cwd_unsupported";
1362
- throw e;
1363
- }
1364
- if (internals?.requestedCwd !== undefined && resume?.workspaceHandle !== undefined) {
1365
- await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (cwd-conflicts-restore leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
1366
- throw cwdConflictsRestoreError(internals.requestedCwd);
1367
- }
1368
- try {
1369
- ownedEnv = deps.executionEnvFactory
1370
- ? await deps.executionEnvFactory({
1371
- sessionId, placementRootSessionId: mintPlacementRootSessionId(placementRootResolved),
1372
- taskId: spec.taskId,
1373
- ...(internals?.isolation ? { isolation: internals.isolation } : {}),
1374
- ...(internals?.parentCwd ? { parentCwd: internals.parentCwd } : {}),
1375
- })
1376
- : undefined;
1377
- if (ownedEnv !== undefined)
1378
- ownedEnvObservedAt = Date.now();
1379
- }
1380
- catch (factoryErr) {
1381
- await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (env-factory-throw leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
1382
- throw factoryErr;
1383
- }
1384
- const executionEnv = ownedEnv ?? deps.executionEnv ?? new StubExecutionEnv();
846
+ const rollback = createRollbackStack((e) => deps.onError?.(e, { phase: "config", sessionId }));
847
+ const { executionEnv, ownedEnvSeat, ownedEnvObservedAt, handsEnabled, taskRootInitial } = await prepareExecutionEnv({ deps, spec, internals, resume, placementRootResolved, sessionId, forgetOnThrow, rollback });
1385
848
  const externalContentTargetActive = executionEnv.externalContentTarget === true || resume?.seed.externalContentTarget === true;
1386
- if (internals?.requestedCwd !== undefined) {
1387
- const canon = async (p) => {
1388
- const r = await executionEnv.canonicalPath(p);
1389
- return r.ok ? r.value : p;
1390
- };
1391
- const want = await canon(internals.requestedCwd);
1392
- const rejectCwd = async (message, code) => {
1393
- await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (cwd-reject leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
1394
- await settleTeardownLeg(() => (ownedEnv && hasDestroy(ownedEnv) ? ownedEnv.destroy() : undefined), "ownedEnv.destroy (cwd-reject leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
1395
- const e = new Error(message);
1396
- e.code = code;
1397
- throw e;
1398
- };
1399
- if (deps.rootPath !== undefined) {
1400
- const root = await canon(deps.rootPath);
1401
- const sep = root.includes("\\") && !root.includes("/") ? "\\" : "/";
1402
- const rootPrefix = root.endsWith(sep) ? root : root + sep;
1403
- if (want !== root && !want.startsWith(rootPrefix)) {
1404
- await rejectCwd(`Agent cwd "${internals.requestedCwd}" is outside this deployment's declared root "${deps.rootPath}" — the cwd must be located within the deployment root.`, "config.cwd_outside_root");
1405
- }
1406
- }
1407
- const got = await canon(executionEnv.cwd);
1408
- if (want !== got) {
1409
- await rejectCwd(`Agent cwd "${internals.requestedCwd}" was not honored by the execution environment (it rooted at "${executionEnv.cwd}") — failing loud instead of running the agent in the wrong tree.`, "config.cwd_not_honored");
1410
- }
1411
- }
1412
- const handsEnabled = ownedEnv !== undefined || deps.executionEnv !== undefined;
1413
- const taskRootInitial = internals?.isolation === "worktree" || internals?.requestedCwd !== undefined || !deps.rootPath ? executionEnv.cwd : deps.rootPath;
1414
849
  const abortController = new AbortController();
1415
850
  if (spec.signal?.aborted)
1416
851
  abortController.abort();
@@ -1426,15 +861,15 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1426
861
  e.code = "isolation.unavailable";
1427
862
  throw e;
1428
863
  };
1429
- if (ownedEnv === undefined) {
864
+ if (ownedEnvSeat.current === undefined) {
1430
865
  failIsolation("no executionEnvFactory is configured (a static/shared execution env cannot mint a per-agent worktree)");
1431
866
  }
1432
- else if (ownedEnv === deps.executionEnv) {
1433
- ownedEnv = undefined;
867
+ else if (ownedEnvSeat.current === deps.executionEnv) {
868
+ ownedEnvSeat.current = undefined;
1434
869
  failIsolation("the executionEnvFactory returned the shared static env instead of a worktree-rooted env");
1435
870
  }
1436
- else if (isRemoteExecutionEnv(ownedEnv)) {
1437
- if (ownedEnv.capabilities.isolation !== true) {
871
+ else if (isRemoteExecutionEnv(ownedEnvSeat.current)) {
872
+ if (ownedEnvSeat.current.capabilities.isolation !== true) {
1438
873
  failIsolation("the executionEnvFactory returned a remote env without capabilities.isolation — a non-isolated remote target is a shared remote checkout, not a per-agent worktree");
1439
874
  }
1440
875
  }
@@ -1448,637 +883,25 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1448
883
  }
1449
884
  };
1450
885
  if (deps.rootPath !== undefined || internals.parentCwd !== undefined) {
1451
- const envCwd = canonical(ownedEnv.cwd, "the factory env's cwd");
886
+ const envCwd = canonical(ownedEnvSeat.current.cwd, "the factory env's cwd");
1452
887
  if (deps.rootPath !== undefined && envCwd === canonical(deps.rootPath, "the shared base root")) {
1453
- failIsolation("the executionEnvFactory rooted the env at the shared base root instead of a worktree");
1454
- }
1455
- if (internals.parentCwd !== undefined && envCwd === canonical(internals.parentCwd, "the parent's cwd")) {
1456
- failIsolation("the executionEnvFactory rooted the env at the parent's cwd instead of a worktree");
1457
- }
1458
- }
1459
- }
1460
- }
1461
- const { taskRootFinal, rebaseRestoredPath } = await prepareWorkspaceRestore({ ownedEnv, deps, spec, internals, resume, taskRootInitial, abortController, sessionId });
1462
- const rewindNotes = [];
1463
- const onTrackFailure = deps.onTrackFailure === undefined ? "refuse" : deps.onTrackFailure;
1464
- if (onTrackFailure !== "refuse" && onTrackFailure !== "proceed-unprotected") {
1465
- const e = new Error(`RunnerDeps.onTrackFailure must be "refuse" or "proceed-unprotected"; got ${JSON.stringify(deps.onTrackFailure)} — refusing prepare rather than silently defaulting a first-touch-failure policy.`);
1466
- e.code = "config.invalid_on_track_failure";
1467
- throw e;
1468
- }
1469
- const fileHistoryStore = resolveWiredFileHistoryStore(deps.fileHistoryStore);
1470
- const legacyRewindFiles = spec.rewindFiles;
1471
- if (legacyRewindFiles === true) {
1472
- if (spec.resumeAt !== undefined) {
1473
- const e = new Error(`rewind-files: TaskSpec.rewindFiles is retired (design/381 — rewind now converges the per-edited-file history, not a whole-tree snapshot). This request pairs it with resumeAt, i.e. the RESTORE sense: set restoreFiles: true instead. (The capture sense needs no request anymore — first-touch tracking is always on when a RunnerDeps.fileHistoryStore is wired.)`);
1474
- e.code = "rewind.rewind_files_retired";
1475
- throw e;
1476
- }
1477
- try {
1478
- deps.onError?.(new Error(`TaskSpec.rewindFiles (capture sense) is retired and was ignored: per-edited-file first-touch tracking is always on when RunnerDeps.fileHistoryStore is wired, so a per-turn capture request has nothing left to ask for. Drop the field; the restore sense moved to TaskSpec.restoreFiles.`), { phase: "config", sessionId });
1479
- }
1480
- catch {
1481
- }
1482
- }
1483
- if (spec.restoreFiles === true && spec.resumeAt === undefined) {
1484
- const e = new Error(`rewind-files: restoreFiles: true needs a resumeAt anchor — restoring files means converging them to a specific prior entry's boundary, and this spec names none. Use resumeAt + restoreFiles to move conversation and files together, or rewindFilesTo alone for a code-only restore.`);
1485
- e.code = "rewind.invalid_spec";
1486
- throw e;
1487
- }
1488
- if (spec.resumeAt !== undefined && spec.rewindFilesTo !== undefined) {
1489
- const e = new Error(`rewind-files: resumeAt ("${spec.resumeAt}") and rewindFilesTo ("${spec.rewindFilesTo}") were both set — resumeAt already anchors the file restore when restoreFiles is true, so a separate rewindFilesTo target is a conflicting request. Drop one of the two.`);
1490
- e.code = "rewind.conflicting_targets";
1491
- throw e;
1492
- }
1493
- let rewindTarget = spec.resumeAt !== undefined ? (spec.restoreFiles === true ? spec.resumeAt : undefined) : spec.rewindFilesTo;
1494
- const rewindBefore = rewindTarget !== undefined && spec.resumeAt !== undefined && spec.resumeAtMode === "before";
1495
- if (spec.resumeAt !== undefined && spec.restoreFiles !== true) {
1496
- rewindNotes.push({
1497
- code: "conversation_only",
1498
- message: `the conversation was branched at entry "${spec.resumeAt}" but the working tree was NOT rewound — this task set resumeAt without restoreFiles, so files remain at their current state`,
1499
- });
1500
- }
1501
- const fileHistoryEnabled = fileHistoryStore !== undefined && handsEnabled;
1502
- const { historyRoot, historyScope, historyFs } = await resolveFileHistoryCoordinates(fileHistoryEnabled, executionEnv, taskRootFinal, internals?.fileHistoryLineage, sessionId);
1503
- if (fileHistoryStore !== undefined && internals?.fileHistoryLineage === undefined)
1504
- await adoptForkedFileHistory(fileHistoryStore, session, sessionId, deps.onError);
1505
- if (rewindTarget !== undefined && historyScope !== sessionId)
1506
- throw childScopeRewindRefusal(historyScope, rewindTarget);
1507
- if (fileHistoryStore === undefined) {
1508
- if (rewindTarget !== undefined) {
1509
- const e = new Error(`rewind-files: restoring files to entry "${rewindTarget}" requires a history backend, but this deployment wired no RunnerDeps.fileHistoryStore — no file history was ever recorded, so the files were NOT rewound`);
1510
- e.code = "rewind.store_unconfigured";
1511
- throw e;
1512
- }
1513
- }
1514
- else if (!handsEnabled) {
1515
- if (rewindTarget !== undefined) {
1516
- rewindNotes.push({
1517
- code: "files_env_unsupported",
1518
- message: "the file side of rewind was inert: this deployment mounts no filesystem-capable ExecutionEnv, so no file history was recorded or restored",
1519
- });
1520
- }
1521
- }
1522
- const legacyEpochTail = (entryId, form = "at-or-above") => legacyRewindEpochTail(deps, sessionId, entryId, form);
1523
- let restoredFilePaths = [];
1524
- if (rewindTarget !== undefined && fileHistoryStore !== undefined && handsEnabled) {
1525
- const historyStore = fileHistoryStore;
1526
- if (rewindBefore) {
1527
- let anchor;
1528
- if (await historyStore.canRestore(sessionId, rewindTarget)) {
1529
- anchor = rewindTarget;
1530
- }
1531
- else {
1532
- const walked = new Set();
1533
- let cursor = resumeAtBeforeParentId;
1534
- while (cursor !== null && !walked.has(cursor)) {
1535
- walked.add(cursor);
1536
- if (await historyStore.canRestore(sessionId, cursor)) {
1537
- anchor = cursor;
1538
- break;
1539
- }
1540
- cursor = (await session.getEntry(cursor))?.parentId ?? null;
1541
- }
1542
- }
1543
- if (anchor === undefined) {
1544
- const e = new Error(`rewind-files: no file-history boundary exists at or above the "before" branch point on session "${sessionId}" — cannot restore files consistent with the rewound context. ${await legacyEpochTail(rewindTarget)}`);
1545
- e.code = "rewind_snapshot.unresolvable";
1546
- throw e;
1547
- }
1548
- rewindTarget = anchor;
1549
- }
1550
- const restoreRoot = historyRoot;
1551
- const restoreSignal = spec.signal ? AbortSignal.any([abortController.signal, spec.signal]) : abortController.signal;
1552
- const restored = await historyStore.restore(sessionId, rewindTarget, executionEnv, restoreRoot, restoreSignal);
1553
- const ledgerLine = (r) => `applied ${r.filesChanged.length}, identical ${r.identicalSkipped.length}, refused ${r.refused.length}${r.refused.length > 0 ? ` (${r.refused.map((f) => `${f.path}: ${f.reason}`).join("; ")})` : ""}, failed ${r.failed.length}${r.failed.length > 0 ? ` (${r.failed.map((f) => `${f.path}: ${f.reason}`).join("; ")})` : ""}`;
1554
- if (restored.outcome === "failed") {
1555
- if (restored.error?.code === "not_found") {
1556
- const e = new Error(rewindBefore
1557
- ? `rewind-files: the resolved "before" history anchor "${rewindTarget}" disappeared before restore — files were NOT rewound`
1558
- : `rewind-files: no file-history boundary exists for entry "${rewindTarget}" on session "${sessionId}" — the working tree was NOT rewound. ${await legacyEpochTail(rewindTarget, "keyed")}`);
1559
- e.code = "rewind_snapshot.unresolvable";
1560
- throw e;
1561
- }
1562
- const e = new Error(`rewind-files restore failed (${restored.error?.code ?? "restore_failed"}): ${restored.error?.message ?? "unknown"} — ledger: ${ledgerLine(restored)}`);
1563
- e.code = "rewind.restore_failed";
1564
- throw e;
1565
- }
1566
- if (restored.outcome === "partial") {
1567
- if (spec.acceptPartialRestore === true) {
1568
- rewindNotes.push({
1569
- code: "restore_partial",
1570
- message: `file restore to entry "${rewindTarget}" was PARTIAL and was tolerated by acceptPartialRestore — ledger: ${ledgerLine(restored)}. Re-running the same restore converges (per-file idempotent).`,
1571
- });
1572
- }
1573
- else {
1574
- const e = new Error(`rewind-files: restore to entry "${rewindTarget}" was PARTIAL — ledger: ${ledgerLine(restored)}. The task is refused rather than run on a mixed-state tree (set acceptPartialRestore: true to tolerate and disclose instead); re-running the same restore converges (per-file idempotent).`);
1575
- e.code = "rewind.restore_failed";
1576
- throw e;
1577
- }
1578
- }
1579
- restoredFilePaths = restoredAbsPathsOf(restored, restoreRoot);
1580
- }
1581
- const trackFileEdit = fileHistoryEnabled
1582
- ? async (req) => {
1583
- const r = await fileHistoryStore.trackEdit(historyScope, req.key, executionEnv, historyRoot, req.signal);
1584
- if (r.ok) {
1585
- if (!r.minted)
1586
- return { ok: true };
1587
- return {
1588
- ok: true,
1589
- annul: async (proof) => {
1590
- const a = await fileHistoryStore.annulTrack(historyScope, req.key, historyRoot, proof === "verify" ? { env: executionEnv, ...(req.signal !== undefined ? { signal: req.signal } : {}) } : undefined);
1591
- if (a.ok)
1592
- return;
1593
- try {
1594
- deps.onError?.(new Error(`file-history could not retract the first-touch record for "${req.key}" after its edit failed to land (${a.error.code}: ${a.error.message}) — the path stays tracked, so a rewind to a boundary below it may converge bytes this engine never wrote`), { phase: "rewind", sessionId });
1595
- }
1596
- catch {
1597
- }
1598
- },
1599
- };
1600
- }
1601
- if (onTrackFailure === "proceed-unprotected") {
1602
- try {
1603
- deps.onError?.(new Error(`file-history first-touch backup for "${req.key}" failed (${r.error.code}: ${r.error.message}) — proceeding UNPROTECTED per RunnerDeps.onTrackFailure: the pristine promise for this path is void, and only in this process's memory (no durable trace exists, so after a process restart the next edit can still mint already-modified bytes as pristine v1)`), { phase: "rewind", sessionId });
1604
- }
1605
- catch {
1606
- }
1607
- return { ok: true };
1608
- }
1609
- return {
1610
- ok: false,
1611
- refusal: `Error (${req.tool}): the edit was refused because its first-touch file-history backup could not be persisted (${r.error.code}: ${r.error.message}). Proceeding would leave "${req.path}" with no durable pristine record — after the history store recovers, a retried edit would mint already-modified bytes as the pristine baseline. Retry when the file-history store is healthy.`,
1612
- };
1613
- }
1614
- : undefined;
1615
- const { note: noteFileEdited, snapshot: editedFilesSnapshot } = createEditedFilesLedger();
1616
- const fileHistoryBoundary = fileHistoryEnabled && historyScope === sessionId
1617
- ? createFileHistoryBoundarySeat({ store: fileHistoryStore, sessionId, env: executionEnv, root: historyRoot, onError: deps.onError })
1618
- : undefined;
1619
- const harnessRef = {};
1620
- const skillScope = new ActiveSkillScope();
1621
- const forwardSink = internals?.onForwardEvent;
1622
- const forwardEvent = forwardSink
1623
- ? (e) => {
1624
- if (e.type === "task_progress")
1625
- forwardSink(e);
1626
- else if (spec.forwardSubagentEvents === true &&
1627
- (e.type === "text_delta" || e.type === "text_end" || e.type === "reasoning_delta" || e.type === "tool_start" || e.type === "tool_end")) {
1628
- forwardSink(e);
1629
- }
1630
- }
1631
- : undefined;
1632
- const reviewRequestRef = {};
1633
- const requestReview = (opts) => {
1634
- if (reviewRequestRef.pending === undefined) {
1635
- reviewRequestRef.pending = opts?.reason !== undefined ? { reason: opts.reason } : {};
1636
- }
1637
- };
1638
- const stopRequestedRef = { current: false };
1639
- const requestStopAfterTurn = () => {
1640
- stopRequestedRef.current = true;
1641
- harnessRef.current?.requestStopAfterTurn();
1642
- };
1643
- const planModeRef = { active: false };
1644
- const enterPlanMode = () => {
1645
- planModeRef.active = true;
1646
- };
1647
- const subagentRetain = spec.retainSubagentSessions ? new SubagentRetainLedger(spec.retainSubagentSessions) : undefined;
1648
- const worktreeIsolation = spec.handsReadOnly !== true ? createSubagentWorktreeHelper(executionEnv, taskRootFinal) : undefined;
1649
- const liveInheritedGate = internals?.inheritedGate;
1650
- const seedInheritedGate = resume?.seed.inheritedGate;
1651
- const inheritedAncestorRules = (() => {
1652
- const seedRules = seedInheritedGate?.ancestorRules;
1653
- const liveRules = liveInheritedGate?.ancestorRules;
1654
- if (seedRules === undefined || seedRules.length === 0)
1655
- return liveRules ?? seedRules;
1656
- if (liveRules === undefined || liveRules.length === 0)
1657
- return seedRules;
1658
- const merged = seedRules.map((s) => {
1659
- const live = liveRules.find((l) => l.sessionId === s.sessionId && l.principal === s.principal);
1660
- return live !== undefined && live.rev >= s.rev ? live : s;
1661
- });
1662
- const extras = liveRules.filter((l) => !seedRules.some((s) => s.sessionId === l.sessionId && s.principal === l.principal));
1663
- return [...merged, ...extras];
1664
- })();
1665
- const shellGateRank = { off: 0, classify: 1, always: 2 };
1666
- const liveShellGate = liveInheritedGate?.shellGate;
1667
- const seedShellGate = seedInheritedGate?.shellGate;
1668
- const inheritedShellGate = liveShellGate === undefined
1669
- ? seedShellGate
1670
- : seedShellGate === undefined
1671
- ? liveShellGate
1672
- : shellGateRank[liveShellGate] >= shellGateRank[seedShellGate]
1673
- ? liveShellGate
1674
- : seedShellGate;
1675
- const inheritedParentConstraints = attachRebuiltDenialTrackers(liveInheritedGate?.parentConstraints, deps.autoMode?.denialLimit);
1676
- const autoModeIntent = autoModeSeat || liveInheritedGate?.autoModeRequested === true || seedInheritedGate?.autoModeRequested === true;
1677
- const liveAdmittedOrg = liveInheritedGate?.admittedOrgScopes;
1678
- const seedAdmittedOrg = seedInheritedGate?.admittedOrgScopes;
1679
- const inheritedAdmittedOrgScopes = liveAdmittedOrg === undefined ? seedAdmittedOrg : seedAdmittedOrg === undefined ? liveAdmittedOrg : liveAdmittedOrg.filter((s) => seedAdmittedOrg.includes(s));
1680
- const inheritedOrgGoverned = liveInheritedGate?.orgAdmissionGoverned === true || seedInheritedGate?.orgAdmissionGoverned === true;
1681
- const checkpointOwnOrgVerdict = resume?.seed.inheritedGate?.ownAdmittedOrgScopes !== undefined
1682
- ? { scopes: resume.seed.inheritedGate.ownAdmittedOrgScopes, writeScope: resume.seed.inheritedGate.ownAdmittedOrgWriteScope ?? null }
1683
- : undefined;
1684
- const refOwnOrgVerdict = internals?.ownOrgAdmissionRef?.current;
1685
- const priorOwnOrgVerdict = checkpointOwnOrgVerdict === undefined
1686
- ? refOwnOrgVerdict
1687
- : refOwnOrgVerdict === undefined
1688
- ? checkpointOwnOrgVerdict
1689
- : {
1690
- scopes: checkpointOwnOrgVerdict.scopes.filter((sc) => refOwnOrgVerdict.scopes.includes(sc)),
1691
- writeScope: checkpointOwnOrgVerdict.writeScope !== null && checkpointOwnOrgVerdict.writeScope === refOwnOrgVerdict.writeScope
1692
- ? checkpointOwnOrgVerdict.writeScope
1693
- : null,
1694
- };
1695
- const orgGovernedProvenance = deps.memoryScopeAdmission !== undefined ||
1696
- (deps.deploymentMemoryScopes !== undefined && deps.deploymentMemoryScopes.length > 0) ||
1697
- deps.compliancePostureResolver !== undefined ||
1698
- inheritedOrgGoverned ||
1699
- priorOwnOrgVerdict !== undefined;
1700
- const specShellGate = spec.shellGate ?? "off";
1701
- const effectiveShellGate = inheritedShellGate !== undefined && shellGateRank[inheritedShellGate] > shellGateRank[specShellGate] ? inheritedShellGate : specShellGate;
1702
- const ownSessionRulesRef = {};
1703
- const memoryAdmittedOrgScopesRef = { current: [] };
1704
- const ownOrgVerdictRef = { current: undefined };
1705
- const orgAdmissionCheckpointState = () => inheritedAdmittedOrgScopes !== undefined || ownOrgVerdictRef.current !== undefined || orgGovernedProvenance;
1706
- const faceCheckpointSection = () => {
1707
- if (resolvedReadFace === undefined && resume !== undefined) {
1708
- const seed = resume.seed.readFace;
1709
- return seed !== undefined ? { face: seed.face, ...(seed.denyEntries !== undefined ? { denyEntries: seed.denyEntries.map((e) => ({ ...e })) } : {}) } : undefined;
1710
- }
1711
- const face = resolvedReadFace ?? handsLessResolvedFace;
1712
- if (face === undefined)
1713
- return undefined;
1714
- if (face === "open" || readDenyAdditionsNormalized.length > 0) {
1715
- return { face, ...(readDenyAdditionsNormalized.length > 0 ? { denyEntries: readDenyAdditionsNormalized.map((e) => ({ ...e })) } : {}) };
1716
- }
1717
- return undefined;
1718
- };
1719
- const faceCheckpointState = () => faceCheckpointSection() !== undefined;
1720
- const f012CheckpointState = () => (inheritedParentConstraints?.length ?? 0) > 0 ||
1721
- seedInheritedGate?.constraintChain !== undefined ||
1722
- internals?.delegationProvenance !== undefined;
1723
- const frozenOnAsk = spec.onAsk ?? deps.onAsk;
1724
- const hookEnvSource = (ownedEnv ?? deps.executionEnv) != null ? executionEnv : undefined;
1725
- const notifyOwnHookCrash = (err) => {
1726
- try {
1727
- deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId });
1728
- }
1729
- catch {
1730
- }
1731
- };
1732
- const frozenOnQuestion = spec.onQuestion ?? deps.onQuestion;
1733
- const provenanceForChildrenRef = {};
1734
- const delegationProvenanceForChildren = () => provenanceForChildrenRef.current;
1735
- const delegationSettlementRef = {};
1736
- const delegationSettlementForChildren = () => delegationSettlementRef.current;
1737
- const inheritedGateForChildren = () => {
1738
- const ancestorRules = [
1739
- ...(inheritedAncestorRules ?? []),
1740
- ...(ownSessionRulesRef.current !== undefined ? [ownSessionRulesRef.current] : []),
1741
- ];
1742
- const ownCallerPolicy = lockedPreflight.toolPolicy;
1743
- const durableMandate = runtimeCaps?.forceDurableGate === true ||
1744
- (spec.durableApproval !== undefined && !isLiveApproverSeat(frozenOnAsk));
1745
- const contentMandate = runtimeCaps?.forceDurableGate === true ||
1746
- (spec.durableApproval !== undefined && !isLiveQuestionFace(frozenOnQuestion));
1747
- const ownPreToolUse = preToolUseObservational ? undefined : hooks?.preToolUse;
1748
- const hookConstraint = ownPreToolUse !== undefined &&
1749
- !(inheritedParentConstraints ?? []).some((pc) => pc.preToolUse === ownPreToolUse &&
1750
- askApproverIdentity(pc.onAsk) === askApproverIdentity(frozenOnAsk) &&
1751
- (pc.durableMandate === true) === durableMandate &&
1752
- (pc.contentMandate === true) === contentMandate &&
1753
- pc.hookEnv === hookEnvSource &&
1754
- pc.autoMode?.decider === autoModeDecider)
1755
- ? [
1756
- {
1757
- policy: createPreToolUseConstraintPolicy(ownPreToolUse, hookEnvFace, notifyOwnHookCrash, hookTimeoutMs),
1758
- preToolUse: ownPreToolUse,
1759
- ...(hookEnvSource !== undefined ? { hookEnv: hookEnvSource } : {}),
1760
- ...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
1761
- ...(durableMandate ? { durableMandate: true } : {}),
1762
- ...(contentMandate ? { contentMandate: true } : {}),
1763
- ...(autoModeDecider !== undefined
1764
- ? { autoMode: { decider: autoModeDecider, ...(autoModeDenialTracking !== undefined ? { denialTracking: autoModeDenialTracking } : {}), ...(autoModeArming !== undefined ? { arming: autoModeArming } : {}) } }
1765
- : {}),
1766
- },
1767
- ]
1768
- : [];
1769
- const parentConstraints = [
1770
- ...(inheritedParentConstraints ?? []),
1771
- ...hookConstraint,
1772
- ...(ownCallerPolicy !== undefined
1773
- ? [
1774
- {
1775
- policy: ownCallerPolicy,
1776
- ...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
1777
- ...(durableMandate ? { durableMandate: true } : {}),
1778
- ...(contentMandate ? { contentMandate: true } : {}),
1779
- ...(autoModeDecider !== undefined
1780
- ? { autoMode: { decider: autoModeDecider, ...(autoModeDenialTracking !== undefined ? { denialTracking: autoModeDenialTracking } : {}), ...(autoModeArming !== undefined ? { arming: autoModeArming } : {}) } }
1781
- : {}),
1782
- },
1783
- ]
1784
- : []),
1785
- ];
1786
- return {
1787
- ...(ancestorRules.length > 0 ? { ancestorRules } : {}),
1788
- ...(effectiveShellGate !== "off" ? { shellGate: effectiveShellGate } : {}),
1789
- ...(autoModeIntent ? { autoModeRequested: true } : {}),
1790
- ...(parentConstraints.length > 0 ? { parentConstraints } : {}),
1791
- admittedOrgScopes: memoryAdmittedOrgScopesRef.current,
1792
- ...(orgGovernedProvenance ? { orgAdmissionGoverned: true } : {}),
1793
- };
1794
- };
1795
- let agentForkDenial;
1796
- let observersActive = false;
1797
- let resolvedReadFace;
1798
- let readDenyAdditionsNormalized = [];
1799
- let handsLessResolvedFace;
1800
- const carrierReadFace = () => resolvedReadFace ?? handsLessResolvedFace;
1801
- const fullShellReachable = handsEnabled &&
1802
- !(executionEnv instanceof StubExecutionEnv) &&
1803
- spec.handsReadOnly !== true &&
1804
- !(toolFaceSnapshot.exclude?.includes("Bash") ?? false);
1805
- let autoModeDecider;
1806
- let autoModeDenialTracking;
1807
- const { gateStopRef, stopForDenialLimit } = createDenialLimitStop({ sessionId, runId, onNotice: deps.onNotice, abort: () => { abortController.abort(); void harness.abort(); } });
1808
- let autoModeArming;
1809
- const delegationEntryCapsResolved = resolveDelegationEntryCaps(deps.delegationEntryCaps);
1810
- const enrichSpecToolCtx = (ctx) => ({
1811
- ...ctx,
1812
- delegationEntryCaps: delegationEntryCapsResolved,
1813
- reportUsage,
1814
- model: harnessRef.current?.getModel(),
1815
- thinkingLevel: harnessRef.current?.getThinkingLevel(),
1816
- principal: spec.principal,
1817
- ...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
1818
- ...(frozenOnQuestion !== undefined ? { onQuestion: frozenOnQuestion } : {}),
1819
- ...(spec.handsReadOnly === true ? { handsReadOnly: true } : {}),
1820
- ...(carrierReadFace() === "roots" ? { readFace: "roots" } : {}),
1821
- ...(readDenyAdditionsNormalized.length > 0 ? { readDenyPatterns: Object.freeze(readDenyAdditionsNormalized.map((e) => ({ ...e }))) } : {}),
1822
- ...(spec.interactiveTools === false ? { interactiveTools: false } : {}),
1823
- oneShot: spec.oneShot,
1824
- clientContext: spec.clientContext,
1825
- excludeTools: toolFaceSnapshot.exclude,
1826
- deferTools: toolFaceSnapshot.defer,
1827
- alwaysLoadTools: toolFaceSnapshot.alwaysLoad,
1828
- ...(toolFaceSnapshot.restoreGated !== undefined ? { restoreGatedTools: toolFaceSnapshot.restoreGated } : {}),
1829
- promptProfile,
1830
- ...(spec.additionalDirectories !== undefined ? { additionalDirectories: Object.freeze([...spec.additionalDirectories]) } : {}),
1831
- ...(spec.additionalReadDirectories !== undefined ? { additionalReadDirectories: Object.freeze([...spec.additionalReadDirectories]) } : {}),
1832
- ...(spec.envFacts !== undefined ? { envFacts: { ...spec.envFacts } } : {}),
1833
- ...(spec.memoryPersistenceCapable !== undefined ? { memoryPersistenceCapable: spec.memoryPersistenceCapable } : {}),
1834
- get memoryCaptureOptedOut() {
1835
- return memoryEngineSession?.captureOptOut?.optedOut() ?? false;
1836
- },
1837
- get memoryCaptureIndeterminate() {
1838
- return memoryEngineSession?.captureOptOut?.indeterminate() ?? false;
1839
- },
1840
- get memoryCaptureControlDir() {
1841
- return memoryEngineSession?.engine.controlPlaneDir;
1842
- },
1843
- get memoryCaptureAncestors() {
1844
- const ctl = memoryEngineSession?.engine.controlPlaneDir;
1845
- return [...(internals?.memoryCaptureAncestors ?? []), { sessionId, ...(ctl !== undefined ? { controlDir: ctl } : {}) }];
1846
- },
1847
- getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
1848
- parentCwd: taskRootFinal,
1849
- ...(fileHistoryEnabled ? { fileHistoryLineage: { scope: historyScope, root: historyRoot, fs: historyFs } } : {}),
1850
- reminderMark,
1851
- reminderDisclosureCounts,
1852
- ...(centerAdoption !== undefined ? { centerArtifactDigest: centerAdoption.artifact.artifactDigest } : {}),
1853
- ...(centerAdoption?.sourceRevision !== undefined ? { centerSourceRevision: centerAdoption.sourceRevision } : {}),
1854
- activeSkillScope: () => skillScope.active(),
1855
- inheritedGateForChildren,
1856
- delegationProvenanceForChildren,
1857
- delegationSettlement: delegationSettlementForChildren,
1858
- ...(autoModeDecider ? { autoModeReview: { decider: autoModeDecider } } : {}),
1859
- ...(spec.durableApproval !== undefined ? { durableApprovalForChildren: { ...spec.durableApproval } } : {}),
1860
- ...(spec.checkpointStore === null ? { checkpointStoreDisabledForChildren: true } : {}),
1861
- taskId: hostTaskId,
1862
- ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
1863
- ...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}),
1864
- ...(internals?.rootSessionId !== undefined ? { rootSessionId: internals.rootSessionId } : {}), ...(internals?.placementRoot !== undefined ? { placementRoot: internals.placementRoot } : {}),
1865
- ...(internals?.explicitAgentName !== undefined ? { spawnedAgentName: internals.explicitAgentName } : {}),
1866
- ...(internals?.peerSelfRef !== undefined ? { peerSelfRef: internals.peerSelfRef } : {}),
1867
- ...(internals?.peerInboundChainRef !== undefined ? { peerInboundChainRef: internals.peerInboundChainRef } : {}),
1868
- ...(deps.rosterStore !== undefined ? { roster: deps.rosterStore } : {}),
1869
- ...(sessions.fork !== undefined
1870
- ? {
1871
- hostSessionFork: async () => {
1872
- const forked = (await sessions.fork(sessionId, spec.principal ?? null)) ?? null;
1873
- if (forked === null)
1874
- return null;
1875
- return {
1876
- sessionId: forked,
1877
- release: async () => {
1878
- await sessions.release?.(forked);
1879
- },
1880
- };
1881
- },
1882
- }
1883
- : {}),
1884
- sessionId,
1885
- ...(spec.backgroundScope !== undefined ? { backgroundScope: spec.backgroundScope } : {}),
1886
- ...(resolvedInteractionPosture !== undefined ? { interactionPosture: resolvedInteractionPosture } : {}),
1887
- ...(deps.onBackgroundChildEvent ? { onBackgroundChildEvent: deps.onBackgroundChildEvent } : {}),
1888
- ...(internals?.onTaskNotification ? { onTaskNotification: internals.onTaskNotification } : {}),
1889
- ...(forwardEvent ? { forwardEvent } : {}),
1890
- ...(internals?.onSubagentSpawn ? { onSubagentSpawn: internals.onSubagentSpawn } : {}),
1891
- ...(subagentRetain ? { subagentRetain } : {}),
1892
- ...(worktreeIsolation ? { worktreeIsolation } : {}),
1893
- ...(internals?.insideFork === true ? { insideFork: true } : {}),
1894
- ...(agentForkDenial !== undefined ? { forkAccess: { denied: agentForkDenial } } : {}),
1895
- ...(observersActive ? { observersAllowed: true } : {}),
1896
- requestReview,
1897
- requestStopAfterTurn,
1898
- ...(spec.enablePlanMode === true ? { enterPlanMode } : {}),
1899
- });
1900
- const tools = (spec.tools ?? []).map((t) => {
1901
- if (isDefineToolProduct(t)) {
1902
- return maybeOffload(t, t);
1903
- }
1904
- return maybeOffload(defineTool({
1905
- ...t,
1906
- execute: (args, ctx) => t.execute(args, enrichSpecToolCtx(ctx)),
1907
- }), t);
1908
- });
1909
- const blockedRef = {};
1910
- if (spec.enableBlockedReport !== false) {
1911
- tools.push(createReportBlockedTool(blockedRef));
1912
- }
1913
- if (!(spec.tools ?? []).some((t) => t.name === REPORT_FINDINGS_TOOL_NAME || t.aliases?.includes(REPORT_FINDINGS_TOOL_NAME))) {
1914
- tools.push(createReportFindingsTool());
1915
- }
1916
- if (spec.enablePlanMode === true && spec.interactiveTools !== false) {
1917
- const planReviewFace = resolveCheckpointStore(spec, deps) !== undefined;
1918
- if (spec.interactiveTools === true || planReviewFace) {
1919
- tools.push(defineTool(createPresentPlanTool(requestReview)));
1920
- if (planReviewFace) {
1921
- tools.push(defineTool(createEnterPlanModeTool(enterPlanMode)));
1922
- }
1923
- }
1924
- }
1925
- let runtimeCaps;
1926
- let runtimeCapsFaulted = false;
1927
- if (deps.runtimeCapsResolver) {
1928
- try {
1929
- runtimeCaps = (await deps.runtimeCapsResolver(spec.principal)) ?? undefined;
1930
- }
1931
- catch (err) {
1932
- deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
1933
- runtimeCaps = { allowWorkflows: false, allowFork: false, autoMode: false };
1934
- runtimeCapsFaulted = true;
1935
- }
1936
- const screened = screenAutoModeCap(runtimeCaps, deps.onError, sessionId);
1937
- runtimeCaps = screened.caps;
1938
- runtimeCapsFaulted ||= screened.faulted;
1939
- }
1940
- let complianceDenies = new Set();
1941
- let complianceDegraded = false;
1942
- if (deps.compliancePostureResolver) {
1943
- try {
1944
- const posture = (await deps.compliancePostureResolver(spec.principal)) ?? undefined;
1945
- if (posture !== undefined)
1946
- complianceDenies = resolveComplianceDenies(posture);
1947
- }
1948
- catch (err) {
1949
- deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
1950
- complianceDenies = new Set(COMPLIANCE_CAPABILITIES);
1951
- complianceDegraded = true;
1952
- }
1953
- const complianceRefuse = (capability, requested) => {
1954
- const e = new Error(`${requested} is denied for this principal by the compliance posture (capability "${capability}"` +
1955
- `${complianceDegraded ? "; the posture resolver is currently failing, so every managed capability is denied fail-closed" : ""}) — ` +
1956
- `the task is refused rather than silently narrowed.`);
1957
- e.code = complianceDegraded ? "config.compliance_required" : "config.compliance_denied";
1958
- throw e;
1959
- };
1960
- if (complianceDenies.has("mcp_servers") && lockedPreflight.mcp?.length) {
1961
- complianceRefuse("mcp_servers", "TaskSpec.mcp (MCP server materialization)");
1962
- }
1963
- if (complianceDenies.has("workflows") && spec.selfOrchestration === true) {
1964
- complianceRefuse("workflows", "TaskSpec.selfOrchestration (workflow self-orchestration)");
1965
- }
1966
- if (complianceDenies.has("web_fetch")) {
1967
- const webTool = (spec.tools ?? []).find((t) => t.name === WEB_FETCH_TOOL_NAME || (t.aliases ?? []).includes(WEB_FETCH_TOOL_NAME));
1968
- if (webTool !== undefined) {
1969
- complianceRefuse("web_fetch", `TaskSpec.tools["${webTool.name}"] (the WebFetch tool face)`);
1970
- }
1971
- }
1972
- }
1973
- agentForkDenial = forkGovernanceDenial(spec.enableFork, runtimeCaps?.allowFork);
1974
- observersActive = runtimeCaps?.allowObservers === true;
1975
- const autoModeArmReason = autoModeArmReasonOf(autoModeIntent, deps.autoMode !== undefined, runtimeCaps?.autoMode, runtimeCapsFaulted);
1976
- if (autoModeIntent && deps.autoMode !== undefined && runtimeCaps?.autoMode !== false) {
1977
- const am = deps.autoMode;
1978
- let classifierModel;
1979
- try {
1980
- classifierModel = resolveTaskModel({ modelRole: "classifier" }, deps).model;
1981
- }
1982
- catch {
1983
- classifierModel = model;
1984
- }
1985
- if (await derivedRouteFallsBack({ seat: "auto-mode-classifier", derived: classifierModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice, sessionId, runId })) {
1986
- classifierModel = model;
1987
- }
1988
- const classifierLaneRule = peerLaneActive && peerSendMessageBuiltIn;
1989
- const classifierSystemPrompt = buildAutoModePrompt(classifierLaneRule ? { ...am, crossSessionMessagesRule: CROSS_SESSION_CLASSIFIER_RULE } : am);
1990
- const classifierRuntime = brainToRuntime(deps.brain);
1991
- autoModeDenialTracking = createAutoModeDenialTracker(am.denialLimit);
1992
- autoModeDecider = createAutoModeDecider({
1993
- ...(am.timeoutMs !== undefined ? { timeoutMs: am.timeoutMs } : {}),
1994
- ...(am.failureThreshold !== undefined ? { failureThreshold: am.failureThreshold } : {}),
1995
- ...(am.onBreakerOpen !== undefined ? { onBreakerOpen: am.onBreakerOpen } : {}),
1996
- classify: async (input, signal) => {
1997
- const ctx = await session.buildContext();
1998
- const known = ctx.messages.filter((m) => !isSyntheticApiErrorMessage(m) &&
1999
- (m.role === "user" || m.role === "assistant" || m.role === "toolResult"));
2000
- const userPrompt = renderAutoModeWindow(known, am.window) + renderAutoModeAction(input);
2001
- const classifierAuth = await spec.getApiKeyAndHeaders?.(classifierModel);
2002
- const response = await classifierRuntime.completeSimple(classifierModel, { systemPrompt: classifierSystemPrompt, messages: [{ role: "user", content: userPrompt, timestamp: Date.now() }] }, {
2003
- signal,
2004
- ...(classifierAuth?.apiKey !== undefined ? { apiKey: classifierAuth.apiKey } : {}),
2005
- ...(classifierAuth?.headers !== undefined ? { headers: classifierAuth.headers } : {}),
2006
- });
2007
- return response.content
2008
- .filter((c) => c.type === "text")
2009
- .map((c) => c.text)
2010
- .join("");
2011
- },
2012
- });
2013
- if (am.persistArming === true)
2014
- autoModeArming = mintPersistedArming(am, classifierLaneRule, classifierSystemPrompt, deps.onError, sessionId);
2015
- }
2016
- const deploymentWorkflowReady = runnerSelf !== undefined && isSelfOrchestrationActive(spec, deps);
2017
- const selfOrchestrationActive = deploymentWorkflowReady && runtimeCaps?.allowWorkflows !== false;
2018
- if (deploymentWorkflowReady && runtimeCaps?.allowWorkflows === false) {
2019
- deps.onError?.(new Error(`self-orchestration DENIED for principal "${spec.principal ?? ""}" by runtimeCaps.allowWorkflows=false ` +
2020
- `(per-principal entitlement governance, not a misconfiguration — the run_workflow tool is not mounted)`), { phase: "config", sessionId });
2021
- }
2022
- let workflowToolsActive = false;
2023
- let workflowSizeGuideline;
2024
- if (selfOrchestrationActive && runnerSelf && deps.workflowScriptRunner && deps.workflowGovernanceBaseline) {
2025
- workflowToolsActive = true;
2026
- const currentSizeGuideline = () => resolveWorkflowSizeGuideline(deps.workflowLimits?.sizeGuideline).size;
2027
- workflowSizeGuideline = { legGuideline: currentSizeGuideline(), current: currentSizeGuideline };
2028
- toolEffects.set(RUN_WORKFLOW_TOOL_NAME, "write");
2029
- tools.push(await createRunWorkflowTool({
2030
- runner: runnerSelf,
2031
- scriptRunner: deps.workflowScriptRunner,
2032
- governanceBaseline: deps.workflowGovernanceBaseline,
2033
- parentExcludeTools: toolFaceSnapshot.exclude,
2034
- parentDeferTools: toolFaceSnapshot.defer,
2035
- parentAlwaysLoadTools: toolFaceSnapshot.alwaysLoad,
2036
- ...(toolFaceSnapshot.restoreGated !== undefined ? { parentRestoreGatedTools: toolFaceSnapshot.restoreGated } : {}),
2037
- parentPromptProfile: promptProfile,
2038
- models: deps.models,
2039
- agents: deps.agents,
2040
- builtinAgents: deps.builtinAgents,
2041
- store: deps.workflowRunStore,
2042
- journalStore: deps.workflowJournalStore,
2043
- ...(deps.onBackgroundChildEvent ? { onBackgroundChildEvent: deps.onBackgroundChildEvent } : {}),
2044
- scriptStore: deps.workflowScriptStore,
2045
- builtinWorkflows: deps.builtinWorkflows,
2046
- onAgentSpawn: deps.onWorkflowAgentSpawn,
2047
- scope: taskScope,
2048
- notifier: deps.workflowCompletionNotifier,
2049
- originatingSessionId: sessionId,
2050
- rootSessionId: internals?.rootSessionId ?? sessionId, ...(internals?.placementRoot !== undefined ? { placementRoot: internals.placementRoot } : {}),
2051
- taskRegistry: defaultTaskRegistry,
2052
- taskNotification: internals?.onTaskNotification,
2053
- taskOwner: hostTaskId,
2054
- limits: deps.workflowLimits,
2055
- sourceTaskId: hostTaskId,
2056
- parentModel: () => harnessRef.current?.getModel(),
2057
- ...(spec.getApiKeyAndHeaders !== undefined ? { parentGetApiKeyAndHeaders: spec.getApiKeyAndHeaders } : {}),
2058
- ...(frozenOnAsk !== undefined ? { parentOnAsk: frozenOnAsk } : {}),
2059
- principal: spec.principal,
2060
- oneShot: spec.oneShot,
2061
- ...(resolvedInteractionPosture !== undefined ? { parentInteractionPosture: resolvedInteractionPosture } : {}),
2062
- parentMemoryCaptureState: () => {
2063
- const ctl = memoryEngineSession?.engine.controlPlaneDir;
2064
- const co = memoryEngineSession?.captureOptOut;
2065
- return assembleParentCaptureState(co?.optedOut() ?? false, co?.indeterminate() ?? false, ctl, [...(internals?.memoryCaptureAncestors ?? []), { sessionId, ...(ctl !== undefined ? { controlDir: ctl } : {}) }]);
2066
- },
2067
- autoModeReview: () => (autoModeDecider !== undefined ? { decider: autoModeDecider } : undefined),
2068
- workflowDepth: internals?.workflowDepth,
2069
- parentCwd: taskRootFinal,
2070
- parentThinking: () => harnessRef.current?.getThinkingLevel() ?? thinking,
2071
- parentReadFace: () => carrierReadFace(),
2072
- parentReadDenyPatterns: () => (readDenyAdditionsNormalized.length > 0 ? readDenyAdditionsNormalized : undefined),
2073
- ...(spec.handsReadOnly === true ? { parentHandsReadOnly: true } : {}), ...(spec.interactiveTools === false ? { parentInteractiveTools: false } : {}),
2074
- onNotice: deps.onNotice,
2075
- parentCheckpointStoreDisabled: spec.checkpointStore === null,
2076
- parentCenterArtifactDigest: () => centerAdoption?.artifact.artifactDigest,
2077
- parentCenterSourceRevision: () => centerAdoption?.sourceRevision,
2078
- ...(forwardEvent ? { forwardEvent } : {}),
2079
- inheritedGateForChildren,
2080
- }));
888
+ failIsolation("the executionEnvFactory rooted the env at the shared base root instead of a worktree");
889
+ }
890
+ if (internals.parentCwd !== undefined && envCwd === canonical(internals.parentCwd, "the parent's cwd")) {
891
+ failIsolation("the executionEnvFactory rooted the env at the parent's cwd instead of a worktree");
892
+ }
893
+ }
894
+ }
2081
895
  }
896
+ const ownedEnv = ownedEnvSeat.current;
897
+ const { taskRootFinal, rebaseRestoredPath } = await prepareWorkspaceRestore({ ownedEnv, deps, spec, internals, resume, taskRootInitial, abortController, sessionId });
898
+ const { trackFileEdit, fileHistoryBoundary, restoredFilePaths, rewindNotes, fileHistoryEnabled, historyScope, historyRoot, historyFs } = await prepareFileHistory({ deps, spec, internals, executionEnv, taskRootFinal, handsEnabled, sessionId, session, resumeAtBeforeParentId, abortController });
899
+ const { noteFileEdited, editedFilesSnapshot, harnessRef, skillScope, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, planModeRef, enterPlanMode, subagentRetain, worktreeIsolation } = prepareRunRefs({ spec, internals, executionEnv, taskRootFinal });
900
+ let resolvedReadFace;
901
+ let readDenyAdditionsNormalized = [];
902
+ let handsLessResolvedFace;
903
+ const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDecider, autoModeDenialTracking: () => autoModeDenialTracking, autoModeArming: () => autoModeArming, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, centerAdoption: () => centerAdoption, memoryEngineSession: () => memoryEngineSession });
904
+ const { tools, blockedRef, runtimeCaps, runtimeCapsFaulted, complianceDenies, complianceDegraded, agentForkDenial, observersActive, autoModeArmReason, autoModeDecider, autoModeDenialTracking, autoModeArming, selfOrchestrationActive, workflowToolsActive, workflowSizeGuideline } = await prepareCapsAndWorkflow({ spec, deps, internals, session, sessionId, runId, hostTaskId, taskScope, taskRootFinal, model, thinking, lockedPreflight, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, carrierReadFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoption, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
2082
905
  const suspendRef = {};
2083
906
  const reviewRef = {};
2084
907
  const remoteEnvFailures = [];
@@ -2111,7 +934,6 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2111
934
  resourceSpentMicroUsd: priorLedger?.spentMicroUsd ?? 0,
2112
935
  suspendCount: priorSuspendCount,
2113
936
  };
2114
- let worktreeSessionRef;
2115
937
  let suspendForResource;
2116
938
  let suspendForPlatformLimit;
2117
939
  let suspendForReview;
@@ -2141,162 +963,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2141
963
  }
2142
964
  tools.push(createOutputTool(outputRef, spec.outputSchema, compiled.strict ? compiled.modelSchema : undefined));
2143
965
  }
2144
- mcp = lockedPreflight.mcp?.length
2145
- ? await materializeMcpTools(lockedPreflight.mcp, spec.principal, deps.onElicit, deps.mcpImageResizer, { reminderMark, counts: reminderDisclosureCounts }, mcpRevocationWiring(deps, runId))
2146
- : { tools: [], toolAxes: [], warnings: [], serverInstructions: [], instructionsDelta: { pendingAdds: [], pendingRemovals: [] }, droppedTools: [], statuses: [], refresh: async () => [], dispose: async () => { } };
2147
- for (const w of mcp.warnings)
2148
- onceLedger.onError(w, { phase: "mcp", sessionId });
2149
- for (const d of mcp.droppedTools) {
2150
- onceLedger.onError(new Error(`MCP tool dropped at intake — ${d.tool} (server "${d.server}"): ${d.reason}`), { phase: "mcp", sessionId });
2151
- }
2152
- onceLedger.pruneDroppedTools(mcp.droppedTools);
2153
- {
2154
- const callerNames = new Set((spec.tools ?? []).flatMap((t) => [t.name, ...(t.aliases ?? [])]));
2155
- const clash = mcp.tools.find((t) => [t.name, ...(t.aliases ?? [])].some((name) => callerNames.has(name)));
2156
- if (clash) {
2157
- await mcp.dispose();
2158
- const e = new Error(`Tool name "${clash.name}" is reserved by an injected MCP tool — a caller tool of the same name would silently shadow it.`);
2159
- e.code = "config.reserved_tool_name";
2160
- throw e;
2161
- }
2162
- }
2163
- tools.push(...mcp.tools.map((t) => remoteToolOffload(t)));
2164
- const rebuildHarnessToolsRef = {};
2165
- const contentOriginWrapRef = {};
2166
- const toolCallGateArmedRef = { armed: false };
2167
- if (lockedPreflight.mcp?.length) {
2168
- tools.push({
2169
- name: "RefreshMcpTools",
2170
- label: "RefreshMcpTools",
2171
- description: "Refresh the tool list of connected MCP servers. Never dials or re-dials connections — it only re-reads the tool list over the existing connection. The refreshed tools are available immediately; you can call them on your next step. Use when a server's expected tool is missing, or the tool list looks stale after a connection recovered. Omit `server` to refresh every connected server.",
2172
- parameters: Type.Object({
2173
- server: Type.Optional(Type.String({ description: "Name of a single MCP server to refresh; omit to refresh all connected servers." })),
2174
- }),
2175
- execute: async (_toolCallId, params) => {
2176
- const serverArg = params?.server;
2177
- const results = await mcp.refresh(typeof serverArg === "string" && serverArg.length > 0 ? serverArg : undefined);
2178
- const lines = [];
2179
- let changed = false;
2180
- let anyActiveFailure = false;
2181
- for (const r of results) {
2182
- if (r.status !== "refreshed" || r.tools === undefined) {
2183
- lines.push(`${r.server}: ${r.status}${r.error !== undefined ? ` (${r.error})` : ""}`);
2184
- if (r.status === "failed")
2185
- anyActiveFailure = true;
2186
- continue;
2187
- }
2188
- const excludedSet = new Set(toolFaceSnapshot.exclude ?? []);
2189
- const pushable = r.tools.filter((t) => !excludedSet.has(t.name));
2190
- const excludedNow = r.tools.filter((t) => excludedSet.has(t.name)).map((t) => t.name);
2191
- const domainSnapshot = (m) => new Map([...m].filter(([name]) => name.startsWith(r.prefix)));
2192
- const restoreDomain = (m, snap) => {
2193
- for (const name of [...m.keys()])
2194
- if (name.startsWith(r.prefix))
2195
- m.delete(name);
2196
- for (const [name, v] of snap)
2197
- m.set(name, v);
2198
- };
2199
- const priorDomainEffects = domainSnapshot(toolEffects);
2200
- const priorDomainNegatives = domainSnapshot(axisExplicitNegatives);
2201
- for (const name of priorDomainEffects.keys())
2202
- toolEffects.delete(name);
2203
- for (const name of priorDomainNegatives.keys())
2204
- axisExplicitNegatives.delete(name);
2205
- try {
2206
- foldProtocolAxes((r.axes ?? []).filter((a) => !excludedSet.has(a.name)), "MCP");
2207
- }
2208
- catch (foldErr) {
2209
- restoreDomain(toolEffects, priorDomainEffects);
2210
- restoreDomain(axisExplicitNegatives, priorDomainNegatives);
2211
- lines.push(`${r.server}: failed (${foldErr instanceof Error ? foldErr.message : String(foldErr)})`);
2212
- anyActiveFailure = true;
2213
- continue;
2214
- }
2215
- let domainAnchor = -1;
2216
- for (let i = tools.length - 1; i >= 0; i--) {
2217
- const t = tools[i];
2218
- if (t.name.startsWith(r.prefix)) {
2219
- domainAnchor = i;
2220
- tools.splice(i, 1);
2221
- }
2222
- }
2223
- const refreshedMounts = pushable.map((t) => remoteToolOffload(t));
2224
- if (domainAnchor >= 0)
2225
- tools.splice(domainAnchor, 0, ...refreshedMounts);
2226
- else
2227
- tools.push(...refreshedMounts);
2228
- changed = true;
2229
- const detail = [];
2230
- const shownAdded = r.added.filter((n) => !excludedSet.has(n));
2231
- if (shownAdded.length > 0)
2232
- detail.push(`added: ${shownAdded.join(", ")}`);
2233
- if (r.removed.length > 0)
2234
- detail.push(`removed: ${r.removed.join(", ")}`);
2235
- if (excludedNow.length > 0)
2236
- detail.push(`excluded by deployment config (not mounted): ${excludedNow.join(", ")}`);
2237
- if ((r.dropped?.length ?? 0) > 0)
2238
- detail.push(`dropped: ${r.dropped.map((d) => `${d.tool} (${d.reason.length > 90 ? `${d.reason.slice(0, 90)}…` : d.reason})`).join("; ")}`);
2239
- lines.push(`${r.server}: refreshed — ${pushable.length} tool${pushable.length === 1 ? "" : "s"}${detail.length > 0 ? ` (${detail.join("; ")})` : ""}`);
2240
- }
2241
- if (changed)
2242
- await rebuildHarnessToolsRef.current?.();
2243
- if (changed && !toolCallGateArmedRef.armed && (irreversibleTools.size > 0 || egressTools.size > 0)) {
2244
- lines.push("WARNING: refreshed tools declare irreversible/egress safety hints, but this task started with no approval gate registered (no policy/hooks and no gated tools at start) — these hints cannot arm a gate mid-task (RB-46); the refreshed tools run ungated on this deployment shape.");
2245
- }
2246
- const text = lines.length === 0 ? "No connected MCP servers to refresh." : lines.join("\n");
2247
- return {
2248
- content: [{ type: "text", text }],
2249
- details: { results },
2250
- terminate: false,
2251
- ...(anyActiveFailure && !changed ? { isError: true } : {}),
2252
- };
2253
- },
2254
- });
2255
- toolEffects.set("RefreshMcpTools", "read");
2256
- }
2257
- const foldProtocolAxes = (axes, protocolLabel) => {
2258
- for (const axis of axes) {
2259
- if (axis.irreversibility === "always") {
2260
- irreversibilityTier.set(axis.name, "always");
2261
- irreversibleTools.add(axis.name);
2262
- }
2263
- if (axis.irreversibility === "never") {
2264
- axisExplicitNegatives.set(axis.name, { ...axisExplicitNegatives.get(axis.name), irreversible: false });
2265
- }
2266
- if (axis.egress === false) {
2267
- axisExplicitNegatives.set(axis.name, { ...axisExplicitNegatives.get(axis.name), egress: false });
2268
- }
2269
- if (axis.egress) {
2270
- if (axis.effect !== undefined && axis.effect !== "write") {
2271
- const e = new Error(`${protocolLabel} tool "${axis.name}" resolves to egress:true with effect:"${axis.effect}" — an egress tool (external write) must have effect:"write". Clear egress (toolAxes egress:false) if it is a pure read, or set effect:"write".`);
2272
- e.code = "config.egress_requires_write_effect";
2273
- throw e;
2274
- }
2275
- egressTools.add(axis.name);
2276
- }
2277
- if (!toolEffects.has(axis.name))
2278
- toolEffects.set(axis.name, axis.effect ?? "write");
2279
- }
2280
- };
2281
- foldProtocolAxes(mcp.toolAxes, "MCP");
2282
- a2a = spec.a2a?.length
2283
- ? await materializeA2aTools(spec.a2a, spec.principal, abortController.signal)
2284
- : { tools: [], toolAxes: [], warnings: [], statuses: [], refresh: async () => [], dispose: async () => { } };
2285
- for (const w of a2a.warnings)
2286
- deps.onError?.(w, { phase: "a2a", sessionId });
2287
- {
2288
- const callerNames = new Set((spec.tools ?? []).flatMap((t) => [t.name, ...(t.aliases ?? [])]));
2289
- const clash = a2a.tools.find((t) => callerNames.has(t.name));
2290
- if (clash) {
2291
- await a2a.dispose();
2292
- await mcp.dispose();
2293
- const e = new Error(`Tool name "${clash.name}" is reserved by an injected A2A tool — a caller tool of the same name would silently shadow it.`);
2294
- e.code = "config.reserved_tool_name";
2295
- throw e;
2296
- }
2297
- }
2298
- tools.push(...a2a.tools.map((t) => remoteToolOffload(t)));
2299
- foldProtocolAxes(a2a.toolAxes, "A2A");
966
+ const { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef } = await prepareProtocolTools({ lockedPreflight, spec, deps, reminderMark, reminderDisclosureCounts, runId, sessionId, onceLedger, tools, remoteToolOffload, toolFaceSnapshot, toolEffects, axisExplicitNegatives, irreversibilityTier, irreversibleTools, egressTools, abortController, rollback });
2300
967
  const callIssuedAtRef = {};
2301
968
  const memoryWriteGateRef = {};
2302
969
  const handsReadFaceInput = { handsEnabled, executionEnv, taskRootFinal, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals, memoryWriteGateRef, firstPartyOffload, tools, egressTools, irreversibilityTier, irreversibleTools, reversibilityProbes, reminderMark, reminderDisclosureCounts, ...(trackFileEdit !== undefined ? { trackFileEdit } : {}), onFileEdited: noteFileEdited, ...(restoredFilePaths.length > 0 ? { restoredFilePaths } : {}) };
@@ -2307,191 +974,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2307
974
  handsLessResolvedFace = handsReadFace.handsLessResolvedFace;
2308
975
  shellGatedBash = handsReadFace.shellGatedBash;
2309
976
  shellGatedMonitor = handsReadFace.shellGatedMonitor;
2310
- const delegationSurfaceActive = backgroundTaskToolsActive || workflowToolsActive;
2311
- if (delegationSurfaceActive || internals?.parentNotify !== undefined || peerLaneActive) {
2312
- if (delegationSurfaceActive) {
2313
- toolEffects.set("TaskOutput", "read");
2314
- toolEffects.set("TaskStop", "write");
2315
- tools.push(firstPartyOffload(createTaskOutputTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore, notificationWired: internals?.onTaskNotification !== undefined, oneShot: spec.oneShot, toolResultStore: offloadStore })), firstPartyOffload(createTaskStopTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore })));
2316
- }
2317
- if (runnerSelf && !(spec.tools ?? []).some((t) => t.name === SEND_MESSAGE_TOOL_NAME)) {
2318
- toolEffects.set(SEND_MESSAGE_TOOL_NAME, "write");
2319
- axisExplicitNegatives.set(SEND_MESSAGE_TOOL_NAME, { ...axisExplicitNegatives.get(SEND_MESSAGE_TOOL_NAME), egress: false });
2320
- const delegationForRevive = (spec.tools ?? []).find((t) => t.agentListing !== undefined);
2321
- const reviveSpawn = deps.backgroundAgentStore !== undefined && deps.mailboxStore !== undefined ? buildTier3ReviveSpawn(delegationForRevive, enrichSpecToolCtx) : undefined;
2322
- tools.push(firstPartyOffload(createSendMessageTool({
2323
- runner: runnerSelf,
2324
- registry: defaultTaskRegistry,
2325
- ...(subagentRetain ? { retain: subagentRetain } : {}),
2326
- owner: hostTaskId,
2327
- scope: taskScope,
2328
- ...(sessionId !== undefined ? { sessionId } : {}),
2329
- ...(internals?.onTaskNotification !== undefined ? { notify: internals.onTaskNotification } : {}), ...(spec.oneShot !== undefined ? { oneShot: spec.oneShot } : {}), retrievalToolMounted: delegationSurfaceActive,
2330
- ...(internals?.onSubagentSpawn !== undefined ? { sink: internals.onSubagentSpawn } : {}),
2331
- ...(internals?.parentNotify !== undefined
2332
- ? { uplink: internals.parentNotify, ...(internals.parentPeerRef !== undefined ? { uplinkRecipient: internals.parentPeerRef } : {}) }
2333
- : {}),
2334
- ...(internals?.explicitAgentName !== undefined ? { senderName: internals.explicitAgentName } : {}),
2335
- ...(internals?.parentRetainLedger !== undefined ? { siblingRetain: internals.parentRetainLedger } : {}),
2336
- ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
2337
- ...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}),
2338
- enrichCtx: enrichSpecToolCtx,
2339
- ...(deps.rosterStore !== undefined ? { roster: deps.rosterStore } : {}),
2340
- ...(deps.peerAdmission !== undefined ? { admission: deps.peerAdmission } : {}),
2341
- ...(deps.onNotice !== undefined ? { onNotice: deps.onNotice } : {}),
2342
- ...(internals?.peerSelfRef !== undefined ? { peerSelf: internals.peerSelfRef } : {}),
2343
- ...(internals?.peerInboundChainRef !== undefined ? { peerInbound: internals.peerInboundChainRef } : {}),
2344
- ...(deps.onBackgroundChildEvent ? { onBackgroundChildEvent: deps.onBackgroundChildEvent } : {}),
2345
- ...(deps.backgroundAgentStore !== undefined ? { agentStore: deps.backgroundAgentStore } : {}),
2346
- ...(deps.mailboxStore !== undefined ? { mailbox: deps.mailboxStore } : {}),
2347
- ...(reviveSpawn !== undefined ? { reviveSpawn } : {}),
2348
- ...(peerLaneActive ? peerLaneSendMessageSeats({ peerDirectory: deps.peerDirectory, sessionId, scope: taskScope, ...(internals?.explicitAgentName !== undefined ? { name: internals.explicitAgentName } : {}), refs: peerLaneRefs, listingMounted: listAgentsMountable({ exclude: toolFaceSnapshot.exclude, specTools: spec.tools ?? [] }) }) : {}),
2349
- onNotifyError: (f) => emitTrace(deps.tracer, () => ({ kind: "observer.notify_failed", version: 1, taskId: hostTaskId, site: f.site, message: f.error.message, ts: Date.now() })),
2350
- onTranscriptIntegrityGap: (handle, scope) => deliverEngineNotice(deps.onNotice, {
2351
- code: "delegation.transcript_integrity",
2352
- message: `delegation transcript integrity: agent ${handle}'s durable row binds a transcript session the session store attests is gone — the declared transcript durability is being contradicted (check the session store wiring/retention)`,
2353
- detail: { handle, ...(scope !== undefined ? { scope } : {}) },
2354
- }),
2355
- })));
2356
- if (delegationSurfaceActive && !(spec.tools ?? []).some((t) => t.name === AGENT_TRANSCRIPT_TOOL_NAME)) {
2357
- tools.push(firstPartyOffload(createAgentTranscriptTool({
2358
- runner: runnerSelf,
2359
- registry: defaultTaskRegistry,
2360
- agentStore: deps.backgroundAgentStore,
2361
- owner: hostTaskId,
2362
- scope: taskScope,
2363
- ...(sessionId !== undefined ? { sessionId } : {}),
2364
- enrichCtx: enrichSpecToolCtx,
2365
- onTranscriptIntegrityGap: (handle, scope) => deliverEngineNotice(deps.onNotice, {
2366
- code: "delegation.transcript_integrity",
2367
- message: `delegation transcript integrity: agent ${handle}'s durable row binds a transcript session the session store attests is gone — the declared transcript durability is being contradicted (check the session store wiring/retention)`,
2368
- detail: { handle, ...(scope !== undefined ? { scope } : {}) },
2369
- }),
2370
- })));
2371
- }
2372
- }
2373
- }
2374
- const listAgents = peerLaneActive && peerSendMessageBuiltIn && listAgentsMountable({ exclude: toolFaceSnapshot.exclude, specTools: spec.tools ?? [] }) ? mountListAgents({ peerDirectory: deps.peerDirectory, sessionId, scope: taskScope, hostTaskId, ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}), ...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}), registry: defaultTaskRegistry, ...(deps.rosterStore !== undefined ? { roster: deps.rosterStore } : {}), specTools: spec.tools ?? [], toolEffects }) : undefined;
2375
- if (listAgents !== undefined)
2376
- tools.push(firstPartyOffload(listAgents));
2377
- if (backgroundTaskToolsActive) {
2378
- toolEffects.set("Monitor", "write");
2379
- envHandToolNames.add("Monitor");
2380
- tools.push(firstPartyOffload(createMonitorTool(executionEnv, {
2381
- registry: defaultTaskRegistry,
2382
- owner: hostTaskId,
2383
- scope: taskScope,
2384
- ...(sessionId !== undefined ? { sessionId } : {}),
2385
- ...(internals?.onTaskNotification !== undefined ? { onTaskNotification: internals.onTaskNotification } : {}),
2386
- ...(handsCwdRef !== undefined ? { cwdRef: handsCwdRef } : {}),
2387
- ...(offloadStore !== undefined ? { toolResultStore: offloadStore } : {}),
2388
- ...(spec.retainBackgroundProcesses === true ? { retainBackgroundProcesses: true } : {}),
2389
- })));
2390
- }
2391
- if (handsCwdRef !== undefined) {
2392
- toolEffects.set("EnterWorktree", "write");
2393
- toolEffects.set("ExitWorktree", "write");
2394
- worktreeSessionRef = {
2395
- ...(resume?.seed.activeWorktree
2396
- ? {
2397
- current: {
2398
- ...resume.seed.activeWorktree,
2399
- worktreeDir: rebaseRestoredPath(resume.seed.activeWorktree.worktreeDir),
2400
- originalCwd: rebaseRestoredPath(resume.seed.activeWorktree.originalCwd),
2401
- },
2402
- }
2403
- : wsSnapshot?.activeWorktree !== undefined
2404
- ? {
2405
- current: {
2406
- ...wsSnapshot.activeWorktree,
2407
- worktreeDir: rebaseWsPath(wsSnapshot.activeWorktree.worktreeDir),
2408
- originalCwd: rebaseWsPath(wsSnapshot.activeWorktree.originalCwd),
2409
- },
2410
- }
2411
- : {}),
2412
- };
2413
- if (workspaceStateSettle !== undefined)
2414
- workspaceStateSettle.restoredWorktreeDir = worktreeSessionRef.current?.worktreeDir;
2415
- tools.push(...createWorktreeTools(executionEnv, { repoRoot: taskRootFinal, cwdRef: handsCwdRef, session: worktreeSessionRef }).map((t) => (envHandToolNames.add(t.name), firstPartyOffload(t))));
2416
- }
2417
- if (offloadStore)
2418
- tools.push(createReadToolResultTool(offloadStore));
2419
- const onQuestion = frozenOnQuestion;
2420
- const liveQuestionFace = isLiveQuestionFace(onQuestion) ? onQuestion : undefined;
2421
- const contentAskBindings = new Map();
2422
- const CONTENT_ASK_BINDING_CAP = 32;
2423
- const checkpointStore = resolveCheckpointStore(spec, deps);
2424
- const questionParkStoreWired = checkpointStore !== undefined;
2425
- const questionDurableMandateBinding = runtimeCaps?.forceDurableGate === true && questionParkStoreWired;
2426
- const contentAskRoutable = (toolCallId) => liveQuestionFace !== undefined &&
2427
- mountedQuestionTool !== undefined &&
2428
- tools.includes(mountedQuestionTool) &&
2429
- !questionDurableMandateBinding &&
2430
- !inheritedUnavailableAsks.has(toolCallId);
2431
- const lateStrandedAnswers = [];
2432
- const discloseStrandedAnswers = (records, why) => {
2433
- if (records.length === 0)
2434
- return;
2435
- try {
2436
- deps.onError?.(new Error(`AskUserQuestion: ${records.length} question(s) were answered by a person but the call never ` +
2437
- `executed to collect the answer (${records.map((r) => `${r.toolCallId} [delivery ${r.deliveryId}]`).join(", ")}) — ${why}. The answer(s) were NOT ` +
2438
- `delivered to the model and are gone with this leg; re-ask if the decision is still needed.`), { phase: "degraded", sessionId, classification: "unconsumed-human-answer" });
2439
- }
2440
- catch {
2441
- }
2442
- };
2443
- const settleContentAskBindings = () => {
2444
- const stranded = [];
2445
- for (const [callId, bound] of contentAskBindings) {
2446
- if (bound.kind === "answered")
2447
- stranded.push({ deliveryId: bound.deliveryId, toolCallId: callId });
2448
- }
2449
- contentAskBindings.clear();
2450
- discloseStrandedAnswers(stranded, "the leg ended first (abort, batch teardown, or a loop failure)");
2451
- const byDelivery = new Map();
2452
- for (const r of [...stranded, ...lateStrandedAnswers])
2453
- byDelivery.set(r.deliveryId, r);
2454
- return [...byDelivery.values()];
2455
- };
2456
- const durableQuestionFace = questionParkStoreWired && (spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true);
2457
- const mountedQuestionFace = liveQuestionFace !== undefined
2458
- ? async (req, signal) => {
2459
- const bound = contentAskBindings.get(req.toolCallId);
2460
- if (bound !== undefined) {
2461
- if (bound.questionsHash === boundInputHashOf(req.questions)) {
2462
- contentAskBindings.delete(req.toolCallId);
2463
- if (bound.kind === "answered")
2464
- return bound.answer;
2465
- throw bound.error;
2466
- }
2467
- contentAskBindings.delete(req.toolCallId);
2468
- }
2469
- return liveQuestionFace(req, signal);
2470
- }
2471
- : onQuestion;
2472
- const questionToolMounted = spec.interactiveTools === true || (spec.interactiveTools !== false && (onQuestion !== undefined || durableQuestionFace));
2473
- let mountedQuestionTool;
2474
- if (questionToolMounted)
2475
- tools.push((mountedQuestionTool =
2476
- createAskUserQuestionTool(mountedQuestionFace, { principal: spec.principal, sourceTaskId: sessionId }, {
2477
- ...(resume?.redeemedContentAskCallId !== undefined
2478
- ? {
2479
- redeemedApprovalCallId: resume.redeemedContentAskCallId,
2480
- ...(resume.redeemedContentAskQuestionsHash !== undefined ? { redeemedApprovalQuestionsHash: resume.redeemedContentAskQuestionsHash } : {}),
2481
- }
2482
- : {}),
2483
- ...(resolvedInteractionPosture !== undefined ? { posture: resolvedInteractionPosture } : {}),
2484
- ...(spec.interactiveQuestionFallback === true ? { interactiveFallback: true } : {}),
2485
- onSyntheticContinuation: ({ questionId, reason }) => {
2486
- deps.onError?.(new Error(`AskUserQuestion ${questionId}: no human answer was obtainable (` +
2487
- (reason === "seam_absent"
2488
- ? "no onQuestion seam is wired"
2489
- : reason === "declined_unavailable"
2490
- ? "the wired question channel reported nobody was reachable"
2491
- : "the wired question channel failed") +
2492
- `) — the model was instructed to self-answer and the run CONTINUES (warning, not a failure).`), { phase: "degraded", sessionId, classification: "no-human-autoanswered" });
2493
- },
2494
- })));
977
+ const { worktreeSessionRef } = prepareDelegationSurface({ spec, deps, internals, resume, sessionId, hostTaskId, taskScope, taskRootFinal, executionEnv, runnerSelf, backgroundTaskToolsActive, workflowToolsActive, peerLaneActive, peerSendMessageBuiltIn, peerLaneRefs, tools, toolEffects, axisExplicitNegatives, envHandToolNames, toolFaceSnapshot, firstPartyOffload, offloadStore, subagentRetain, enrichSpecToolCtx, handsCwdRef, wsSnapshot, rebaseWsPath, rebaseRestoredPath, workspaceStateSettle });
978
+ const { checkpointStore, liveQuestionFace, contentAskBindings, contentAskRoutable, lateStrandedAnswers, discloseStrandedAnswers, settleContentAskBindings, durableQuestionFace, questionToolMounted, mountedQuestionTool } = prepareQuestionFace({ frozenOnQuestion, spec, deps, runtimeCaps, resolvedInteractionPosture, resume, sessionId, tools, inheritedUnavailableAsks: () => inheritedUnavailableAsks });
2495
979
  if (spec.handsReadOnly !== true) {
2496
980
  const sessionScope = sessionId ?? spec.principal ?? spec.taskId ?? "default";
2497
981
  tools.push(...createSchedulerTools(executionEnv, {
@@ -2509,27 +993,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2509
993
  deps.onError?.(new Error(`Fork DENIED for principal "${spec.principal ?? ""}" by runtimeCaps.allowFork=false ` +
2510
994
  `(per-principal entitlement governance, not a misconfiguration — Agent(subagent_type:"fork") will refuse honestly)`), { phase: "config", sessionId });
2511
995
  }
2512
- const lspManager = spec.lspManager ?? deps.lspManager;
2513
- if (lspManager) {
2514
- const lspRoot = taskRootFinal;
2515
- tools.push(createLspTool(lspManager, { isPathIgnored: gitCheckIgnoreFilter(executionEnv, lspRoot), env: executionEnv }));
2516
- envHandToolNames.add("LSP");
2517
- }
2518
- const lspDiagnostics = spec.lspDiagnostics !== false && lspManager?.diagnostics !== undefined && handsEnabled && spec.handsReadOnly !== true
2519
- ? lspManager.diagnostics
2520
- : undefined;
2521
- const lspRunIdent = sessionId;
2522
- const nudgeLspOnEdit = lspDiagnostics
2523
- ? (rawPath) => {
2524
- const baseDir = handsCwdRef?.current ?? taskRootFinal;
2525
- const filePath = resolveLspPath(rawPath, baseDir);
2526
- lspDiagnostics.fileEdited(lspRunIdent, pathToUri(filePath));
2527
- void lspManager
2528
- .sessionFor(filePath, undefined, executionEnv)
2529
- .then((session) => session?.notifyFileChanged?.(filePath))
2530
- .catch(() => undefined);
2531
- }
2532
- : undefined;
996
+ const { lspDiagnostics, nudgeLspOnEdit, lspRunIdent } = prepareLsp({ spec, deps, executionEnv, taskRootFinal, handsEnabled, sessionId, handsCwdRef, tools, envHandToolNames });
2533
997
  const memoryPairNameDomain = new Set();
2534
998
  for (const t of tools) {
2535
999
  memoryPairNameDomain.add(t.name);
@@ -2621,7 +1085,6 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2621
1085
  live.incomplete = true;
2622
1086
  }
2623
1087
  }
2624
- let memoryBlock = memoryBlockFromEngine;
2625
1088
  if (memorySeedFiles?.length && seedContextFiles) {
2626
1089
  try {
2627
1090
  await seedContextFiles(memorySeedFiles);
@@ -2630,366 +1093,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2630
1093
  deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
2631
1094
  }
2632
1095
  }
2633
- let instructionSources;
2634
- let projectInstructionContent;
2635
- if (deps.loadProjectMemory) {
2636
- let projectMemoryPhase = spec.sessionId ? "resume" : "fresh";
2637
- if (spec.sessionId) {
2638
- try {
2639
- const branch = await session.getBranch();
2640
- const hasConversation = hasConversationContent(branch);
2641
- if (!hasConversation)
2642
- projectMemoryPhase = "fresh";
2643
- else {
2644
- for (let i = branch.length - 1; i >= 0; i--) {
2645
- const e = branch[i];
2646
- if (e.type === "compaction") {
2647
- projectMemoryPhase = "post-compact";
2648
- break;
2649
- }
2650
- if (e.type === "message" && e.message.role === "user")
2651
- break;
2652
- }
2653
- }
2654
- }
2655
- catch {
2656
- }
2657
- }
2658
- let loaded = null;
2659
- try {
2660
- loaded = await Promise.resolve(deps.loadProjectMemory({
2661
- cwd: taskRootFinal,
2662
- handsEnabled,
2663
- isSubagent: delegation.isNonForkChild,
2664
- ...(internals?.agentName ? { agentName: internals.agentName } : {}),
2665
- sessionId,
2666
- phase: projectMemoryPhase,
2667
- }));
2668
- }
2669
- catch (err) {
2670
- deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
2671
- }
2672
- const projectMem = loaded !== null && typeof loaded === "object" ? loaded.content : loaded;
2673
- const seededFiles = loaded !== null && typeof loaded === "object" ? loaded.seededFiles : undefined;
2674
- const declaredSources = loaded !== null && typeof loaded === "object" ? loaded.instructionSources : undefined;
2675
- if (declaredSources !== undefined && declaredSources.length > 0)
2676
- instructionSources = declaredSources;
2677
- if (seededFiles?.length && seedContextFiles) {
2678
- try {
2679
- await seedContextFiles(seededFiles);
2680
- }
2681
- catch (err) {
2682
- deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
2683
- }
2684
- }
2685
- if (projectMem != null && projectMem.trim()) {
2686
- projectInstructionContent = projectMem;
2687
- const projectBlock = `${PROJECT_CONTEXT_FRAMING}\n\n${composeMemoryBlock(projectMem, "project")}`;
2688
- memoryBlock = memoryBlock ? `${memoryBlock}\n\n${projectBlock}` : projectBlock;
2689
- }
2690
- }
2691
- const declaredSkillRank = new Map();
2692
- for (const s of spec.skills ?? [])
2693
- if (!declaredSkillRank.has(s.name))
2694
- declaredSkillRank.set(s.name, declaredSkillRank.size);
2695
- const skillSpecs = normalizeSkills(spec.skills ?? []).filter((s) => {
2696
- if (s.content.length <= SKILL_CONTENT_MAX_CHARS)
2697
- return true;
2698
- deps.onError?.(new Error(`Skill "${s.name}" content is ${s.content.length} chars, over the ${SKILL_CONTENT_MAX_CHARS}-char load limit — skill not loaded (skills are never truncated; shrink the body or move material to attachments/files).`), { phase: "config", sessionId });
2699
- return false;
2700
- });
2701
- const inheritedFrames = internals?.inheritedManifestScope ?? [];
2702
- for (const frame of inheritedFrames) {
2703
- skillScope.push(frame);
2704
- }
2705
- const hasSkillManifest = skillSpecs.some((s) => s.manifest !== undefined) || inheritedFrames.length > 0;
2706
- if (skillSpecs.length > 0) {
2707
- if (tools.some((t) => t.name === SKILL_TOOL_NAME)) {
2708
- const e = new Error(`Tool name "${SKILL_TOOL_NAME}" is reserved when spec.skills is present.`);
2709
- e.code = "config.reserved_tool_name";
2710
- throw e;
2711
- }
2712
- tools.push(createSkillTool(skillSpecs, skillScope));
2713
- }
2714
- const sharedMemoryProvider = deps.sharedMemoryStores;
2715
- let sharedMemoryPairMounted = false;
2716
- if (sharedMemoryProvider !== undefined) {
2717
- const excluded = new Set(toolFaceSnapshot.exclude ?? []);
2718
- const mountedNameDomain = new Set();
2719
- for (const t of tools) {
2720
- mountedNameDomain.add(t.name);
2721
- for (const alias of t.aliases ?? [])
2722
- mountedNameDomain.add(alias);
2723
- }
2724
- const excludedName = SHARED_MEMORY_TOOL_NAMES.find((n) => excluded.has(n));
2725
- const occupiedName = SHARED_MEMORY_TOOL_NAMES.find((n) => mountedNameDomain.has(n));
2726
- if (excludedName !== undefined || occupiedName !== undefined) {
2727
- onceLedger.onError(new Error(`Shared memory tools ${SHARED_MEMORY_TOOL_NAMES.join("/")} were NOT mounted: ` +
2728
- (excludedName !== undefined
2729
- ? `excludeTools removes "${excludedName}"`
2730
- : `this task already declares a tool named "${occupiedName}" (the built-in yields to it)`) +
2731
- " — the pair mounts together or not at all."), { phase: "config", sessionId, classification: "shared-memory-not-mounted" });
2732
- }
2733
- else {
2734
- tools.push(...createSharedMemoryTools({
2735
- provider: sharedMemoryProvider,
2736
- context: { sessionId, taskId: hostTaskId, ...(spec.principal !== undefined ? { principal: spec.principal } : {}) },
2737
- }).map((s) => defineTool(s)));
2738
- sharedMemoryPairMounted = true;
2739
- }
2740
- }
2741
- let memoryEnginePairMounted = false;
2742
- if (memoryTools !== undefined && memoryTools.length > 0) {
2743
- tools.push(...memoryTools.map((s) => defineTool(s)));
2744
- memoryEnginePairMounted = true;
2745
- }
2746
- else if (memoryEngineSession !== undefined && !memorySearchToolsPlanned) {
2747
- onceLedger.onError(new Error(`Memory tools ${MEMORY_ENGINE_TOOL_NAMES.join("/")} were NOT mounted: ` +
2748
- (memoryPairExcludedName !== undefined
2749
- ? `excludeTools removes "${memoryPairExcludedName}"`
2750
- : `this task already declares a tool named "${memoryPairOccupiedName}" (the built-in yields to it)`) +
2751
- " — these tools mount together or not at all."), { phase: "config", sessionId, classification: "memory-tools-not-mounted" });
2752
- }
2753
- const skillsListing = skillSpecs.length > 0
2754
- ? {
2755
- entries: skillSpecs.map((s) => ({
2756
- name: s.name,
2757
- description: s.description,
2758
- ...(s.files !== undefined ? { files: s.files.map((f) => ({ path: f.path })) } : {}),
2759
- ...(declaredSkillRank.get(s.name) !== undefined ? { declaredRank: declaredSkillRank.get(s.name) } : {}),
2760
- })),
2761
- seedAnnounced: resume !== undefined || spec.sessionId !== undefined,
2762
- }
2763
- :
2764
- (resume?.seed.announcedListings?.skills?.length ?? 0) > 0 || spec.sessionId !== undefined
2765
- ?
2766
- { entries: [], seedAnnounced: true }
2767
- : undefined;
2768
- const provider = spec.promptProvider ?? deps.promptProvider ?? defaultPromptProvider;
2769
- const promptPolicyEnabled = Boolean(lockedPreflight.toolPolicy);
2770
- const promptHooks = spec.hooks ?? deps.hooks;
2771
- const promptHooksEnabled = Boolean(promptHooks?.preToolUse || promptHooks?.postToolUse);
2772
- const failClosedReason = selfOrchestrationFailClosedReason(spec, deps);
2773
- if (failClosedReason) {
2774
- deps.onError?.(new Error(failClosedReason), { phase: "config", sessionId });
2775
- }
2776
- const featureFlags = {
2777
- policyEnabled: promptPolicyEnabled,
2778
- hooksEnabled: promptHooksEnabled,
2779
- isolationEnabled: isIsolated(executionEnv),
2780
- reminderMark,
2781
- readFaceOpen: resolvedReadFace === "open",
2782
- orchestrationEnabled: selfOrchestrationActive,
2783
- orchestrationDeferred: selfOrchestrationActive && (toolFaceSnapshot.defer?.includes("Workflow") ?? false),
2784
- promptProfile,
2785
- fableMitigations,
2786
- goalEnabled: internals?.goalMode === true,
2787
- awarenessEnabled: thinking !== undefined && ULTRA_REASONING_TIERS.has(thinking),
2788
- worktreeIsolated: internals?.isolation === "worktree" && ownedEnv !== undefined,
2789
- withinTaskCompactionEnabled: (spec.compaction?.enabled ?? true) && (spec.compaction?.withinTask ?? true),
2790
- isSubagent: delegation.isNonForkChild,
2791
- };
2792
- const userSystemPrompt = spec.systemPrompt ?? resolvedRole.systemPrompt ?? internals?.defaultSystemPrompt;
2793
- const userAppendSystemPrompt = spec.appendSystemPrompt;
2794
- const mcpInstructionsBlock = mcp.serverInstructions.length
2795
- ? `# MCP Server Instructions\n${mcp.serverInstructions.map((s) => fenceMcpServerInstructions(s.server, s.text)).join("\n\n")}`
2796
- : undefined;
2797
- const userTz = spec.clientContext?.timeZone;
2798
- const tzValid = userTz !== undefined && isValidTimeZone(userTz);
2799
- const envFacts = { date: formatLocalDate(new Date(), tzValid ? userTz : undefined), modelId: model.id };
2800
- if (tzValid)
2801
- envFacts.timeZone = userTz;
2802
- if (spec.clientContext?.userEmail)
2803
- envFacts.userEmail = spec.clientContext.userEmail;
2804
- const declaredCutoff = deps.modelKnowledgeCutoffs?.[model.id];
2805
- if (declaredCutoff)
2806
- envFacts.knowledgeCutoff = declaredCutoff;
2807
- if (spec.envFacts?.profile)
2808
- envFacts.sandboxProfile = spec.envFacts.profile;
2809
- if (spec.envFacts?.capabilities && spec.envFacts.capabilities.length > 0)
2810
- envFacts.sandboxCapabilities = [...spec.envFacts.capabilities];
2811
- if (spec.envFacts?.pkgSource)
2812
- envFacts.sandboxPkgSource = spec.envFacts.pkgSource;
2813
- if (spec.envFacts?.egress === "none" || spec.envFacts?.egress === "allowlist" || spec.envFacts?.egress === "full")
2814
- envFacts.sandboxEgress = spec.envFacts.egress;
2815
- if (spec.envFacts?.scratchpadDir)
2816
- envFacts.scratchpadDir = spec.envFacts.scratchpadDir;
2817
- if (externalContentTargetActive)
2818
- envFacts.externalContentTarget = true;
2819
- if (resume !== undefined && spec.envFacts?.resumeFacts) {
2820
- const rf = spec.envFacts.resumeFacts;
2821
- const copy = {};
2822
- if (rf.processes === "preserved" || rf.processes === "lost")
2823
- copy.processes = rf.processes;
2824
- if (rf.scratch === "preserved" || rf.scratch === "lost")
2825
- copy.scratch = rf.scratch;
2826
- if (typeof rf.note === "string" && rf.note.length > 0)
2827
- copy.note = rf.note;
2828
- if (Object.keys(copy).length > 0)
2829
- envFacts.resumeFacts = copy;
2830
- }
2831
- if (handsEnabled) {
2832
- envFacts.cwd = taskRootFinal;
2833
- if (additionalRootsCanonical.length > 0) {
2834
- envFacts.additionalDirectories = [...additionalRootsCanonical];
2835
- }
2836
- if (resolvedReadFace !== undefined)
2837
- envFacts.readFace = resolvedReadFace;
2838
- if (additionalReadRootsCanonical.length > 0) {
2839
- envFacts.additionalReadDirectories = [...additionalReadRootsCanonical];
2840
- }
2841
- try {
2842
- const probe = await executionEnv.exec('uname -s; uname -r; (git rev-parse --is-inside-work-tree 2>/dev/null || echo false); (git symbolic-ref --short -q HEAD 2>/dev/null || echo "HEAD (detached)"); (git rev-parse --show-toplevel 2>/dev/null || echo); (test -n "$(git status --porcelain 2>/dev/null | head -1)" && echo dirty || echo clean); (s=$(ps -p $$ -o comm= 2>/dev/null); s=${s##*/}; echo "${s#-}"); (test "$(git rev-parse --git-dir 2>/dev/null)" != "$(git rev-parse --git-common-dir 2>/dev/null)" && echo linked || echo main); (pwd -P 2>/dev/null || pwd)', { cwd: envFacts.cwd, timeout: 10 });
2843
- if (probe.ok && probe.value.exitCode === 0) {
2844
- const lines = probe.value.stdout.split("\n");
2845
- if (lines.length >= 6) {
2846
- const [osName, osVer, git, branch, worktreeRoot, dirty] = lines;
2847
- if (osName?.trim())
2848
- envFacts.platform = osName.trim();
2849
- if (osVer?.trim())
2850
- envFacts.osVersion = osVer.trim();
2851
- envFacts.isGitRepo = (git ?? "").trim() === "true";
2852
- if (envFacts.isGitRepo) {
2853
- const b = (branch ?? "").trim();
2854
- if (b)
2855
- envFacts.gitBranch = b;
2856
- const root = (worktreeRoot ?? "").trim();
2857
- if (root)
2858
- envFacts.gitWorktreeRoot = root;
2859
- const d = (dirty ?? "").trim();
2860
- if (d === "dirty" || d === "clean")
2861
- envFacts.gitDirty = d === "dirty";
2862
- }
2863
- const sh = (lines[6] ?? "").trim();
2864
- if (sh)
2865
- envFacts.shell = sh;
2866
- if (envFacts.isGitRepo && (lines[7] ?? "").trim() === "linked")
2867
- envFacts.isLinkedWorktree = true;
2868
- const probedPwd = (lines[8] ?? "").trim();
2869
- if (probedPwd && envFacts.cwd && probedPwd !== envFacts.cwd) {
2870
- const canonOf = async (p) => {
2871
- try {
2872
- const c = await executionEnv.canonicalPath(p);
2873
- return c.ok ? c.value : p;
2874
- }
2875
- catch {
2876
- return p;
2877
- }
2878
- };
2879
- const canonicalProbed = await canonOf(probedPwd);
2880
- if (canonicalProbed !== (await canonOf(envFacts.cwd))) {
2881
- try {
2882
- deps.onError?.(new Error(`execution-env cwd misalignment: task root is "${envFacts.cwd}" but the env's shell reports pwd "${canonicalProbed}" — per-exec cwd injection may be ignored on this lane (execs/artifacts can land outside the workspace root)`), { phase: "config", sessionId });
2883
- }
2884
- catch {
2885
- }
2886
- }
2887
- }
2888
- }
2889
- }
2890
- }
2891
- catch {
2892
- }
2893
- }
2894
- const gitStatusRef = await probeGitStatusLane({
2895
- executionEnv,
2896
- envFacts,
2897
- handsEnabled,
2898
- taskRoot: taskRootFinal,
2899
- onDegrade: (reason) => deps.onError?.(new Error(`env git snapshot degraded — ${reason}`), { phase: "degraded", sessionId, classification: "env-git-snapshot" }),
2900
- });
2901
- if (toolFaceSnapshot.exclude !== undefined && toolFaceSnapshot.exclude.length > 0) {
2902
- const excluded = new Set(toolFaceSnapshot.exclude);
2903
- for (let i = tools.length - 1; i >= 0; i--)
2904
- if (excluded.has(tools[i].name))
2905
- tools.splice(i, 1);
2906
- }
2907
- if (handsEnabled) {
2908
- const fullShellOnRoster = tools.some((t) => t.name === "Bash" && getToolContract(t, "core").contractId === FULL_SHELL_CONTRACT_ID);
2909
- if (fullShellOnRoster !== fullShellReachable) {
2910
- const e = new Error(`internal invariant: fullShellReachable=${String(fullShellReachable)} but the post-exclusion roster ` +
2911
- `${fullShellOnRoster ? "carries" : "does not carry"} the full shell (${FULL_SHELL_CONTRACT_ID}). ` +
2912
- `A new roster-affecting mechanism was added without updating the predicate.`);
2913
- e.code = "internal.full_shell_reachable_mismatch";
2914
- throw e;
2915
- }
2916
- }
2917
- if (promptProfile === "classic") {
2918
- for (let i = 0; i < tools.length; i++) {
2919
- const t = tools[i];
2920
- if (t.descriptionClassic !== undefined)
2921
- tools[i] = { ...t, description: t.descriptionClassic };
2922
- }
2923
- }
2924
- const userToolNames = (spec.tools ?? []).map((t) => t.name);
2925
- const protocolToolNames = [...mcp.tools.map((t) => t.name), ...a2a.tools.map((t) => t.name)];
2926
- const mcpAlwaysLoadNames = mcp.tools
2927
- .filter((t) => t.mcpAlwaysLoad === true && !(toolFaceSnapshot.defer ?? []).includes(t.name))
2928
- .map((t) => t.name);
2929
- const classifyDeferredOverFace = (face, builtinDeferNames) => {
2930
- const deferredSet = classifyDeferred({
2931
- specs: spec.tools ?? [],
2932
- protocolToolNames,
2933
- fullTools: face.filter((t) => userToolNames.includes(t.name) || protocolToolNames.includes(t.name)),
2934
- deferMode: deps.deferMode,
2935
- model,
2936
- deferNames: [...(toolFaceSnapshot.defer ?? []), ...builtinDeferNames].filter((n) => face.some((t) => t.name === n)),
2937
- alwaysLoadNames: [
2938
- ASK_USER_QUESTION_TOOL_NAME,
2939
- ...(toolFaceSnapshot.alwaysLoad ?? []),
2940
- ...mcpAlwaysLoadNames,
2941
- ],
2942
- });
2943
- for (const n of [...deferredSet]) {
2944
- if (!face.some((t) => t.name === n))
2945
- deferredSet.delete(n);
2946
- }
2947
- return deferredSet;
2948
- };
2949
- const sharedMemoryPair = sharedMemoryPairMounted ? SHARED_MEMORY_TOOL_NAMES.filter((n) => tools.some((t) => t.name === n)) : [];
2950
- const memoryEnginePair = explicitlyDeferredMemoryTrio(memoryEnginePairMounted, tools, toolFaceSnapshot.defer);
2951
- const builtinDeferPairNames = [...sharedMemoryPair, ...memoryEnginePair];
2952
- let deferred;
2953
- if (builtinDeferPairNames.length > 0) {
2954
- const withoutPairs = tools.filter((t) => !builtinDeferPairNames.some((n) => n === t.name));
2955
- const d1 = classifyDeferredOverFace(tools, builtinDeferPairNames);
2956
- const d0 = classifyDeferredOverFace(withoutPairs, []);
2957
- const soleCause = d0.size === 0 && d1.size > 0;
2958
- const supportNameTaken = tools.some((t) => t.name === TOOL_SEARCH_NAME || (t.aliases ?? []).includes(TOOL_SEARCH_NAME));
2959
- if (soleCause && supportNameTaken) {
2960
- const retractNames = memoryGroupRetractionSet(builtinDeferPairNames, memoryEnginePair.length > 0);
2961
- for (let i = tools.length - 1; i >= 0; i--) {
2962
- if (retractNames.has(tools[i].name))
2963
- tools.splice(i, 1);
2964
- }
2965
- if (sharedMemoryPair.length > 0) {
2966
- sharedMemoryPairMounted = false;
2967
- onceLedger.onError(new Error(`Shared memory tools ${SHARED_MEMORY_TOOL_NAMES.join("/")} were NOT mounted: mounting them would inject ` +
2968
- `the "${TOOL_SEARCH_NAME}" tool, whose name this task already declares — the pair mounts together or not at all.`), { phase: "config", sessionId, classification: "shared-memory-not-mounted" });
2969
- }
2970
- if (memoryEnginePair.length > 0) {
2971
- memoryEnginePairMounted = false;
2972
- if (memoryBlock !== undefined && memoryRecallSegment !== undefined) {
2973
- const segment = memoryRecallSegment;
2974
- if (memoryBlock === segment)
2975
- memoryBlock = undefined;
2976
- else if (memoryBlock.includes(`\n\n${segment}`))
2977
- memoryBlock = memoryBlock.replace(`\n\n${segment}`, "");
2978
- else if (memoryBlock.startsWith(`${segment}\n\n`))
2979
- memoryBlock = memoryBlock.slice(segment.length + 2);
2980
- }
2981
- onceLedger.onError(new Error(`Memory tools ${MEMORY_ENGINE_TOOL_NAMES.join("/")} were NOT mounted: mounting them would inject ` +
2982
- `the "${TOOL_SEARCH_NAME}" tool, whose name this task already declares — these tools mount together or not at all.`), { phase: "config", sessionId, classification: "memory-tools-not-mounted" });
2983
- }
2984
- deferred = d0;
2985
- }
2986
- else {
2987
- deferred = d1;
2988
- }
2989
- }
2990
- else {
2991
- deferred = classifyDeferredOverFace(tools, []);
2992
- }
1096
+ const projectContext = await prepareProjectContext({ deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillScope, tools, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume });
1097
+ const { memoryBlock: memoryBlockWithProject, instructionSources, projectInstructionContent, hasSkillManifest, skillsListing, sharedMemoryPairMounted, memoryEnginePairMounted } = projectContext;
1098
+ const { provider, featureFlags, userSystemPrompt, userAppendSystemPrompt, mcpInstructionsBlock, envFacts, tzValid, userTz, gitStatusRef } = await preparePromptInputs({ spec, deps, failClosedReason: selfOrchestrationFailClosedReason(spec, deps), lockedPreflight, selfOrchestrationActive, executionEnv, reminderMark, resolvedReadFace, toolFaceSnapshot, promptProfile, fableMitigations, internals, thinking, ownedEnv, delegation, resolvedRole, mcp, model, externalContentTargetActive, resume, handsEnabled, taskRootFinal, additionalRootsCanonical, additionalReadRootsCanonical, sessionId });
1099
+ const { deferred, memoryBlock } = prepareDeferClassify({ spec, deps, tools, toolFaceSnapshot, handsEnabled, fullShellReachable, promptProfile, model, mcp, a2a, sharedMemoryPairMounted, memoryEnginePairMounted, memoryBlock: memoryBlockWithProject, memoryRecallSegment, onceLedger, sessionId });
2993
1100
  const delegationProvenanceChannel = internals?.delegationProvenance;
2994
1101
  if (memoryEngineSession !== undefined || delegationProvenanceChannel !== undefined) {
2995
1102
  const pollution = memoryEngineSession?.pollution;
@@ -3307,263 +1414,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3307
1414
  });
3308
1415
  }
3309
1416
  }
3310
- const activeTools = new Set();
3311
- const fpRef = {};
3312
- const turnSnapshotRef = {};
3313
- let harnessTools = tools;
3314
- const failedMcpServers = onceLedger.mcpFailuresToAnnounce(mcp.statuses);
3315
- let toolsDeltaRef;
3316
- let toolMaterializeStatic = false;
3317
- let deferDirectCall = false;
3318
- const staticFaceForRef = {};
3319
- if (deferred.size > 0 || failedMcpServers.length > 0) {
3320
- toolsDeltaRef = { pending: [], pendingRemoved: [], pendingReadded: [], pendingFailed: failedMcpServers };
3321
- }
3322
- const listingRideRef = {};
3323
- {
3324
- const raw = process.env.SEMA_TOOL_MATERIALIZE_STRATEGY;
3325
- if (raw !== undefined && raw !== "swap" && raw !== "static" && deferred.size === 0) {
3326
- const line = `SEMA_TOOL_MATERIALIZE_STRATEGY=${JSON.stringify(raw)} is not "swap" or "static" — inert on this task (no deferred tools), but a deferring task WITHOUT an explicit spec strategy will refuse to prepare under it (an explicit legal spec outranks and discards it, loudly). Fix or unset the flag.`;
3327
- emitMaterializeEnvNotice(deps.onNotice, line, { raw });
3328
- }
3329
- }
3330
- if (deferred.size > 0) {
3331
- if (deferred.has(TOOL_SEARCH_NAME) || tools.some((t) => t.name === TOOL_SEARCH_NAME)) {
3332
- const e = new Error(`Tool name "${TOOL_SEARCH_NAME}" is reserved when deferred tools are present.`);
3333
- e.code = "config.reserved_tool_name";
3334
- throw e;
3335
- }
3336
- const registry = buildDeferredRegistry(deferred, tools);
3337
- const rawEnvStrategy = process.env.SEMA_TOOL_MATERIALIZE_STRATEGY;
3338
- const envStrategyInvalid = rawEnvStrategy !== undefined && rawEnvStrategy !== "swap" && rawEnvStrategy !== "static";
3339
- if (envStrategyInvalid && spec.toolMaterializeStrategy === undefined) {
3340
- const e = new Error(`SEMA_TOOL_MATERIALIZE_STRATEGY must be "swap" or "static" (got ${JSON.stringify(rawEnvStrategy)}).`);
3341
- e.code = "config.tool_materialize_invalid";
3342
- throw e;
3343
- }
3344
- if (envStrategyInvalid) {
3345
- const line = `SEMA_TOOL_MATERIALIZE_STRATEGY=${JSON.stringify(rawEnvStrategy)} was ignored — not "swap" or "static", and the task spec pins toolMaterializeStrategy=${JSON.stringify(spec.toolMaterializeStrategy)} which outranks it. Fix or unset the env flag.`;
3346
- emitMaterializeEnvNotice(deps.onNotice, line, { raw: rawEnvStrategy, specStrategy: spec.toolMaterializeStrategy });
3347
- }
3348
- const envStrategy = envStrategyInvalid ? undefined : rawEnvStrategy;
3349
- const requestedStrategy = spec.toolMaterializeStrategy ?? envStrategy ?? "swap";
3350
- const laneDegrade = requestedStrategy === "static" && spec.deferSelfResolve === false;
3351
- const materializeStatic = requestedStrategy === "static" && !laneDegrade;
3352
- toolMaterializeStatic = materializeStatic;
3353
- deferDirectCall = spec.deferSelfResolve !== false;
3354
- promptManifest.toolDisclosure = {
3355
- deferredTools: deferred.size,
3356
- strategy: materializeStatic ? "static" : "swap",
3357
- source: laneDegrade ? "degraded_no_direct_lane" : spec.toolMaterializeStrategy !== undefined ? "spec" : envStrategy !== undefined ? "env" : "default",
3358
- };
3359
- const everExempt = new Set();
3360
- const staticFaceFor = (name) => {
3361
- if (!materializeStatic)
3362
- return false;
3363
- if (everExempt.has(name))
3364
- return false;
3365
- if (staticSchemaRenderable(tools.find((t) => t.name === name)?.parameters))
3366
- return true;
3367
- everExempt.add(name);
3368
- return false;
3369
- };
3370
- staticFaceForRef.current = staticFaceFor;
3371
- let activationChain = Promise.resolve();
3372
- const serializeActivation = (section) => {
3373
- const p = activationChain.then(section);
3374
- activationChain = p.then(() => undefined, () => undefined);
3375
- return p;
3376
- };
3377
- const directCallFor = (name) => {
3378
- if (spec.deferSelfResolve === false)
3379
- return undefined;
3380
- const executionMode = tools.find((t) => t.name === name)?.executionMode;
3381
- return {
3382
- resolveReal: () => {
3383
- const real = tools.find((t) => t.name === name);
3384
- if (real === undefined)
3385
- return undefined;
3386
- return {
3387
- parameters: real.parameters,
3388
- invoke: (toolCallId, params, signal, onUpdate) => real.execute(toolCallId, params, signal, onUpdate),
3389
- };
3390
- },
3391
- ...(executionMode !== undefined ? { executionMode } : {}),
3392
- staticFace: () => staticFaceFor(name),
3393
- activate: async () => serializeActivation(async () => {
3394
- if (activeTools.has(name))
3395
- return undefined;
3396
- activeTools.add(name);
3397
- try {
3398
- await rematerialize(activeTools);
3399
- }
3400
- catch (e) {
3401
- activeTools.delete(name);
3402
- throw e;
3403
- }
3404
- return listingRideRef.current?.([name]);
3405
- }),
3406
- };
3407
- };
3408
- const placeholders = new Map([...registry.values()].map((i) => [i.name, createPlaceholderTool(i, directCallFor(i.name))]));
3409
- const { messages } = await session.buildContext();
3410
- for (const n of extractDiscoveredToolNames(messages, registry)) {
3411
- if (deferred.has(n))
3412
- activeTools.add(n);
3413
- }
3414
- for (const entry of await session.getBranch()) {
3415
- if (entry.type !== "compaction" || entry.fromHook === true)
3416
- continue;
3417
- for (const n of readCompactionActiveTools(entry.details)) {
3418
- if (deferred.has(n))
3419
- activeTools.add(n);
3420
- }
3421
- }
3422
- if (resume) {
3423
- for (const n of resume.seed.activeTools)
3424
- if (deferred.has(n))
3425
- activeTools.add(n);
3426
- }
3427
- const callableToolNames = () => {
3428
- const s = new Set(tools.map((t) => t.name));
3429
- for (const n of deferred)
3430
- if (!activeTools.has(n))
3431
- s.delete(n);
3432
- s.add(TOOL_SEARCH_NAME);
3433
- return s;
3434
- };
3435
- offloadReachableToolsRef.current = callableToolNames;
3436
- let toolSearch;
3437
- const buildToolList = (active) => {
3438
- const list = tools.map((t) => (deferred.has(t.name) && (staticFaceFor(t.name) || !active.has(t.name)) ? placeholders.get(t.name) : t));
3439
- list.push(toolSearch);
3440
- return list;
3441
- };
3442
- const announcedTools = new Set(activeTools);
3443
- const withdrawnTools = new Set();
3444
- const deltaRef = toolsDeltaRef;
3445
- const rematerialize = async (active) => {
3446
- contentOriginWrapRef.current?.();
3447
- const list = buildToolList(active);
3448
- await harnessRef.current.setTools(list, list.map((t) => t.name));
3449
- if (fpRef.current)
3450
- fpRef.current.tools = toolsToFingerprintInputs(list);
3451
- turnSnapshotRef.current?.refreshTools(toolsToFingerprintInputs(list));
3452
- const live = new Set(list.map((t) => t.name));
3453
- for (const n of [...announcedTools]) {
3454
- if (!live.has(n)) {
3455
- announcedTools.delete(n);
3456
- withdrawnTools.add(n);
3457
- deltaRef.pendingRemoved.push(n);
3458
- }
3459
- }
3460
- for (const n of active) {
3461
- if (!live.has(n))
3462
- continue;
3463
- if (!announcedTools.has(n)) {
3464
- announcedTools.add(n);
3465
- if (withdrawnTools.delete(n))
3466
- deltaRef.pendingReadded.push(n);
3467
- else
3468
- deltaRef.pending.push(n);
3469
- }
3470
- }
3471
- };
3472
- rebuildHarnessToolsRef.current = () => rematerialize(activeTools);
3473
- toolSearch = createToolSearchTool({
3474
- registry,
3475
- active: activeTools,
3476
- rematerialize,
3477
- listingRide: (newly) => listingRideRef.current?.(newly),
3478
- mountedNames: callableToolNames,
3479
- directCallEnabled: spec.deferSelfResolve !== false,
3480
- isMounted: (name) => tools.some((t) => t.name === name),
3481
- ...(materializeStatic
3482
- ? { staticSchemaFor: (name) => (staticFaceFor(name) ? tools.find((t) => t.name === name)?.parameters : undefined) }
3483
- : {}),
3484
- serializeActivation,
3485
- });
3486
- harnessTools = buildToolList(activeTools);
3487
- }
3488
- else {
3489
- rebuildHarnessToolsRef.current = async () => {
3490
- contentOriginWrapRef.current?.();
3491
- const list = [...tools];
3492
- await harnessRef.current.setTools(list, list.map((t) => t.name));
3493
- if (fpRef.current)
3494
- fpRef.current.tools = toolsToFingerprintInputs(list);
3495
- turnSnapshotRef.current?.refreshTools(toolsToFingerprintInputs(list));
3496
- };
3497
- }
3498
- if (spec.agents !== undefined && spec.agents.length > 0) {
3499
- const known = new Set(tools.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
3500
- for (const def of spec.agents) {
3501
- const unknownAllow = (def.allowTools ?? []).filter((n) => n !== "*" && !known.has(n));
3502
- if (unknownAllow.length > 0) {
3503
- try {
3504
- onceLedger.onError(new Error(`TaskSpec.agents: agent "${def.name}" allows tool(s) ${unknownAllow.join(", ")} not present in this task's assembled roster — likely a typo (the entry would be item-filtered at spawn; the agent stays usable). Advisory only: the delegation pool can differ from this roster, so a tool mounted only on the delegation tool or produced by per-spawn extraTools makes this spurious.`), { phase: "config", sessionId });
3505
- }
3506
- catch {
3507
- }
3508
- }
3509
- for (const n of def.denyTools ?? []) {
3510
- if (n === "*")
3511
- continue;
3512
- if (!known.has(n)) {
3513
- const e = new Error(`TaskSpec.agents: agent "${def.name}" declares tool "${n}" in its denied tools, but no such tool exists in this deployment — fix the agent's tools list or mount the tool.`);
3514
- e.code = "config.agent.unknown_tool";
3515
- throw e;
3516
- }
3517
- }
3518
- }
3519
- }
3520
- const rosterBearingSpec = (spec.tools ?? []).find((t) => t.agentListing !== undefined && t.agentListing.length > 0);
3521
- const rosterFaceDeferred = rosterBearingSpec !== undefined &&
3522
- !(toolFaceSnapshot.exclude?.includes(rosterBearingSpec.name) ?? false) &&
3523
- (toolFaceSnapshot.defer?.includes(rosterBearingSpec.name) ?? false) &&
3524
- !activeTools.has(rosterBearingSpec.name);
3525
- const agentListingSpec = rosterBearingSpec !== undefined &&
3526
- !rosterFaceDeferred &&
3527
- !(toolFaceSnapshot.exclude?.includes(rosterBearingSpec.name) ?? false)
3528
- ? rosterBearingSpec
3529
- : undefined;
3530
- const agentListing = agentListingSpec
3531
- ? {
3532
- entries: agentListingSpec.agentListing,
3533
- toolName: agentListingSpec.name,
3534
- seedAnnounced: resume !== undefined || spec.sessionId !== undefined,
3535
- ...(agentListingSpec.agentModels !== undefined ? { models: agentListingSpec.agentModels } : {}),
3536
- }
3537
- :
3538
- !rosterFaceDeferred && ((resume?.seed.announcedListings?.agents?.length ?? 0) > 0 || spec.sessionId !== undefined)
3539
- ?
3540
- {
3541
- entries: [],
3542
- toolName: (spec.tools ?? []).find((t) => t.agentListing !== undefined)?.name ?? "Agent",
3543
- seedAnnounced: true,
3544
- }
3545
- : undefined;
3546
- if (spec.attachments?.agentListing === false && agentListing !== undefined && agentListing.entries.length > 0) {
3547
- onceLedger.onError(new Error(`attachments.agentListing is explicitly false but the "${agentListing.toolName}" delegation tool mounts a ${agentListing.entries.length}-type roster — the model will never see the agent-type listing its tool description points to.`), { phase: "config", sessionId });
3548
- }
3549
- if (spec.attachments?.skillsListing === false && skillsListing !== undefined && skillsListing.entries.length > 0) {
3550
- onceLedger.onError(new Error(`attachments.skillsListing is explicitly false but ${skillsListing.entries.length} skill(s) are mounted — the model will never see the skills listing the ${SKILL_TOOL_NAME} tool description points to.`), { phase: "config", sessionId });
3551
- }
3552
- const announcedListingsRef = onceLedger.arms();
3553
- if (rosterFaceDeferred && rosterBearingSpec !== undefined) {
3554
- const rideEntries = rosterBearingSpec.agentListing;
3555
- const rideModels = rosterBearingSpec.agentModels;
3556
- listingRideRef.current = (newly) => {
3557
- if (!newly.includes(rosterBearingSpec.name))
3558
- return undefined;
3559
- if ((announcedListingsRef.agents?.length ?? 0) > 0)
3560
- return undefined;
3561
- return renderAgentListingDelta({ announcedAgentTypes: undefined }, rideEntries, rosterBearingSpec.name, rideModels);
3562
- };
3563
- }
3564
- const dateChange = renderWithDate
3565
- ? { legDate: envFacts.date, today: () => formatLocalDate(new Date(), tzValid ? userTz : undefined) }
3566
- : undefined;
1417
+ const { activeTools, fpRef, turnSnapshotRef, harnessTools, toolsDeltaRef, toolMaterializeStatic, deferDirectCall, staticFaceForRef, listingRideRef } = await prepareToolDisclosureMount({ spec, deps, session, resume, deferred, tools, onceLedger, mcp, promptManifest, offloadReachableToolsRef, contentOriginWrapRef, rebuildHarnessToolsRef, harnessRef });
1418
+ const { agentListing, announcedListingsRef, dateChange } = prepareListings({ spec, tools, onceLedger, sessionId, toolFaceSnapshot, activeTools, resume, skillsListing, listingRideRef, renderWithDate, envFacts, tzValid, userTz });
3567
1419
  const lastBrainContextRef = {};
3568
1420
  const requestLossyRef = { current: false };
3569
1421
  const staleOffloadWrittenRefs = new Set();
@@ -4218,132 +2070,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4218
2070
  },
4219
2071
  }
4220
2072
  : foldedPolicy;
4221
- const onAsk = spec.onAsk ?? deps.onAsk;
4222
- const handWriteTools = handsEnabled && spec.handsReadOnly !== true
4223
- ? Object.keys(HAND_TOOL_EFFECTS).filter((name) => {
4224
- const eff = HAND_TOOL_EFFECTS[name];
4225
- return eff === "write" || eff === "idempotent";
4226
- })
4227
- : [];
4228
- const hasEffectAwareGate = Boolean(policyLayers.length > 0 || (hooks?.preToolUse !== undefined && !preToolUseObservational));
4229
- const destructiveMcpUngated = mcp.tools.some((t) => !irreversibleTools.has(t.name) && !egressTools.has(t.name) && (toolEffects.get(t.name) ?? "write") !== "read");
4230
- const firstPartyWriteUngated = (spec.tools ?? [])
4231
- .map((t) => t.name)
4232
- .filter((name) => !irreversibleTools.has(name) && !egressTools.has(name) && toolEffects.get(name) === "write");
4233
- if (!hasEffectAwareGate && (handWriteTools.length > 0 || destructiveMcpUngated || firstPartyWriteUngated.length > 0)) {
4234
- const what = [
4235
- handWriteTools.length > 0 ? `hand tools [${handWriteTools.join(", ")}]` : undefined,
4236
- destructiveMcpUngated ? "MCP write tools" : undefined,
4237
- firstPartyWriteUngated.length > 0 ? `write tools [${firstPartyWriteUngated.join(", ")}]` : undefined,
4238
- ]
4239
- .filter(Boolean)
4240
- .join(" + ");
4241
- if (!ungatedWarnedShapes.get(deps)?.has(what)) {
4242
- let shapes = ungatedWarnedShapes.get(deps);
4243
- if (!shapes) {
4244
- shapes = new Set();
4245
- ungatedWarnedShapes.set(deps, shapes);
4246
- }
4247
- shapes.add(what);
4248
- deps.onError?.(new Error(`write-capable ${what} are present but UNGATED — wire an effect-aware tool policy (or a PreToolUse hook). ` +
4249
- `bash/MCP irreversible actions are otherwise unadjudicated; the deployment sandbox/egress boundary is the only protection (design/77 §4, design/53 §2.H). ` +
4250
- `(This warning fires once per deployment per tool-set shape.)`), { phase: "config", sessionId });
4251
- }
4252
- }
4253
- const preToolContexts = new Map();
4254
- const blockedToolCalls = new Set();
4255
- const approvalSettlement = new Map();
4256
- const humanBareRejections = new Set();
4257
- const batchHaltRef = {};
4258
- const blockedTracked = Boolean(hooks?.postToolUse || hooks?.preToolUse || hooks?.postToolUseFailure || hooks?.postToolBatch);
4259
- const restoreSurfaceGap = ownedEnv !== undefined && isRemoteExecutionEnv(ownedEnv) && ownedEnv.capabilities.suspendable ? missingRestoreSurface(ownedEnv) : [];
4260
- const incompleteSuspendAdapter = restoreSurfaceGap.length > 0 ? restoreSurfaceGap : undefined;
4261
- const durableSuspendInfraReady = resolveCheckpointStore(spec, deps) !== undefined &&
4262
- !(offloadStore !== undefined && isVolatileOffloadStore(offloadStore)) &&
4263
- (ownedEnv === undefined || isRemoteExecutionEnv(ownedEnv)) &&
4264
- incompleteSuspendAdapter === undefined;
4265
- const resourceSuspendEligible = spec.resourceSuspend !== undefined && durableSuspendInfraReady;
4266
- if (spec.resourceSuspend !== undefined && !resourceSuspendEligible) {
4267
- const why = resolveCheckpointStore(spec, deps) === undefined
4268
- ? "no CheckpointStore is wired"
4269
- : offloadStore !== undefined && isVolatileOffloadStore(offloadStore)
4270
- ? "tool-result offload uses the in-memory store (a resume needs durable results)"
4271
- : incompleteSuspendAdapter !== undefined
4272
- ? `the per-task execution env declares capabilities.suspendable but its adapter does not implement ${incompleteSuspendAdapter.join(" or ")} (a snapshot nothing can restore is worse than no snapshot)`
4273
- : "the per-task execution env is not a RemoteExecutionEnv (it would be destroyed on suspend)";
4274
- onceLedger.onError(new Error(`resourceSuspend is set but INACTIVE: ${why}; resource limits will hard-fail, not suspend`), {
4275
- phase: "config",
4276
- sessionId,
4277
- });
4278
- }
4279
- const usageGovernance = buildUsageGovernance(usageWindows, deps, spec.principal, sessionId);
4280
- const platformSuspendArmed = durableSuspendInfraReady && (envLifetimeSuspendAt !== undefined || usageGovernance !== undefined);
4281
- const durableApproval = spec.durableApproval ??
4282
- (runtimeCaps?.forceDurableGate ? { scope: spec.principal || DEFAULT_IRREVERSIBLE_SCOPE } : undefined);
4283
- const wiringQuestionSeam = resolveQuestionSeam(spec, deps);
4284
- const wiringQuestionStripped = internals?.questionFaceStripped === true;
4285
- const wiringAskSeam = resolveAskSeamForm(spec, deps);
4286
- const wiringManifest = deriveWiringManifest({
4287
- half: "effective",
4288
- leg: resume !== undefined ? "resume" : internals?.isDelegatedChild === true ? "child" : "root",
4289
- askForm: wiringAskSeam.form,
4290
- ...(wiringAskSeam.provenance !== undefined ? { askProvenance: wiringAskSeam.provenance } : {}),
4291
- questionWired: wiringQuestionSeam.wired,
4292
- ...(wiringQuestionSeam.provenance !== undefined ? { questionProvenance: wiringQuestionSeam.provenance } : {}),
4293
- ...(wiringQuestionStripped ? { questionStrippedByEngine: true } : {}),
4294
- ...(resolvedInteractionPosture !== undefined ? { interactionPosture: resolvedInteractionPosture } : {}),
4295
- ...(spec.interactiveTools === true && !wiringQuestionSeam.wired && !durableQuestionFace && !wiringQuestionStripped
4296
- ? { interactiveToolsWithoutDeliveryFace: true }
4297
- : {}),
4298
- elicitSeamWired: resolveElicitSeam(deps),
4299
- elicitServersOptedIn: countElicitOptIns(spec),
4300
- parkCapable: checkpointStore !== undefined,
4301
- parkDurableApprovalOptIn: spec.durableApproval !== undefined,
4302
- parkForceDurableGate: runtimeCaps?.forceDurableGate === true,
4303
- parkSafetyVocabularyArmed: irreversibleTools.size > 0 || egressTools.size > 0,
4304
- ...(checkpointStore !== undefined ? { checkpointDurability: resolveDeclaredDurability(checkpointStore, "checkpointStore") } : {}),
4305
- sessionDurability: resolveDeclaredDurability(sessions, "sessionStore"),
4306
- backgroundAgentStoreWired: deps.backgroundAgentStore !== undefined,
4307
- subagentTranscriptTier: resolveSubagentTranscriptTier(deps.backgroundAgentStore !== undefined, sessions),
4308
- permissionRuleStoreWired: deps.permissionRuleStore?.partitions.durable === true,
4309
- permissionRuleSyncWired: deps.permissionRuleSyncWired === true,
4310
- permissionRuleOrgGoverned: deps.permissionRuleStore?.partitions.org === true,
4311
- hostChildEventSinkWired: deps.onBackgroundChildEvent !== undefined,
4312
- lockedConfigWired: deps.lockedConfig !== undefined,
4313
- complianceWired: deps.compliancePostureResolver !== undefined,
4314
- memoryAdmissionWired: deps.memoryScopeAdmission !== undefined,
4315
- retentionPolicyWired: deps.retentionPolicy !== undefined,
4316
- ...(modelGateManifest !== undefined ? { modelGate: modelGateManifest } : {}),
4317
- autoMode: { armed: autoModeArmReason === "armed", reason: autoModeArmReason },
4318
- mcp: mcpManifestEntries(lockedPreflight.mcp, mcp?.statuses ?? []),
4319
- });
4320
- peerLaneRefs.askEffective = wiringManifest.ask.effective;
4321
- if (peerLaneActive && internals?.onTaskNotification !== undefined) {
4322
- bindPeerLaneDrain(harness, {
4323
- deps,
4324
- sessionId,
4325
- runId,
4326
- scope: taskScope,
4327
- inject: internals.onTaskNotification,
4328
- ownTokens: () => internals?.peerSelfRef?.current.ownTokens ?? [],
4329
- refs: peerLaneRefs,
4330
- selfName: () => internals?.explicitAgentName,
4331
- parked: () => suspendRef.token !== undefined || reviewRef.token !== undefined,
4332
- });
4333
- }
4334
- const parkLaneArmed = wiringManifest.parkLane.effective === true;
4335
- announceDurableGateUnavailable({ onNotice: deps.onNotice, forceDurableGate: runtimeCaps?.forceDurableGate === true, storeWired: checkpointStore !== undefined, taskStoreNull: spec.checkpointStore === null, liveApprover: isLiveApproverSeat(frozenOnAsk), liveQuestionFace: liveQuestionFace !== undefined, parentConstraints: liveInheritedGate?.parentConstraints, sessionId, runId, principal: spec.principal });
4336
- const hookIdentity = mintHookInvocationIdentity({
4337
- sessionId,
4338
- taskId: hostTaskId,
4339
- legKind: wiringManifest.leg.kind,
4340
- isDelegatedChild: delegation.isDelegatedChild,
4341
- ...(internals?.insideFork === true ? { insideFork: true } : {}),
4342
- ...(internals?.agentName !== undefined ? { agentName: internals.agentName } : {}),
4343
- ...(internals?.parentToolCallId !== undefined ? { parentToolCallId: internals.parentToolCallId } : {}),
4344
- });
4345
- const hookContextConsumerWired = hooks?.preToolUse !== undefined || hooks?.postToolUse !== undefined || hooks?.postToolUseFailure !== undefined;
4346
- const hookEnvFace = hookContextConsumerWired && (ownedEnv ?? deps.executionEnv) != null ? createHookEnvCapabilities(executionEnv) : undefined;
2073
+ const { onAsk, preToolContexts, blockedToolCalls, approvalSettlement, humanBareRejections, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, platformSuspendArmed, durableApproval, wiringManifest, parkLaneArmed, hookIdentity, hookEnvFace } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, handsEnabled, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, suspendRef, reviewRef, frozenOnAsk, liveInheritedGate, delegation });
4347
2074
  if (fileHistoryBoundary !== undefined) {
4348
2075
  harness.on("tool_call", async () => {
4349
2076
  await fileHistoryBoundary.settle();
@@ -5639,24 +3366,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
5639
3366
  const effectiveReadDenyObserved = readDenyAdditionsNormalized.length > 0 ? readDenyAdditionsNormalized.map((e) => ({ ...e })) : undefined;
5640
3367
  await onceLedger.settle(session, announcedListingsRef);
5641
3368
  const preparedHolder = {};
5642
- const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettlement, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, gateStopRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, gitStatusRef, listBackgroundTasks, hookIdentity, hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
3369
+ const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettlement, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, gateStopRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, gitStatusRef, listBackgroundTasks, hookIdentity, hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
3370
+ rollback.commit();
5643
3371
  const prepared = buildPrepared();
5644
3372
  preparedHolder.current = prepared;
5645
3373
  return prepared;
5646
3374
  }
5647
3375
  catch (prepareErr) {
5648
- if (a2a) {
5649
- const a2aHandle = a2a;
5650
- await settleTeardownLeg(() => a2aHandle.dispose(), "a2a.dispose (prepare-throw leg)", (e) => deps.onError?.(e, { phase: "config", sessionId }));
5651
- }
5652
- if (mcp) {
5653
- const mcpHandle = mcp;
5654
- await settleTeardownLeg(() => mcpHandle.dispose(), "mcp.dispose (prepare-throw leg)", (e) => deps.onError?.(e, { phase: "config", sessionId }));
5655
- }
5656
- if (ownedEnv && hasDestroy(ownedEnv)) {
5657
- const env = ownedEnv;
5658
- await settleTeardownLeg(() => env.destroy(), "ownedEnv.destroy (prepare-throw leg)", (e) => deps.onError?.(e, { phase: "config", sessionId }));
5659
- }
3376
+ await rollback.unwindAll();
5660
3377
  await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (prepare-throw leg)", (e) => deps.onError?.(e, { phase: "config", sessionId }));
5661
3378
  throw prepareErr;
5662
3379
  }