@sema-agent/core 7.24.4 → 7.25.0

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 (101) hide show
  1. package/CHANGELOG.md +78 -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/background-agent-store.d.ts +3 -3
  8. package/dist/core/checkpoint-store.d.ts +1 -0
  9. package/dist/core/delegation-frames.d.ts +5 -0
  10. package/dist/core/gate-fold.js +85 -0
  11. package/dist/core/gate-lanes.js +23 -1
  12. package/dist/core/gate-outcome.d.ts +8 -2
  13. package/dist/core/gate-outcome.js +3 -1
  14. package/dist/core/hooks.d.ts +29 -0
  15. package/dist/core/hooks.js +2 -0
  16. package/dist/core/mailbox-store.d.ts +5 -0
  17. package/dist/core/mailbox-store.js +1 -1
  18. package/dist/core/parent-spec-seat.d.ts +7 -1
  19. package/dist/core/parent-spec-seat.js +2 -0
  20. package/dist/core/permission-rule-consent.js +29 -47
  21. package/dist/core/permission-rule-provider.js +4 -3
  22. package/dist/core/permission-rule-store.d.ts +143 -7
  23. package/dist/core/permission-rule-store.js +165 -15
  24. package/dist/core/runner/contracts.d.ts +36 -2
  25. package/dist/core/runner/gate-exit.d.ts +14 -0
  26. package/dist/core/runner/gate-exit.js +4 -0
  27. package/dist/core/runner/prepare-delegation-surface.d.ts +4 -4
  28. package/dist/core/runner/prepare-delegation-surface.js +2 -1
  29. package/dist/core/runner/prepare-gate-stations.d.ts +4 -0
  30. package/dist/core/runner/prepare-gate-stations.js +7 -4
  31. package/dist/core/runner/prepare-hands-readface.d.ts +8 -4
  32. package/dist/core/runner/prepare-hands-readface.js +25 -10
  33. package/dist/core/runner/prepare-inherited-gate.d.ts +23 -0
  34. package/dist/core/runner/prepare-inherited-gate.js +11 -6
  35. package/dist/core/runner/prepare-policy-chain.d.ts +15 -1
  36. package/dist/core/runner/prepare-policy-chain.js +14 -4
  37. package/dist/core/runner/prepare-suspend-saga.d.ts +5 -2
  38. package/dist/core/runner/prepare-suspend-saga.js +4 -3
  39. package/dist/core/runner/prepare-task.js +10 -9
  40. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -4
  41. package/dist/core/runner/prepare-wiring-manifest.js +8 -4
  42. package/dist/core/runner/resume-apply.js +2 -0
  43. package/dist/core/runner/run-harness-handlers.js +1 -0
  44. package/dist/core/runner/run-identity-wiring.d.ts +2 -2
  45. package/dist/core/runner/run-leg.js +26 -0
  46. package/dist/core/runner/run-notification-lane.d.ts +2 -2
  47. package/dist/core/runner/run-notification-lane.js +2 -1
  48. package/dist/core/runner/run-settle-and-teardown.js +1 -0
  49. package/dist/core/runner/session-rule-policy.d.ts +3 -4
  50. package/dist/core/runner/session-rule-policy.js +11 -3
  51. package/dist/core/session-policy-store.d.ts +27 -3
  52. package/dist/core/session-policy-store.js +12 -4
  53. package/dist/core/store-contracts/background-agent-store-contract.js +10 -0
  54. package/dist/core/store-contracts/peer-directory-contract.d.ts +23 -0
  55. package/dist/core/store-contracts/peer-directory-contract.js +82 -0
  56. package/dist/core/task-event.d.ts +6 -0
  57. package/dist/core/task-notification.d.ts +79 -4
  58. package/dist/core/task-notification.js +90 -1
  59. package/dist/core/task-registry-agent.js +1 -2
  60. package/dist/core/task-registry-monitor.js +2 -1
  61. package/dist/core/task-registry-shared.d.ts +53 -5
  62. package/dist/core/task-registry-shared.js +13 -2
  63. package/dist/core/task-registry.js +8 -5
  64. package/dist/core/task-spec.d.ts +12 -5
  65. package/dist/core/tool-catalog-entries.js +1 -0
  66. package/dist/core/tool-spec.d.ts +1 -1
  67. package/dist/core/wiring-manifest.d.ts +2 -2
  68. package/dist/engine/loop/agent-loop.js +1 -1
  69. package/dist/engine/loop/types.d.ts +4 -0
  70. package/dist/index.d.ts +6 -3
  71. package/dist/index.js +4 -3
  72. package/dist/prompts/coordinator.d.ts +2 -1
  73. package/dist/stores/file/adoption/adopt.js +23 -1
  74. package/dist/stores/file/permission-rule-store.js +3 -1
  75. package/dist/stores/file/session-policy-store.d.ts +31 -0
  76. package/dist/stores/file/session-policy-store.js +87 -7
  77. package/dist/tools/fs/bash-program-position.d.ts +4 -26
  78. package/dist/tools/fs/bash-program-position.js +0 -11
  79. package/dist/tools/fs/bash-readonly-classifier.d.ts +20 -309
  80. package/dist/tools/fs/bash-readonly-classifier.js +28 -766
  81. package/dist/tools/fs/fs-bash.d.ts +43 -207
  82. package/dist/tools/fs/fs-bash.js +20 -341
  83. package/dist/tools/fs/index.d.ts +12 -13
  84. package/dist/tools/fs/index.js +3 -4
  85. package/dist/tools/fs/read-deny.d.ts +25 -0
  86. package/dist/tools/fs/read-deny.js +19 -0
  87. package/dist/tools/fs/safety.js +2 -6
  88. package/dist/tools/fs/shell-read-check.d.ts +99 -0
  89. package/dist/tools/fs/shell-read-check.js +229 -0
  90. package/dist/tools/fs/shell-read-establish.d.ts +70 -115
  91. package/dist/tools/fs/shell-read-establish.js +216 -102
  92. package/dist/tools/fs/shell-read-judge.d.ts +231 -0
  93. package/dist/tools/fs/shell-read-judge.js +170 -0
  94. package/dist/tools/fs/shell-read-plan.d.ts +3 -0
  95. package/dist/tools/fs/shell-read-plan.js +82 -19
  96. package/dist/tools/monitor.d.ts +5 -5
  97. package/dist/tools/monitor.js +3 -5
  98. package/dist/tools/task-list.d.ts +4 -0
  99. package/package.json +1 -1
  100. package/test/export-surface.snapshot.json +19 -3
  101. package/wire-changes/core@7.25.0.json +671 -0
@@ -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",
@@ -1,5 +1,5 @@
1
1
  import type { PeerInboundChainRef } from "../../agents/peer-admission.js";
2
- import { type PendingSessionNotifications, type SystemInjectionPriority, type TaskNotificationPayload } from "../task-notification.js";
2
+ import { type NotificationInjectionOutcome, type PendingSessionNotifications, type SystemInjectionPriority, type TaskNotificationPayload } from "../task-notification.js";
3
3
  import type { PushQueue } from "../push-queue.js";
4
4
  import type { TaskEvent, TaskSpec } from "../types.js";
5
5
  import type { Stats } from "./assemble-result.js";
@@ -32,7 +32,7 @@ export interface RunIdentityWiringInput {
32
32
  /** borrowed-readonly — the notification lane's ONE injection entry (the R0 lane's). */
33
33
  injectTaskNotification: (notification: TaskNotificationPayload, opts?: {
34
34
  priority?: SystemInjectionPriority;
35
- }) => Promise<"queued" | "parked" | "dropped_duplicate">;
35
+ }) => Promise<NotificationInjectionOutcome>;
36
36
  /** borrowed-mutable — the turn-open dedup set (the cross-layer double-park dedup): a redelivered frame's key is registered here. */
37
37
  deliveredAtTurnOpen: Set<string>;
38
38
  /** borrowed-mutable — the peer inbound chain, overwritten by each consumed peer-class payload. */
@@ -19,6 +19,7 @@ import { DEFAULT_PRECALL_OUTPUT_TOKENS, platformLimitTerminal } from "./clock-an
19
19
  import { deepJsonEqual, resumeContinuation } from "./decide-continuation.js";
20
20
  import { settleExecutionRecord } from "./execution-record.js";
21
21
  import { mintGateOutcome } from "./gate-exit.js";
22
+ import { withGateTimeout } from "../gate-bounded-await.js";
22
23
  import { resolveGitLegDelivery, wrapGitFrame } from "./git-leg-delivery.js";
23
24
  import { GIT_STATUS_ECHO_PREVIEW } from "./git-status-frame.js";
24
25
  import { toImageContent } from "./image.js";
@@ -678,6 +679,31 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
678
679
  return;
679
680
  }
680
681
  }
682
+ if (prepared.shellReadCheck !== undefined) {
683
+ const edited = outcome.updatedInput !== undefined && !deepJsonEqual(outcome.updatedInput, pendingAction.args);
684
+ const judged = prepared.shellReadCheck.judge(pendingAction.toolName, resolvedArgs, prepared.cwdRef?.current, prepared.abortController.signal, {
685
+ toolCallId: pendingAction.toolCallId,
686
+ ...(edited ? { seat: "rejudge" } : {}),
687
+ });
688
+ if (judged !== null) {
689
+ let verdict;
690
+ let answered = true;
691
+ try {
692
+ verdict = await withGateTimeout(judged, ORG_ADJUDICATION_TIMEOUT_MS, prepared.abortController.signal, "shellReadStation");
693
+ }
694
+ catch {
695
+ answered = false;
696
+ verdict = prepared.shellReadCheck.unanswered(pendingAction.toolName, "the read station could not answer on the resumed leg");
697
+ }
698
+ if (!answered || verdict.disposition === "refuse" || (edited && verdict.disposition !== "vouch")) {
699
+ const readDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${verdict.sentence ?? "the read boundary does not vouch for what it reads on this leg"} This approval is spent — the call has to be re-issued and approved again.`, prepared.reminderMark);
700
+ await settleEnd(true, { content: readDenial, ...(verdict.details !== undefined ? { details: verdict.details } : {}) }, vetoed("read_boundary"));
701
+ const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, readDenial, true));
702
+ emitCommitted(eid, "toolResult", pendingAction.toolCallId);
703
+ return;
704
+ }
705
+ }
706
+ }
681
707
  const tool = prepared.tools.find((t) => t.name === pendingAction.toolName);
682
708
  if (!tool) {
683
709
  const e = new Error(`the approved tool "${pendingAction.toolName}" is no longer available on resume`);
@@ -1,7 +1,7 @@
1
1
  import { type PeerInboundChainRef } from "../../agents/peer-admission.js";
2
2
  import type { PushQueue } from "../push-queue.js";
3
3
  import type { SessionStore } from "../session.js";
4
- import { type PendingSessionNotifications, type SystemInjectionPriority, type TaskNotificationPayload } from "../task-notification.js";
4
+ import { type NotificationInjectionOutcome, type PendingSessionNotifications, type SystemInjectionPriority, type TaskNotificationPayload } from "../task-notification.js";
5
5
  import type { RunnerDeps, TaskEvent, TaskSpec } from "../types.js";
6
6
  import type { SessionReadFileStates } from "./prepare-hands-readface.js";
7
7
  import type { NotificationLaneBindings, Prepared, PrepareTaskFn, ResumeRun, RunInternals, RunnerDepsSeat, RunnerSelfSeat, TaskIdRef } from "./contracts.js";
@@ -41,7 +41,7 @@ export interface RunNotificationLaneResult {
41
41
  /** The ONE injection entry every notification producer rides (the identity lane binds the notify() bridge to it). */
42
42
  injectTaskNotification: (notification: TaskNotificationPayload, opts?: {
43
43
  priority?: SystemInjectionPriority;
44
- }) => Promise<"queued" | "parked" | "dropped_duplicate">;
44
+ }) => Promise<NotificationInjectionOutcome>;
45
45
  /** The turn-open dedup set (the cross-layer double-park dedup): the identity lane registers a redelivered frame's key here. */
46
46
  deliveredAtTurnOpen: Set<string>;
47
47
  /** The peer inbound chain ref this run carries (the spawn chain's, or a fresh one). */
@@ -118,9 +118,10 @@ export async function runNotificationLane(input) {
118
118
  }
119
119
  catch {
120
120
  }
121
- injectTaskNotification(notification, opts);
121
+ return injectTaskNotification(notification, opts);
122
122
  },
123
123
  }, self, taskIdRef);
124
+ pendingSessionNotifications.bindSessionBox(prepared.sessionId, prepared.sessionBox);
124
125
  const notificationLane = {
125
126
  get harness() { return notificationHarness; }, set harness(h) { notificationHarness = h; },
126
127
  get sessionId() { return notificationSessionId; },
@@ -266,6 +266,7 @@ export async function runSettleAndTeardown(input) {
266
266
  ...(internals?.cycleSeq !== undefined ? { seq: internals.cycleSeq } : {}),
267
267
  ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
268
268
  ...(subagentName ? { name: subagentName } : {}),
269
+ ...(internals?.explicitAgentName !== undefined ? { spawnName: internals.explicitAgentName } : {}),
269
270
  model: prepared.model.id,
270
271
  usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - rs.telemetry.taskStart },
271
272
  status: result.terminal.kind === "completed" ? "completed" : "failed",
@@ -87,10 +87,9 @@ export interface SessionRuleSource {
87
87
  * DELETED and written again starts over at 1. At `>=`, a recreated row whose rev merely TIES the
88
88
  * enforced one would replace it — an unauthorized loosening dressed as an update. At `>`, a tie enforces
89
89
  * the floor, which is a no-op for the honest case (equal rev is the same row) and fail-closed for the
90
- * recycled one. It raises the cost of the recycle rather than closing it: the rev can still be climbed
91
- * past the floor by repeated writes, because the tighten gate has no prior row to compare against after
92
- * a delete. Closing it belongs to the STORE's rev lifecycle (a counter that survives deletion, or a
93
- * generation identity), which is a cross-backend contract change, not a reader-side one.
90
+ * recycled one. Closed since 7.25.0 by the STORE's lineage identity (`StoredSessionRules.gen`): a
91
+ * recreated row is another generation and is never compared to the enforced one, whatever its rev, and
92
+ * a backend without lineages (gen 0 throughout) keeps the `>` protection above.
94
93
  *
95
94
  * The per-call handoff mirrors the permission-rule lanes': the first reader of a `toolCallId` takes the read
96
95
  * and hands it on, a second reader of the SAME call claims that one snapshot, and an unclaimed entry is
@@ -60,9 +60,17 @@ export function createLiveSessionRules(store, floor, hooks) {
60
60
  }
61
61
  return enforced;
62
62
  }
63
- if (vanished) {
64
- announce(`session ${JSON.stringify(floor.sessionId)} has a new rules row at rev ${live.rev}, which does not continue the deleted row this leg enforces`);
65
- return enforced;
63
+ if (enforced.rev > 0) {
64
+ if (live.gen !== undefined && enforced.gen !== undefined) {
65
+ if (live.gen !== enforced.gen) {
66
+ announce(`session ${JSON.stringify(floor.sessionId)} has a rules row of a new lineage (generation ${live.gen}, rev ${live.rev}), which does not continue the row this leg enforces (generation ${enforced.gen})`);
67
+ return enforced;
68
+ }
69
+ }
70
+ else if (vanished) {
71
+ announce(`session ${JSON.stringify(floor.sessionId)} has a new rules row at rev ${live.rev} after the row this leg enforced was deleted, and its backend carries no lineage to continue it by`);
72
+ return enforced;
73
+ }
66
74
  }
67
75
  if (live.rev <= enforced.rev) {
68
76
  if (live.rev < enforced.rev) {