@sema-agent/core 7.44.0 → 7.46.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 (159) hide show
  1. package/CHANGELOG.md +418 -1
  2. package/dist/agents/extra-tool-names.d.ts +65 -0
  3. package/dist/agents/extra-tool-names.js +124 -0
  4. package/dist/agents/subagent.d.ts +17 -5
  5. package/dist/agents/subagent.js +65 -38
  6. package/dist/agents/tool-filter.d.ts +82 -5
  7. package/dist/agents/tool-filter.js +134 -4
  8. package/dist/core/agent-config-dir.d.ts +20 -0
  9. package/dist/core/agent-config-dir.js +49 -0
  10. package/dist/core/agent-definition.d.ts +2 -2
  11. package/dist/core/allow-entry.d.ts +30 -4
  12. package/dist/core/allow-entry.js +32 -5
  13. package/dist/core/ask-origin.d.ts +23 -4
  14. package/dist/core/ask-origin.js +11 -5
  15. package/dist/core/checkpoint-store.d.ts +41 -4
  16. package/dist/core/decision-receipt.js +1 -1
  17. package/dist/core/effective-path-target.d.ts +143 -10
  18. package/dist/core/effective-path-target.js +113 -9
  19. package/dist/core/engine-notice.d.ts +25 -3
  20. package/dist/core/engine-notice.js +12 -6
  21. package/dist/core/gate-fold.js +3 -0
  22. package/dist/core/gate-lanes.js +11 -3
  23. package/dist/core/gate-outcome.d.ts +5 -0
  24. package/dist/core/gate-outcome.js +2 -0
  25. package/dist/core/governance-codes.d.ts +3 -1
  26. package/dist/core/governance-codes.js +3 -0
  27. package/dist/core/hooks.d.ts +5 -0
  28. package/dist/core/memory-engine/content-origin.d.ts +0 -34
  29. package/dist/core/memory-engine/content-origin.js +9 -4
  30. package/dist/core/memory-engine/layout.d.ts +5 -1
  31. package/dist/core/memory-engine/layout.js +1 -0
  32. package/dist/core/path-rule-binding.d.ts +65 -0
  33. package/dist/core/path-rule-binding.js +99 -0
  34. package/dist/core/permission-rule-model.d.ts +47 -18
  35. package/dist/core/permission-rule-model.js +38 -42
  36. package/dist/core/permission-rule-provider.d.ts +7 -0
  37. package/dist/core/permission-rule-provider.js +10 -3
  38. package/dist/core/permission-rule-store.d.ts +8 -0
  39. package/dist/core/permission-rule-store.js +6 -2
  40. package/dist/core/permission-rule-sync.js +2 -2
  41. package/dist/core/permission-rules.d.ts +22 -36
  42. package/dist/core/permission-rules.js +50 -40
  43. package/dist/core/real-approval.d.ts +9 -6
  44. package/dist/core/real-approval.js +5 -5
  45. package/dist/core/runner/child-tree.d.ts +110 -0
  46. package/dist/core/runner/child-tree.js +107 -0
  47. package/dist/core/runner/construction-doors.d.ts +5 -0
  48. package/dist/core/runner/construction-doors.js +6 -0
  49. package/dist/core/runner/contracts.d.ts +54 -2
  50. package/dist/core/runner/permission-rule-lanes.d.ts +34 -25
  51. package/dist/core/runner/permission-rule-lanes.js +18 -12
  52. package/dist/core/runner/persisted-path-rows.d.ts +31 -7
  53. package/dist/core/runner/persisted-path-rows.js +31 -12
  54. package/dist/core/runner/prepare-ask-lane.d.ts +6 -0
  55. package/dist/core/runner/prepare-ask-lane.js +4 -2
  56. package/dist/core/runner/prepare-config-doors.js +3 -1
  57. package/dist/core/runner/prepare-execution-env.d.ts +5 -2
  58. package/dist/core/runner/prepare-execution-env.js +11 -3
  59. package/dist/core/runner/prepare-gate-stations.d.ts +6 -0
  60. package/dist/core/runner/prepare-gate-stations.js +3 -1
  61. package/dist/core/runner/prepare-hands-readface.d.ts +8 -4
  62. package/dist/core/runner/prepare-hands-readface.js +6 -6
  63. package/dist/core/runner/prepare-inherited-gate.d.ts +8 -1
  64. package/dist/core/runner/prepare-inherited-gate.js +8 -2
  65. package/dist/core/runner/prepare-listings.d.ts +4 -4
  66. package/dist/core/runner/prepare-listings.js +2 -22
  67. package/dist/core/runner/prepare-park-ask.d.ts +5 -0
  68. package/dist/core/runner/prepare-park-ask.js +6 -4
  69. package/dist/core/runner/prepare-policy-chain.d.ts +16 -2
  70. package/dist/core/runner/prepare-policy-chain.js +14 -5
  71. package/dist/core/runner/prepare-suspend-saga.d.ts +10 -0
  72. package/dist/core/runner/prepare-suspend-saga.js +7 -1
  73. package/dist/core/runner/prepare-task.js +41 -18
  74. package/dist/core/runner/prepare-turn-wiring.d.ts +2 -0
  75. package/dist/core/runner/prepare-turn-wiring.js +2 -1
  76. package/dist/core/runner/prepare-workspace-restore.d.ts +3 -0
  77. package/dist/core/runner/prepare-workspace-restore.js +1 -0
  78. package/dist/core/runner/resume-apply.js +17 -3
  79. package/dist/core/runner/resume-checkpoint-screen.js +13 -0
  80. package/dist/core/runner/resume-policy-outcome.js +2 -1
  81. package/dist/core/runner/run-leg.js +5 -1
  82. package/dist/core/runner/run-notification-lane.js +2 -2
  83. package/dist/core/runner/runtask.d.ts +2 -0
  84. package/dist/core/runner/runtask.js +4 -2
  85. package/dist/core/runner/stream-steer-verb.js +2 -2
  86. package/dist/core/runner/sub-agent-deny-lists.d.ts +32 -0
  87. package/dist/core/runner/sub-agent-deny-lists.js +121 -0
  88. package/dist/core/runner/synthetic-tools.d.ts +1 -1
  89. package/dist/core/runner/synthetic-tools.js +3 -2
  90. package/dist/core/runner/write-protection-station.d.ts +3 -2
  91. package/dist/core/runner/write-protection-station.js +1 -0
  92. package/dist/core/runner-deps.d.ts +14 -0
  93. package/dist/core/sensitive-path-policy.d.ts +8 -0
  94. package/dist/core/sensitive-path-policy.js +18 -2
  95. package/dist/core/session-policy-store.d.ts +6 -2
  96. package/dist/core/session-policy-store.js +2 -1
  97. package/dist/core/shell-wrapper-projection.d.ts +11 -0
  98. package/dist/core/shell-wrapper-projection.js +56 -0
  99. package/dist/core/shell-wrapper-table.d.ts +144 -34
  100. package/dist/core/shell-wrapper-table.js +40 -20
  101. package/dist/core/skill-grant.d.ts +10 -2
  102. package/dist/core/skill-grant.js +20 -4
  103. package/dist/core/synthetic-read-verdict.d.ts +3 -0
  104. package/dist/core/synthetic-read-verdict.js +3 -1
  105. package/dist/core/task-notification.d.ts +12 -1
  106. package/dist/core/task-notification.js +13 -1
  107. package/dist/core/task-spec.d.ts +5 -3
  108. package/dist/core/tool-catalog-entries.d.ts +21 -0
  109. package/dist/core/tool-catalog-entries.js +9 -0
  110. package/dist/core/tool-policy.d.ts +45 -12
  111. package/dist/core/tool-policy.js +18 -20
  112. package/dist/core/tool-spec.d.ts +23 -4
  113. package/dist/core/types.d.ts +1 -1
  114. package/dist/core/types.js +1 -1
  115. package/dist/core/write-protect.d.ts +4 -0
  116. package/dist/core/write-protect.js +30 -9
  117. package/dist/index.d.ts +10 -6
  118. package/dist/index.js +9 -5
  119. package/dist/orchestration/run-spec.js +8 -7
  120. package/dist/orchestration/workflow.js +3 -2
  121. package/dist/shell/lex.d.ts +7 -0
  122. package/dist/shell/lex.js +17 -0
  123. package/dist/tools/fs/bash-program-position.d.ts +8 -87
  124. package/dist/tools/fs/bash-program-position.js +33 -69
  125. package/dist/tools/fs/bash-readonly-classifier.d.ts +17 -444
  126. package/dist/tools/fs/bash-readonly-classifier.js +169 -1225
  127. package/dist/tools/fs/index.d.ts +3 -2
  128. package/dist/tools/fs/program-read-model.d.ts +694 -0
  129. package/dist/tools/fs/program-read-model.js +1252 -0
  130. package/dist/tools/fs/program-read-model.rows.d.ts +18 -0
  131. package/dist/tools/fs/program-read-model.rows.js +613 -0
  132. package/dist/tools/fs/read-deny-home.d.ts +19 -1
  133. package/dist/tools/fs/read-deny-home.js +62 -24
  134. package/dist/tools/fs/read-deny.d.ts +54 -14
  135. package/dist/tools/fs/read-deny.js +104 -50
  136. package/dist/tools/fs/removal-targets.d.ts +83 -0
  137. package/dist/tools/fs/removal-targets.js +236 -0
  138. package/dist/tools/fs/safety.d.ts +3 -0
  139. package/dist/tools/fs/safety.js +10 -3
  140. package/dist/tools/fs/shell-read-argv.d.ts +85 -0
  141. package/dist/tools/fs/shell-read-argv.js +386 -0
  142. package/dist/tools/fs/shell-read-bindings.d.ts +273 -0
  143. package/dist/tools/fs/shell-read-bindings.js +746 -0
  144. package/dist/tools/fs/shell-read-check.js +23 -15
  145. package/dist/tools/fs/shell-read-establish.js +79 -5
  146. package/dist/tools/fs/shell-read-judge.d.ts +11 -1
  147. package/dist/tools/fs/shell-read-judge.js +9 -1
  148. package/dist/tools/fs/shell-read-plan.d.ts +31 -7
  149. package/dist/tools/fs/shell-read-plan.js +299 -393
  150. package/dist/tools/fs/shell-read-word.d.ts +29 -26
  151. package/dist/tools/fs/shell-read-word.js +30 -38
  152. package/dist/tools/fs/shell-read-write-targets.d.ts +15 -37
  153. package/dist/tools/fs/shell-read-write-targets.js +83 -136
  154. package/dist/tools/fs/shell-reentry-text.d.ts +38 -6
  155. package/dist/tools/fs/shell-reentry-text.js +90 -53
  156. package/package.json +1 -1
  157. package/test/export-surface.snapshot.json +49 -3
  158. package/wire-changes/core@7.45.0.json +1084 -0
  159. package/wire-changes/core@7.46.0.json +1522 -0
@@ -1,3 +1,4 @@
1
+ import { withArgsReading } from "../effective-path-target.js";
1
2
  import { policyAskClassOf } from "../ask-class.js";
2
3
  import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
3
4
  import { classifierUnavailableSeat } from "./classifier-ladder.js";
@@ -24,7 +25,7 @@ import { createLiveSessionRules, createSessionRulePolicy } from "./session-rule-
24
25
  import { thrownText } from "../thrown-value.js";
25
26
  import { heldBy, heldValues, seatOf, wireOf } from "../tool-name-readings.js";
26
27
  export async function preparePolicyChain(input) {
27
- const { spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, skillSpecs, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, permissionMode, scratchpadDir, toolRosterDeltas, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = input;
28
+ const { spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, pathRuleAnchors, headLeads, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, announceRuleRowsDropped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, skillSpecs, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, readFace, permissionMode, scratchpadDir, toolRosterDeltas, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = input;
28
29
  const ancestorArbitrations = new Map();
29
30
  const arbitrateInherited = (ask, toolCallId, judged) => {
30
31
  const verdict = arbitrateByMode(permissionMode.current, judged.classification, { origin: judged.origin, ...askMandateMember(ancestorFacts(judged.ask.action === "ask" ? judged.ask : undefined)) });
@@ -305,15 +306,17 @@ export async function preparePolicyChain(input) {
305
306
  const grantBindingOf = (call, args) => skillGrantBindingMembers(call, args, skillSpecs, SKILL_TOOL_NAME);
306
307
  const inheritedStationFacts = (call) => {
307
308
  const seat = seatMarksOf(call);
309
+ const face = readFace();
308
310
  return {
309
311
  ...INHERITED_STATION_FACTS,
310
312
  mandateMarks: { egress: seat.egress, shellGated: false, ...(seat.tier === "always" && !seat.shellGated ? { irreversibility: "always" } : {}) },
311
313
  ...(seat.sources.size > 0 ? { toolMarkSources: seat.sources } : {}),
312
314
  ...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}),
315
+ ...(face !== undefined ? { readFace: face } : {}),
313
316
  };
314
317
  };
315
318
  const inheritedAskEvidence = inheritedAskRuleEvidence(deps.permissionRuleStore);
316
- const listFacts = { root: taskRootFinal, sessionId, liveCwd: () => handsCwdRef?.current, home: executionEnv.homeDir };
319
+ const listFacts = { root: taskRootFinal, sessionId, liveCwd: () => handsCwdRef?.current, anchors: pathRuleAnchors.leg };
317
320
  const skillGrantLane = skillGrants.lane(listFacts);
318
321
  const allowEntryLanes = { request: allowedToolTables.request.lane(listFacts), deployment: allowedToolTables.deployment.lane(listFacts) };
319
322
  const { personal: permissionRuleLane, org: permissionRuleOrgLane } = createPermissionRuleLanes({
@@ -322,6 +325,9 @@ export async function preparePolicyChain(input) {
322
325
  principal: spec.principal,
323
326
  sessionId,
324
327
  root: taskRootFinal,
328
+ rootCanonical: pathRuleAnchors.leg.root,
329
+ chainTop: pathRuleAnchors.chainTop,
330
+ leads: headLeads,
325
331
  liveCwd: () => handsCwdRef?.current,
326
332
  home: executionEnv.homeDir,
327
333
  questionToolMounted,
@@ -329,6 +335,7 @@ export async function preparePolicyChain(input) {
329
335
  onRevisionDefect: (message) => deps.onError?.(new Error(message), { phase: "config", sessionId }),
330
336
  onDisclosure: (message) => emitTrace(deps.tracer, () => ({ kind: "permission.rule_store_unreadable", version: 1, taskId: hostTaskId, message, ts: Date.now() })),
331
337
  onStoreUnreadable: announceRuleStoreSkipped,
338
+ onRowsDropped: announceRuleRowsDropped,
332
339
  signal: abortController.signal,
333
340
  stripsAllow: (rule, req) => permissionMode.current === "auto" && autoStrippedRule(rule, req),
334
341
  });
@@ -364,7 +371,8 @@ export async function preparePolicyChain(input) {
364
371
  }
365
372
  let re;
366
373
  try {
367
- re = refuseOutOfContractDecision(await pol.check({ ...creq, args: editArgs }, csignal ?? abortController.signal));
374
+ const editReq = withArgsReading(creq, editArgs, csignal ?? abortController.signal);
375
+ re = refuseOutOfContractDecision(await pol.check(editReq !== undefined ? await editReq : { ...creq, args: editArgs }, csignal ?? abortController.signal));
368
376
  }
369
377
  catch (err) {
370
378
  return vetoed({
@@ -616,7 +624,7 @@ export async function preparePolicyChain(input) {
616
624
  ...instrumentedChain,
617
625
  ...(rewriteCapableLayers ? narrowingLayers : []),
618
626
  ];
619
- const integrityGuardLayers = [createTranscriptIntegrityPolicy(), createUnverifiableDeletePolicy()];
627
+ const integrityGuardLayers = [createTranscriptIntegrityPolicy(), createUnverifiableDeletePolicy({ removalSeat: { env: executionEnv, additionalRoots: () => additionalRootsCanonical } })];
620
628
  const guardedPolicyLayers = rewriteCapableLayers
621
629
  ? [...integrityGuardLayers, ...policyLayers, modeGateLayer, ...integrityGuardLayers]
622
630
  : [...integrityGuardLayers, ...policyLayers, modeGateLayer];
@@ -644,7 +652,8 @@ export async function preparePolicyChain(input) {
644
652
  }
645
653
  }
646
654
  const recheckLayers = [...recheckPrefix, ...postEmitterSameInstanceAdapters];
647
- const finalReq = { ...req, args: decision.updatedInput };
655
+ const finalRead = withArgsReading(req, decision.updatedInput, signal);
656
+ const finalReq = finalRead !== undefined ? await finalRead : { ...req, args: decision.updatedInput };
648
657
  let asked;
649
658
  const revalidated = {};
650
659
  let restated;
@@ -118,6 +118,10 @@ export interface PrepareSuspendSagaInput {
118
118
  reminderMark: string;
119
119
  /** borrowed-readonly — the hand's live tracked cwd, or undefined; `current` is read at mint time. Not written here. */
120
120
  handsCwdRef: CwdRef | undefined;
121
+ /** borrowed-readonly — the hands' root (the `root` every adjudicated request of the leg carries), or undefined (hands-less). */
122
+ handsRoot: string | undefined;
123
+ /** borrowed-readonly — the home the leg's caller policies installed `~/` at (absolute), or undefined. */
124
+ handsHome: string | undefined;
121
125
  /** borrowed-readonly — the active EnterWorktree session seat, or undefined; `current` is read at mint time. */
122
126
  worktreeSessionRef: Prepared["worktreeSessionRef"];
123
127
  /** borrowed-readonly — the resumed leg's not-yet-delivered continuation content; its orphan obituaries fold into this
@@ -140,6 +144,12 @@ export interface PrepareSuspendSagaInput {
140
144
  permissionMode: PermissionModeController;
141
145
  /** borrowed-readonly — the inherited admitted org scopes, or undefined (copied onto the row). */
142
146
  inheritedAdmittedOrgScopes: readonly string[] | undefined;
147
+ /** borrowed-readonly — the chain's top leg's anchors this leg carries (absent on a leg that is its own top): recorded on
148
+ * the data half so a resumed child judges the person's global / session rows and `~/` rows where its chain did. */
149
+ inheritedPathRuleAnchors: InheritedGate["pathRuleAnchors"];
150
+ /** borrowed-readonly — where each parent constraint this leg runs under was installed (position for position), recorded on
151
+ * the data half so a resume that re-supplies the chain uninstalled installs each entry there. */
152
+ inheritedPathRuleInstallations: InheritedGate["pathRuleInstallations"];
143
153
  /** borrowed-readonly — this leg's OWN org verdict seat; `current` is read at mint time. Not written here. */
144
154
  ownOrgVerdictRef: {
145
155
  current: OwnOrgAdmissionVerdict | undefined;
@@ -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, skillGrantCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, permissionMode, 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, skillGrantCheckpointSection, reminderMark, handsCwdRef, handsRoot, handsHome, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, permissionMode, inheritedAdmittedOrgScopes, inheritedPathRuleAnchors, inheritedPathRuleInstallations, 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 = () => {
@@ -115,6 +115,8 @@ export function prepareSuspendSaga(input) {
115
115
  repairBundle: repairBundleForCheckpoint(parkedSpendMicroUsd),
116
116
  workspaceHandle,
117
117
  handsCwd: handsCwdRef?.current,
118
+ handsRoot,
119
+ handsHome,
118
120
  activeWorktree: worktreeSessionRef?.current ? { ...worktreeSessionRef.current } : undefined,
119
121
  runningBackgroundTasks: (() => {
120
122
  const live = defaultTaskRegistry
@@ -142,6 +144,8 @@ export function prepareSuspendSaga(input) {
142
144
  inheritedShellGate !== undefined ||
143
145
  modeRecorded ||
144
146
  inheritedAdmittedOrgScopes !== undefined ||
147
+ inheritedPathRuleAnchors !== undefined ||
148
+ inheritedPathRuleInstallations !== undefined ||
145
149
  ownOrgVerdictRef.current !== undefined ||
146
150
  orgGovernedProvenance ||
147
151
  requiresParentConstraint
@@ -151,6 +155,8 @@ export function prepareSuspendSaga(input) {
151
155
  ...(modeRecorded ? { permissionMode: declaredMode } : {}),
152
156
  ...(permissionMode.prePlanMode !== undefined ? { prePlanMode: permissionMode.prePlanMode } : {}),
153
157
  ...(inheritedAdmittedOrgScopes !== undefined ? { admittedOrgScopes: [...inheritedAdmittedOrgScopes] } : {}),
158
+ ...(inheritedPathRuleAnchors !== undefined ? { pathRuleAnchors: { ...inheritedPathRuleAnchors } } : {}),
159
+ ...(inheritedPathRuleInstallations !== undefined ? { pathRuleInstallations: inheritedPathRuleInstallations.map((r) => (r === null ? null : { ...r })) } : {}),
154
160
  ...(ownOrgVerdictRef.current !== undefined
155
161
  ? { ownAdmittedOrgScopes: [...ownOrgVerdictRef.current.scopes], ownAdmittedOrgWriteScope: ownOrgVerdictRef.current.writeScope }
156
162
  : {}),
@@ -8,7 +8,9 @@ import { brainToRuntime } from "../runtime.js";
8
8
  import { hasSessionFork } from "../session.js";
9
9
  import { SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
10
10
  import { callerToolMembership } from "../../agents/inherited-tool-pool.js";
11
- import { toolPolicyPathDenyRules, toolPolicyWriteTargetGuard } from "../tool-policy.js";
11
+ import { toolPolicyPathRules, toolPolicyWriteTargetGuard } from "../tool-policy.js";
12
+ import { bindPathRules, createHeadLeads } from "../path-rule-binding.js";
13
+ import { whereItsAnsweredPrefixLeads } from "../../tools/fs/read-deny-home.js";
12
14
  import { loadAnnounceOnceLedger } from "./announce-once-ledger.js";
13
15
  import { isValidReminderMark, mintReminderMark } from "../reminder-mint.js";
14
16
  import { clampPermissionMode } from "../ask-origin.js";
@@ -16,16 +18,18 @@ import { modePolicyOf, modeRequestAfterResume, refusePermissionModeDoors } from
16
18
  import { resolveOutputStyleFrame } from "../output-style.js";
17
19
  import { recordedModeSeed } from "../checkpoint-store.js";
18
20
  import { emitTrace } from "../trace.js";
19
- import { persistedPathRowsOfTask, ruleStoreUnreadableNotice } from "./persisted-path-rows.js";
21
+ import { persistedPathRowsOfTask, ruleRowsDroppedNotice, ruleStoreUnreadableNotice } from "./persisted-path-rows.js";
20
22
  import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
21
23
  import { STALL_FIRST_BYTE_MS, STALL_FIRST_TOKEN_MS, STALL_IDLE_MS, withBrainCallGuardrail } from "../../brain/timeout.js";
22
24
  import { OFFLOAD_TOOL_NAME } from "../tool-result-store.js";
23
25
  import { OUTPUT_TOOL_NAME, SKILL_TOOL_NAME, createOutputTool } from "./synthetic-tools.js";
24
- import { skillGateWiring } from "../skill-grant.js";
26
+ import { compileSkillGrant, skillGateWiring } from "../skill-grant.js";
27
+ import { legCallTargetStamp } from "../effective-path-target.js";
25
28
  import { AllowEntryTable, screenAllowedToolsValue } from "../allow-entry.js";
26
29
  import { compileOutputSchema } from "./strict-output-schema.js";
27
30
  import { MEMORY_ENGINE_TOOL_NAMES } from "../memory-engine/tools.js";
28
31
  import { foldAdmissionFreeze } from "../memory-admission.js";
32
+ import { chainAnchorRecords, chainPolicyOnLeg, chainTopAnchors, deploymentCanRerootChild } from "./child-tree.js";
29
33
  import { prepareMemory } from "./prepare-memory.js";
30
34
  import { prepareMemoryEngineSession } from "./prepare-memory-engine-session.js";
31
35
  import { prepareDeferClassify } from "./prepare-defer-classify.js";
@@ -39,6 +43,7 @@ export { runGuardChain } from "./prepare-context-lane.js";
39
43
  import { preparePromptAssembly } from "./prepare-prompt-assembly.js";
40
44
  import { limitConfigError, prepareConfigDoors } from "./prepare-config-doors.js";
41
45
  import { prepareSafetyScan } from "./prepare-safety-scan.js";
46
+ import { isAbsolutePathForm } from "../../tools/fs/safety.js";
42
47
  import { prepareAcquireReconcile } from "./prepare-acquire-reconcile.js";
43
48
  import { settleBranchWriteBack } from "./branch-writeback.js";
44
49
  import { prepareHandsMount, resolveHandsLessReadFace } from "./prepare-hands-readface.js";
@@ -346,7 +351,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
346
351
  throw e;
347
352
  }
348
353
  const rollback = createRollbackStack((e) => deps.onError?.(e, { phase: "config", sessionId }));
349
- const { executionEnv, ownedEnvSeat, ownedEnvObservedAt, handsEnabled, taskRootInitial } = await prepareExecutionEnv({ deps, spec, internals, resume, placementRootResolved, sessionId, forgetOnThrow, rollback });
354
+ const { executionEnv, ownedEnvSeat, ownedEnvObservedAt, handsEnabled, taskRootInitial, placement } = await prepareExecutionEnv({ deps, spec, internals, resume, placementRootResolved, sessionId, forgetOnThrow, rollback });
350
355
  const externalContentTargetActive = executionEnv.externalContentTarget === true || resume?.seed.externalContentTarget === true;
351
356
  const abortController = new AbortController();
352
357
  if (spec.signal?.aborted)
@@ -363,9 +368,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
363
368
  e.code = "isolation.unavailable";
364
369
  throw e;
365
370
  };
366
- if (ownedEnvSeat.current === undefined) {
371
+ if (!deploymentCanRerootChild(deps)) {
367
372
  failIsolation("no executionEnvFactory is configured (a static/shared execution env cannot mint a per-agent worktree)");
368
373
  }
374
+ else if (ownedEnvSeat.current === undefined) {
375
+ failIsolation("the executionEnvFactory returned no env instead of a worktree-rooted env");
376
+ }
369
377
  else if (ownedEnvSeat.current === deps.executionEnv) {
370
378
  ownedEnvSeat.current = undefined;
371
379
  failIsolation("the executionEnvFactory returned the shared static env instead of a worktree-rooted env");
@@ -396,8 +404,15 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
396
404
  }
397
405
  }
398
406
  const ownedEnv = ownedEnvSeat.current;
399
- const { taskRootFinal, rebaseRestoredPath } = await prepareWorkspaceRestore({ ownedEnv, deps, spec, internals, resume, taskRootInitial, abortController, sessionId });
407
+ const { taskRootFinal, rebaseRestoredPath } = await prepareWorkspaceRestore({ ownedEnv, deps, spec, internals, resume, taskRootInitial, placement, abortController, sessionId });
400
408
  const { canonical: taskRootCanonical } = await resolveTaskRootCanonical({ env: executionEnv, given: taskRootFinal, onNotice: deps.onNotice, sessionId, runId });
409
+ const legHome = executionEnv.homeDir;
410
+ const legAnchors = { root: taskRootCanonical, ...(legHome !== undefined && isAbsolutePathForm(legHome) ? { home: legHome } : {}) };
411
+ const chainTop = chainTopAnchors({ leg: legAnchors, live: internals?.inheritedGate?.pathRuleAnchors, seed: resume?.seed.inheritedGate?.pathRuleAnchors, placement, rebase: rebaseRestoredPath });
412
+ const onLeg = chainPolicyOnLeg({ leg: legAnchors, chainTop, placement, chain: (internals?.inheritedGate?.parentConstraints ?? []).map((c) => c.policy), installations: { live: internals?.inheritedGate?.pathRuleInstallations, seed: resume?.seed.inheritedGate?.pathRuleInstallations }, rebase: rebaseRestoredPath });
413
+ const [installedToolPolicy, installedBaseEdit] = [lockedPreflight.toolPolicy, lockedPreflight.basePolicyForResumeEdit].map(onLeg.own);
414
+ const installedPreflight = { ...lockedPreflight, toolPolicy: installedToolPolicy, basePolicyForResumeEdit: installedBaseEdit };
415
+ const headLeads = createHeadLeads((part) => whereItsAnsweredPrefixLeads(executionEnv, part));
401
416
  const { trackFileEdit, fileHistoryBoundary, restoredFilePaths, rewindNotes, fileHistoryEnabled, fileHistoryFact, historyScope, historyRoot, historyFs } = await prepareFileHistory({ deps, spec, request: fileHistoryRequest, internals, executionEnv, taskRootFinal, handsEnabled, sessionId, session, abortController });
402
417
  const { noteFileEdited, editedFilesSnapshot, harnessRef, toolProgressRef, skillGrants, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, bindPermissionModeSeat, enterPlanMode, subagentRetain, worktreeIsolation } = prepareRunRefs({ spec, internals, executionEnv, taskRootFinal });
403
418
  const allowedToolTables = { request: new AllowEntryTable(), deployment: new AllowEntryTable() };
@@ -407,14 +422,19 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
407
422
  let readDenyAdditionsNormalized = [];
408
423
  let handsLessResolvedFace;
409
424
  let handsLessResolvedWriteFace;
410
- const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, permissionMode, commitModeSeats, endModeCeiling, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, skillGrantCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, askSeat: doors.askSeat, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, permissionModeSeat, handsEnabled, reportUsage, harnessRef, skillGrants, requestAllowedTools: allowedToolTables.request, 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: () => autoModeDeciderRef.current, autoModeDenialTracking: () => autoModeDenialTrackingRef.current, autoModeArming: () => autoModeArmingRef.current, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
425
+ const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, permissionMode, commitModeSeats, endModeCeiling, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, skillGrantCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, askSeat: doors.askSeat, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight: installedPreflight, pathRuleAnchors: { chainTop, onLeg: onLeg.chained }, checkpointSeat, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, permissionModeSeat, handsEnabled, reportUsage, harnessRef, skillGrants, requestAllowedTools: allowedToolTables.request, 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: () => autoModeDeciderRef.current, autoModeDenialTracking: () => autoModeDenialTrackingRef.current, autoModeArming: () => autoModeArmingRef.current, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
411
426
  bindPermissionModeSeat(permissionMode);
412
427
  const announceRuleStoreSkipped = ruleStoreUnreadableNotice(deps.onNotice, { sessionId, runId });
413
- const persistedPathRowsRead = persistedPathRowsOfTask({ deps, principal: spec.principal, sessionId, root: taskRootFinal, home: executionEnv.homeDir, taskId: hostTaskId, handsEnabled, signals: [abortController.signal, ...(spec.signal !== undefined ? [spec.signal] : [])], onUnread: announceRuleStoreSkipped });
428
+ const announceRuleRowsDropped = ruleRowsDroppedNotice(deps.onNotice, { sessionId, runId });
429
+ const persistedPathRowsRead = persistedPathRowsOfTask({ deps, principal: spec.principal, sessionId, root: taskRootFinal, rootCanonical: taskRootCanonical, leads: headLeads, chainTop, taskId: hostTaskId, handsEnabled, onRowsDropped: announceRuleRowsDropped, signals: [abortController.signal, ...(spec.signal !== undefined ? [spec.signal] : [])], onUnread: announceRuleStoreSkipped });
414
430
  const persistedPathRows = persistedPathRowsRead === undefined ? { readDirectories: [], denyRows: [] } : await persistedPathRowsRead;
415
- const effectivePathDenyRules = [...toolPolicyPathDenyRules([lockedPreflight.toolPolicy, ...(inheritedParentConstraints ?? []).map((c) => c.policy)]), ...persistedPathRows.denyRows];
431
+ const policyDenyRows = toolPolicyPathRules([installedToolPolicy, ...(inheritedParentConstraints ?? []).map((c) => c.policy)], legAnchors).filter((r) => r.faces.length > 0 && r.behavior === "deny");
432
+ for (const r of policyDenyRows)
433
+ if (r.unanchored !== undefined)
434
+ emitTrace(deps.tracer, () => ({ kind: "permission.rule_store_unreadable", version: 1, taskId: hostTaskId, message: `the deny rule ${r.rule} needs a ${r.unanchored.join(" / ")} base this task does not declare — the shell's file commands and the search tools do not judge it on this leg (the gate asks)`, ts: Date.now() }));
435
+ const effectivePathDenyRules = [...policyDenyRows.filter((r) => r.unanchored === undefined), ...persistedPathRows.denyRows];
416
436
  const writeProtectionJudgeRef = {};
417
- const writeGuards = { protection: () => writeProtectionJudgeRef.current, target: toolPolicyWriteTargetGuard([lockedPreflight.toolPolicy, ...(inheritedParentConstraints ?? []).map((c) => c.policy)]) };
437
+ const writeGuards = { protection: () => writeProtectionJudgeRef.current, target: toolPolicyWriteTargetGuard([installedToolPolicy, ...(inheritedParentConstraints ?? []).map((c) => c.policy)]) };
418
438
  const { roster, blockedRef, runtimeCaps, runtimeCapsFaulted, complianceDenies, complianceDegraded, agentForkDenial, observersActive, autoModeArmReason, autoModeDeciderRef, autoModeDenialTrackingRef, autoModeArmingRef, armAutoMode, selfOrchestrationActive, workflowToolsActive, workflowSizeGuideline } = await prepareCapsAndWorkflow({ spec, deps, askSeat: doors.askSeat, internals, session, sessionId, runId, hostTaskId, taskScope, taskRootFinal, model, thinking, lockedPreflight, checkpointSeat, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, parentSpecSeat, carrierReadFace, carrierWriteFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, permissionMode, readDenyEntries: () => ({ entries: readDenyMatcher?.entries ?? [], ruleRows: effectivePathDenyRules }), peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoptionRef.current, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
419
439
  const tools = roster.tools;
420
440
  const pausedRef = {};
@@ -552,7 +572,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
552
572
  if (internals?.ownOrgAdmissionRef !== undefined && ownOrgVerdictRef.current !== undefined) {
553
573
  internals.ownOrgAdmissionRef.current = ownOrgVerdictRef.current;
554
574
  }
555
- const handsReadFaceInput = { permissionMode, pathDenyRules: effectivePathDenyRules, writeGuards, persistedReadDirectories: persistedPathRows.readDirectories, 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 } : {}) };
575
+ const handsReadFaceInput = { permissionMode, denyRuleRows: effectivePathDenyRules, headLeads, writeGuards, persistedReadDirectories: persistedPathRows.readDirectories, 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 } : {}) };
556
576
  const handsReadFace = handsEnabled ? await prepareHandsMount(handsReadFaceInput) : resolveHandsLessReadFace(handsReadFaceInput);
557
577
  const { readFileStateForCheckpoint, seedContextFiles, handsCwdRef, workspaceStateSettle, wsSnapshot, rebaseWsPath, backgroundTaskToolsActive, shellReadCheck, shellReadAdmission, additionalRootsCanonical, additionalReadRootsCanonical, attachmentRootCanonical, readDenyMatcher, envHandToolNames, sealReadStateSeat } = handsReadFace;
558
578
  resolvedReadFace = handsReadFace.resolvedReadFace;
@@ -733,7 +753,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
733
753
  releaseSignal = () => sig.removeEventListener("abort", onExternalAbort);
734
754
  }
735
755
  }
736
- const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, allowEntryLanes, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, grantBindingOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, skillSpecs, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider: () => autoModeDeciderRef.current, permissionMode, scratchpadDir: spec.envFacts?.scratchpadDir, toolRosterDeltas: rosterSeat, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
756
+ const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, allowEntryLanes, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, grantBindingOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight: installedPreflight, pathRuleAnchors: { leg: legAnchors, chainTop }, headLeads, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, announceRuleRowsDropped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, skillSpecs, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider: () => autoModeDeciderRef.current, readFace: () => resolvedReadFace, permissionMode, scratchpadDir: spec.envFacts?.scratchpadDir, toolRosterDeltas: rosterSeat, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
737
757
  const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtection, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace, sessionBoxBound } = prepareWiringManifest({ spec, deps, askSeat: doors.askSeat, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, shellReadStationArmed: handsReadFace.shellReadStationArmed, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, permissionMode, lockedPreflight, peerLaneRefs, frameSettlement, harness, pausedRef, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive: handsReadFace.readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined, fileHistoryFact, sessionBox });
738
758
  const writeProtectionCheck = writeProtection?.check;
739
759
  writeProtectionJudgeRef.current = writeProtection?.judgeTarget;
@@ -743,18 +763,21 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
743
763
  return undefined;
744
764
  });
745
765
  }
746
- const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, grantBindingOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking: autoModeDenialTrackingRef, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot: handsReadFace.grantReadRoot });
747
- const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, skillGrantCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, permissionMode, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
766
+ const listPathRows = () => skillSpecs.flatMap((s) => compileSkillGrant(s, "model").rows).flatMap((r) => (r.behavior === "deny" && r.compiled.kind === "path" ? [r.compiled.rule] : []));
767
+ const stampCallTarget = attachmentRootCanonical === undefined ? undefined : legCallTargetStamp({ env: executionEnv, root: attachmentRootCanonical, cwd: () => handsCwdRef?.current, judgesPaths: () => effectivePolicy?.pathRules !== undefined || basePolicyForResumeEdit?.pathRules !== undefined || permissionRuleLane !== undefined || listPathRows().length > 0, leads: () => headLeads.resolve([...toolPolicyPathRules([effectivePolicy, basePolicyForResumeEdit, ...(inheritedParentConstraints ?? []).map((c) => c.policy)], legAnchors), ...bindPathRules(listPathRows().map((rule) => ({ parsed: rule, ruleText: rule.rule })), legAnchors)]) });
768
+ const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, handsRoot: attachmentRootCanonical, stampCallTarget, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, grantBindingOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking: autoModeDenialTrackingRef, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot: handsReadFace.grantReadRoot });
769
+ const inheritedAnchorRecords = chainAnchorRecords(chainTop, inheritedParentConstraints ?? []);
770
+ const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, skillGrantCheckpointSection, reminderMark, handsCwdRef, handsRoot: attachmentRootCanonical, handsHome: attachmentRootCanonical !== undefined ? legAnchors.home : undefined, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, permissionMode, inheritedAdmittedOrgScopes, inheritedPathRuleAnchors: chainTop !== legAnchors ? inheritedAnchorRecords.pathRuleAnchors : undefined, inheritedPathRuleInstallations: inheritedAnchorRecords.pathRuleInstallations, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
748
771
  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 });
749
- 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, irreversibilityTier, toolMarkSources, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, grantBindingOf, now, humanReviewRef, abortController, harness, pausedRef });
772
+ const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas: rosterSeat, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, handsRoot: attachmentRootCanonical, stampCallTarget, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, irreversibilityTier, toolMarkSources, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, grantBindingOf, now, humanReviewRef, abortController, harness, pausedRef });
750
773
  const seatInputsFor = skillGateWiring({ tools, skillToolName: SKILL_TOOL_NAME, seatOf: skillSeatOf, mode: () => permissionMode.current, lane: skillGrantLane, onAllowed: (info) => emitTrace(deps.tracer, () => ({ kind: "permission.skill_allowed", version: 1, taskId: hostTaskId, ...info, entries: [...info.entries], ts: Date.now() })), allowEntries: { deploymentAllows: { ...allowEntryLanes.deployment, onAllowed: (info) => emitTrace(deps.tracer, () => ({ kind: "permission.allowed_tools_allowed", version: 1, taskId: hostTaskId, source: "deployment", ...info, entries: [...info.entries], ts: Date.now() })) }, requestAllows: { ...allowEntryLanes.request, onAllowed: (info) => emitTrace(deps.tracer, () => ({ kind: "permission.allowed_tools_allowed", version: 1, taskId: hostTaskId, source: "request", ...info, entries: [...info.entries], ts: Date.now() })) } } });
751
- const { approvedCallScreen } = 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, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, readDenyInForce: () => ({ tiers: handsReadFace.readDenyBuiltinTiersActive, rows: [...readDenyAdditionsNormalized.map((e) => e.pattern), ...handsReadFaceInput.pathDenyRules.filter((r) => r.faces.includes("read")).map((r) => r.rule)] }), hookEnvFace, writeProtectionCheck, shellReadCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider: autoModeDeciderRef, autoModeDenialTracking: autoModeDenialTrackingRef, askRoute: { ...wiringManifest.ask, effective: askSeatEffective(wiringManifest) }, permissionMode, toolMarkSources, ...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}), ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, seatInputsFor, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement });
774
+ const { approvedCallScreen } = 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, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, stampCallTarget, readDenyInForce: () => ({ tiers: handsReadFace.readDenyBuiltinTiersActive, rows: [...readDenyAdditionsNormalized.map((e) => e.pattern), ...handsReadFaceInput.denyRuleRows.filter((r) => r.faces.includes("read")).map((r) => r.rule)] }), hookEnvFace, writeProtectionCheck, shellReadCheck, readFace: () => resolvedReadFace, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider: autoModeDeciderRef, autoModeDenialTracking: autoModeDenialTrackingRef, askRoute: { ...wiringManifest.ask, effective: askSeatEffective(wiringManifest) }, permissionMode, toolMarkSources, ...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}), ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, seatInputsFor, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement });
752
775
  const postureFrameRef = { report: (err) => deps.onError?.(err, { phase: "degraded", sessionId }) };
753
776
  const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, toolRosterDeltas: rosterSeat, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, contextOverlay: legContextOverlay, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
754
777
  const preparedHolder = {};
755
- 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, skillGrants: skillGrantLane, callerPolicy: lockedPreflight.toolPolicy, inheritedPolicies: (inheritedParentConstraints ?? []).map((c) => c.policy), persistedRuleLane: permissionRuleLane, orgRuleLane: permissionRuleOrgLane, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
778
+ 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, skillGrants: skillGrantLane, callerPolicy: installedToolPolicy, inheritedPolicies: (inheritedParentConstraints ?? []).map((c) => c.policy), persistedRuleLane: permissionRuleLane, orgRuleLane: permissionRuleOrgLane, stampCallTarget, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
756
779
  await onceLedger.settle(session, announcedListingsRef);
757
- const buildPrepared = () => ({ harness, session, sessionId, runId, registryAccess: { owner: hostTaskId, scope: taskScope }, reminderMark, reminderDisclosureCounts, fullShellReachable, approvalPosture: { get mode() { return permissionMode.current; }, askEffective: askSeatEffective(wiringManifest) }, ...(outputStyleFrame !== undefined ? { outputStyleFrame } : {}), permissionMode, 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, skillGrants: skillGrantLane, skillSpecs, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, endModeCeiling, 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, stopForDenialLimit, 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 } } : {}), 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 }, frameSettlement, ...(peerLaneRefs.boundaryRound !== undefined ? { peerBoundaryRound: peerLaneRefs.boundaryRound } : {}), gitStatusRef, postureFrameRef, listBackgroundTasks, hookIdentity, ...(sessionBoxBound !== undefined ? { sessionBox: sessionBoxBound } : {}), hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(shellReadCheck !== undefined ? { shellReadCheck } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}), ...(approvedCallScreen !== undefined ? { approvedCallScreen } : {}) });
780
+ const buildPrepared = () => ({ harness, session, sessionId, runId, registryAccess: { owner: hostTaskId, scope: taskScope }, reminderMark, reminderDisclosureCounts, fullShellReachable, approvalPosture: { get mode() { return permissionMode.current; }, askEffective: askSeatEffective(wiringManifest) }, ...(outputStyleFrame !== undefined ? { outputStyleFrame } : {}), permissionMode, 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, ...(attachmentRootCanonical !== undefined ? { handsRoot: attachmentRootCanonical } : {}), ...(stampCallTarget !== undefined ? { stampCallTarget } : {}), toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, skillGrants: skillGrantLane, skillSpecs, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, endModeCeiling, 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, stopForDenialLimit, 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 } } : {}), 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 }, frameSettlement, ...(peerLaneRefs.boundaryRound !== undefined ? { peerBoundaryRound: peerLaneRefs.boundaryRound } : {}), gitStatusRef, postureFrameRef, listBackgroundTasks, hookIdentity, ...(sessionBoxBound !== undefined ? { sessionBox: sessionBoxBound } : {}), hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(shellReadCheck !== undefined ? { shellReadCheck } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}), ...(approvedCallScreen !== undefined ? { approvedCallScreen } : {}) });
758
781
  rollback.commit();
759
782
  commitModeSeats(abortController.signal);
760
783
  const prepared = buildPrepared();
@@ -92,6 +92,8 @@ export interface PrepareTurnWiringInput {
92
92
  persistedRuleLane: Prepared["persistedRuleLane"];
93
93
  /** borrowed-readonly — the org rule lane, or undefined (a layer of the judge). */
94
94
  orgRuleLane: Prepared["permissionRuleOrg"];
95
+ /** borrowed-readonly — the leg's target-reading stamp, or undefined (hands-less): the visibility judge stamps its request. */
96
+ stampCallTarget: Prepared["stampCallTarget"];
95
97
  /** borrowed-readonly — the run's abort controller; only `signal` (the deny door's and the stat's signal). */
96
98
  abortController: Pick<AbortController, "signal">;
97
99
  /** borrowed-readonly — the per-task identity (the background listing's owner). */
@@ -13,7 +13,7 @@ import { PROMPT_HASH_SALT } from "./prompt-hash-salt.js";
13
13
  import { CHANGED_FILES_MTIME_EPS_MS } from "./turn-attachments.js";
14
14
  import { thrownText } from "../thrown-value.js";
15
15
  export function prepareTurnWiring(input) {
16
- const { deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, additionalRootsCanonical, additionalReadRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, skillGrants, callerPolicy, inheritedPolicies, persistedRuleLane, orgRuleLane, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder } = input;
16
+ const { deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, additionalRootsCanonical, additionalReadRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, skillGrants, callerPolicy, inheritedPolicies, persistedRuleLane, orgRuleLane, stampCallTarget, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder } = input;
17
17
  const cacheBreakDetector = deps.cacheBreakDetection === false ? undefined : new CacheBreakDetector();
18
18
  const cacheFingerprint = cacheBreakDetector
19
19
  ? { systemPrompt: systemPromptSeat.current, tools: toolsToFingerprintInputs(harnessTools) }
@@ -109,6 +109,7 @@ export function prepareTurnWiring(input) {
109
109
  inheritedPolicies,
110
110
  ...(persistedRuleLane !== undefined ? { persistedRuleLane } : {}),
111
111
  ...(orgRuleLane !== undefined ? { orgRuleLane } : {}),
112
+ ...(stampCallTarget !== undefined ? { stampCallTarget } : {}),
112
113
  signal: abortController.signal,
113
114
  });
114
115
  const changedFilesReadDenied = async (paths) => {
@@ -29,6 +29,9 @@ export interface PrepareWorkspaceRestoreInput {
29
29
  * driver). Dead after this call by design (R-5): later phases read the returned
30
30
  * `taskRootFinal`, never this spelling. */
31
31
  taskRootInitial: string;
32
+ /** borrowed-readonly — where a delegated child landed against where it was asked (the execution-env phase's one
33
+ * judgment), reported beside the root it resolved; absent on a leg no delegation caller placed. */
34
+ placement?: import("./contracts.js").ChildPlacement | undefined;
32
35
  /** borrowed-readonly — prepare's abort seat; only `.signal` is read (kept as the object so the
33
36
  * moved body's `abortController.signal` spelling stays verbatim). */
34
37
  abortController: {
@@ -115,6 +115,7 @@ export async function prepareWorkspaceRestore(input) {
115
115
  isolated: internals.isolation === "worktree",
116
116
  remote: workspaceEnv !== undefined &&
117
117
  (isRemoteExecutionEnv(workspaceEnv) || workspaceEnv.hostLocalPaths === false),
118
+ ...(input.placement !== undefined ? { placement: input.placement } : {}),
118
119
  });
119
120
  }
120
121
  catch {
@@ -4,8 +4,10 @@ import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
4
4
  import { CheckpointError, remainingBudgetMicroUsd } from "../checkpoint-store.js";
5
5
  import { settledByDecider } from "../gate-outcome.js";
6
6
  import { mintHookInvocationIdentity, resolveHookTimeoutMs, runHookSeat } from "../hooks.js";
7
+ import { chainPolicyOnLeg, chainTopAnchors } from "./child-tree.js";
7
8
  import { checkToolPolicyProjection, policyCannotAnswer, refuseOutOfContractDecision } from "../tool-policy.js";
8
9
  import { callFaceSeat, parkedCallFaceOf } from "../tool-roster.js";
10
+ import { readCallTarget } from "../effective-path-target.js";
9
11
  import { inlineUntrusted } from "../untrusted-text.js";
10
12
  import { deepJsonEqual, describeSuppliedValue } from "./decide-continuation.js";
11
13
  import { orgVerdictBeforeClaim } from "./permission-rule-lanes.js";
@@ -110,14 +112,16 @@ export async function resumeApply(input) {
110
112
  resourceSpentMicroUsd: cp.resourceLedger?.spentMicroUsd ?? 0,
111
113
  suspendCount: cp.suspendCount ?? 0,
112
114
  };
113
- const editedReq = {
115
+ const editedCall = {
114
116
  toolName: cp.pendingAction.toolName,
115
117
  args: editedArgs,
116
118
  toolCallId: cp.pendingAction.toolCallId,
117
119
  budget: editBudget,
118
120
  ...callFaceSeat(parkedCallFaceOf(cp.pendingAction)),
119
121
  ...(cp.state.handsCwd !== undefined ? { cwd: cp.state.handsCwd } : {}),
122
+ ...(cp.state.handsRoot !== undefined ? { root: cp.state.handsRoot } : {}),
120
123
  };
124
+ const editedReq = { ...editedCall, targetReading: await readCallTarget(undefined, editedCall) };
121
125
  const ig = cp.state.inheritedGate;
122
126
  if (ig?.requiresParentConstraint === true) {
123
127
  const chain = ig.constraintChain;
@@ -139,10 +143,20 @@ export async function resumeApply(input) {
139
143
  const editBasePolicy = toolPolicyLocked
140
144
  ? (runner.deps.basePolicyForResumeEdit ?? runner.deps.toolPolicy)
141
145
  : (config.basePolicyForResumeEdit ?? runner.deps.basePolicyForResumeEdit ?? config.toolPolicy ?? runner.deps.toolPolicy);
142
- if (editBasePolicy !== undefined) {
146
+ const rowLeg = { ...(typeof cp.state.handsRoot === "string" ? { root: cp.state.handsRoot } : {}), ...(typeof cp.state.handsHome === "string" ? { home: cp.state.handsHome } : {}) };
147
+ const asRecorded = (p) => p;
148
+ const installedEditBase = chainPolicyOnLeg({
149
+ leg: rowLeg,
150
+ chainTop: chainTopAnchors({ leg: rowLeg, live: internals?.inheritedGate?.pathRuleAnchors, seed: cp.state.inheritedGate?.pathRuleAnchors, placement: undefined, rebase: asRecorded }),
151
+ placement: undefined,
152
+ chain: (internals?.inheritedGate?.parentConstraints ?? []).map((c) => c.policy),
153
+ installations: { live: internals?.inheritedGate?.pathRuleInstallations, seed: cp.state.inheritedGate?.pathRuleInstallations },
154
+ rebase: asRecorded,
155
+ }).own(editBasePolicy);
156
+ if (installedEditBase !== undefined) {
143
157
  let rechecked;
144
158
  try {
145
- rechecked = refuseOutOfContractDecision(await raceEditAbort(Promise.resolve(editBasePolicy.check(editedReq, editSignal))));
159
+ rechecked = refuseOutOfContractDecision(await raceEditAbort(Promise.resolve(installedEditBase.check(editedReq, editSignal))));
146
160
  }
147
161
  catch (err) {
148
162
  if (err instanceof CheckpointError)
@@ -1,8 +1,10 @@
1
1
  import { persistedReadDenyEntryProblem } from "../../tools/fs/read-deny.js";
2
+ import { isAbsolutePathForm } from "../../tools/fs/safety.js";
2
3
  import { isAskOrigin } from "../ask-origin.js";
3
4
  import { CHECKPOINT_FEATURES, CheckpointError, checkpointVersionOf, MAX_SUPPORTED_CHECKPOINT_VERSION, realApprovalOrgFact } from "../checkpoint-store.js";
4
5
  import { constraintChainDigest, constraintChainEntryOfSuppliedLayer } from "../tool-policy.js";
5
6
  import { readFrozenSkillGrants } from "../skill-grant.js";
7
+ import { chainInstallationGaps, chainInstallationRefusal } from "./child-tree.js";
6
8
  export function resumeCheckpointScreen(input) {
7
9
  const { cp, resumeSignal, internals, runner } = input;
8
10
  const { realApproval: realApprovalRow, face: faceRow, f012: f012Row } = CHECKPOINT_FEATURES;
@@ -49,6 +51,11 @@ export function resumeCheckpointScreen(input) {
49
51
  }
50
52
  if (readFrozenSkillGrants(cp.state.skillGrants) === undefined)
51
53
  throw new CheckpointError("checkpoint.invalid_outcome", "this checkpoint carries a skill-grant section (state.skillGrants) that does not read as grant rows under this engine — a damaged section, or a row an earlier release wrote whose entry this release reads as nothing (an entry with a surrounding space, say) — refusing to resume without the grant rows and refusals it carries; the checkpoint stays pending: finish it on a worker of the release that parked it, or expire it", { reason: "skill_grants_damaged" });
54
+ for (const field of ["handsRoot", "handsHome", "handsCwd"]) {
55
+ const coordinate = cp.state[field];
56
+ if (coordinate !== undefined && (typeof coordinate !== "string" || !isAbsolutePathForm(coordinate)))
57
+ throw new CheckpointError("checkpoint.invalid_outcome", `this checkpoint's state.${field} is not an absolute path — refusing to resume a row whose hands' coordinates are damaged (corruption guard), the checkpoint stays pending`, { reason: "hands_coordinates_damaged" });
58
+ }
52
59
  }
53
60
  if (realApprovalOrgFact(preCasGateBit) !== undefined && runner.deps.permissionRuleStore?.partitions.org !== true) {
54
61
  throw new CheckpointError("checkpoint.unsupported_version", `this checkpoint's approval was minted under organization governance (org ${String(realApprovalOrgFact(preCasGateBit))}, routed as ${String(preCasGateBit?.origin)}) and this worker has no org adjudication wiring (its permission-rule store carries no org partition) — a governed approval may only be redeemed where governance can be enforced; the checkpoint stays pending, resume it on an org-wired worker`, { reason: "governed_unwired" });
@@ -106,6 +113,12 @@ export function resumeCheckpointScreen(input) {
106
113
  }
107
114
  }
108
115
  }
116
+ const resupplied = (internals?.inheritedGate?.parentConstraints ?? []).map((pc) => pc.policy);
117
+ if (resupplied.length > 0) {
118
+ const gaps = chainInstallationGaps(resupplied, { live: internals?.inheritedGate?.pathRuleInstallations, seed: cp.state.inheritedGate?.pathRuleInstallations }, (p) => p);
119
+ if (gaps.length > 0)
120
+ throw chainInstallationRefusal(gaps, true);
121
+ }
109
122
  const resumeInternals = internals?.inheritedGate?.parentConstraints !== undefined
110
123
  ? {
111
124
  ...internals,
@@ -1,4 +1,5 @@
1
1
  import { canonicalizeCapturedPlainData, classifyQuestionOutcome, isLiveQuestionFace } from "../ask-question.js";
2
+ import { isHostDecider } from "../gate-outcome.js";
2
3
  import { CHECKPOINT_FEATURES, CheckpointError, checkpointVersionOf, winnerFromOutcome } from "../checkpoint-store.js";
3
4
  import { policyCannotAnswer, screenApproverAttribution } from "../tool-policy.js";
4
5
  import { sanitizeUntrustedText } from "../untrusted-text.js";
@@ -52,7 +53,7 @@ export function resumePolicyOutcome(input) {
52
53
  throw new CheckpointError("checkpoint.invalid_outcome", `resume carries no hostDecision — a policy_ask decide names who decided it ({ decidedBy: "person" | "sla_timeout" | "policy", approver? }); refusing pre-CAS, the checkpoint stays pending`, { field: "hostDecision" });
53
54
  }
54
55
  const decidedBy = hostDecisionRaw.decidedBy;
55
- if (decidedBy !== "person" && decidedBy !== "sla_timeout" && decidedBy !== "policy") {
56
+ if (!isHostDecider(decidedBy)) {
56
57
  throw new CheckpointError("checkpoint.invalid_outcome", `resume hostDecision.decidedBy "${describeSuppliedValue(decidedBy)}" is outside the closed set — it is exactly "person", "sla_timeout" or "policy"; refusing pre-CAS, the checkpoint stays pending`, { field: "hostDecision" });
57
58
  }
58
59
  const cannotAllow = decision !== "allow"
@@ -614,7 +614,8 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
614
614
  }
615
615
  const executedArgs = humanRewrite !== undefined ? resolvedArgs : screened !== undefined ? screened.args : resolvedArgs;
616
616
  const unseen = !deepJsonEqual(executedArgs, pendingAction.args);
617
- const beltReq = () => ({ toolName: pendingAction.toolName, args: executedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)), budget: beltBudget, ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}), ...advertisedWritableRootsSeat(prepared.advertisedWritableDirs) });
617
+ let beltReading;
618
+ const beltReq = () => ({ toolName: pendingAction.toolName, args: executedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)), budget: beltBudget, ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}), ...(prepared.handsRoot !== undefined ? { root: prepared.handsRoot } : {}), ...advertisedWritableRootsSeat(prepared.advertisedWritableDirs), ...(beltReading !== undefined ? { targetReading: beltReading } : {}) });
618
619
  const unjudgeable = unseen ? chainCannotJudge(resume.cp.state.inheritedGate) : undefined;
619
620
  if (unjudgeable !== undefined) {
620
621
  const chainDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: its input changed after the approval and the inherited ancestor chain cannot judge the changed input (${unjudgeable}). This approval is spent — the call has to be re-issued.`, prepared.reminderMark);
@@ -627,6 +628,9 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
627
628
  resourceSpentMicroUsd: prepared.resourceLedger?.spentMicroUsd ?? 0,
628
629
  suspendCount: resume.cp.suspendCount ?? 0,
629
630
  };
631
+ const beltStamped = prepared.stampCallTarget?.(beltReq(), prepared.abortController.signal);
632
+ if (beltStamped !== undefined)
633
+ beltReading = (await beltStamped).targetReading;
630
634
  if (unseen && prepared.basePolicyForResumeEdit) {
631
635
  const rechecked = refuseOutOfContractDecision(await prepared.basePolicyForResumeEdit.check(beltReq(), prepared.abortController.signal));
632
636
  const personUnanswered = policyCannotAnswer(rechecked, outcome.hostDecision.decidedBy);
@@ -1,6 +1,6 @@
1
1
  import { createPeerInboundChainRef, createPeerSelfRef } from "../../agents/peer-admission.js";
2
2
  import { boundInputHashOf } from "../canonical-json.js";
3
- import { createLiveNotificationLane, DurableAppendObligations, isDelegatedAgentTerminal, isTerminalTaskNotification, renderTaskNotificationXml, routeOrParkNotification, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
3
+ import { createLiveNotificationLane, DurableAppendObligations, injectionLaneOf, isDelegatedAgentTerminal, isTerminalTaskNotification, renderTaskNotificationXml, routeOrParkNotification, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
4
4
  import { ToolDetachHub } from "../tool-detach.js";
5
5
  import { pendingContentAskCallId } from "./decide-continuation.js";
6
6
  export function runNotificationLane(input) {
@@ -76,7 +76,7 @@ async function runNotificationLaneAs(input, obligations) {
76
76
  engineNoteKey: taskNotificationDedupKey(item.payload),
77
77
  ...(item.payload.task_type !== "external" && isTerminalTaskNotification(item.payload) ? { capPreferred: true } : {}),
78
78
  };
79
- const deliver = item.priority === "later"
79
+ const deliver = injectionLaneOf(item.priority) === "followUp"
80
80
  ? notificationHarness.followUp(xml, noteOptions)
81
81
  : notificationHarness.steer(xml, { ...noteOptions, ...(item.priority === "now" ? { immediate: true } : {}) });
82
82
  void deliver.then(() => item.onDisposition?.("queued"), () => {
@@ -68,6 +68,8 @@ export declare class Runner implements RunnerSelfSeat {
68
68
  * thin compositions OVER the Runner (`runGoal`) can emit at their terminal state without reaching
69
69
  * into private deps. */
70
70
  emitTaskOutcome(outcome: TaskOutcome): void;
71
+ /** Whether a child this Runner runs can be placed in another tree: {@link RunnerSelfSeat.canRerootChild}. */
72
+ canRerootChild(): boolean;
71
73
  /** The deployment agent catalog (RunnerDeps.agents/builtinAgents), exposed READ-ONLY
72
74
  * so the Agent-tool lane (`createSubagentTool`) defaults to the SAME registry the workflow lane
73
75
  * consumes (single source; the types.ts "pass the same array" follow-on made structural). The array
@@ -45,7 +45,7 @@ import { streamLifecycleVerbs } from "./stream-lifecycle-verbs.js";
45
45
  import { streamHaltVerbs } from "./stream-halt-verbs.js";
46
46
  import { streamModeVerb } from "./stream-mode-verb.js";
47
47
  import { runNotificationLane } from "./run-notification-lane.js";
48
- import { assertNoRetiredRunnerDeps } from "./retired-runner-deps.js";
48
+ import { assertRunnerDepsDoors } from "./construction-doors.js";
49
49
  import { runLeg } from "./run-leg.js";
50
50
  import { runTerminalAdoption } from "./run-terminal-adoption.js";
51
51
  import { runSettleAndTeardown } from "./run-settle-and-teardown.js";
@@ -62,6 +62,7 @@ import { runCompactionMachinery } from "./run-compaction-machinery.js";
62
62
  import { runStopAndFinalVerify } from "./run-stop-and-final-verify.js";
63
63
  import { runRecoveryLanes } from "./run-recovery-lanes.js";
64
64
  import { runGitLane } from "./run-git-lane.js";
65
+ import { deploymentCanRerootChild } from "./child-tree.js";
65
66
  import { thrownText } from "../thrown-value.js";
66
67
  export class Runner {
67
68
  deps;
@@ -76,7 +77,7 @@ export class Runner {
76
77
  static PARENT_CONSTRAINT_REGISTRY_CAP = 1024;
77
78
  depsSeat;
78
79
  constructor(deps) {
79
- assertNoRetiredRunnerDeps(deps);
80
+ assertRunnerDepsDoors(deps);
80
81
  const runner = this;
81
82
  this.depsSeat = { get deps() { return runner.deps; } };
82
83
  this.deps = withDeploymentAllowedTools(deps);
@@ -125,6 +126,7 @@ export class Runner {
125
126
  emitTaskOutcome(outcome) {
126
127
  emitTaskOutcome(this.deps, outcome);
127
128
  }
129
+ canRerootChild() { return deploymentCanRerootChild(this.deps); }
128
130
  get agentCatalog() {
129
131
  return {
130
132
  ...(this.deps.agents ? { agents: [...this.deps.agents] } : {}),