@sema-agent/core 7.45.0 → 7.47.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 (139) hide show
  1. package/CHANGELOG.md +298 -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 +33 -25
  6. package/dist/agents/tool-filter.d.ts +17 -1
  7. package/dist/agents/tool-filter.js +7 -0
  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/allow-entry.d.ts +6 -4
  11. package/dist/core/allow-entry.js +6 -5
  12. package/dist/core/ask-origin.d.ts +35 -12
  13. package/dist/core/ask-origin.js +24 -7
  14. package/dist/core/checkpoint-store.d.ts +39 -2
  15. package/dist/core/effective-path-target.d.ts +136 -4
  16. package/dist/core/effective-path-target.js +110 -2
  17. package/dist/core/engine-notice.d.ts +12 -4
  18. package/dist/core/gate-fold.js +5 -2
  19. package/dist/core/gate-lanes.js +11 -4
  20. package/dist/core/gate-outcome.d.ts +4 -2
  21. package/dist/core/gate-outcome.js +2 -2
  22. package/dist/core/governance-codes.d.ts +3 -1
  23. package/dist/core/governance-codes.js +3 -0
  24. package/dist/core/hooks.d.ts +3 -0
  25. package/dist/core/path-rule-binding.d.ts +65 -0
  26. package/dist/core/path-rule-binding.js +99 -0
  27. package/dist/core/permission-rule-model.d.ts +59 -24
  28. package/dist/core/permission-rule-model.js +48 -44
  29. package/dist/core/permission-rule-provider.d.ts +7 -0
  30. package/dist/core/permission-rule-provider.js +10 -3
  31. package/dist/core/permission-rule-store.d.ts +8 -0
  32. package/dist/core/permission-rule-store.js +6 -2
  33. package/dist/core/permission-rule-sync.js +2 -2
  34. package/dist/core/permission-rules.d.ts +26 -45
  35. package/dist/core/permission-rules.js +77 -57
  36. package/dist/core/runner/child-tree.d.ts +97 -0
  37. package/dist/core/runner/child-tree.js +104 -0
  38. package/dist/core/runner/construction-doors.d.ts +5 -0
  39. package/dist/core/runner/construction-doors.js +6 -0
  40. package/dist/core/runner/contracts.d.ts +46 -0
  41. package/dist/core/runner/gate-exit.d.ts +4 -1
  42. package/dist/core/runner/gate-exit.js +5 -1
  43. package/dist/core/runner/permission-rule-lanes.d.ts +34 -25
  44. package/dist/core/runner/permission-rule-lanes.js +18 -12
  45. package/dist/core/runner/persisted-path-rows.d.ts +31 -7
  46. package/dist/core/runner/persisted-path-rows.js +31 -12
  47. package/dist/core/runner/prepare-ask-lane.d.ts +4 -0
  48. package/dist/core/runner/prepare-ask-lane.js +4 -2
  49. package/dist/core/runner/prepare-execution-env.d.ts +5 -2
  50. package/dist/core/runner/prepare-execution-env.js +10 -3
  51. package/dist/core/runner/prepare-gate-stations.d.ts +3 -0
  52. package/dist/core/runner/prepare-gate-stations.js +2 -1
  53. package/dist/core/runner/prepare-hands-readface.d.ts +8 -4
  54. package/dist/core/runner/prepare-hands-readface.js +11 -11
  55. package/dist/core/runner/prepare-inherited-gate.d.ts +8 -1
  56. package/dist/core/runner/prepare-inherited-gate.js +8 -2
  57. package/dist/core/runner/prepare-park-ask.d.ts +3 -0
  58. package/dist/core/runner/prepare-park-ask.js +5 -3
  59. package/dist/core/runner/prepare-policy-chain.d.ts +14 -2
  60. package/dist/core/runner/prepare-policy-chain.js +13 -5
  61. package/dist/core/runner/prepare-suspend-saga.d.ts +10 -0
  62. package/dist/core/runner/prepare-suspend-saga.js +7 -1
  63. package/dist/core/runner/prepare-task.js +37 -18
  64. package/dist/core/runner/prepare-turn-wiring.d.ts +2 -0
  65. package/dist/core/runner/prepare-turn-wiring.js +2 -1
  66. package/dist/core/runner/prepare-workspace-restore.d.ts +3 -0
  67. package/dist/core/runner/prepare-workspace-restore.js +1 -0
  68. package/dist/core/runner/resume-apply.js +17 -3
  69. package/dist/core/runner/resume-checkpoint-screen.js +13 -0
  70. package/dist/core/runner/run-leg.js +5 -1
  71. package/dist/core/runner/runtask.js +2 -2
  72. package/dist/core/runner/sub-agent-deny-lists.d.ts +1 -1
  73. package/dist/core/runner/write-protection-station.d.ts +3 -2
  74. package/dist/core/runner/write-protection-station.js +1 -0
  75. package/dist/core/runner-deps.d.ts +14 -0
  76. package/dist/core/sensitive-path-policy.d.ts +8 -0
  77. package/dist/core/sensitive-path-policy.js +18 -2
  78. package/dist/core/shell-wrapper-projection.d.ts +11 -0
  79. package/dist/core/shell-wrapper-projection.js +56 -0
  80. package/dist/core/shell-wrapper-table.d.ts +144 -34
  81. package/dist/core/shell-wrapper-table.js +40 -20
  82. package/dist/core/synthetic-read-verdict.d.ts +3 -0
  83. package/dist/core/synthetic-read-verdict.js +3 -1
  84. package/dist/core/tool-catalog-entries.d.ts +21 -0
  85. package/dist/core/tool-catalog-entries.js +12 -2
  86. package/dist/core/tool-face.d.ts +10 -1
  87. package/dist/core/tool-name-readings.d.ts +8 -0
  88. package/dist/core/tool-name-readings.js +2 -0
  89. package/dist/core/tool-policy.d.ts +40 -17
  90. package/dist/core/tool-policy.js +11 -9
  91. package/dist/core/tool-registry.d.ts +9 -0
  92. package/dist/core/tool-registry.js +10 -0
  93. package/dist/core/tool-spec.d.ts +3 -2
  94. package/dist/core/write-protect.d.ts +14 -3
  95. package/dist/core/write-protect.js +41 -10
  96. package/dist/index.d.ts +6 -4
  97. package/dist/index.js +5 -3
  98. package/dist/orchestration/run-spec.js +8 -7
  99. package/dist/orchestration/workflow.js +1 -1
  100. package/dist/shell/lex.d.ts +22 -0
  101. package/dist/shell/lex.js +75 -13
  102. package/dist/tools/fs/bash-program-position.d.ts +14 -90
  103. package/dist/tools/fs/bash-program-position.js +41 -74
  104. package/dist/tools/fs/bash-readonly-classifier.d.ts +17 -444
  105. package/dist/tools/fs/bash-readonly-classifier.js +169 -1225
  106. package/dist/tools/fs/index.d.ts +3 -2
  107. package/dist/tools/fs/program-read-model.d.ts +711 -0
  108. package/dist/tools/fs/program-read-model.js +1259 -0
  109. package/dist/tools/fs/program-read-model.rows.d.ts +18 -0
  110. package/dist/tools/fs/program-read-model.rows.js +624 -0
  111. package/dist/tools/fs/read-deny-home.d.ts +19 -1
  112. package/dist/tools/fs/read-deny-home.js +62 -24
  113. package/dist/tools/fs/read-deny.d.ts +54 -14
  114. package/dist/tools/fs/read-deny.js +104 -50
  115. package/dist/tools/fs/removal-targets.d.ts +7 -6
  116. package/dist/tools/fs/removal-targets.js +34 -24
  117. package/dist/tools/fs/safety.d.ts +3 -0
  118. package/dist/tools/fs/safety.js +10 -3
  119. package/dist/tools/fs/shell-read-argv.d.ts +86 -0
  120. package/dist/tools/fs/shell-read-argv.js +388 -0
  121. package/dist/tools/fs/shell-read-bindings.d.ts +300 -0
  122. package/dist/tools/fs/shell-read-bindings.js +755 -0
  123. package/dist/tools/fs/shell-read-check.js +23 -15
  124. package/dist/tools/fs/shell-read-establish.js +82 -8
  125. package/dist/tools/fs/shell-read-judge.d.ts +11 -1
  126. package/dist/tools/fs/shell-read-judge.js +9 -1
  127. package/dist/tools/fs/shell-read-plan.d.ts +33 -8
  128. package/dist/tools/fs/shell-read-plan.js +309 -407
  129. package/dist/tools/fs/shell-read-word.d.ts +60 -26
  130. package/dist/tools/fs/shell-read-word.js +47 -37
  131. package/dist/tools/fs/shell-read-write-targets.d.ts +15 -64
  132. package/dist/tools/fs/shell-read-write-targets.js +85 -137
  133. package/dist/tools/fs/shell-reentry-text.d.ts +136 -6
  134. package/dist/tools/fs/shell-reentry-text.js +203 -53
  135. package/dist/tools/monitor.d.ts +3 -3
  136. package/package.json +1 -1
  137. package/test/export-surface.snapshot.json +25 -5
  138. package/wire-changes/core@7.46.0.json +1522 -0
  139. package/wire-changes/core@7.47.0.json +412 -0
@@ -43,7 +43,8 @@ import type { OwnOrgAdmissionVerdict } from "../memory-admission.js";
43
43
  import type { DelegationContentSafety } from "../memory-engine/delegation-provenance.js";
44
44
  import { type SessionPermissionRules } from "../session-policy-store.js";
45
45
  import type { SessionStore } from "../session.js";
46
- import { type OnAsk } from "../tool-policy.js";
46
+ import { type OnAsk, type ToolPolicy } from "../tool-policy.js";
47
+ import type { PathRuleBases } from "../permission-rule-model.js";
47
48
  import type { PermissionMode, RunnerDeps, RuntimeCaps, ShellGateDoctrine, TaskEvent, TaskSpec, ToolExecuteContext, ToolSpec } from "../types.js";
48
49
  import { PermissionModeController } from "../mode-policy.js";
49
50
  import { type SkillGrantTable } from "../skill-grant.js";
@@ -89,6 +90,12 @@ export interface PrepareInheritedGateInput {
89
90
  ownedEnv: ExecutionEnv | undefined;
90
91
  /** borrowed-readonly — the frozen preflight; only `toolPolicy` (the RESOLVED caller policy the children's chain carries). */
91
92
  lockedPreflight: Pick<LockedPreflight, "toolPolicy">;
93
+ /** borrowed-readonly — the chain's top leg's anchors as this leg reads them (handed on to children), and how this leg
94
+ * installs the policies its chain hands it (`chainPolicyOnLeg`). */
95
+ pathRuleAnchors: {
96
+ readonly chainTop: PathRuleBases;
97
+ readonly onLeg: (policy: ToolPolicy, position: number) => ToolPolicy;
98
+ };
92
99
  /** borrowed-readonly — the frozen tool-face snapshot (the ctx forwards exclude / defer / alwaysLoad / restoreGated; the
93
100
  * full-shell predicate reads the exclusion of Bash). */
94
101
  toolFaceSnapshot: ToolFaceSnapshot;
@@ -5,6 +5,7 @@ import { createPreToolUseConstraintPolicy } from "../hooks.js";
5
5
  import { continuesLineage } from "../session-policy-store.js";
6
6
  import { StubExecutionEnv } from "../stub-env.js";
7
7
  import { askApproverIdentity, durableMandateSourceOf } from "../tool-policy.js";
8
+ import { chainAnchorRecords } from "./child-tree.js";
8
9
  import { deliverEngineNotice } from "../types.js";
9
10
  import { PermissionModeController, modeRequestAfterResume } from "../mode-policy.js";
10
11
  import { SessionModeSeats } from "./session-mode-seats.js";
@@ -13,7 +14,7 @@ import { frozenSkillGrants } from "../skill-grant.js";
13
14
  import { attachRebuiltDenialTrackers, createDenialLimitStop } from "./denial-limit-arms.js";
14
15
  import { asThrownError } from "../thrown-value.js";
15
16
  export function prepareInheritedGate(input) {
16
- const { spec, deps, askSeat, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, permissionModeSeat, handsEnabled, reportUsage, harnessRef, skillGrants, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun } = input;
17
+ const { spec, deps, askSeat, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, pathRuleAnchors, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, permissionModeSeat, handsEnabled, reportUsage, harnessRef, skillGrants, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun } = input;
17
18
  const liveInheritedGate = internals?.inheritedGate;
18
19
  const seedInheritedGate = resume?.seed.inheritedGate;
19
20
  const inheritedAncestorRules = (() => {
@@ -35,7 +36,11 @@ export function prepareInheritedGate(input) {
35
36
  })();
36
37
  const modeSeed = recordedModeSeed(seedInheritedGate);
37
38
  const inheritedShellGate = liveInheritedGate?.shellGate ?? modeSeed.shellGate;
38
- const chainView = askSeat.reach === "none" ? liveInheritedGate?.parentConstraints?.map((pc) => (typeof pc.onAsk === "function" ? ((({ onAsk: _person, ...rest }) => rest)(pc)) : pc)) : liveInheritedGate?.parentConstraints;
39
+ const askView = askSeat.reach === "none" ? liveInheritedGate?.parentConstraints?.map((pc) => (typeof pc.onAsk === "function" ? ((({ onAsk: _person, ...rest }) => rest)(pc)) : pc)) : liveInheritedGate?.parentConstraints;
40
+ const chainView = askView?.map((pc, position) => {
41
+ const policy = pathRuleAnchors.onLeg(pc.policy, position);
42
+ return policy === pc.policy ? pc : { ...pc, policy };
43
+ });
39
44
  const inheritedParentConstraints = attachRebuiltDenialTrackers(chainView, deps.autoMode?.denialLimit);
40
45
  const parentSeat = liveInheritedGate?.permissionModeCeiling;
41
46
  const ceiling = parentSeat ?? liveInheritedGate?.permissionMode;
@@ -186,6 +191,7 @@ export function prepareInheritedGate(input) {
186
191
  permissionMode: permissionMode.current,
187
192
  permissionModeCeiling: legModeCeiling,
188
193
  ...(parentConstraints.length > 0 ? { parentConstraints } : {}),
194
+ ...chainAnchorRecords(pathRuleAnchors.chainTop, parentConstraints),
189
195
  admittedOrgScopes: memoryAdmittedOrgScopesRef.current,
190
196
  ...(orgGovernedProvenance ? { orgAdmissionGoverned: true } : {}),
191
197
  };
@@ -20,6 +20,7 @@
20
20
  * closures, so the orchestrator grows no branch.
21
21
  */
22
22
  import type { AgentHarness, AgentMessage, AgentTool, ExecutionEnv } from "../../internal/harness.js";
23
+ import type { CallTargetStamp } from "../effective-path-target.js";
23
24
  import { type OnQuestion } from "../ask-question.js";
24
25
  import { type CheckpointStore } from "../checkpoint-store.js";
25
26
  import type { GrantBindingOf } from "../skill-grant-binding.js";
@@ -96,6 +97,8 @@ export interface PrepareParkAskInput {
96
97
  handsCwdRef: CwdRef | undefined;
97
98
  /** borrowed-readonly — the root the live gate stamps beside the cwd ({@link import("./prepare-ask-lane.js").PrepareAskLaneInput.handsRoot}). */
98
99
  handsRoot: string | undefined;
100
+ /** borrowed-readonly — the live gate's target-reading stamp ({@link import("./prepare-ask-lane.js").PrepareAskLaneInput.stampCallTarget}). */
101
+ stampCallTarget: CallTargetStamp | undefined;
99
102
  /** borrowed-readonly — the SAME seat the live gate stamps from ({@link import("./prepare-ask-lane.js").PrepareAskLaneInput.advertisedWritableDirs}): a parked call's projection re-adjudication asks one question about one call, so it must ask it against the same admitted list. Read at park time, not written here. */
100
103
  advertisedWritableDirs: Pick<AdvertisedWritableDirs, "roots"> | undefined;
101
104
  /** borrowed-readonly — the run's offload store, or undefined (a volatile store holding results refuses the park). */
@@ -27,7 +27,7 @@ const wordlessDemand = (toolName) => {
27
27
  throw new Error(`a real-approval demand for "${toolName}" reached the park with no origin word — the origin stamp precedes every park`);
28
28
  };
29
29
  export function prepareParkAsk(input) {
30
- const { askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, handsRoot, 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 } = input;
30
+ const { askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, handsRoot, 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 } = input;
31
31
  if (askLane === undefined || saga === undefined)
32
32
  return { parkAsk: undefined };
33
33
  const { composedCallSignal, approvalPreviewOf } = askLane;
@@ -159,7 +159,7 @@ export function prepareParkAsk(input) {
159
159
  if (!presented.ok) {
160
160
  throw new ParkRefusal(`the stored form of "${req.toolName}"'s arguments could not be presented for re-adjudication (${describeThrown(presented.cause)})`, { cause: presented.cause });
161
161
  }
162
- const reprojectedRaced = await raceSettlementAgainstSignal(Promise.resolve(basePolicyForResumeEdit.check({
162
+ const reprojectedReq = () => ({
163
163
  toolName: req.toolName,
164
164
  args: presented.value,
165
165
  toolCallId: req.toolCallId,
@@ -167,7 +167,9 @@ export function prepareParkAsk(input) {
167
167
  ...callFaceSeat(req.face),
168
168
  ...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}), ...(handsRoot !== undefined ? { root: handsRoot } : {}),
169
169
  ...advertisedWritableRootsSeat(advertisedWritableDirs),
170
- }, cutSignal)), cutSignal);
170
+ });
171
+ const withReading = stampCallTarget?.(reprojectedReq(), cutSignal);
172
+ const reprojectedRaced = await raceSettlementAgainstSignal(withReading === undefined ? Promise.resolve(basePolicyForResumeEdit.check(reprojectedReq(), cutSignal)) : withReading.then((r) => basePolicyForResumeEdit.check(r, cutSignal)), cutSignal);
171
173
  if (reprojectedRaced.tag === "aborted")
172
174
  return undefined;
173
175
  if (reprojectedRaced.tag === "threw")
@@ -27,6 +27,8 @@ import { type Hooks } from "../hooks.js";
27
27
  import type { LockedPreflight } from "../locked-config.js";
28
28
  import type { SessionPermissionRules } from "../session-policy-store.js";
29
29
  import { type OnAsk, type ToolPolicy } from "../tool-policy.js";
30
+ import type { PathRuleBases } from "../permission-rule-model.js";
31
+ import type { HeadLeads } from "../path-rule-binding.js";
30
32
  import type { RunnerDeps, RuntimeCaps, TaskSpec, ToolEffect } from "../types.js";
31
33
  import type { CwdRef } from "../../tools/fs/fs-shared.js";
32
34
  import type { ReadDenyMatcher } from "../../tools/fs/read-deny.js";
@@ -65,9 +67,17 @@ export interface PreparePolicyChainInput {
65
67
  additionalRootsCanonical: readonly string[];
66
68
  /** borrowed-readonly — the run's resolved env (path rules resolve in it; the sandbox declaration is read off it). */
67
69
  executionEnv: ExecutionEnv;
68
- /** borrowed-readonly — the frozen preflight: `toolPolicy` (the caller slot) and `basePolicyForResumeEdit` (the lock-aware
69
- * resume-edit override). */
70
+ /** borrowed-readonly — the frozen preflight as installed on this leg: `toolPolicy` (the caller slot) and
71
+ * `basePolicyForResumeEdit` (the lock-aware resume-edit override), their path rules anchored where this leg installed them. */
70
72
  lockedPreflight: Pick<LockedPreflight, "toolPolicy" | "basePolicyForResumeEdit">;
73
+ /** borrowed-readonly — the path rules' anchors on this leg: `leg` (this leg's root and environment home — where this
74
+ * leg's own lists are installed) and `chainTop` (the top leg's: the person's global / session rows and every `~/`). */
75
+ pathRuleAnchors: {
76
+ readonly leg: PathRuleBases;
77
+ readonly chainTop: PathRuleBases;
78
+ };
79
+ /** borrowed-readonly — the leg's one resolution of where rule heads lead (the rows' twins). */
80
+ headLeads: HeadLeads;
71
81
  /** borrowed-readonly — the run's ONE tool roster, read only here (the name audit's universe: names + aliases). */
72
82
  tools: ReadonlyArray<AgentTool>;
73
83
  /** borrowed-readonly — the harness list (placeholders keep their deferred names), the audit universe's second half. */
@@ -96,6 +106,8 @@ export interface PreparePolicyChainInput {
96
106
  readDenyMatcher: ReadDenyMatcher | undefined;
97
107
  /** borrowed-readonly — the run's one notice for a person's rule store that could not be read (`ruleStoreUnreadableNotice`). */
98
108
  announceRuleStoreSkipped: (at: "prepare" | "call") => void;
109
+ /** borrowed-readonly — the run's one notice for rows the person's store served that were left out (`ruleRowsDroppedNotice`). */
110
+ announceRuleRowsDropped: (rows: import("../permission-rule-provider.js").EffectivePermissionRules["droppedRows"]) => void;
99
111
  /** borrowed-readonly — whether the question tool mounted (the lanes' question-tool exclusion). */
100
112
  questionToolMounted: boolean;
101
113
  /** borrowed-readonly — the resolved per-principal entitlements; only `forceDurableGate` (the mandate-park facility and
@@ -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";
@@ -13,7 +14,7 @@ import { emitTrace } from "../trace.js";
13
14
  import { skillGrantBindingMembers } from "../skill-grant.js";
14
15
  import { SKILL_TOOL_NAME } from "./synthetic-tools.js";
15
16
  import { autoStrippedRule, cardDisclosureOf } from "../allow-entry.js";
16
- import { arbitrateByMode, askClassMembersOf, askMandateMember } from "../ask-origin.js";
17
+ import { arbitrateByMode, askClassMembersOf, askFaceOf, askMandateMember } from "../ask-origin.js";
17
18
  import { createModeGatePolicy, mountedToolDeclaresReadOnly } from "../mode-policy.js";
18
19
  const ancestorFacts = (ask) => ({ ...(ask ?? {}), ancestorResolved: true });
19
20
  import { headlessDenyAtFold, headlessDenyAtRecheck, INHERITED_STATION_FACTS, inheritedAskCarry, inheritedAskFallbackMessage, judgeInheritedClassifier, lateAskSettlementObserver, resolveInheritedAsk, settleDenialLimitFallback } from "./denial-limit-arms.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, readFace, 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)) });
@@ -312,10 +313,11 @@ export async function preparePolicyChain(input) {
312
313
  ...(seat.sources.size > 0 ? { toolMarkSources: seat.sources } : {}),
313
314
  ...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}),
314
315
  ...(face !== undefined ? { readFace: face } : {}),
316
+ askFace: askFaceOf(wireOf("inherited.askFace", call)),
315
317
  };
316
318
  };
317
319
  const inheritedAskEvidence = inheritedAskRuleEvidence(deps.permissionRuleStore);
318
- const listFacts = { root: taskRootFinal, sessionId, liveCwd: () => handsCwdRef?.current, home: executionEnv.homeDir };
320
+ const listFacts = { root: taskRootFinal, sessionId, liveCwd: () => handsCwdRef?.current, anchors: pathRuleAnchors.leg };
319
321
  const skillGrantLane = skillGrants.lane(listFacts);
320
322
  const allowEntryLanes = { request: allowedToolTables.request.lane(listFacts), deployment: allowedToolTables.deployment.lane(listFacts) };
321
323
  const { personal: permissionRuleLane, org: permissionRuleOrgLane } = createPermissionRuleLanes({
@@ -324,6 +326,9 @@ export async function preparePolicyChain(input) {
324
326
  principal: spec.principal,
325
327
  sessionId,
326
328
  root: taskRootFinal,
329
+ rootCanonical: pathRuleAnchors.leg.root,
330
+ chainTop: pathRuleAnchors.chainTop,
331
+ leads: headLeads,
327
332
  liveCwd: () => handsCwdRef?.current,
328
333
  home: executionEnv.homeDir,
329
334
  questionToolMounted,
@@ -331,6 +336,7 @@ export async function preparePolicyChain(input) {
331
336
  onRevisionDefect: (message) => deps.onError?.(new Error(message), { phase: "config", sessionId }),
332
337
  onDisclosure: (message) => emitTrace(deps.tracer, () => ({ kind: "permission.rule_store_unreadable", version: 1, taskId: hostTaskId, message, ts: Date.now() })),
333
338
  onStoreUnreadable: announceRuleStoreSkipped,
339
+ onRowsDropped: announceRuleRowsDropped,
334
340
  signal: abortController.signal,
335
341
  stripsAllow: (rule, req) => permissionMode.current === "auto" && autoStrippedRule(rule, req),
336
342
  });
@@ -366,7 +372,8 @@ export async function preparePolicyChain(input) {
366
372
  }
367
373
  let re;
368
374
  try {
369
- re = refuseOutOfContractDecision(await pol.check({ ...creq, args: editArgs }, csignal ?? abortController.signal));
375
+ const editReq = withArgsReading(creq, editArgs, csignal ?? abortController.signal);
376
+ re = refuseOutOfContractDecision(await pol.check(editReq !== undefined ? await editReq : { ...creq, args: editArgs }, csignal ?? abortController.signal));
370
377
  }
371
378
  catch (err) {
372
379
  return vetoed({
@@ -646,7 +653,8 @@ export async function preparePolicyChain(input) {
646
653
  }
647
654
  }
648
655
  const recheckLayers = [...recheckPrefix, ...postEmitterSameInstanceAdapters];
649
- const finalReq = { ...req, args: decision.updatedInput };
656
+ const finalRead = withArgsReading(req, decision.updatedInput, signal);
657
+ const finalReq = finalRead !== undefined ? await finalRead : { ...req, args: decision.updatedInput };
650
658
  let asked;
651
659
  const revalidated = {};
652
660
  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,17 +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";
29
- import { deploymentCanRerootChild } from "./child-tree.js";
32
+ import { chainAnchorRecords, chainPolicyOnLeg, chainTopAnchors, deploymentCanRerootChild } from "./child-tree.js";
30
33
  import { prepareMemory } from "./prepare-memory.js";
31
34
  import { prepareMemoryEngineSession } from "./prepare-memory-engine-session.js";
32
35
  import { prepareDeferClassify } from "./prepare-defer-classify.js";
@@ -40,6 +43,7 @@ export { runGuardChain } from "./prepare-context-lane.js";
40
43
  import { preparePromptAssembly } from "./prepare-prompt-assembly.js";
41
44
  import { limitConfigError, prepareConfigDoors } from "./prepare-config-doors.js";
42
45
  import { prepareSafetyScan } from "./prepare-safety-scan.js";
46
+ import { isAbsolutePathForm } from "../../tools/fs/safety.js";
43
47
  import { prepareAcquireReconcile } from "./prepare-acquire-reconcile.js";
44
48
  import { settleBranchWriteBack } from "./branch-writeback.js";
45
49
  import { prepareHandsMount, resolveHandsLessReadFace } from "./prepare-hands-readface.js";
@@ -347,7 +351,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
347
351
  throw e;
348
352
  }
349
353
  const rollback = createRollbackStack((e) => deps.onError?.(e, { phase: "config", sessionId }));
350
- 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 });
351
355
  const externalContentTargetActive = executionEnv.externalContentTarget === true || resume?.seed.externalContentTarget === true;
352
356
  const abortController = new AbortController();
353
357
  if (spec.signal?.aborted)
@@ -400,8 +404,15 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
400
404
  }
401
405
  }
402
406
  const ownedEnv = ownedEnvSeat.current;
403
- 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 });
404
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));
405
416
  const { trackFileEdit, fileHistoryBoundary, restoredFilePaths, rewindNotes, fileHistoryEnabled, fileHistoryFact, historyScope, historyRoot, historyFs } = await prepareFileHistory({ deps, spec, request: fileHistoryRequest, internals, executionEnv, taskRootFinal, handsEnabled, sessionId, session, abortController });
406
417
  const { noteFileEdited, editedFilesSnapshot, harnessRef, toolProgressRef, skillGrants, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, bindPermissionModeSeat, enterPlanMode, subagentRetain, worktreeIsolation } = prepareRunRefs({ spec, internals, executionEnv, taskRootFinal });
407
418
  const allowedToolTables = { request: new AllowEntryTable(), deployment: new AllowEntryTable() };
@@ -411,14 +422,19 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
411
422
  let readDenyAdditionsNormalized = [];
412
423
  let handsLessResolvedFace;
413
424
  let handsLessResolvedWriteFace;
414
- 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 });
415
426
  bindPermissionModeSeat(permissionMode);
416
427
  const announceRuleStoreSkipped = ruleStoreUnreadableNotice(deps.onNotice, { sessionId, runId });
417
- 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 });
418
430
  const persistedPathRows = persistedPathRowsRead === undefined ? { readDirectories: [], denyRows: [] } : await persistedPathRowsRead;
419
- 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];
420
436
  const writeProtectionJudgeRef = {};
421
- 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)]) };
422
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 });
423
439
  const tools = roster.tools;
424
440
  const pausedRef = {};
@@ -556,7 +572,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
556
572
  if (internals?.ownOrgAdmissionRef !== undefined && ownOrgVerdictRef.current !== undefined) {
557
573
  internals.ownOrgAdmissionRef.current = ownOrgVerdictRef.current;
558
574
  }
559
- 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 } : {}) };
560
576
  const handsReadFace = handsEnabled ? await prepareHandsMount(handsReadFaceInput) : resolveHandsLessReadFace(handsReadFaceInput);
561
577
  const { readFileStateForCheckpoint, seedContextFiles, handsCwdRef, workspaceStateSettle, wsSnapshot, rebaseWsPath, backgroundTaskToolsActive, shellReadCheck, shellReadAdmission, additionalRootsCanonical, additionalReadRootsCanonical, attachmentRootCanonical, readDenyMatcher, envHandToolNames, sealReadStateSeat } = handsReadFace;
562
578
  resolvedReadFace = handsReadFace.resolvedReadFace;
@@ -737,7 +753,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
737
753
  releaseSignal = () => sig.removeEventListener("abort", onExternalAbort);
738
754
  }
739
755
  }
740
- 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, readFace: () => resolvedReadFace, 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 });
741
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 });
742
758
  const writeProtectionCheck = writeProtection?.check;
743
759
  writeProtectionJudgeRef.current = writeProtection?.judgeTarget;
@@ -747,18 +763,21 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
747
763
  return undefined;
748
764
  });
749
765
  }
750
- const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, handsRoot: attachmentRootCanonical, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, grantBindingOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking: autoModeDenialTrackingRef, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot: handsReadFace.grantReadRoot });
751
- 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 });
752
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 });
753
- 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, 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 });
754
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() })) } } });
755
- 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, 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 });
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 });
756
775
  const postureFrameRef = { report: (err) => deps.onError?.(err, { phase: "degraded", sessionId }) };
757
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 } : {}) });
758
777
  const preparedHolder = {};
759
- 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 });
760
779
  await onceLedger.settle(session, announcedListingsRef);
761
- 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 } : {}), 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 } : {}) });
762
781
  rollback.commit();
763
782
  commitModeSeats(abortController.signal);
764
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)