@sema-agent/core 7.5.0 → 7.5.2

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 (106) hide show
  1. package/CHANGELOG.md +44 -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/protocol-table.d.ts +5 -0
  6. package/dist/core/protocol-table.js +1 -0
  7. package/dist/core/runner/abort-race.d.ts +41 -0
  8. package/dist/core/runner/abort-race.js +38 -0
  9. package/dist/core/runner/checkpoint-scope.d.ts +44 -0
  10. package/dist/core/runner/checkpoint-scope.js +7 -0
  11. package/dist/core/runner/compaction-call-options.d.ts +1 -1
  12. package/dist/core/runner/content-ask-bindings.d.ts +27 -0
  13. package/dist/core/runner/content-ask-bindings.js +1 -0
  14. package/dist/core/runner/contracts.d.ts +1990 -0
  15. package/dist/core/runner/contracts.js +1 -0
  16. package/dist/core/runner/denial-limit-arms.d.ts +24 -1
  17. package/dist/core/runner/denial-limit-arms.js +21 -0
  18. package/dist/core/runner/derived-route-fallback.d.ts +34 -0
  19. package/dist/core/runner/derived-route-fallback.js +16 -0
  20. package/dist/core/runner/inherited-ask-grants.d.ts +46 -0
  21. package/dist/core/runner/inherited-ask-grants.js +29 -0
  22. package/dist/core/runner/park-commit.d.ts +108 -0
  23. package/dist/core/runner/park-commit.js +32 -0
  24. package/dist/core/runner/{prepare-permission-rules.d.ts → permission-rule-lanes.d.ts} +109 -3
  25. package/dist/core/runner/{prepare-permission-rules.js → permission-rule-lanes.js} +47 -1
  26. package/dist/core/runner/prepare-acquire-reconcile.d.ts +1 -1
  27. package/dist/core/runner/prepare-ask-lane.d.ts +110 -0
  28. package/dist/core/runner/prepare-ask-lane.js +133 -0
  29. package/dist/core/runner/prepare-boundary-parks.d.ts +105 -0
  30. package/dist/core/runner/prepare-boundary-parks.js +169 -0
  31. package/dist/core/runner/prepare-caps-and-workflow.d.ts +170 -0
  32. package/dist/core/runner/prepare-caps-and-workflow.js +255 -0
  33. package/dist/core/runner/prepare-config-doors.d.ts +2 -10
  34. package/dist/core/runner/prepare-context-lane.d.ts +119 -0
  35. package/dist/core/runner/prepare-context-lane.js +230 -0
  36. package/dist/core/runner/prepare-defer-classify.d.ts +86 -0
  37. package/dist/core/runner/prepare-defer-classify.js +107 -0
  38. package/dist/core/runner/prepare-delegation-surface.d.ts +104 -0
  39. package/dist/core/runner/prepare-delegation-surface.js +144 -0
  40. package/dist/core/runner/prepare-execution-env.d.ts +54 -0
  41. package/dist/core/runner/prepare-execution-env.js +86 -0
  42. package/dist/core/runner/prepare-file-history.d.ts +95 -0
  43. package/dist/core/runner/prepare-file-history.js +383 -0
  44. package/dist/core/runner/prepare-gate-stations.d.ts +177 -0
  45. package/dist/core/runner/prepare-gate-stations.js +290 -0
  46. package/dist/core/runner/prepare-hands-readface.d.ts +9 -11
  47. package/dist/core/runner/prepare-hands-readface.js +1 -1
  48. package/dist/core/runner/prepare-inherited-gate.d.ts +268 -0
  49. package/dist/core/runner/prepare-inherited-gate.js +266 -0
  50. package/dist/core/runner/prepare-listings.d.ts +77 -0
  51. package/dist/core/runner/prepare-listings.js +76 -0
  52. package/dist/core/runner/prepare-lsp.d.ts +55 -0
  53. package/dist/core/runner/prepare-lsp.js +27 -0
  54. package/dist/core/runner/prepare-memory-engine-session.d.ts +84 -0
  55. package/dist/core/runner/prepare-memory-engine-session.js +233 -0
  56. package/dist/core/runner/prepare-memory.d.ts +1 -1
  57. package/dist/core/runner/prepare-offload-wrappers.d.ts +62 -0
  58. package/dist/core/runner/prepare-offload-wrappers.js +45 -0
  59. package/dist/core/runner/prepare-park-ask.d.ts +164 -0
  60. package/dist/core/runner/prepare-park-ask.js +377 -0
  61. package/dist/core/runner/prepare-policy-chain.d.ts +208 -0
  62. package/dist/core/runner/prepare-policy-chain.js +584 -0
  63. package/dist/core/runner/prepare-project-context.d.ts +119 -0
  64. package/dist/core/runner/prepare-project-context.js +148 -0
  65. package/dist/core/runner/prepare-prompt-assembly.d.ts +95 -0
  66. package/dist/core/runner/prepare-prompt-assembly.js +162 -0
  67. package/dist/core/runner/prepare-prompt-inputs.d.ts +119 -0
  68. package/dist/core/runner/prepare-prompt-inputs.js +141 -0
  69. package/dist/core/runner/prepare-protocol-tools.d.ts +91 -0
  70. package/dist/core/runner/prepare-protocol-tools.js +179 -0
  71. package/dist/core/runner/prepare-question-face.d.ts +101 -0
  72. package/dist/core/runner/prepare-question-face.js +84 -0
  73. package/dist/core/runner/prepare-run-refs.d.ts +89 -0
  74. package/dist/core/runner/prepare-run-refs.js +39 -0
  75. package/dist/core/runner/prepare-safety-scan.d.ts +3 -7
  76. package/dist/core/runner/prepare-safety-scan.js +1 -2
  77. package/dist/core/runner/prepare-suspend-saga.d.ts +170 -0
  78. package/dist/core/runner/prepare-suspend-saga.js +308 -0
  79. package/dist/core/runner/prepare-task.d.ts +14 -1976
  80. package/dist/core/runner/prepare-task.js +182 -5162
  81. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +111 -0
  82. package/dist/core/runner/prepare-tool-disclosure-mount.js +219 -0
  83. package/dist/core/runner/prepare-turn-wiring.d.ts +154 -0
  84. package/dist/core/runner/prepare-turn-wiring.js +201 -0
  85. package/dist/core/runner/prepare-wiring-manifest.d.ts +192 -0
  86. package/dist/core/runner/prepare-wiring-manifest.js +247 -0
  87. package/dist/core/runner/prepare-workspace-restore.d.ts +3 -56
  88. package/dist/core/runner/prepare-workspace-restore.js +4 -38
  89. package/dist/core/runner/prompt-hash-salt.d.ts +1 -0
  90. package/dist/core/runner/prompt-hash-salt.js +2 -0
  91. package/dist/core/runner/remote-env-retry.d.ts +29 -0
  92. package/dist/core/runner/remote-env-retry.js +16 -0
  93. package/dist/core/runner/rollback-stack.d.ts +32 -0
  94. package/dist/core/runner/rollback-stack.js +30 -0
  95. package/dist/core/runner/runtask.js +1 -1
  96. package/dist/core/runner/workspace-path.d.ts +33 -0
  97. package/dist/core/runner/workspace-path.js +22 -0
  98. package/dist/core/session.d.ts +12 -0
  99. package/dist/core/session.js +3 -0
  100. package/dist/core/tool-policy.d.ts +16 -0
  101. package/dist/core/tool-policy.js +3 -0
  102. package/dist/core/types.d.ts +2 -2
  103. package/dist/core/write-protect.js +3 -2
  104. package/package.json +6 -2
  105. /package/dist/core/runner/{prepare-announce-once.d.ts → announce-once-ledger.d.ts} +0 -0
  106. /package/dist/core/runner/{prepare-announce-once.js → announce-once-ledger.js} +0 -0
@@ -0,0 +1,201 @@
1
+ import { createHash } from "node:crypto";
2
+ import { assemblePrompt } from "../../prompt-assembly/assemble.js";
3
+ import { artifactDeclarations } from "../../prompt-assembly/artifact.js";
4
+ import { buildTurnPromptSnapshot } from "../../prompt-assembly/turn-snapshot.js";
5
+ import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-detector.js";
6
+ import { defaultTaskRegistry } from "../task-registry.js";
7
+ import { refuseOutOfContractDecision } from "../tool-policy.js";
8
+ import { decodeTextBytes } from "../../tools/fs/encoding.js";
9
+ import { applyCompactionToReadFileState, isReadDedupStubResult } from "../../tools/fs/index.js";
10
+ import { resolveKey } from "../../tools/fs/safety.js";
11
+ import { PROMPT_HASH_SALT } from "./prompt-hash-salt.js";
12
+ import { CHANGED_FILES_MTIME_EPS_MS } from "./turn-attachments.js";
13
+ export function prepareTurnWiring(input) {
14
+ const { deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, additionalRootsCanonical, additionalReadRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, readDenyAdditionsNormalized, preparedHolder } = input;
15
+ const cacheBreakDetector = deps.cacheBreakDetection === false ? undefined : new CacheBreakDetector();
16
+ const cacheFingerprint = cacheBreakDetector
17
+ ? { systemPrompt: systemPromptSeat.current, tools: toolsToFingerprintInputs(harnessTools) }
18
+ : undefined;
19
+ fpRef.current = cacheFingerprint;
20
+ const turnSnapshot = buildTurnPromptSnapshot({
21
+ provider: model.provider,
22
+ modelId: model.id,
23
+ artifactDigest: epochArtifactDigestForSnapshot,
24
+ promptProfile,
25
+ fableMitigations,
26
+ layoutGroups: (systemBlocks ?? [{ groupId: "body", cacheControlBoundary: true }]).map((b) => ({ groupId: b.groupId, cacheControlBoundary: b.cacheControlBoundary })),
27
+ stableSystemText: systemPromptSeat.current,
28
+ toolWire: toolsToFingerprintInputs(harnessTools),
29
+ thinkingLevel: (thinking ?? "off"),
30
+ maxTokens: model.maxTokens,
31
+ hasOutputSchema: spec.outputSchema !== undefined,
32
+ sessionId,
33
+ taskId: spec.taskId,
34
+ }, model.api, (text) => `salted:${createHash("sha256").update(PROMPT_HASH_SALT).update(text).digest("hex")}`);
35
+ turnSnapshotRef.current = turnSnapshot;
36
+ promptManifest.snapshot = { cacheIdentity: turnSnapshot.cacheIdentity, elements: { ...turnSnapshot.elements } };
37
+ promptManifest.lowering = turnSnapshot.lowering;
38
+ const promptOverheadTokens = Math.ceil((systemPromptSeat.current.length +
39
+ harnessTools.reduce((a, t) => a + t.name.length + (t.description?.length ?? 0) + JSON.stringify(t.parameters ?? {}).length, 0)) / charsPerToken);
40
+ const announceAttachmentSkip = (path, reason) => {
41
+ deps.onError?.(new Error(`working-file attachment skipped — ${path}: ${reason}`), {
42
+ phase: "degraded",
43
+ sessionId,
44
+ classification: "attachment-read",
45
+ });
46
+ };
47
+ const readTaskFile = handsEnabled
48
+ ? async (path) => {
49
+ try {
50
+ let key = path;
51
+ if (attachmentRootCanonical !== undefined) {
52
+ const rk = await resolveKey(executionEnv, attachmentRootCanonical, path, undefined, undefined, [...additionalRootsCanonical, ...additionalReadRootsCanonical], undefined, readDenyMatcher, resolvedReadFace);
53
+ if (!rk.ok) {
54
+ announceAttachmentSkip(path, rk.violation.message);
55
+ if (rk.violation.code === "read_path_denied" && rk.violation.pattern !== undefined) {
56
+ return { withheld: { pattern: rk.violation.pattern } };
57
+ }
58
+ return null;
59
+ }
60
+ key = rk.key;
61
+ }
62
+ const r = await executionEnv.readBinaryFile(key);
63
+ if (!r.ok) {
64
+ announceAttachmentSkip(path, r.error.message);
65
+ return null;
66
+ }
67
+ const d = decodeTextBytes(r.value);
68
+ if (d.malformed) {
69
+ announceAttachmentSkip(path, "malformed text encoding (truncated UTF-16 body)");
70
+ return null;
71
+ }
72
+ return d.text;
73
+ }
74
+ catch (err) {
75
+ announceAttachmentSkip(path, err instanceof Error ? err.message : String(err));
76
+ return null;
77
+ }
78
+ }
79
+ : undefined;
80
+ const normalizeAttachmentPath = handsEnabled
81
+ ? async (path) => {
82
+ if (attachmentRootCanonical === undefined)
83
+ return path;
84
+ try {
85
+ const rk = await resolveKey(executionEnv, attachmentRootCanonical, path, undefined, handsCwdRef?.current, [...additionalRootsCanonical, ...additionalReadRootsCanonical], undefined, undefined, resolvedReadFace);
86
+ return rk.ok ? rk.key : path;
87
+ }
88
+ catch {
89
+ return path;
90
+ }
91
+ }
92
+ : undefined;
93
+ const isDedupStubResult = handsEnabled ? isReadDedupStubResult : undefined;
94
+ const recentlyReadFiles = handsEnabled && readFileStateForCheckpoint
95
+ ? () => [...readFileStateForCheckpoint.entries()]
96
+ .filter(([, v]) => v.seededFromContext !== true)
97
+ .sort((a, b) => (b[1].lastReadAt ?? 0) - (a[1].lastReadAt ?? 0))
98
+ .map(([path]) => path)
99
+ : undefined;
100
+ const onCompactionApplied = handsEnabled && readFileStateForCheckpoint
101
+ ? (attachedComplete, preserveReadState) => applyCompactionToReadFileState(readFileStateForCheckpoint, attachedComplete, preserveReadState)
102
+ : undefined;
103
+ const changedFilesReadDenied = denyNarrowingPolicy === undefined
104
+ ? undefined
105
+ : async (path) => {
106
+ try {
107
+ const d = refuseOutOfContractDecision(await denyNarrowingPolicy.check({ toolName: "Read", args: { file_path: path }, toolCallId: "changed-files-visibility-probe" }, abortController.signal));
108
+ return d.action === "deny";
109
+ }
110
+ catch {
111
+ return false;
112
+ }
113
+ };
114
+ const detectExternalChanges = handsEnabled && readFileStateForCheckpoint
115
+ ? async (maxFiles) => {
116
+ const candidates = [...readFileStateForCheckpoint.entries()]
117
+ .filter(([, e]) => e.lastReadAt !== undefined)
118
+ .filter(([, e]) => e.truncated !== true)
119
+ .sort((a, b) => (b[1].lastReadAt ?? 0) - (a[1].lastReadAt ?? 0))
120
+ .slice(0, Math.max(0, maxFiles));
121
+ const changed = [];
122
+ const evicted = [];
123
+ for (const [path, entry] of candidates) {
124
+ if (changedFilesReadDenied !== undefined && (await changedFilesReadDenied(path)))
125
+ continue;
126
+ try {
127
+ const info = await executionEnv.fileInfo(path, abortController.signal);
128
+ if (!info.ok) {
129
+ if (info.error.code === "not_found") {
130
+ readFileStateForCheckpoint.delete(path);
131
+ evicted.push(path);
132
+ }
133
+ continue;
134
+ }
135
+ if (info.value.mtimeMs > (entry.lastReadAt ?? 0) + CHANGED_FILES_MTIME_EPS_MS) {
136
+ changed.push({ path, mtimeMs: info.value.mtimeMs });
137
+ }
138
+ }
139
+ catch {
140
+ }
141
+ }
142
+ return { changed, evicted };
143
+ }
144
+ : undefined;
145
+ const listBackgroundTasks = () => defaultTaskRegistry
146
+ .list({ owner: hostTaskId, scope: taskScope, sessionId })
147
+ .filter((t) => t.status === "pending" || t.status === "running" || t.status === "parked")
148
+ .map((t) => ({ id: t.task_id, ...(t.description !== undefined ? { description: t.description } : {}), status: t.status }));
149
+ const overheadState = { promptChars: 0 };
150
+ const centerCompactionCandidate = deps.promptSource !== undefined && centerAdoptionRef.current !== undefined
151
+ ? () => {
152
+ const state = deps.promptSource.sourceState?.() ?? "unavailable";
153
+ const cand = deps.promptSource.current();
154
+ const toBundled = state === "disabled";
155
+ if (!toBundled && (cand === undefined || cand.artifact.artifactDigest === centerAdoptionRef.current.artifact.artifactDigest))
156
+ return undefined;
157
+ let a2;
158
+ try {
159
+ a2 = assemblePrompt(buildAssembleInputs(toBundled ? undefined : artifactDeclarations(cand.artifact)));
160
+ }
161
+ catch {
162
+ return undefined;
163
+ }
164
+ if (!toBundled && a2.centerMounted !== true)
165
+ return undefined;
166
+ const declared2 = (a2.sections ?? [])
167
+ .filter((sec) => sec.origin === "provider")
168
+ .map((sec) => ({ id: sec.id, slot: sec.slot, chars: sec.text.length, ...(sec.declaredContentHash !== undefined ? { contentHash: sec.declaredContentHash } : {}) }));
169
+ const overheadTokensAfter = preparedHolder.current.promptOverheadTokens -
170
+ Math.ceil(overheadState.promptChars / charsPerToken) +
171
+ Math.ceil(a2.systemPrompt.length / charsPerToken);
172
+ return {
173
+ ...(toBundled
174
+ ? { clear: true }
175
+ : {
176
+ centerArtifactDigest: cand.artifact.artifactDigest,
177
+ ...(cand.sourceRevision !== undefined ? { sourceRevision: cand.sourceRevision } : {}),
178
+ }),
179
+ declaredSections: declared2,
180
+ overheadTokensAfter,
181
+ apply: (committedArtifactDigest) => {
182
+ systemPromptSeat.current = a2.systemPrompt;
183
+ harnessRef.current?.setSystemPrompt(a2.systemPrompt, a2.systemBlocks);
184
+ if (fpRef.current)
185
+ fpRef.current.systemPrompt = a2.systemPrompt;
186
+ turnSnapshotRef.current?.refreshStableSystem(a2.systemPrompt, committedArtifactDigest);
187
+ preparedHolder.current.epochDeclaredSections = declared2;
188
+ preparedHolder.current.promptOverheadTokens = overheadTokensAfter;
189
+ overheadState.promptChars = a2.systemPrompt.length;
190
+ centerAdoptionRef.current = toBundled
191
+ ? undefined
192
+ : { artifact: cand.artifact, ...(cand.sourceRevision !== undefined ? { sourceRevision: cand.sourceRevision } : {}) };
193
+ },
194
+ };
195
+ }
196
+ : undefined;
197
+ overheadState.promptChars = systemPromptSeat.current.length;
198
+ const effectiveReadFaceObserved = carrierReadFace();
199
+ const effectiveReadDenyObserved = readDenyAdditionsNormalized.length > 0 ? readDenyAdditionsNormalized.map((e) => ({ ...e })) : undefined;
200
+ return { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveReadDenyObserved };
201
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * design/390 §1.2 M18 — prepareTask's WIRING-MANIFEST phase, verbatim from the driver: the run's own frozen approver
3
+ * seat, the loud ungated-write warning (hand band / destructive MCP / first-party write tools with no effect-aware gate,
4
+ * deduped per deployment and tool-set shape), the PreToolUse context stash and the blocked-call set, the settlement and
5
+ * bare-rejection sidebands, the turn halt fact, the suspend infrastructure predicates (restore surface gap, durable
6
+ * infra, the task-axis lane with its INACTIVE line), usage governance, the platform-suspend arming, the synthesized
7
+ * durableApproval, the effective wiring manifest, the peer lane's ask fold and drain binding, the park predicate as ONE
8
+ * named const, the durable-gate-unavailable notice, the hook identity mint and the hook env face. The four helpers only
9
+ * this segment called moved with it (usage governance, the per-deployment warn ledger, the MCP manifest projection, the
10
+ * durable-gate notice). The interface is the dependency list the segment had implicitly (design/238 R-1).
11
+ *
12
+ * SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await, so the phase adds no yield between the policy-chain
13
+ * station before it and the gate registration after it (design/390 §1.5: a stretch with no await is extracted as a sync
14
+ * function). Read-stability of the host handles for the call: {@link RunInternals} (`@contract prepare.deps-read-stable`).
15
+ *
16
+ * One read changed spelling and nothing else: the manifest's MCP face read `mcp?.statuses ?? []` while `mcp` had long
17
+ * been the protocol-tools phase's non-null handle; the optional chain was dead and is now the plain member read.
18
+ */
19
+ import type { AgentHarness, ExecutionEnv } from "../../internal/harness.js";
20
+ import { type PeerLaneRefs } from "../../agents/peer-session-drain.js";
21
+ import type { OnQuestion } from "../ask-question.js";
22
+ import type { CheckpointStore } from "../checkpoint-store.js";
23
+ import { type HookEnvCapabilities, type HookInvocationIdentity, type Hooks } from "../hooks.js";
24
+ import type { LockedPreflight } from "../locked-config.js";
25
+ import type { MaterializedMcp } from "../mcp.js";
26
+ import type { SessionStore } from "../session.js";
27
+ import { type ToolResultStore } from "../tool-result-store.js";
28
+ import { type ApprovalSettledBy, type OnAsk, type ToolPolicy } from "../tool-policy.js";
29
+ import type { RunnerDeps, RuntimeCaps, TaskSpec, ToolEffect, ToolExecuteContext } from "../types.js";
30
+ import { type UsageWindow } from "../usage-window-store.js";
31
+ import { type AutoModeArmReason, type WiringManifest, type WiringManifestMcpEntry } from "../wiring-manifest.js";
32
+ import type { AnnounceOnceSink, Prepared, PrepareResume, RunInternals, UsageGovernance } from "./contracts.js";
33
+ export interface PrepareWiringManifestInput {
34
+ /** borrowed-readonly — the REBOUND spec. Read: `onAsk` (the own seat), `onQuestion` (the question seam), `handsReadOnly` / `tools` (the ungated scan),
35
+ * `resourceSuspend` (the task-axis lane), `durableApproval` / `principal` (the synthesis and the notice), `interactiveTools` /
36
+ * `mcp` / `checkpointStore` (manifest cells and the notice's cause), `enablePlanMode` (the gate-machinery predicate's
37
+ * plan-mode term). Never mutated. */
38
+ spec: Pick<TaskSpec, "onAsk" | "onQuestion" | "handsReadOnly" | "tools" | "resourceSuspend" | "durableApproval" | "principal" | "interactiveTools" | "mcp" | "checkpointStore" | "enablePlanMode">;
39
+ /** borrowed-readonly — the deployment seats, whole: `onAsk` (the seat's deps half), `onError` (the warning), `executionEnv`
40
+ * (the hook-env predicate), `checkpointStore` (durable infra), the manifest's dozen wiring facts (`backgroundAgentStore`,
41
+ * `permissionRuleStore`, `permissionRuleSyncWired`, `onBackgroundChildEvent`, `lockedConfig`, `compliancePostureResolver`,
42
+ * `memoryScopeAdmission`, `retentionPolicy`, `onElicit`), `onNotice`, and the usage-governance seats (`usageWindowStore`,
43
+ * `now`, `onUsageGovernance`…) the helper reads through the whole record. */
44
+ deps: RunnerDeps;
45
+ /** borrowed-readonly — the trusted spawn-side channel: `questionFaceStripped`, `isDelegatedChild`, `onTaskNotification`,
46
+ * `peerSelfRef`, `explicitAgentName` (the drain), `insideFork` / `agentName` / `parentToolCallId` (the identity). */
47
+ internals: Pick<RunInternals, "questionFaceStripped" | "isDelegatedChild" | "onTaskNotification" | "peerSelfRef" | "explicitAgentName" | "insideFork" | "agentName" | "parentToolCallId"> | undefined;
48
+ /** borrowed-readonly — the resume leg, or undefined (the manifest's leg derivation reads presence only). */
49
+ resume: Pick<PrepareResume, "seed"> | undefined;
50
+ /** borrowed-readonly — the acquired session id. */
51
+ sessionId: string;
52
+ /** borrowed-readonly — the run's invocation id. */
53
+ runId: string;
54
+ /** borrowed-readonly — the per-task identity (the hook identity's taskId). */
55
+ hostTaskId: string;
56
+ /** borrowed-readonly — the registry scope the peer drain binds under. */
57
+ taskScope: string;
58
+ /** borrowed-readonly — the resolved hook record (`spec.hooks ?? deps.hooks`): the effect-aware-gate term, the tracked flag
59
+ * and the hook-env face's consumer test. */
60
+ hooks: Hooks | undefined;
61
+ /** borrowed-readonly — whether the deployment declared its screening face observational (then it is no gate). */
62
+ preToolUseObservational: boolean;
63
+ /** borrowed-readonly — the caller-facing policy layers; only their COUNT is read (an effect-aware gate exists). */
64
+ policyLayers: ReadonlyArray<unknown>;
65
+ /** borrowed-readonly — the policy chain's effective policy, or undefined; only its PRESENCE is read (the first term of the
66
+ * gate-machinery predicate below). Never called here. */
67
+ effectivePolicy: ToolPolicy | undefined;
68
+ /** borrowed-readonly — whether an execution env is wired (the hand band exists at all). */
69
+ handsEnabled: boolean;
70
+ /** borrowed-readonly — the materialized MCP handle: `tools` (the destructive-ungated scan) and `statuses` (the manifest face). */
71
+ mcp: Pick<MaterializedMcp, "tools" | "statuses">;
72
+ /** borrowed-readonly — the assembled safety vocabulary (the ungated scans and the manifest's armed cell). Read only. */
73
+ irreversibleTools: ReadonlySet<string>;
74
+ /** borrowed-readonly — see `irreversibleTools`. */
75
+ egressTools: ReadonlySet<string>;
76
+ /** borrowed-readonly — the effect collection; read only here (the ungated scans). */
77
+ toolEffects: ReadonlyMap<string, ToolEffect>;
78
+ /** borrowed-readonly — the per-task owned env when a factory minted one (durable infra, the hook-env predicate). */
79
+ ownedEnv: ExecutionEnv | undefined;
80
+ /** borrowed-readonly — the run's resolved env (the hook env face is built over it). */
81
+ executionEnv: ExecutionEnv;
82
+ /** borrowed-readonly — the run's offload store, or undefined (a volatile store disqualifies durable suspend). */
83
+ offloadStore: ToolResultStore | undefined;
84
+ /** borrowed-readonly — the once-per-session ledger's operator sink (the INACTIVE line, once per session). */
85
+ onceLedger: Pick<AnnounceOnceSink, "onError">;
86
+ /** borrowed-readonly — the resolved usage windows, or undefined. */
87
+ usageWindows: readonly UsageWindow[] | undefined;
88
+ /** borrowed-readonly — the env-lifetime suspend instant, or undefined (platform-suspend arming). */
89
+ envLifetimeSuspendAt: number | undefined;
90
+ /** borrowed-readonly — the resolved per-principal entitlements (forceDurableGate). */
91
+ runtimeCaps: RuntimeCaps | undefined;
92
+ /** borrowed-readonly — the resolved interaction posture (a manifest cell). */
93
+ resolvedInteractionPosture: ToolExecuteContext["interactionPosture"];
94
+ /** borrowed-readonly — the question-face phase's durable-face decision (the composition-lie detector reuses it). */
95
+ durableQuestionFace: boolean;
96
+ /** borrowed-readonly — the live question face, or undefined (the notice's effective fact). */
97
+ liveQuestionFace: OnQuestion | undefined;
98
+ /** borrowed-readonly — the leg's checkpoint store, resolved once by the question-face phase. */
99
+ checkpointStore: CheckpointStore | undefined;
100
+ /** borrowed-readonly — the session store (durability declaration and transcript tier). */
101
+ sessions: SessionStore;
102
+ /** borrowed-readonly — the model gate's per-leg read face, or undefined. */
103
+ modelGateManifest: WiringManifest["modelGate"];
104
+ /** borrowed-readonly — the arming outcome the caps phase minted (the manifest's auto-mode face). */
105
+ autoModeArmReason: AutoModeArmReason;
106
+ /** borrowed-readonly — the frozen preflight; only `mcp` (the declared servers the manifest face joins). */
107
+ lockedPreflight: Pick<LockedPreflight, "mcp">;
108
+ /** borrowed-mutable — the peer lane's late-bound facts. Writer here: `askEffective` ← the manifest's ask derivation (the
109
+ * one fold of this session's permission-mode class). */
110
+ peerLaneRefs: PeerLaneRefs;
111
+ /** borrowed-readonly — the peer lane's mount verdict (the drain binds only while mounted). */
112
+ peerLaneActive: boolean;
113
+ /** borrowed-readonly — the built harness the peer drain binds to. */
114
+ harness: AgentHarness;
115
+ /** borrowed-readonly — the durable-suspend holder; the drain's parked predicate reads `token` at run time. Not written here. */
116
+ suspendRef: Prepared["suspendRef"];
117
+ /** borrowed-readonly — the review holder; same read. Not written here. */
118
+ reviewRef: Prepared["reviewRef"];
119
+ /** borrowed-readonly — the FROZEN approver seat (the notice's liveApprover fact). */
120
+ frozenOnAsk: OnAsk | undefined;
121
+ /** borrowed-readonly — the LIVE half of the inherited chain (the notice reads its content mandates). */
122
+ liveInheritedGate: RunInternals["inheritedGate"];
123
+ /** borrowed-readonly — the effective delegation facts (the identity's isDelegatedChild). */
124
+ delegation: {
125
+ isDelegatedChild: boolean;
126
+ };
127
+ }
128
+ export interface PrepareWiringManifestResult {
129
+ /** owned — the run's own approver seat (spec over deps), read by the gate-fold station. */
130
+ onAsk: OnAsk | undefined;
131
+ /** borrowed-mutable — PreToolUse `additionalContext` per call id. Writers: the gate (phase 1 stash), the post-tool handler
132
+ * (one-shot drain). */
133
+ preToolContexts: Map<string, string[]>;
134
+ /** borrowed-mutable — the call ids the gate blocked or suspended (`Prepared.blockedToolCalls`). Writers: the gate; readers
135
+ * delete one-shot. */
136
+ blockedToolCalls: Set<string>;
137
+ /** borrowed-mutable — the settlement sideband (`Prepared.approvalSettlement`). Writer: the gate; the frame mint reads and
138
+ * deletes. Nothing else may write it. */
139
+ approvalSettlement: Map<string, {
140
+ settledBy?: ApprovalSettledBy;
141
+ approver?: string;
142
+ resolution?: import("../tool-policy.js").AskDenyResolution;
143
+ autoDenied?: true;
144
+ }>;
145
+ /** borrowed-mutable — the bare-human-rejection sideband. Writer: resolveAskBound; the gate handler consumes one-shot. */
146
+ humanBareRejections: Set<string>;
147
+ /** borrowed-mutable — the turn's halt fact (`Prepared.batchHaltRef`). Writer: the judgment site; cleared at the next request. */
148
+ batchHaltRef: Prepared["batchHaltRef"];
149
+ /** owned — whether any consumer of the blocked set is wired (populate only then). */
150
+ blockedTracked: boolean;
151
+ /** owned — the restore-surface gap of a declared-suspendable adapter, or undefined for a complete / non-suspendable one. */
152
+ incompleteSuspendAdapter: readonly ("resumeVM" | "postResumeInit")[] | undefined;
153
+ /** owned — the infrastructure half of suspend eligibility (no task opt-in in it). */
154
+ durableSuspendInfraReady: boolean;
155
+ /** owned — the task-axis lane: the infrastructure plus the caller's opt-in. */
156
+ resourceSuspendEligible: boolean;
157
+ /** owned — the run's usage-governance handle, or undefined (`Prepared.usageGovernance`). */
158
+ usageGovernance: UsageGovernance | undefined;
159
+ /** owned — whether the suspend saga must be armed for a PLATFORM cause. */
160
+ platformSuspendArmed: boolean;
161
+ /** owned — the effective durable-approval opt-in: the caller's, or the scope synthesized for a forced durable gate. */
162
+ durableApproval: TaskSpec["durableApproval"];
163
+ /** owned — this leg's EFFECTIVE wiring manifest (`Prepared.wiringManifest`). */
164
+ wiringManifest: WiringManifest;
165
+ /** owned — THE park predicate as one named const (`parkLane.effective === true`). */
166
+ parkLaneArmed: boolean;
167
+ /** owned — THE gate-machinery activation predicate as one named const: a policy, a screening hook, an egress or
168
+ * irreversible tool, an eligible resource lane, an armed platform lane, or plan mode. The ask lane, the suspend saga,
169
+ * the boundary parks, the park closure and the gate station are built exactly when it holds. */
170
+ gateMachineryActive: boolean;
171
+ /** owned — THE identity mint of this leg (`Prepared.hookIdentity`), one frozen envelope every hook station reuses. */
172
+ hookIdentity: HookInvocationIdentity;
173
+ /** owned — the read-only env capability face every hook context carries, or undefined (no hook consumer, or no env wired). */
174
+ hookEnvFace: HookEnvCapabilities | undefined;
175
+ }
176
+ /**
177
+ * The wiring manifest's per-server MCP read face for one leg ({@link WiringManifest.mcp}): every
178
+ * server the leg's FROZEN entry snapshot declared, in declaration order, joined by name to this leg's
179
+ * materialization statuses. Pure projection — no connection logic, no second look at any transport:
180
+ * · a status `connected` ⇒ `connected` with the mounted roster size (the intake's post-filter count);
181
+ * · a status `failed` ⇒ `failed` with the class the materialization stamped ({@link McpFailureCode})
182
+ * and the remote text SECRET-REDACTED (an HTTP endpoint's error body rides the SDK's message
183
+ * verbatim, and a body that echoes the bearer it rejected must not reach a stream consumer),
184
+ * then neutralized + bounded to the reminder's own 240 (the same bound the once-per-session
185
+ * ledger applies to that text — one face, one width);
186
+ * · no status at all ⇒ `skipped` (the declaration stands, this leg produced no attempt record for it).
187
+ * `source` is echoed from the declaration (single-line, bounded; core reads nothing off it). The
188
+ * entries are fresh objects so the manifest never aliases the status records the refresh face mutates.
189
+ */
190
+ export declare function mcpManifestEntries(entries: ReadonlyArray<import("../types.js").McpServerSpec> | undefined, statuses: ReadonlyArray<import("../mcp.js").McpServerStatus>): WiringManifestMcpEntry[];
191
+ /** The M18 phase body — prepareTask's wiring-manifest stretch, verbatim (see the module header). */
192
+ export declare function prepareWiringManifest(input: PrepareWiringManifestInput): PrepareWiringManifestResult;
@@ -0,0 +1,247 @@
1
+ import { bindPeerLaneDrain } from "../../agents/peer-session-drain.js";
2
+ import { HAND_TOOL_EFFECTS } from "../../tools/fs/index.js";
3
+ import { resolveCheckpointStore } from "../checkpoint-store.js";
4
+ import { createHookEnvCapabilities, mintHookInvocationIdentity } from "../hooks.js";
5
+ import { isRemoteExecutionEnv, missingRestoreSurface } from "../remote-env.js";
6
+ import { isVolatileOffloadStore } from "../tool-result-store.js";
7
+ import { isLiveApproverSeat } from "../tool-policy.js";
8
+ import { deliverEngineNotice } from "../types.js";
9
+ import { inlineUntrusted } from "../untrusted-text.js";
10
+ import { GLOBAL_USAGE_KEY, usageRetryAfterMs, windowsGovernCost } from "../usage-window-store.js";
11
+ import { countElicitOptIns, deriveWiringManifest, resolveAskSeamForm, resolveDeclaredDurability, resolveElicitSeam, resolveQuestionSeam, resolveSubagentTranscriptTier } from "../wiring-manifest.js";
12
+ import { DEFAULT_IRREVERSIBLE_SCOPE } from "./checkpoint-scope.js";
13
+ export function mcpManifestEntries(entries, statuses) {
14
+ return (entries ?? []).map((entry) => {
15
+ const status = statuses.find((s) => s.name === entry.name);
16
+ const source = typeof entry.source === "string" && entry.source.length > 0 ? { source: inlineUntrusted(entry.source, 80) } : {};
17
+ if (status === undefined)
18
+ return { name: entry.name, ...source, status: "skipped", toolCount: 0 };
19
+ if (status.status === "connected")
20
+ return { name: entry.name, ...source, status: "connected", toolCount: status.toolNames?.length ?? 0 };
21
+ return {
22
+ name: entry.name,
23
+ ...source,
24
+ status: "failed",
25
+ ...(status.errorCode !== undefined ? { errorCode: status.errorCode } : {}),
26
+ ...(status.error !== undefined ? { error: inlineUntrusted(status.error, 240) } : {}),
27
+ toolCount: 0,
28
+ };
29
+ });
30
+ }
31
+ function buildUsageGovernance(windows, deps, principal, sessionId) {
32
+ if (windows === undefined || windows.length === 0)
33
+ return undefined;
34
+ const store = deps.usageWindowStore;
35
+ if (store === undefined) {
36
+ 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 });
37
+ return undefined;
38
+ }
39
+ const key = principal || GLOBAL_USAGE_KEY;
40
+ const governsCost = windowsGovernCost(windows);
41
+ let chargedTokens = 0;
42
+ let chargedCostMicroUsd = 0;
43
+ let announcedCostGap = false;
44
+ let recordedUnpricedGap = false;
45
+ return {
46
+ key,
47
+ governsCost,
48
+ async check(now) {
49
+ const readings = await store.read(key, windows, now);
50
+ if (!announcedCostGap && readings.some((r) => r.costUnknown === true)) {
51
+ announcedCostGap = true;
52
+ 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 });
53
+ }
54
+ return usageRetryAfterMs(readings, windows);
55
+ },
56
+ async commit(cumulativeTokens, cumulativeCostMicroUsd, now) {
57
+ if (!governsCost) {
58
+ const delta = cumulativeTokens - chargedTokens;
59
+ if (delta <= 0)
60
+ return;
61
+ await store.charge(key, delta, now, windows);
62
+ chargedTokens = cumulativeTokens;
63
+ return;
64
+ }
65
+ if (cumulativeCostMicroUsd === undefined) {
66
+ const tokenDelta = cumulativeTokens - chargedTokens;
67
+ if (tokenDelta > 0 || !recordedUnpricedGap) {
68
+ await store.charge(key, Math.max(0, tokenDelta), now, windows, null);
69
+ chargedTokens = Math.max(chargedTokens, cumulativeTokens);
70
+ recordedUnpricedGap = true;
71
+ }
72
+ 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.");
73
+ e.code = "config.usage_window_unpriced";
74
+ throw e;
75
+ }
76
+ const tokenDelta = cumulativeTokens - chargedTokens;
77
+ const costDelta = cumulativeCostMicroUsd - chargedCostMicroUsd;
78
+ if (tokenDelta <= 0 && costDelta <= 0)
79
+ return;
80
+ await store.charge(key, Math.max(0, tokenDelta), now, windows, Math.max(0, costDelta));
81
+ chargedTokens = cumulativeTokens;
82
+ chargedCostMicroUsd = cumulativeCostMicroUsd;
83
+ },
84
+ };
85
+ }
86
+ const ungatedWarnedShapes = new WeakMap();
87
+ function announceDurableGateUnavailable(args) {
88
+ if (!args.forceDurableGate || args.storeWired)
89
+ return;
90
+ const cause = args.taskStoreNull ? "task_store_null" : "no_deployment_store";
91
+ const inheritedContentMandate = (args.parentConstraints ?? []).some((pc) => pc.contentMandate === true);
92
+ const liveQuestionFace = args.liveQuestionFace && !inheritedContentMandate;
93
+ deliverEngineNotice(args.onNotice, {
94
+ code: "config.durable_gate_unavailable",
95
+ message: `Durable approval gate mandated but unavailable: the runtime entitlement forceDurableGate is in force for this ` +
96
+ `principal, but ${cause === "task_store_null" ? "this task disabled the checkpoint store (checkpointStore: null)" : "this deployment wired no checkpoint store"}, ` +
97
+ `so no interactive ask of this task can be parked for a durable answer. Asks resolve on the live chain instead ` +
98
+ `(${args.liveApprover ? "a live approver answers permission asks in-stream" : "permission asks are denied fail-closed — no live approver is wired"}; ` +
99
+ `${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"}) ` +
100
+ `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."}`,
101
+ detail: {
102
+ sessionId: args.sessionId,
103
+ runId: args.runId,
104
+ ...(args.principal ? { principal: args.principal } : {}),
105
+ cause,
106
+ liveApprover: args.liveApprover,
107
+ liveQuestionFace,
108
+ },
109
+ });
110
+ }
111
+ export function prepareWiringManifest(input) {
112
+ const { spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, 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 } = input;
113
+ const onAsk = spec.onAsk ?? deps.onAsk;
114
+ const handWriteTools = handsEnabled && spec.handsReadOnly !== true
115
+ ? Object.keys(HAND_TOOL_EFFECTS).filter((name) => {
116
+ const eff = HAND_TOOL_EFFECTS[name];
117
+ return eff === "write" || eff === "idempotent";
118
+ })
119
+ : [];
120
+ const hasEffectAwareGate = Boolean(policyLayers.length > 0 || (hooks?.preToolUse !== undefined && !preToolUseObservational));
121
+ const destructiveMcpUngated = mcp.tools.some((t) => !irreversibleTools.has(t.name) && !egressTools.has(t.name) && (toolEffects.get(t.name) ?? "write") !== "read");
122
+ const firstPartyWriteUngated = (spec.tools ?? [])
123
+ .map((t) => t.name)
124
+ .filter((name) => !irreversibleTools.has(name) && !egressTools.has(name) && toolEffects.get(name) === "write");
125
+ if (!hasEffectAwareGate && (handWriteTools.length > 0 || destructiveMcpUngated || firstPartyWriteUngated.length > 0)) {
126
+ const what = [
127
+ handWriteTools.length > 0 ? `hand tools [${handWriteTools.join(", ")}]` : undefined,
128
+ destructiveMcpUngated ? "MCP write tools" : undefined,
129
+ firstPartyWriteUngated.length > 0 ? `write tools [${firstPartyWriteUngated.join(", ")}]` : undefined,
130
+ ]
131
+ .filter(Boolean)
132
+ .join(" + ");
133
+ if (!ungatedWarnedShapes.get(deps)?.has(what)) {
134
+ let shapes = ungatedWarnedShapes.get(deps);
135
+ if (!shapes) {
136
+ shapes = new Set();
137
+ ungatedWarnedShapes.set(deps, shapes);
138
+ }
139
+ shapes.add(what);
140
+ deps.onError?.(new Error(`write-capable ${what} are present but UNGATED — wire an effect-aware tool policy (or a PreToolUse hook). ` +
141
+ `bash/MCP irreversible actions are otherwise unadjudicated; the deployment sandbox/egress boundary is the only protection (design/77 §4, design/53 §2.H). ` +
142
+ `(This warning fires once per deployment per tool-set shape.)`), { phase: "config", sessionId });
143
+ }
144
+ }
145
+ const preToolContexts = new Map();
146
+ const blockedToolCalls = new Set();
147
+ const approvalSettlement = new Map();
148
+ const humanBareRejections = new Set();
149
+ const batchHaltRef = {};
150
+ const blockedTracked = Boolean(hooks?.postToolUse || hooks?.preToolUse || hooks?.postToolUseFailure || hooks?.postToolBatch);
151
+ const restoreSurfaceGap = ownedEnv !== undefined && isRemoteExecutionEnv(ownedEnv) && ownedEnv.capabilities.suspendable ? missingRestoreSurface(ownedEnv) : [];
152
+ const incompleteSuspendAdapter = restoreSurfaceGap.length > 0 ? restoreSurfaceGap : undefined;
153
+ const durableSuspendInfraReady = resolveCheckpointStore(spec, deps) !== undefined &&
154
+ !(offloadStore !== undefined && isVolatileOffloadStore(offloadStore)) &&
155
+ (ownedEnv === undefined || isRemoteExecutionEnv(ownedEnv)) &&
156
+ incompleteSuspendAdapter === undefined;
157
+ const resourceSuspendEligible = spec.resourceSuspend !== undefined && durableSuspendInfraReady;
158
+ if (spec.resourceSuspend !== undefined && !resourceSuspendEligible) {
159
+ const why = resolveCheckpointStore(spec, deps) === undefined
160
+ ? "no CheckpointStore is wired"
161
+ : offloadStore !== undefined && isVolatileOffloadStore(offloadStore)
162
+ ? "tool-result offload uses the in-memory store (a resume needs durable results)"
163
+ : incompleteSuspendAdapter !== undefined
164
+ ? `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)`
165
+ : "the per-task execution env is not a RemoteExecutionEnv (it would be destroyed on suspend)";
166
+ onceLedger.onError(new Error(`resourceSuspend is set but INACTIVE: ${why}; resource limits will hard-fail, not suspend`), {
167
+ phase: "config",
168
+ sessionId,
169
+ });
170
+ }
171
+ const usageGovernance = buildUsageGovernance(usageWindows, deps, spec.principal, sessionId);
172
+ const platformSuspendArmed = durableSuspendInfraReady && (envLifetimeSuspendAt !== undefined || usageGovernance !== undefined);
173
+ const durableApproval = spec.durableApproval ??
174
+ (runtimeCaps?.forceDurableGate ? { scope: spec.principal || DEFAULT_IRREVERSIBLE_SCOPE } : undefined);
175
+ const wiringQuestionSeam = resolveQuestionSeam(spec, deps);
176
+ const wiringQuestionStripped = internals?.questionFaceStripped === true;
177
+ const wiringAskSeam = resolveAskSeamForm(spec, deps);
178
+ const wiringManifest = deriveWiringManifest({
179
+ half: "effective",
180
+ leg: resume !== undefined ? "resume" : internals?.isDelegatedChild === true ? "child" : "root",
181
+ askForm: wiringAskSeam.form,
182
+ ...(wiringAskSeam.provenance !== undefined ? { askProvenance: wiringAskSeam.provenance } : {}),
183
+ questionWired: wiringQuestionSeam.wired,
184
+ ...(wiringQuestionSeam.provenance !== undefined ? { questionProvenance: wiringQuestionSeam.provenance } : {}),
185
+ ...(wiringQuestionStripped ? { questionStrippedByEngine: true } : {}),
186
+ ...(resolvedInteractionPosture !== undefined ? { interactionPosture: resolvedInteractionPosture } : {}),
187
+ ...(spec.interactiveTools === true && !wiringQuestionSeam.wired && !durableQuestionFace && !wiringQuestionStripped
188
+ ? { interactiveToolsWithoutDeliveryFace: true }
189
+ : {}),
190
+ elicitSeamWired: resolveElicitSeam(deps),
191
+ elicitServersOptedIn: countElicitOptIns(spec),
192
+ parkCapable: checkpointStore !== undefined,
193
+ parkDurableApprovalOptIn: spec.durableApproval !== undefined,
194
+ parkForceDurableGate: runtimeCaps?.forceDurableGate === true,
195
+ parkSafetyVocabularyArmed: irreversibleTools.size > 0 || egressTools.size > 0,
196
+ ...(checkpointStore !== undefined ? { checkpointDurability: resolveDeclaredDurability(checkpointStore, "checkpointStore") } : {}),
197
+ sessionDurability: resolveDeclaredDurability(sessions, "sessionStore"),
198
+ backgroundAgentStoreWired: deps.backgroundAgentStore !== undefined,
199
+ subagentTranscriptTier: resolveSubagentTranscriptTier(deps.backgroundAgentStore !== undefined, sessions),
200
+ permissionRuleStoreWired: deps.permissionRuleStore?.partitions.durable === true,
201
+ permissionRuleSyncWired: deps.permissionRuleSyncWired === true,
202
+ permissionRuleOrgGoverned: deps.permissionRuleStore?.partitions.org === true,
203
+ hostChildEventSinkWired: deps.onBackgroundChildEvent !== undefined,
204
+ lockedConfigWired: deps.lockedConfig !== undefined,
205
+ complianceWired: deps.compliancePostureResolver !== undefined,
206
+ memoryAdmissionWired: deps.memoryScopeAdmission !== undefined,
207
+ retentionPolicyWired: deps.retentionPolicy !== undefined,
208
+ ...(modelGateManifest !== undefined ? { modelGate: modelGateManifest } : {}),
209
+ autoMode: { armed: autoModeArmReason === "armed", reason: autoModeArmReason },
210
+ mcp: mcpManifestEntries(lockedPreflight.mcp, mcp.statuses),
211
+ });
212
+ peerLaneRefs.askEffective = wiringManifest.ask.effective;
213
+ if (peerLaneActive && internals?.onTaskNotification !== undefined) {
214
+ bindPeerLaneDrain(harness, {
215
+ deps,
216
+ sessionId,
217
+ runId,
218
+ scope: taskScope,
219
+ inject: internals.onTaskNotification,
220
+ ownTokens: () => internals?.peerSelfRef?.current.ownTokens ?? [],
221
+ refs: peerLaneRefs,
222
+ selfName: () => internals?.explicitAgentName,
223
+ parked: () => suspendRef.token !== undefined || reviewRef.token !== undefined,
224
+ });
225
+ }
226
+ const parkLaneArmed = wiringManifest.parkLane.effective === true;
227
+ const gateMachineryActive = effectivePolicy !== undefined ||
228
+ hooks?.preToolUse !== undefined ||
229
+ egressTools.size > 0 ||
230
+ irreversibleTools.size > 0 ||
231
+ resourceSuspendEligible ||
232
+ platformSuspendArmed ||
233
+ spec.enablePlanMode === true;
234
+ 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 });
235
+ const hookIdentity = mintHookInvocationIdentity({
236
+ sessionId,
237
+ taskId: hostTaskId,
238
+ legKind: wiringManifest.leg.kind,
239
+ isDelegatedChild: delegation.isDelegatedChild,
240
+ ...(internals?.insideFork === true ? { insideFork: true } : {}),
241
+ ...(internals?.agentName !== undefined ? { agentName: internals.agentName } : {}),
242
+ ...(internals?.parentToolCallId !== undefined ? { parentToolCallId: internals.parentToolCallId } : {}),
243
+ });
244
+ const hookContextConsumerWired = hooks?.preToolUse !== undefined || hooks?.postToolUse !== undefined || hooks?.postToolUseFailure !== undefined;
245
+ const hookEnvFace = hookContextConsumerWired && (ownedEnv ?? deps.executionEnv) != null ? createHookEnvCapabilities(executionEnv) : undefined;
246
+ return { onAsk, preToolContexts, blockedToolCalls, approvalSettlement, humanBareRejections, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, platformSuspendArmed, durableApproval, wiringManifest, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace };
247
+ }