@sema-agent/core 7.24.4 → 7.25.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 (107) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/dist/agents/peer-session-drain.d.ts +16 -1
  3. package/dist/agents/peer-session-drain.js +16 -1
  4. package/dist/agents/send-message-tool.d.ts +10 -2
  5. package/dist/agents/send-message-tool.js +18 -7
  6. package/dist/agents/subagent.js +13 -10
  7. package/dist/core/arg-summary.d.ts +0 -7
  8. package/dist/core/arg-summary.js +2 -1
  9. package/dist/core/background-agent-store.d.ts +3 -3
  10. package/dist/core/checkpoint-store.d.ts +1 -0
  11. package/dist/core/delegation-frames.d.ts +5 -0
  12. package/dist/core/gate-fold.js +85 -0
  13. package/dist/core/gate-lanes.js +23 -1
  14. package/dist/core/gate-outcome.d.ts +8 -2
  15. package/dist/core/gate-outcome.js +3 -1
  16. package/dist/core/hooks.d.ts +29 -0
  17. package/dist/core/hooks.js +2 -0
  18. package/dist/core/mailbox-store.d.ts +5 -0
  19. package/dist/core/mailbox-store.js +1 -1
  20. package/dist/core/parent-spec-seat.d.ts +7 -1
  21. package/dist/core/parent-spec-seat.js +2 -0
  22. package/dist/core/permission-rule-consent.js +29 -47
  23. package/dist/core/permission-rule-provider.js +4 -3
  24. package/dist/core/permission-rule-store.d.ts +143 -7
  25. package/dist/core/permission-rule-store.js +165 -15
  26. package/dist/core/runner/contracts.d.ts +36 -2
  27. package/dist/core/runner/gate-exit.d.ts +14 -0
  28. package/dist/core/runner/gate-exit.js +4 -0
  29. package/dist/core/runner/prepare-delegation-surface.d.ts +4 -4
  30. package/dist/core/runner/prepare-delegation-surface.js +2 -1
  31. package/dist/core/runner/prepare-gate-stations.d.ts +4 -0
  32. package/dist/core/runner/prepare-gate-stations.js +7 -4
  33. package/dist/core/runner/prepare-hands-readface.d.ts +8 -4
  34. package/dist/core/runner/prepare-hands-readface.js +25 -10
  35. package/dist/core/runner/prepare-inherited-gate.d.ts +23 -0
  36. package/dist/core/runner/prepare-inherited-gate.js +11 -6
  37. package/dist/core/runner/prepare-policy-chain.d.ts +15 -1
  38. package/dist/core/runner/prepare-policy-chain.js +14 -4
  39. package/dist/core/runner/prepare-suspend-saga.d.ts +5 -2
  40. package/dist/core/runner/prepare-suspend-saga.js +4 -3
  41. package/dist/core/runner/prepare-task.js +10 -9
  42. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -4
  43. package/dist/core/runner/prepare-wiring-manifest.js +8 -4
  44. package/dist/core/runner/resume-apply.js +2 -0
  45. package/dist/core/runner/run-harness-handlers.js +1 -0
  46. package/dist/core/runner/run-identity-wiring.d.ts +2 -2
  47. package/dist/core/runner/run-leg.js +26 -0
  48. package/dist/core/runner/run-notification-lane.d.ts +2 -2
  49. package/dist/core/runner/run-notification-lane.js +2 -1
  50. package/dist/core/runner/run-settle-and-teardown.js +1 -0
  51. package/dist/core/runner/session-rule-policy.d.ts +3 -4
  52. package/dist/core/runner/session-rule-policy.js +11 -3
  53. package/dist/core/session-policy-store.d.ts +27 -3
  54. package/dist/core/session-policy-store.js +12 -4
  55. package/dist/core/store-contracts/background-agent-store-contract.js +10 -0
  56. package/dist/core/store-contracts/peer-directory-contract.d.ts +23 -0
  57. package/dist/core/store-contracts/peer-directory-contract.js +82 -0
  58. package/dist/core/task-event.d.ts +6 -0
  59. package/dist/core/task-notification.d.ts +79 -4
  60. package/dist/core/task-notification.js +90 -1
  61. package/dist/core/task-registry-agent.js +1 -2
  62. package/dist/core/task-registry-monitor.js +2 -1
  63. package/dist/core/task-registry-shared.d.ts +53 -5
  64. package/dist/core/task-registry-shared.js +13 -2
  65. package/dist/core/task-registry.js +8 -5
  66. package/dist/core/task-spec.d.ts +12 -5
  67. package/dist/core/tool-catalog-entries.js +1 -0
  68. package/dist/core/tool-policy.js +30 -23
  69. package/dist/core/tool-spec.d.ts +1 -1
  70. package/dist/core/wiring-manifest.d.ts +2 -2
  71. package/dist/engine/loop/agent-loop.js +1 -1
  72. package/dist/engine/loop/types.d.ts +4 -0
  73. package/dist/index.d.ts +6 -3
  74. package/dist/index.js +4 -3
  75. package/dist/prompts/coordinator.d.ts +2 -1
  76. package/dist/stores/file/adoption/adopt.js +23 -1
  77. package/dist/stores/file/permission-rule-store.js +3 -1
  78. package/dist/stores/file/session-policy-store.d.ts +31 -0
  79. package/dist/stores/file/session-policy-store.js +87 -7
  80. package/dist/tools/fs/bash-lexer.d.ts +13 -0
  81. package/dist/tools/fs/bash-lexer.js +61 -0
  82. package/dist/tools/fs/bash-program-position.d.ts +36 -29
  83. package/dist/tools/fs/bash-program-position.js +33 -13
  84. package/dist/tools/fs/bash-readonly-classifier.d.ts +26 -313
  85. package/dist/tools/fs/bash-readonly-classifier.js +81 -782
  86. package/dist/tools/fs/fs-bash.d.ts +43 -207
  87. package/dist/tools/fs/fs-bash.js +20 -341
  88. package/dist/tools/fs/index.d.ts +12 -13
  89. package/dist/tools/fs/index.js +3 -4
  90. package/dist/tools/fs/read-deny.d.ts +25 -0
  91. package/dist/tools/fs/read-deny.js +19 -0
  92. package/dist/tools/fs/safety.js +2 -6
  93. package/dist/tools/fs/shell-read-check.d.ts +99 -0
  94. package/dist/tools/fs/shell-read-check.js +229 -0
  95. package/dist/tools/fs/shell-read-establish.d.ts +70 -115
  96. package/dist/tools/fs/shell-read-establish.js +216 -102
  97. package/dist/tools/fs/shell-read-judge.d.ts +231 -0
  98. package/dist/tools/fs/shell-read-judge.js +170 -0
  99. package/dist/tools/fs/shell-read-plan.d.ts +3 -0
  100. package/dist/tools/fs/shell-read-plan.js +88 -27
  101. package/dist/tools/monitor.d.ts +5 -5
  102. package/dist/tools/monitor.js +3 -5
  103. package/dist/tools/task-list.d.ts +4 -0
  104. package/package.json +1 -1
  105. package/test/export-surface.snapshot.json +19 -3
  106. package/wire-changes/core@7.25.0.json +671 -0
  107. package/wire-changes/core@7.25.1.json +64 -0
@@ -41,7 +41,7 @@ export function maybeHumanRejectionHalt(input) {
41
41
  };
42
42
  }
43
43
  export function prepareGateStations(input) {
44
- const { askLane, parkAsk, tools, toolRosterDeltas, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, planModeRef, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, writeProtectionCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash } = input;
44
+ const { askLane, parkAsk, tools, toolRosterDeltas, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, planModeRef, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, writeProtectionCheck, shellReadCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash } = input;
45
45
  if (askLane !== undefined && parkAsk !== undefined) {
46
46
  const { adjudicate, resolveAskBound, notifyPermissionDenied, notifyHookError } = askLane;
47
47
  const { resolveContentAsk, suspendAsk } = parkAsk;
@@ -58,7 +58,8 @@ export function prepareGateStations(input) {
58
58
  irreversibleTools.size > 0 ||
59
59
  spec.enablePlanMode === true ||
60
60
  complianceDenies.has("web_fetch") ||
61
- writeProtectionArmed;
61
+ writeProtectionArmed ||
62
+ shellReadCheck !== undefined;
62
63
  if (toolCallGateArmedRef.armed) {
63
64
  harness.on("tool_call", async (e) => {
64
65
  blockedToolCalls.delete(e.toolCallId);
@@ -112,6 +113,7 @@ export function prepareGateStations(input) {
112
113
  egress: egressTools.has(e.toolName),
113
114
  peerMessage: e.toolName === SEND_MESSAGE_TOOL_NAME,
114
115
  ...(writeProtectionCheck !== undefined ? { writeProtectionCheck } : {}),
116
+ ...(shellReadCheck !== undefined ? { shellReadCheck } : {}),
115
117
  irreversibility: irreversibilityTier.get(e.toolName),
116
118
  reversibilityProbe: reversibilityProbes.get(e.toolName),
117
119
  abortSignal: abortController.signal,
@@ -204,11 +206,12 @@ export function prepareGateStations(input) {
204
206
  toolName: e.toolName,
205
207
  toolCallId: e.toolCallId,
206
208
  });
209
+ const details = result.details !== undefined ? { details: result.details } : {};
207
210
  if (haltRemaining !== undefined) {
208
211
  batchHaltRef.current = { rejectedToolCallId: e.toolCallId };
209
- return { block: true, reason: result.reason, haltRemaining };
212
+ return { block: true, reason: result.reason, ...details, haltRemaining };
210
213
  }
211
- return { block: true, reason: result.reason };
214
+ return { block: true, reason: result.reason, ...details };
212
215
  }
213
216
  return result.updatedInput !== undefined ? { updatedInput: result.updatedInput } : undefined;
214
217
  });
@@ -1,5 +1,6 @@
1
- import { type BashReadonlyRootBoundary, type BeforeWriteHook, type CwdRef, type ReadDenyMatcher, type ContainmentFace } from "../../tools/fs/index.js";
1
+ import { type BeforeWriteHook, type CwdRef, type ReadDenyMatcher, type ContainmentFace } from "../../tools/fs/index.js";
2
2
  import { type ReadFileState } from "../../tools/fs/safety.js";
3
+ import { type ShellReadAdmission, type ShellReadCheck } from "../../tools/fs/shell-read-check.js";
3
4
  import type { RunnerDeps, ShellGateDoctrine, TaskSpec } from "../types.js";
4
5
  import type { Model } from "../../internal/llm.js";
5
6
  import type { AgentHarness, AgentTool, ExecutionEnv, WorkspaceState } from "../../internal/harness.js";
@@ -266,9 +267,12 @@ export interface PrepareHandsReadFaceResult {
266
267
  /** owned — real shell + write hands + background-capable env: gates the driver's TaskOutput/
267
268
  * TaskStop/Monitor/SendMessage mounts (P11) and the Monitor arm of the shellGate fold here. */
268
269
  backgroundTaskToolsActive: boolean;
269
- /** owned — this run's shell read BOUNDARY, published for a P11 face that spawns a shell command and
270
- * mounts LATER than the band (`Monitor`): the SAME closure the band's shell faces got. ⇔ hands. */
271
- shellReadBoundarySeat: (() => BashReadonlyRootBoundary | undefined) | undefined;
270
+ /** owned — this run's shell READ STATION, the gate input (`ToolGateInput.shellReadCheck`): ONE judgment of
271
+ * where a shell command reads, for every shell face this leg mounts, under every doctrine. ⇔ a real shell. */
272
+ shellReadCheck: ShellReadCheck | undefined;
273
+ /** owned — the same station's pre-spawn ADMISSION, published for the P11 face that spawns a shell command
274
+ * and mounts LATER than the band (`Monitor`): the SAME function the band's shell faces got. ⇔ a real shell. */
275
+ shellReadAdmission: ShellReadAdmission | undefined;
272
276
  /** write-back (D-3 hold) — the run's RESOLVED read face where hands mount; undefined ⇔ no hands
273
277
  * (the hands-less value lives in `handsLessResolvedFace`). The driver assigns this onto its
274
278
  * outer `let` for the call-time closures (ctx enrichment / checkpoint mint / envFacts). */
@@ -3,8 +3,10 @@ import { StubExecutionEnv } from "../stub-env.js";
3
3
  import { hasBackgroundShell } from "../background-shell.js";
4
4
  import { boundaryIsTheSandbox } from "../remote-env.js";
5
5
  import { defaultTaskRegistry } from "../task-registry.js";
6
- import { bashReadBoundaryProbe, bashReversibilityProbe, activeReadDenyBuiltinTiers, compileReadDeny, readDenySeat, createHandsToolkit, deploymentFaceClampNotice, pdfModelCapabilitiesOf, inheritSessionReadFileState, resolveContainmentFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, } from "../../tools/fs/index.js";
6
+ import { bashReversibilityProbe, activeReadDenyBuiltinTiers, compileReadDeny, readDenySeat, createHandsToolkit, deploymentFaceClampNotice, pdfModelCapabilitiesOf, inheritSessionReadFileState, resolveContainmentFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, } from "../../tools/fs/index.js";
7
7
  import { resolveKey, sha256 } from "../../tools/fs/safety.js";
8
+ import { createShellOverflowSpoolFence } from "../../tools/fs/fs-shared.js";
9
+ import { createShellReadStation } from "../../tools/fs/shell-read-check.js";
8
10
  import { normalizeFileText } from "../../tools/fs/encoding.js";
9
11
  import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
10
12
  import { deliverEngineNotice } from "../types.js";
@@ -137,7 +139,8 @@ export function resolveHandsLessReadFace(input) {
137
139
  wsSnapshot: undefined,
138
140
  rebaseWsPath: (p) => p,
139
141
  backgroundTaskToolsActive: false,
140
- shellReadBoundarySeat: undefined,
142
+ shellReadCheck: undefined,
143
+ shellReadAdmission: undefined,
141
144
  resolvedReadFace: undefined,
142
145
  resolvedWriteFace: undefined,
143
146
  readDenyAdditionsNormalized,
@@ -168,7 +171,7 @@ export async function prepareHandsMount(input) {
168
171
  let wsSnapshot;
169
172
  let rebaseWsPath = (p) => p;
170
173
  let backgroundTaskToolsActive = false;
171
- let shellReadBoundarySeat;
174
+ let shellReadStation;
172
175
  let resolvedReadFace;
173
176
  let resolvedWriteFace;
174
177
  let readDenyAdditionsNormalized = [];
@@ -337,7 +340,17 @@ export async function prepareHandsMount(input) {
337
340
  ...denySeat,
338
341
  ...(resolvedReadFace !== undefined ? { face: resolvedReadFace } : {}),
339
342
  });
340
- shellReadBoundarySeat = shellReadBoundary;
343
+ if (handsIncludeShell) {
344
+ const readonlyBoundary = handsReadOnly
345
+ ? { roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical], cwd: rootCanonical, ...denySeat, alsoInside: createShellOverflowSpoolFence(executionEnv) }
346
+ : undefined;
347
+ shellReadStation = createShellReadStation({
348
+ env: executionEnv,
349
+ tools: new Set(handsReadOnly ? ["Bash"] : backgroundTaskToolsActive ? ["Bash", "Monitor"] : ["Bash"]),
350
+ seatOf: () => (handsReadOnly ? "readonly" : effectiveShellGate === "classify" ? "bash_classify" : "bash_off"),
351
+ boundaryOf: () => readonlyBoundary ?? shellReadBoundary(),
352
+ });
353
+ }
341
354
  const band = createHandsToolkit(executionEnv, readFileState, rootCanonical, {
342
355
  ...(additionalRootsCanonical.length > 0 ? { additionalRoots: additionalRootsCanonical } : {}),
343
356
  ...(additionalReadRootsCanonical.length > 0 ? { additionalReadRoots: additionalReadRootsCanonical } : {}),
@@ -352,7 +365,7 @@ export async function prepareHandsMount(input) {
352
365
  ...(input.onFileEdited !== undefined ? { onFileEdited: input.onFileEdited } : {}),
353
366
  includeShell: handsIncludeShell,
354
367
  readOnly: handsReadOnly,
355
- shellReadBoundary,
368
+ ...(shellReadStation !== undefined ? { shellReadAdmission: shellReadStation.admit } : {}),
356
369
  ...(handsCwdRef ? { cwdRef: handsCwdRef } : {}),
357
370
  taskRegistry: defaultTaskRegistry,
358
371
  taskOwner: hostTaskId,
@@ -395,7 +408,7 @@ export async function prepareHandsMount(input) {
395
408
  if (!(executionEnv instanceof StubExecutionEnv) && spec.handsReadOnly !== true) {
396
409
  if (shellGate === "off") {
397
410
  deps.onError?.(new Error(`shell gate doctrine is "off" while a real writable shell (Bash) is mounted — the read boundary is ` +
398
- `still judged on every command (a deny-listed or out-of-root read asks; the ask is mandated${boundaryIsTheSandbox(executionEnv) ? "" : "; a read that only leaves the roots after symlink resolution is refused before it runs"}), but no ` +
411
+ `still judged on every command (a deny-listed read is refused; an out-of-root read asks, and the ask is mandated${boundaryIsTheSandbox(executionEnv) ? "; under an isolated sandbox the paths are judged as written" : "; the paths are judged as the filesystem resolves them, symlinks and patterns included"}), but no ` +
399
412
  `residual shell-risk classifier is installed: writes, egress and unknown commands are adjudicated by ` +
400
413
  `the ordinary policy/hook chain only. Set spec.shellGate to "classify" or "always" if this deployment ` +
401
414
  `expects doctrine-gated shell behavior.`), { phase: "config", sessionId, classification: "shell-gate-off" });
@@ -405,12 +418,13 @@ export async function prepareHandsMount(input) {
405
418
  const seatOwned = !egressTools.has(name) && tierBefore !== "always" && tierBefore !== "maybe";
406
419
  if (seatOwned)
407
420
  shellGatedTools.add(name);
421
+ if (shellGate === "off" && tierBefore !== "always")
422
+ return;
408
423
  const tier = shellGate === "always" || tierBefore === "always" ? "always" : "maybe";
409
424
  irreversibilityTier.set(name, tier);
410
425
  irreversibleTools.add(name);
411
- if (seatOwned && tier === "maybe") {
412
- reversibilityProbes.set(name, shellGate === "classify" ? bashReversibilityProbe(undefined, shellReadBoundary) : bashReadBoundaryProbe(shellReadBoundary));
413
- }
426
+ if (seatOwned && tier === "maybe")
427
+ reversibilityProbes.set(name, bashReversibilityProbe(undefined, shellReadBoundary));
414
428
  };
415
429
  installShellSeat("Bash");
416
430
  if (backgroundTaskToolsActive)
@@ -425,7 +439,8 @@ export async function prepareHandsMount(input) {
425
439
  wsSnapshot,
426
440
  rebaseWsPath,
427
441
  backgroundTaskToolsActive,
428
- shellReadBoundarySeat,
442
+ shellReadCheck: shellReadStation?.check,
443
+ shellReadAdmission: shellReadStation?.admit,
429
444
  resolvedReadFace,
430
445
  resolvedWriteFace,
431
446
  handsLessResolvedWriteFace,
@@ -208,9 +208,32 @@ export interface PrepareInheritedGateResult {
208
208
  sessionId: string;
209
209
  principal?: string;
210
210
  rev: number;
211
+ gen?: number;
211
212
  rules: SessionPermissionRules;
212
213
  };
213
214
  };
215
+ /** borrowed-mutable — the ANCESTOR twin of {@link PrepareInheritedGateResult.ownSessionRulesRef}, keyed by
216
+ * the entry's POSITION in `inheritedAncestorRules`. Writers: the policy chain's ancestor layers, each time
217
+ * one comes to enforce a newer row than it prepared with. Read by {@link ancestorRulesNow}. Position, not
218
+ * `(sessionId, principal)`: the chain may legitimately carry two entries of the same identity with
219
+ * DIFFERENT floors (a leg whose own session is also an ancestor's, a seed and a live channel that both
220
+ * name it), each compiled into its own layer with its own floor — and a by-identity key lets the layer
221
+ * that advanced overwrite the entry of the layer that did not, publishing the weaker floor over the
222
+ * stronger one. A layer upgrades its OWN entry; position is what "its own" means. */
223
+ ancestorSessionRulesRef: {
224
+ current: Map<number, {
225
+ sessionId: string;
226
+ principal?: string;
227
+ rev: number;
228
+ gen?: number;
229
+ rules: SessionPermissionRules;
230
+ }>;
231
+ };
232
+ /** owned — the ancestor rules AS THIS LEG NOW ENFORCES THEM: the inherited entries with the seat above
233
+ * overlaid. The ONE projection, called by the child-chain assembly at spawn and by the checkpoint writer
234
+ * at suspend — two faces of one rule ("what a later reader inherits is what this leg enforces"), which
235
+ * were two implementations while the checkpoint cloned the prepare-time list. */
236
+ ancestorRulesNow: () => InheritedGate["ancestorRules"];
214
237
  /** borrowed-mutable — THIS task's own frozen admitted org scopes for its children's chain. Writers: the memory phase's
215
238
  * admission door, once per leg. Read by the chain assembly (always emitted, empty = no org plane). */
216
239
  memoryAdmittedOrgScopesRef: {
@@ -13,13 +13,16 @@ export function prepareInheritedGate(input) {
13
13
  const inheritedAncestorRules = (() => {
14
14
  const seedRules = seedInheritedGate?.ancestorRules;
15
15
  const liveRules = liveInheritedGate?.ancestorRules;
16
- if (seedRules === undefined || seedRules.length === 0)
17
- return liveRules ?? seedRules;
16
+ if (seedRules === undefined || seedRules.length === 0) {
17
+ const only = liveRules ?? seedRules;
18
+ return only === undefined ? undefined : [...only];
19
+ }
18
20
  if (liveRules === undefined || liveRules.length === 0)
19
- return seedRules;
21
+ return [...seedRules];
20
22
  const merged = seedRules.map((s) => {
21
23
  const live = liveRules.find((l) => l.sessionId === s.sessionId && l.principal === s.principal);
22
- return live !== undefined && live.rev >= s.rev ? live : s;
24
+ const sameLineage = live === undefined || live.gen === undefined || s.gen === undefined || live.gen === s.gen;
25
+ return live !== undefined && sameLineage && live.rev >= s.rev ? live : s;
23
26
  });
24
27
  const extras = liveRules.filter((l) => !seedRules.some((s) => s.sessionId === l.sessionId && s.principal === l.principal));
25
28
  return [...merged, ...extras];
@@ -62,6 +65,8 @@ export function prepareInheritedGate(input) {
62
65
  const specShellGate = spec.shellGate ?? "off";
63
66
  const effectiveShellGate = inheritedShellGate !== undefined && shellGateRank[inheritedShellGate] > shellGateRank[specShellGate] ? inheritedShellGate : specShellGate;
64
67
  const ownSessionRulesRef = {};
68
+ const ancestorSessionRulesRef = { current: new Map() };
69
+ const ancestorRulesNow = () => inheritedAncestorRules === undefined ? undefined : inheritedAncestorRules.map((a, i) => ancestorSessionRulesRef.current.get(i) ?? a);
65
70
  const memoryAdmittedOrgScopesRef = { current: [] };
66
71
  const ownOrgVerdictRef = { current: undefined };
67
72
  const orgAdmissionCheckpointState = () => inheritedAdmittedOrgScopes !== undefined || ownOrgVerdictRef.current !== undefined || orgGovernedProvenance;
@@ -111,7 +116,7 @@ export function prepareInheritedGate(input) {
111
116
  const autoModeDenialTracking = input.autoModeDenialTracking();
112
117
  const autoModeArming = input.autoModeArming();
113
118
  const ancestorRules = [
114
- ...(inheritedAncestorRules ?? []),
119
+ ...(ancestorRulesNow() ?? []),
115
120
  ...(ownSessionRulesRef.current !== undefined ? [ownSessionRulesRef.current] : []),
116
121
  ];
117
122
  const ownCallerPolicy = lockedPreflight.toolPolicy;
@@ -271,5 +276,5 @@ export function prepareInheritedGate(input) {
271
276
  ...(spec.enablePlanMode === true ? { enterPlanMode } : {}),
272
277
  };
273
278
  };
274
- return { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx };
279
+ return { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx };
275
280
  }
@@ -89,8 +89,21 @@ export interface PreparePolicyChainInput {
89
89
  /** borrowed-readonly — the resolved per-principal entitlements; only `forceDurableGate` (the mandate-park facility and
90
90
  * the sandbox arming's prerequisite). */
91
91
  runtimeCaps: Pick<RuntimeCaps, "forceDurableGate"> | undefined;
92
- /** borrowed-readonly — the ancestors' session-rule snapshots (each re-read live-preferring, floor kept). */
92
+ /** borrowed-readonly — the ancestors' session-rule snapshots (each re-read live-preferring, floor kept).
93
+ * Never written: an entry is some other leg's or some host's object — see `ancestorSessionRulesRef`. */
93
94
  inheritedAncestorRules: InheritedGate["ancestorRules"];
95
+ /** borrowed-mutable — THIS leg's record of what each ancestor layer now ENFORCES, keyed by the entry's
96
+ * POSITION (see the seat's own declaration in `prepare-inherited-gate.ts` for why position and not
97
+ * identity). Writer: this phase, through each ancestor layer's `onEnforced`. */
98
+ ancestorSessionRulesRef: {
99
+ current: Map<number, {
100
+ sessionId: string;
101
+ principal?: string;
102
+ rev: number;
103
+ gen?: number;
104
+ rules: SessionPermissionRules;
105
+ }>;
106
+ };
94
107
  /** borrowed-readonly — the ancestors' caller-policy constraints, ancestors first (one wrapper each). */
95
108
  inheritedParentConstraints: NonNullable<InheritedGate["parentConstraints"]>[number][] | undefined;
96
109
  /** borrowed-readonly — the resumed leg's SEED half; only `constraintChain` (the frozen deny projections). */
@@ -124,6 +137,7 @@ export interface PreparePolicyChainInput {
124
137
  sessionId: string;
125
138
  principal?: string;
126
139
  rev: number;
140
+ gen?: number;
127
141
  rules: SessionPermissionRules;
128
142
  };
129
143
  };
@@ -17,7 +17,7 @@ import { createPermissionRuleLanes, inheritedAskRuleEvidence, createRuleOffersOf
17
17
  import { createLiveSessionRules, createSessionRulePolicy } from "./session-rule-policy.js";
18
18
  import { thrownText } from "../thrown-value.js";
19
19
  export async function preparePolicyChain(input) {
20
- const { spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, envHandToolNames, handsCwdRef, readDenyMatcher, questionToolMounted, runtimeCaps, inheritedAncestorRules, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = input;
20
+ const { spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, envHandToolNames, handsCwdRef, readDenyMatcher, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = input;
21
21
  const policy = lockedPreflight.toolPolicy;
22
22
  const auditPolicyNames = (auditee) => {
23
23
  const nameGroups = toolPolicyNameSets(auditee);
@@ -89,6 +89,7 @@ export async function preparePolicyChain(input) {
89
89
  sessionId: sessionRuleKey.sessionId,
90
90
  ...(sessionRuleKey.principal !== undefined ? { principal: sessionRuleKey.principal } : {}),
91
91
  rev: row.rev,
92
+ ...(row.gen !== undefined ? { gen: row.gen } : {}),
92
93
  rules: row,
93
94
  };
94
95
  };
@@ -97,12 +98,21 @@ export async function preparePolicyChain(input) {
97
98
  publishOwnRules(rules);
98
99
  }
99
100
  const ancestorRulePolicies = [];
100
- for (const snap of inheritedAncestorRules ?? []) {
101
- const seed = { ...snap.rules, rev: snap.rev };
101
+ for (const [position, snap] of (inheritedAncestorRules ?? []).entries()) {
102
+ const seed = { ...snap.rules, rev: snap.rev, ...(snap.gen !== undefined ? { gen: snap.gen } : {}) };
103
+ const publishAncestorRules = (row) => {
104
+ ancestorSessionRulesRef.current.set(position, {
105
+ sessionId: snap.sessionId,
106
+ ...(snap.principal !== undefined ? { principal: snap.principal } : {}),
107
+ rev: row.rev,
108
+ ...(row.gen !== undefined ? { gen: row.gen } : {}),
109
+ rules: row,
110
+ });
111
+ };
102
112
  ancestorRulePolicies.push(createSessionRulePolicy({
103
113
  seed,
104
114
  ...(deps.sessionPolicyStore
105
- ? { read: createLiveSessionRules(deps.sessionPolicyStore, { sessionId: snap.sessionId, ...(snap.principal !== undefined ? { principal: snap.principal } : {}), seed }, { onUnreadable: announceSessionRulesUnreadable }) }
115
+ ? { read: createLiveSessionRules(deps.sessionPolicyStore, { sessionId: snap.sessionId, ...(snap.principal !== undefined ? { principal: snap.principal } : {}), seed }, { onUnreadable: announceSessionRulesUnreadable, onEnforced: publishAncestorRules }) }
106
116
  : {}),
107
117
  }, { env: executionEnv, rootPath: taskRootFinal, toolEffects }));
108
118
  }
@@ -125,8 +125,11 @@ export interface PrepareSuspendSagaInput {
125
125
  inheritedParentConstraints: NonNullable<InheritedGate["parentConstraints"]>[number][] | undefined;
126
126
  /** borrowed-readonly — the resume seed's inherited gate, or undefined (the carried-forward half of the same fields). */
127
127
  seedInheritedGate: PrepareResume["seed"]["inheritedGate"];
128
- /** borrowed-readonly — the inherited ancestor rules (data half), cloned onto the row. */
129
- inheritedAncestorRules: InheritedGate["ancestorRules"];
128
+ /** borrowed-readonly — the ancestor rules AS THIS LEG NOW ENFORCES THEM, read at MINT time (a getter, not
129
+ * a value: the prepare-time list is not what a resumed leg must inherit — an ancestor rule written
130
+ * mid-run and enforced here has to survive suspend exactly as it survives a spawn, and the same
131
+ * projection serves both). Cloned onto the row. */
132
+ ancestorRulesNow: () => InheritedGate["ancestorRules"];
130
133
  /** borrowed-readonly — the inherited shell-gate doctrine, persisted verbatim. */
131
134
  inheritedShellGate: InheritedGate["shellGate"];
132
135
  /** borrowed-readonly — the auto-mode intent (seat ∨ live ∨ seed), carried while the latch is healthy. */
@@ -79,7 +79,7 @@ function stampPlacementRootSessionId(placementRootResolved) {
79
79
  return placementValueOrAbsent(placementRootResolved);
80
80
  }
81
81
  export function prepareSuspendSaga(input) {
82
- const { gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, autoModeIntent, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter } = input;
82
+ const { gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, autoModeIntent, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter } = input;
83
83
  if (!gateMachineryActive)
84
84
  return { saga: undefined };
85
85
  const inFlightSpendMicroUsd = () => {
@@ -135,7 +135,8 @@ export function prepareSuspendSaga(input) {
135
135
  ? constraintChainDigest(constraintChain)
136
136
  : seedInheritedGate?.constraintDigest;
137
137
  const autoModeIntentCarried = autoModeIntent;
138
- return inheritedAncestorRules !== undefined ||
138
+ const ancestorRulesAtMint = ancestorRulesNow();
139
+ return ancestorRulesAtMint !== undefined ||
139
140
  inheritedShellGate !== undefined ||
140
141
  autoModeIntentCarried ||
141
142
  inheritedAdmittedOrgScopes !== undefined ||
@@ -143,7 +144,7 @@ export function prepareSuspendSaga(input) {
143
144
  orgGovernedProvenance ||
144
145
  requiresParentConstraint
145
146
  ? {
146
- ...(inheritedAncestorRules !== undefined ? { ancestorRules: structuredClone(inheritedAncestorRules) } : {}),
147
+ ...(ancestorRulesAtMint !== undefined ? { ancestorRules: structuredClone(ancestorRulesAtMint) } : {}),
147
148
  ...(inheritedShellGate !== undefined ? { shellGate: inheritedShellGate } : {}),
148
149
  ...(autoModeIntentCarried ? { autoModeRequested: true } : {}),
149
150
  ...(inheritedAdmittedOrgScopes !== undefined ? { admittedOrgScopes: [...inheritedAdmittedOrgScopes] } : {}),
@@ -66,7 +66,7 @@ import { resolveTriggerWindow, } from "../context-edit.js";
66
66
  import { isRemoteExecutionEnv } from "../remote-env.js";
67
67
  import { settleTeardownLeg } from "./teardown-bounded.js";
68
68
  import { defaultTaskRegistry } from "../task-registry.js";
69
- import { announcePeerLaneMount } from "../../agents/peer-session-drain.js";
69
+ import { announcePeerLaneMount, judgeSessionBoxDrain } from "../../agents/peer-session-drain.js";
70
70
  import { createSchedulerTools } from "../../tools/scheduler-tools.js";
71
71
  import { selfOrchestrationFailClosedReason } from "../../orchestration/workflow-script-runner.js";
72
72
  import { remainingBudgetMicroUsd, resolveCheckpointStore } from "../checkpoint-store.js";
@@ -288,6 +288,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
288
288
  internals?.peerSelfRef?.addAxis("s", sessionId);
289
289
  internals?.peerSelfRef?.addAxis("t", hostTaskId);
290
290
  const peerLaneActive = announcePeerLaneMount(deps, { sessionId, runId });
291
+ const sessionBox = judgeSessionBoxDrain(deps, { sessionId, scope: taskScope, isDelegatedChild: delegation.isDelegatedChild });
291
292
  const peerLaneRefs = {};
292
293
  const peerSendMessageBuiltIn = runnerSelf !== undefined && !(toolFaceSnapshot.exclude ?? []).includes(SEND_MESSAGE_TOOL_NAME) && !(spec.tools ?? []).some((t) => t.name === SEND_MESSAGE_TOOL_NAME);
293
294
  const { reminderMark, reminderDisclosureCounts } = await adoptReminderMark(session, sessionId, resume?.seed.reminderMark, internals?.reminderMark, deps.onError);
@@ -375,7 +376,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
375
376
  let readDenyAdditionsNormalized = [];
376
377
  let handsLessResolvedFace;
377
378
  let handsLessResolvedWriteFace;
378
- const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, 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, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
379
+ const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, 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, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
379
380
  const { roster, 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, checkpointSeat, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, parentSpecSeat, carrierReadFace, carrierWriteFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoptionRef.current, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
380
381
  const tools = roster.tools;
381
382
  const pausedRef = {};
@@ -513,14 +514,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
513
514
  }
514
515
  const handsReadFaceInput = { handsEnabled, executionEnv, taskRootFinal, taskRootCanonical, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals, memoryWriteGate, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), firstPartyOffload, roster, egressTools, irreversibilityTier, irreversibleTools, reversibilityProbes, reminderMark, reminderDisclosureCounts, ...(trackFileEdit !== undefined ? { trackFileEdit } : {}), onFileEdited: noteFileEdited, harnessRef, toolProgressRef, ...(restoredFilePaths.length > 0 ? { restoredFilePaths } : {}) };
515
516
  const handsReadFace = handsEnabled ? await prepareHandsMount(handsReadFaceInput) : resolveHandsLessReadFace(handsReadFaceInput);
516
- const { readFileStateForCheckpoint, seedContextFiles, handsCwdRef, workspaceStateSettle, wsSnapshot, rebaseWsPath, backgroundTaskToolsActive, shellReadBoundarySeat, additionalRootsCanonical, additionalReadRootsCanonical, attachmentRootCanonical, readDenyMatcher, envHandToolNames, sealReadStateSeat } = handsReadFace;
517
+ const { readFileStateForCheckpoint, seedContextFiles, handsCwdRef, workspaceStateSettle, wsSnapshot, rebaseWsPath, backgroundTaskToolsActive, shellReadCheck, shellReadAdmission, additionalRootsCanonical, additionalReadRootsCanonical, attachmentRootCanonical, readDenyMatcher, envHandToolNames, sealReadStateSeat } = handsReadFace;
517
518
  resolvedReadFace = handsReadFace.resolvedReadFace;
518
519
  resolvedWriteFace = handsReadFace.resolvedWriteFace;
519
520
  readDenyAdditionsNormalized = handsReadFace.readDenyAdditionsNormalized;
520
521
  handsLessResolvedFace = handsReadFace.handsLessResolvedFace;
521
522
  handsLessResolvedWriteFace = handsReadFace.handsLessResolvedWriteFace;
522
523
  shellGatedTools = handsReadFace.shellGatedTools;
523
- const { worktreeSessionRef } = prepareDelegationSurface({ spec, deps, internals, resume, sessionId, hostTaskId, taskScope, taskRootFinal, executionEnv, runnerSelf, backgroundTaskToolsActive, shellReadBoundarySeat, workflowToolsActive, peerLaneActive, peerSendMessageBuiltIn, peerLaneRefs, roster, toolEffects, axisExplicitNegatives, envHandToolNames, toolFaceSnapshot, firstPartyOffload, offloadStore, subagentRetain, enrichSpecToolCtx, handsCwdRef, wsSnapshot, rebaseWsPath, rebaseRestoredPath, workspaceStateSettle });
524
+ const { worktreeSessionRef } = prepareDelegationSurface({ spec, deps, internals, resume, sessionId, hostTaskId, taskScope, taskRootFinal, executionEnv, runnerSelf, backgroundTaskToolsActive, shellReadAdmission, workflowToolsActive, peerLaneActive, peerSendMessageBuiltIn, peerLaneRefs, roster, toolEffects, axisExplicitNegatives, envHandToolNames, toolFaceSnapshot, firstPartyOffload, offloadStore, subagentRetain, enrichSpecToolCtx, handsCwdRef, wsSnapshot, rebaseWsPath, rebaseRestoredPath, workspaceStateSettle });
524
525
  const { liveQuestionFace, contentAskBindings, contentAskRoutable, lateStrandedAnswers, discloseStrandedAnswers, settleContentAskBindings, durableQuestionFace, questionToolMounted, mountedQuestionTool } = prepareQuestionFace({ frozenOnQuestion, spec, deps, checkpointStore, runtimeCaps, resolvedInteractionPosture, resume, sessionId, roster, inheritedUnavailableAsks: () => inheritedUnavailableAsks });
525
526
  if (spec.handsReadOnly !== true) {
526
527
  const sessionScope = sessionId ?? spec.principal ?? spec.taskId ?? "default";
@@ -690,8 +691,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
690
691
  releaseSignal = () => sig.removeEventListener("abort", onExternalAbort);
691
692
  }
692
693
  }
693
- const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, envHandToolNames, handsCwdRef, readDenyMatcher, questionToolMounted, runtimeCaps, inheritedAncestorRules, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
694
- const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtectionCheck, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive: handsReadFace.readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined });
694
+ const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, envHandToolNames, handsCwdRef, readDenyMatcher, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
695
+ const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtectionCheck, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace, sessionBoxBound } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, shellReadStationArmed: shellReadCheck !== undefined, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive: handsReadFace.readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined, sessionBox });
695
696
  if (fileHistoryBoundary !== undefined) {
696
697
  harness.on("tool_call", async () => {
697
698
  await fileHistoryBoundary.settle();
@@ -699,15 +700,15 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
699
700
  });
700
701
  }
701
702
  const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash });
702
- const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, autoModeIntent, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
703
+ const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, autoModeIntent, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
703
704
  const { suspendForResource, suspendForPlatformLimit, suspendForReview } = prepareBoundaryParks({ saga, spec, checkpointStore, abortController, harness, session, sessions, sessionId, deps, priorLedger, maxSlices, maxSuspends, resourceTotal, priorSuspendCount, suspendChainBase, humanReviewRef, liveSpendRef, now, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, pausedRef, remoteEnvFailures, resourceSuspendEligible, durableSuspendInfraReady, incompleteSuspendAdapter });
704
705
  const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas: rosterSeat, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef });
705
- prepareGateStations({ askLane, parkAsk, tools, toolRosterDeltas: rosterSeat, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, planModeRef, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, writeProtectionCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash });
706
+ prepareGateStations({ askLane, parkAsk, tools, toolRosterDeltas: rosterSeat, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, planModeRef, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, writeProtectionCheck, shellReadCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash });
706
707
  const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, requestLossyRef, trimPressureRef, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
707
708
  const preparedHolder = {};
708
709
  const { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved } = prepareTurnWiring({ 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, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
709
710
  await onceLedger.settle(session, announcedListingsRef);
710
- const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(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, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, suspendForReview, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}), resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, 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, frameReceipts: { pending: [], listingSnapshotOwed: false }, gitStatusRef, listBackgroundTasks, hookIdentity, hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}) });
711
+ const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(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, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, suspendForReview, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}), resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, 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, frameReceipts: { pending: [], listingSnapshotOwed: false }, gitStatusRef, listBackgroundTasks, hookIdentity, ...(sessionBoxBound !== undefined ? { sessionBox: sessionBoxBound } : {}), hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(shellReadCheck !== undefined ? { shellReadCheck } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}) });
711
712
  rollback.commit();
712
713
  const prepared = buildPrepared();
713
714
  preparedHolder.current = prepared;
@@ -18,6 +18,7 @@
18
18
  */
19
19
  import type { AgentHarness, ExecutionEnv } from "../../internal/harness.js";
20
20
  import { type PeerLaneRefs } from "../../agents/peer-session-drain.js";
21
+ import type { SessionBoxBinding } from "../task-notification.js";
21
22
  import type { OnQuestion } from "../ask-question.js";
22
23
  import type { CheckpointStore } from "../checkpoint-store.js";
23
24
  import { type HookEnvCapabilities, type HookInvocationIdentity, type Hooks } from "../hooks.js";
@@ -47,8 +48,8 @@ export interface PrepareWiringManifestInput {
47
48
  * `now`, `onUsageGovernance`…) the helper reads through the whole record. */
48
49
  deps: RunnerDeps;
49
50
  /** borrowed-readonly — the trusted spawn-side channel: `questionFaceStripped`, `isDelegatedChild`, `onTaskNotification`,
50
- * `peerSelfRef`, `explicitAgentName` (the drain), `insideFork` / `agentName` / `parentToolCallId` (the identity). */
51
- internals: Pick<RunInternals, "questionFaceStripped" | "isDelegatedChild" | "onTaskNotification" | "peerSelfRef" | "explicitAgentName" | "insideFork" | "agentName" | "parentToolCallId"> | undefined;
51
+ * `peerSelfRef`, `explicitAgentName` (the drain + the identity), `insideFork` / `agentName` / `spawnAgentType` / `parentToolCallId` (the identity). */
52
+ internals: Pick<RunInternals, "questionFaceStripped" | "isDelegatedChild" | "onTaskNotification" | "peerSelfRef" | "explicitAgentName" | "spawnAgentType" | "insideFork" | "agentName" | "parentToolCallId"> | undefined;
52
53
  /** borrowed-readonly — the resume leg, or undefined (the manifest's leg derivation reads presence only). */
53
54
  resume: Pick<PrepareResume, "seed"> | undefined;
54
55
  /** borrowed-readonly — the acquired session id. */
@@ -77,6 +78,9 @@ export interface PrepareWiringManifestInput {
77
78
  irreversibleTools: ReadonlySet<string>;
78
79
  /** borrowed-readonly — see `irreversibleTools`. */
79
80
  egressTools: ReadonlySet<string>;
81
+ /** borrowed-readonly — the hands phase mounted the shell READ STATION (a real shell was mounted): the gate
82
+ * machinery must run for it, under every doctrine — the read-only face's containment IS that station. */
83
+ shellReadStationArmed: boolean;
80
84
  /** borrowed-readonly — the effect collection; read only here (the ungated scans). */
81
85
  toolEffects: ReadonlyMap<string, ToolEffect>;
82
86
  /** borrowed-readonly — the per-task owned env when a factory minted one (durable infra, the hook-env predicate). */
@@ -121,8 +125,8 @@ export interface PrepareWiringManifestInput {
121
125
  /** borrowed-mutable — the peer lane's late-bound facts. Writer here: `askEffective` ← the manifest's ask derivation (the
122
126
  * one fold of this session's permission-mode class). */
123
127
  peerLaneRefs: PeerLaneRefs;
124
- /** borrowed-readonly — the peer lane's mount verdict (the drain binds only while mounted). */
125
- peerLaneActive: boolean;
128
+ /** borrowed-readonly — the session-box drain's mount verdict (prepare-task's one judgment): present ⇒ this leg drains its own box. */
129
+ sessionBox: SessionBoxBinding | undefined;
126
130
  /** borrowed-readonly — the built harness the peer drain binds to. */
127
131
  harness: AgentHarness;
128
132
  /** borrowed-readonly — the durable-suspend holder; the drain's parked predicate reads `token` at run time. Not written here. */
@@ -179,6 +183,9 @@ export interface PrepareWiringManifestResult {
179
183
  hookIdentity: HookInvocationIdentity;
180
184
  /** owned — the read-only env capability face every hook context carries, or undefined (no hook consumer, or no env wired). */
181
185
  hookEnvFace: HookEnvCapabilities | undefined;
186
+ /** owned — the session box this leg's drain was BOUND on (the mount judgment ∧ an injector to drain into); the notification
187
+ * lane binds the park seat on exactly this value, so "parked here" and "drained there" are one fact. */
188
+ sessionBoxBound: SessionBoxBinding | undefined;
182
189
  }
183
190
  /**
184
191
  * The wiring manifest's per-server MCP read face for one leg ({@link WiringManifest.mcp}): every
@@ -115,7 +115,7 @@ function announceDurableGateUnavailable(args) {
115
115
  });
116
116
  }
117
117
  export function prepareWiringManifest(input) {
118
- const { spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive } = input;
118
+ const { spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, sessionBox, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive, lspManagerWired, shellReadStationArmed, lspDiagnosticsLive } = input;
119
119
  const onAsk = spec.onAsk ?? deps.onAsk;
120
120
  const handWriteTools = toolRoster.entries.filter((e) => e.mountedBy === "hands" && (e.effect === "write" || e.effect === "idempotent")).map((e) => e.name);
121
121
  const hasEffectAwareGate = Boolean(policyLayers.length > 0 || (hooks?.preToolUse !== undefined && !preToolUseObservational));
@@ -193,7 +193,7 @@ export function prepareWiringManifest(input) {
193
193
  parkCapable: checkpointStore !== undefined,
194
194
  parkDurableApprovalOptIn: spec.durableApproval !== undefined,
195
195
  parkForceDurableGate: runtimeCaps?.forceDurableGate === true,
196
- parkSafetyVocabularyArmed: irreversibleTools.size > 0 || egressTools.size > 0,
196
+ parkSafetyVocabularyArmed: irreversibleTools.size > 0 || egressTools.size > 0 || shellReadStationArmed,
197
197
  ...(checkpointStore !== undefined ? { checkpointDurability: resolveDeclaredDurability(checkpointStore, "checkpointStore") } : {}),
198
198
  sessionDurability: resolveDeclaredDurability(sessions, "sessionStore"),
199
199
  backgroundAgentStoreWired: deps.backgroundAgentStore !== undefined,
@@ -217,7 +217,8 @@ export function prepareWiringManifest(input) {
217
217
  ...(lspManagerWired ? { lsp: deriveLspSeam(toolRoster.entries.some((e) => e.contract.contractId === LSP_CONTRACT_ID), lspDiagnosticsLive) } : {}),
218
218
  });
219
219
  peerLaneRefs.askEffective = wiringManifest.ask.effective;
220
- if (peerLaneActive && internals?.onTaskNotification !== undefined) {
220
+ const sessionBoxBound = sessionBox !== undefined && internals?.onTaskNotification !== undefined ? sessionBox : undefined;
221
+ if (sessionBoxBound !== undefined && internals?.onTaskNotification !== undefined) {
221
222
  bindPeerLaneDrain(harness, {
222
223
  deps,
223
224
  sessionId,
@@ -235,6 +236,7 @@ export function prepareWiringManifest(input) {
235
236
  hooks?.preToolUse !== undefined ||
236
237
  egressTools.size > 0 ||
237
238
  irreversibleTools.size > 0 ||
239
+ shellReadStationArmed ||
238
240
  resourceSuspendEligible ||
239
241
  platformSuspendArmed ||
240
242
  spec.enablePlanMode === true;
@@ -246,9 +248,11 @@ export function prepareWiringManifest(input) {
246
248
  isDelegatedChild: delegation.isDelegatedChild,
247
249
  ...(internals?.insideFork === true ? { insideFork: true } : {}),
248
250
  ...(internals?.agentName !== undefined ? { agentName: internals.agentName } : {}),
251
+ ...(internals?.explicitAgentName !== undefined ? { spawnName: internals.explicitAgentName } : {}),
252
+ ...(internals?.spawnAgentType !== undefined ? { agentType: internals.spawnAgentType } : {}),
249
253
  ...(internals?.parentToolCallId !== undefined ? { parentToolCallId: internals.parentToolCallId } : {}),
250
254
  });
251
255
  const hookContextConsumerWired = hooks?.preToolUse !== undefined || hooks?.postToolUse !== undefined || hooks?.postToolUseFailure !== undefined;
252
256
  const hookEnvFace = hookContextConsumerWired && (ownedEnv ?? deps.executionEnv) != null ? createHookEnvCapabilities(executionEnv) : undefined;
253
- return { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, platformSuspendArmed, durableApproval, wiringManifest, writeProtectionCheck: writeProtection?.check, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace };
257
+ return { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, platformSuspendArmed, durableApproval, wiringManifest, writeProtectionCheck: writeProtection?.check, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace, sessionBoxBound };
254
258
  }
@@ -26,6 +26,8 @@ export async function resumeApply(input) {
26
26
  isDelegatedChild: effectiveDelegationFacts(internals, cp.state.isDelegatedChild).isDelegatedChild,
27
27
  ...(internals?.insideFork === true ? { insideFork: true } : {}),
28
28
  ...(internals?.agentName !== undefined ? { agentName: internals.agentName } : {}),
29
+ ...(internals?.explicitAgentName !== undefined ? { spawnName: internals.explicitAgentName } : {}),
30
+ ...(internals?.spawnAgentType !== undefined ? { agentType: internals.spawnAgentType } : {}),
29
31
  ...(internals?.parentToolCallId !== undefined ? { parentToolCallId: internals.parentToolCallId } : {}),
30
32
  });
31
33
  const screenTimeoutMs = resolveHookTimeoutMs(resumeScreenHooks?.timeoutMs, (badErr) => {
@@ -309,6 +309,7 @@ export function createHarnessHandlers(input) {
309
309
  ...(internals?.cycleSeq !== undefined ? { seq: internals.cycleSeq } : {}),
310
310
  ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
311
311
  ...(subagentName ? { name: subagentName } : {}),
312
+ ...(internals?.explicitAgentName !== undefined ? { spawnName: internals.explicitAgentName } : {}),
312
313
  model: prepared.model.id,
313
314
  usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - rs.telemetry.taskStart },
314
315
  status: "running",