@sema-agent/core 7.33.8 → 7.35.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 (228) hide show
  1. package/CHANGELOG.md +462 -3
  2. package/dist/agents/cross-session-judge.d.ts +16 -7
  3. package/dist/agents/cross-session-judge.js +18 -15
  4. package/dist/agents/peer-session-drain.d.ts +4 -0
  5. package/dist/agents/peer-session-drain.js +5 -2
  6. package/dist/agents/repair-loop.d.ts +1 -1
  7. package/dist/agents/subagent.d.ts +3 -0
  8. package/dist/agents/subagent.js +12 -3
  9. package/dist/brain/anthropic.js +1 -0
  10. package/dist/brain/errors.d.ts +23 -1
  11. package/dist/brain/errors.js +19 -2
  12. package/dist/brain/open-responses.js +1 -0
  13. package/dist/brain/openai.js +1 -0
  14. package/dist/brain/status-sink.d.ts +7 -1
  15. package/dist/brain/stream-engine.d.ts +3 -0
  16. package/dist/brain/stream-engine.js +2 -1
  17. package/dist/brain/stream-shared.d.ts +3 -1
  18. package/dist/brain/stream-shared.js +16 -4
  19. package/dist/core/a2a.d.ts +3 -1
  20. package/dist/core/a2a.js +1 -1
  21. package/dist/core/ask-origin.d.ts +379 -154
  22. package/dist/core/ask-origin.js +224 -45
  23. package/dist/core/ask-question.js +2 -1
  24. package/dist/core/ask-unresolvable-notice.d.ts +5 -2
  25. package/dist/core/ask-unresolvable-notice.js +4 -3
  26. package/dist/core/auto-mode-arming.d.ts +17 -4
  27. package/dist/core/auto-mode-arming.js +31 -6
  28. package/dist/core/auto-mode-defaults.d.ts +16 -7
  29. package/dist/core/auto-mode-defaults.js +2 -1
  30. package/dist/core/auto-mode-directive.d.ts +18 -30
  31. package/dist/core/auto-mode-directive.js +9 -18
  32. package/dist/core/auto-mode-prompt.d.ts +62 -1
  33. package/dist/core/auto-mode-prompt.js +42 -4
  34. package/dist/core/auto-mode-rebuild.d.ts +2 -1
  35. package/dist/core/auto-mode-rebuild.js +1 -0
  36. package/dist/core/auto-mode.d.ts +32 -3
  37. package/dist/core/auto-mode.js +80 -41
  38. package/dist/core/checkpoint-store.d.ts +54 -35
  39. package/dist/core/checkpoint-store.js +58 -3
  40. package/dist/core/child-clamp-seats.js +1 -0
  41. package/dist/core/classifier-unavailable-arm.d.ts +57 -0
  42. package/dist/core/classifier-unavailable-arm.js +17 -0
  43. package/dist/core/effective-path-target.d.ts +12 -0
  44. package/dist/core/effective-path-target.js +18 -7
  45. package/dist/core/engine-notice.d.ts +53 -2
  46. package/dist/core/fs-write-gate-policy.d.ts +5 -1
  47. package/dist/core/fs-write-gate-policy.js +4 -1
  48. package/dist/core/gate-fold.js +19 -3
  49. package/dist/core/gate-lanes.js +116 -50
  50. package/dist/core/gate-outcome.d.ts +40 -7
  51. package/dist/core/gate-outcome.js +26 -4
  52. package/dist/core/governance-codes.d.ts +18 -2
  53. package/dist/core/governance-codes.js +23 -1
  54. package/dist/core/hooks.d.ts +53 -29
  55. package/dist/core/hooks.js +1 -3
  56. package/dist/core/mcp.js +1 -1
  57. package/dist/core/message-utils.d.ts +2 -1
  58. package/dist/core/message-utils.js +2 -1
  59. package/dist/core/mode-policy.d.ts +185 -0
  60. package/dist/core/mode-policy.js +184 -0
  61. package/dist/core/oracle-isolation.d.ts +3 -3
  62. package/dist/core/permission-rule-model.d.ts +20 -3
  63. package/dist/core/permission-rule-model.js +40 -3
  64. package/dist/core/permission-rule-provider.d.ts +22 -11
  65. package/dist/core/permission-rule-provider.js +17 -7
  66. package/dist/core/permission-rules.d.ts +23 -1
  67. package/dist/core/permission-rules.js +56 -9
  68. package/dist/core/persisted-rule-arms.d.ts +14 -10
  69. package/dist/core/persisted-rule-arms.js +13 -6
  70. package/dist/core/present-plan-tool.d.ts +1 -1
  71. package/dist/core/present-plan-tool.js +3 -3
  72. package/dist/core/read-only-shell.d.ts +19 -19
  73. package/dist/core/read-only-shell.js +38 -12
  74. package/dist/core/retired-keys.d.ts +9 -3
  75. package/dist/core/retired-keys.js +5 -1
  76. package/dist/core/runner/active-skill-scope.js +5 -4
  77. package/dist/core/runner/assemble-result.js +2 -2
  78. package/dist/core/runner/classifier-ladder.d.ts +43 -1
  79. package/dist/core/runner/classifier-ladder.js +29 -0
  80. package/dist/core/runner/contracts.d.ts +62 -38
  81. package/dist/core/runner/denial-limit-arms.d.ts +35 -7
  82. package/dist/core/runner/denial-limit-arms.js +15 -9
  83. package/dist/core/runner/env-capability-census.js +1 -1
  84. package/dist/core/runner/file-history-request.d.ts +26 -0
  85. package/dist/core/runner/file-history-request.js +90 -0
  86. package/dist/core/runner/gate-exit.d.ts +30 -4
  87. package/dist/core/runner/gate-exit.js +28 -7
  88. package/dist/core/runner/permission-rule-lanes.d.ts +11 -6
  89. package/dist/core/runner/permission-rule-lanes.js +55 -48
  90. package/dist/core/runner/persisted-path-rows.d.ts +64 -0
  91. package/dist/core/runner/persisted-path-rows.js +89 -0
  92. package/dist/core/runner/prepare-artifact.d.ts +5 -1
  93. package/dist/core/runner/prepare-artifact.js +9 -3
  94. package/dist/core/runner/prepare-ask-lane.d.ts +6 -4
  95. package/dist/core/runner/prepare-ask-lane.js +3 -3
  96. package/dist/core/runner/prepare-caps-and-workflow.d.ts +39 -13
  97. package/dist/core/runner/prepare-caps-and-workflow.js +58 -29
  98. package/dist/core/runner/prepare-file-history.d.ts +9 -3
  99. package/dist/core/runner/prepare-file-history.js +4 -90
  100. package/dist/core/runner/prepare-gate-stations.d.ts +37 -9
  101. package/dist/core/runner/prepare-gate-stations.js +46 -29
  102. package/dist/core/runner/prepare-hands-readface.d.ts +40 -17
  103. package/dist/core/runner/prepare-hands-readface.js +64 -53
  104. package/dist/core/runner/prepare-inherited-gate.d.ts +30 -23
  105. package/dist/core/runner/prepare-inherited-gate.js +45 -25
  106. package/dist/core/runner/prepare-park-ask.d.ts +6 -4
  107. package/dist/core/runner/prepare-park-ask.js +7 -5
  108. package/dist/core/runner/prepare-policy-chain.d.ts +25 -4
  109. package/dist/core/runner/prepare-policy-chain.js +203 -180
  110. package/dist/core/runner/prepare-protocol-tools.d.ts +28 -0
  111. package/dist/core/runner/prepare-protocol-tools.js +13 -3
  112. package/dist/core/runner/prepare-run-refs.d.ts +6 -6
  113. package/dist/core/runner/prepare-run-refs.js +9 -4
  114. package/dist/core/runner/prepare-safety-scan.d.ts +5 -0
  115. package/dist/core/runner/prepare-safety-scan.js +19 -3
  116. package/dist/core/runner/prepare-suspend-saga.d.ts +5 -5
  117. package/dist/core/runner/prepare-suspend-saga.js +6 -6
  118. package/dist/core/runner/prepare-task.js +56 -24
  119. package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -6
  120. package/dist/core/runner/prepare-wiring-manifest.js +5 -3
  121. package/dist/core/runner/read-root-grant.d.ts +9 -0
  122. package/dist/core/runner/read-root-grant.js +12 -0
  123. package/dist/core/runner/retired-runner-deps.js +1 -0
  124. package/dist/core/runner/run-brain-sinks.js +3 -1
  125. package/dist/core/runner/run-identity-wiring.js +1 -1
  126. package/dist/core/runner/run-leg.js +29 -18
  127. package/dist/core/runner/run-notification-lane.d.ts +3 -0
  128. package/dist/core/runner/run-notification-lane.js +3 -1
  129. package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
  130. package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
  131. package/dist/core/runner/run-turn-boundary.js +1 -1
  132. package/dist/core/runner/runtask.d.ts +1 -0
  133. package/dist/core/runner/runtask.js +8 -11
  134. package/dist/core/runner/session-mode-seats.d.ts +48 -0
  135. package/dist/core/runner/session-mode-seats.js +73 -0
  136. package/dist/core/runner/session-rule-policy.js +3 -2
  137. package/dist/core/runner/stream-mode-verb.d.ts +27 -0
  138. package/dist/core/runner/stream-mode-verb.js +19 -0
  139. package/dist/core/runner/turn-attachments.d.ts +3 -3
  140. package/dist/core/runner/turn-attachments.js +1 -1
  141. package/dist/core/runner/write-protection-station.d.ts +7 -1
  142. package/dist/core/runner/write-protection-station.js +6 -3
  143. package/dist/core/runner-deps.d.ts +31 -34
  144. package/dist/core/runtime-caps.d.ts +12 -3
  145. package/dist/core/safety-axis-vocab.d.ts +2 -2
  146. package/dist/core/safety-axis-vocab.js +1 -1
  147. package/dist/core/secret-env.d.ts +30 -58
  148. package/dist/core/secret-env.js +149 -586
  149. package/dist/core/sensitive-path-policy.d.ts +4 -2
  150. package/dist/core/sensitive-path-policy.js +41 -31
  151. package/dist/core/store-contracts/checkpoint-store-contract.js +2 -1
  152. package/dist/core/task-event.d.ts +1 -1
  153. package/dist/core/task-result.d.ts +1 -1
  154. package/dist/core/task-spec.d.ts +41 -89
  155. package/dist/core/task-stream.d.ts +16 -0
  156. package/dist/core/terminal-cause.js +1 -0
  157. package/dist/core/tighten-task-spec.d.ts +2 -2
  158. package/dist/core/tighten-task-spec.js +12 -14
  159. package/dist/core/tool-catalog-entries.js +4 -3
  160. package/dist/core/tool-policy.d.ts +84 -30
  161. package/dist/core/tool-policy.js +76 -16
  162. package/dist/core/tool-registry.d.ts +38 -0
  163. package/dist/core/tool-registry.js +25 -1
  164. package/dist/core/tool-roster.d.ts +17 -2
  165. package/dist/core/tool-roster.js +11 -1
  166. package/dist/core/tool-spec.d.ts +8 -9
  167. package/dist/core/trace.d.ts +18 -6
  168. package/dist/core/types.d.ts +2 -1
  169. package/dist/core/wiring-manifest.d.ts +31 -5
  170. package/dist/core/wiring-manifest.js +28 -1
  171. package/dist/core/write-protect.d.ts +11 -1
  172. package/dist/core/write-protect.js +26 -15
  173. package/dist/engine/execution-env/node-execution-env.d.ts +6 -1
  174. package/dist/engine/execution-env/node-execution-env.js +16 -3
  175. package/dist/engine/harness/agent-harness.js +2 -1
  176. package/dist/engine/harness/types.d.ts +6 -7
  177. package/dist/engine/llm/types.d.ts +2 -4
  178. package/dist/engine/loop/types.d.ts +7 -0
  179. package/dist/engine/lsp/node-lsp-manager.js +1 -1
  180. package/dist/index.d.ts +18 -8
  181. package/dist/index.js +15 -6
  182. package/dist/orchestration/workflow-governance.js +1 -1
  183. package/dist/orchestration/workflow-primitives.d.ts +1 -1
  184. package/dist/orchestration/workflow-size-guideline.js +2 -2
  185. package/dist/stores/file/index.d.ts +5 -3
  186. package/dist/stores/file/session-store.d.ts +8 -2
  187. package/dist/stores/file/session-store.js +5 -5
  188. package/dist/tools/artifact/artifact-tool.d.ts +17 -0
  189. package/dist/tools/artifact/artifact-tool.js +16 -1
  190. package/dist/tools/fs/bash-readonly-classifier.d.ts +28 -7
  191. package/dist/tools/fs/bash-readonly-classifier.js +88 -0
  192. package/dist/tools/fs/containment-face.d.ts +9 -3
  193. package/dist/tools/fs/containment-face.js +2 -0
  194. package/dist/tools/fs/file-read-check.d.ts +50 -0
  195. package/dist/tools/fs/file-read-check.js +79 -0
  196. package/dist/tools/fs/fs-bash.d.ts +3 -3
  197. package/dist/tools/fs/fs-read.d.ts +1 -0
  198. package/dist/tools/fs/fs-search-tools.d.ts +2 -2
  199. package/dist/tools/fs/fs-search-tools.js +4 -4
  200. package/dist/tools/fs/fs-shared.js +20 -6
  201. package/dist/tools/fs/fs-write.d.ts +4 -3
  202. package/dist/tools/fs/fs-write.js +9 -9
  203. package/dist/tools/fs/index.d.ts +39 -3
  204. package/dist/tools/fs/index.js +38 -15
  205. package/dist/tools/fs/read-deny-home.d.ts +93 -0
  206. package/dist/tools/fs/read-deny-home.js +225 -0
  207. package/dist/tools/fs/read-deny.d.ts +176 -34
  208. package/dist/tools/fs/read-deny.js +268 -61
  209. package/dist/tools/fs/safety.d.ts +6 -1
  210. package/dist/tools/fs/safety.js +18 -5
  211. package/dist/tools/fs/search.d.ts +3 -0
  212. package/dist/tools/fs/search.js +47 -9
  213. package/dist/tools/fs/shell-read-check.d.ts +52 -13
  214. package/dist/tools/fs/shell-read-check.js +199 -20
  215. package/dist/tools/fs/shell-read-establish.d.ts +7 -3
  216. package/dist/tools/fs/shell-read-establish.js +134 -21
  217. package/dist/tools/fs/shell-read-identity.d.ts +7 -0
  218. package/dist/tools/fs/shell-read-identity.js +21 -0
  219. package/dist/tools/fs/shell-read-judge.d.ts +92 -32
  220. package/dist/tools/fs/shell-read-judge.js +43 -18
  221. package/dist/tools/fs/shell-read-plan.d.ts +4 -0
  222. package/dist/tools/fs/shell-read-plan.js +26 -4
  223. package/dist/tools/fs/shell-read-write-targets.d.ts +73 -0
  224. package/dist/tools/fs/shell-read-write-targets.js +173 -0
  225. package/package.json +1 -1
  226. package/test/export-surface.snapshot.json +117 -11
  227. package/wire-changes/core@7.34.0.json +2621 -0
  228. package/wire-changes/core@7.35.0.json +584 -0
@@ -21,6 +21,7 @@ import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
21
21
  import type { TaskEvent, TaskSpec } from "../types.js";
22
22
  import { ActiveSkillScope } from "./active-skill-scope.js";
23
23
  import type { Prepared, RunInternals, ToolProgressLane } from "./contracts.js";
24
+ import type { PermissionModeController } from "../mode-policy.js";
24
25
  import { type EditedFilesLedger } from "./edited-files-ledger.js";
25
26
  export interface PrepareRunRefsInput {
26
27
  /** borrowed-readonly — the REBOUND spec. Read: `forwardSubagentEvents` (widens the forwarding filter),
@@ -82,12 +83,11 @@ export interface PrepareRunRefsResult {
82
83
  stopRequestedRef: Prepared["stopRequestedRef"];
83
84
  /** owned — the ctx primitive: flips the latch and mirrors it onto the harness's one-way flag. */
84
85
  requestStopAfterTurn: () => void;
85
- /** borrowed-mutable — the plan-mode flag (`Prepared.planModeRef`). Writers: `enterPlanMode` here, and the run
86
- * loop's resume ladder, which re-arms it when a REJECTED `plan_review` resumes into a re-plan. Read by the
87
- * tool-call gate (the write-effect refusal). */
88
- planModeRef: Prepared["planModeRef"];
89
- /** owned — the ctx primitive (mounted on the ctx only under `enablePlanMode`). */
90
- enterPlanMode: () => void;
86
+ /** owned — binds the leg's permission-mode seat once the inherited-gate phase minted it; `enterPlanMode` steers it
87
+ * (the run loop's resume ladder steers the same seat when a REJECTED `plan_review` resumes into a re-plan). */
88
+ bindPermissionModeSeat: (seat: PermissionModeController) => void;
89
+ /** owned — the ctx primitive (mounted on the ctx only under `enablePlanMode`): a steer of the bound seat into `plan`. */
90
+ enterPlanMode: () => Promise<void>;
91
91
  /** ownership-ref — the parent-run subagent-retain ledger, or undefined when the spec did not opt in. Its
92
92
  * lifecycle protocol belongs to the Runner (disposed in the task's terminal finally); a
93
93
  * prepare that throws has nothing to release here — the ledger is empty until a delegation retains. */
@@ -37,11 +37,16 @@ export function prepareRunRefs(input) {
37
37
  stopRequestedRef.current = true;
38
38
  harnessRef.current?.requestStopAfterTurn();
39
39
  };
40
- const planModeRef = { active: false };
41
- const enterPlanMode = () => {
42
- planModeRef.active = true;
40
+ let permissionModeSeat;
41
+ const bindPermissionModeSeat = (seat) => {
42
+ permissionModeSeat = seat;
43
+ };
44
+ const enterPlanMode = async () => {
45
+ if (permissionModeSeat === undefined)
46
+ throw new Error("enterPlanMode: the leg's permission-mode seat is not bound yet");
47
+ await permissionModeSeat.steer("plan");
43
48
  };
44
49
  const subagentRetain = spec.retainSubagentSessions ? new SubagentRetainLedger(spec.retainSubagentSessions) : undefined;
45
50
  const worktreeIsolation = spec.handsReadOnly !== true ? createSubagentWorktreeHelper(executionEnv, taskRootFinal) : undefined;
46
- return { noteFileEdited, editedFilesSnapshot, harnessRef, toolProgressRef, skillScope, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, planModeRef, enterPlanMode, subagentRetain, worktreeIsolation };
51
+ return { noteFileEdited, editedFilesSnapshot, harnessRef, toolProgressRef, skillScope, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, bindPermissionModeSeat, enterPlanMode, subagentRetain, worktreeIsolation };
47
52
  }
@@ -1,5 +1,6 @@
1
1
  import type { RunnerDeps, TaskSpec, ToolEffect } from "../types.js";
2
2
  import type { IrreversibilityTier, ReversibilityProbes } from "./contracts.js";
3
+ import type { ToolMarkSource } from "../ask-origin.js";
3
4
  export interface PrepareSafetyScanInput {
4
5
  /** borrowed-readonly — the REBOUND spec (`spec′`, the config-doors result). The scan reads
5
6
  * `tools`, `handsReadOnly` and `enablePlanMode`; it never mutates. */
@@ -42,6 +43,10 @@ export interface PrepareSafetyScanResult {
42
43
  }>;
43
44
  /** borrowed-mutable — declared reversibility probes by tool name. */
44
45
  reversibilityProbes: ReversibilityProbes;
46
+ /** borrowed-mutable — WHO put each tool's egress / irreversibility mark on its seat (the caller roster's marks are
47
+ * the deployment's own; the protocol fold adds `mcp_annotation` / `a2a_default`). Read by the gate's class
48
+ * derivation: a mark with no recorded source reads as the deployment's (fail-closed). */
49
+ toolMarkSources: Map<string, Set<ToolMarkSource>>;
45
50
  /** borrowed-readonly — the caller roster's own names, snapshotted before the first await. */
46
51
  ownToolNames: ReadonlySet<string>;
47
52
  }
@@ -14,6 +14,14 @@ export function prepareSafetyScan(input) {
14
14
  const irreversibilityTier = new Map();
15
15
  const axisExplicitNegatives = new Map();
16
16
  const reversibilityProbes = new Map();
17
+ const toolMarkSources = new Map();
18
+ const markedBy = (name, source) => {
19
+ const set = toolMarkSources.get(name);
20
+ if (set !== undefined)
21
+ set.add(source);
22
+ else
23
+ toolMarkSources.set(name, new Set([source]));
24
+ };
17
25
  for (const t of spec.tools ?? []) {
18
26
  {
19
27
  const bound = rosterMemberBoundProblem({
@@ -72,8 +80,10 @@ export function prepareSafetyScan(input) {
72
80
  const tier = t.irreversibility ?? (t.reversibilityProbe ? "maybe" : undefined);
73
81
  if (tier !== undefined) {
74
82
  irreversibilityTier.set(t.name, tier);
75
- if (tier === "always" || tier === "maybe")
83
+ if (tier === "always" || tier === "maybe") {
76
84
  irreversibleTools.add(t.name);
85
+ markedBy(t.name, "deployment");
86
+ }
77
87
  }
78
88
  if (t.reversibilityProbe)
79
89
  reversibilityProbes.set(t.name, t.reversibilityProbe);
@@ -84,6 +94,7 @@ export function prepareSafetyScan(input) {
84
94
  throw e;
85
95
  }
86
96
  egressTools.add(t.name);
97
+ markedBy(t.name, "deployment");
87
98
  }
88
99
  for (const alias of t.aliases ?? []) {
89
100
  if (alias === t.name || ownToolNames.has(alias))
@@ -95,13 +106,17 @@ export function prepareSafetyScan(input) {
95
106
  toolEffects.set(alias, t.effect);
96
107
  if (tier !== undefined) {
97
108
  irreversibilityTier.set(alias, tier);
98
- if (tier === "always" || tier === "maybe")
109
+ if (tier === "always" || tier === "maybe") {
99
110
  irreversibleTools.add(alias);
111
+ markedBy(alias, "deployment");
112
+ }
100
113
  }
101
114
  if (t.reversibilityProbe)
102
115
  reversibilityProbes.set(alias, t.reversibilityProbe);
103
- if (t.egress)
116
+ if (t.egress) {
104
117
  egressTools.add(alias);
118
+ markedBy(alias, "deployment");
119
+ }
105
120
  }
106
121
  }
107
122
  toolEffects.set(OFFLOAD_TOOL_NAME, "read");
@@ -122,6 +137,7 @@ export function prepareSafetyScan(input) {
122
137
  irreversibilityTier,
123
138
  axisExplicitNegatives,
124
139
  reversibilityProbes,
140
+ toolMarkSources,
125
141
  ownToolNames,
126
142
  };
127
143
  }
@@ -26,6 +26,7 @@ import type { ReadFileState } from "../../tools/fs/safety.js";
26
26
  import type { OutputRef } from "./synthetic-tools.js";
27
27
  import type { HookInvocationIdentity } from "../hooks.js";
28
28
  import type { InheritedGate, Prepared, PrepareResume, RunInternals, SuspendSaga } from "./contracts.js";
29
+ import type { PermissionModeController } from "../mode-policy.js";
29
30
  /**
30
31
  * The ONE compensation for a paused-but-unparked VM, shared by the fence's
31
32
  * post-pause checkpoints (③ in the park closure, ④ in the saga) and the saga's put-failure absent
@@ -130,12 +131,11 @@ export interface PrepareSuspendSagaInput {
130
131
  * mid-run and enforced here has to survive suspend exactly as it survives a spawn, and the same
131
132
  * projection serves both). Cloned onto the row. */
132
133
  ancestorRulesNow: () => InheritedGate["ancestorRules"];
133
- /** borrowed-readonly — the inherited shell-gate doctrine, persisted verbatim. */
134
+ /** borrowed-readonly — the inherited shell floor, persisted verbatim. */
134
135
  inheritedShellGate: InheritedGate["shellGate"];
135
- /** borrowed-readonly — the auto-mode intent (seat ∨ live ∨ seed), carried while the latch is healthy. */
136
- autoModeIntent: boolean;
137
- /** borrowed-readonly — the resolved approver posture (seat, else live, else seed), persisted verbatim. */
138
- approverPosture: InheritedGate["approverPosture"];
136
+ /** borrowed-readonly — the leg's live permission-mode seat; the DECLARED mode (request clamped under the chain's ceiling,
137
+ * before the deployment's fold) is what the row records — the resume leg re-judges the fold for its own deployment. */
138
+ permissionMode: PermissionModeController;
139
139
  /** borrowed-readonly — the auto-mode decider, or undefined (the latch-health reading only). */
140
140
  /** borrowed-readonly — the inherited admitted org scopes, or undefined (copied onto the row). */
141
141
  inheritedAdmittedOrgScopes: readonly string[] | 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, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, autoModeIntent, approverPosture, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter } = input;
82
+ const { gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, permissionMode, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter } = input;
83
83
  if (!gateMachineryActive)
84
84
  return { saga: undefined };
85
85
  const inFlightSpendMicroUsd = () => {
@@ -134,12 +134,12 @@ export function prepareSuspendSaga(input) {
134
134
  const constraintDigest = (inheritedParentConstraints?.length ?? 0) > 0
135
135
  ? constraintChainDigest(constraintChain)
136
136
  : seedInheritedGate?.constraintDigest;
137
- const autoModeIntentCarried = autoModeIntent;
137
+ const declaredMode = permissionMode.declared;
138
+ const modeRecorded = declaredMode !== "default" || permissionMode.requested !== undefined || seedInheritedGate?.permissionMode !== undefined;
138
139
  const ancestorRulesAtMint = ancestorRulesNow();
139
140
  return ancestorRulesAtMint !== undefined ||
140
141
  inheritedShellGate !== undefined ||
141
- autoModeIntentCarried ||
142
- approverPosture !== undefined ||
142
+ modeRecorded ||
143
143
  inheritedAdmittedOrgScopes !== undefined ||
144
144
  ownOrgVerdictRef.current !== undefined ||
145
145
  orgGovernedProvenance ||
@@ -147,8 +147,8 @@ export function prepareSuspendSaga(input) {
147
147
  ? {
148
148
  ...(ancestorRulesAtMint !== undefined ? { ancestorRules: structuredClone(ancestorRulesAtMint) } : {}),
149
149
  ...(inheritedShellGate !== undefined ? { shellGate: inheritedShellGate } : {}),
150
- ...(autoModeIntentCarried ? { autoModeRequested: true } : {}),
151
- ...(approverPosture !== undefined ? { approverPosture } : {}),
150
+ ...(modeRecorded ? { permissionMode: declaredMode } : {}),
151
+ ...(permissionMode.prePlanMode !== undefined ? { prePlanMode: permissionMode.prePlanMode } : {}),
152
152
  ...(inheritedAdmittedOrgScopes !== undefined ? { admittedOrgScopes: [...inheritedAdmittedOrgScopes] } : {}),
153
153
  ...(ownOrgVerdictRef.current !== undefined
154
154
  ? { ownAdmittedOrgScopes: [...ownOrgVerdictRef.current.scopes], ownAdmittedOrgWriteScope: ownOrgVerdictRef.current.writeScope }
@@ -8,10 +8,15 @@ 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
12
  import { loadAnnounceOnceLedger } from "./announce-once-ledger.js";
12
13
  import { isValidReminderMark, mintReminderMark } from "../reminder-mint.js";
13
- import { refuseInvalidApproverPosture } from "../auto-mode-directive.js";
14
+ import { clampPermissionMode, isPermissionMode, PERMISSION_MODES } from "../ask-origin.js";
15
+ import { modePolicyOf, modeRequestAfterResume } from "../mode-policy.js";
16
+ import { assertNoRetiredKeys, RETIRED_TASK_SPEC_KEYS } from "../retired-keys.js";
17
+ import { migrateLegacyModeSeed } from "../checkpoint-store.js";
14
18
  import { emitTrace } from "../trace.js";
19
+ import { persistedPathRowsOfTask, ruleStoreUnreadableNotice } from "./persisted-path-rows.js";
15
20
  import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
16
21
  import { STALL_FIRST_BYTE_MS, STALL_FIRST_TOKEN_MS, STALL_IDLE_MS, withBrainCallGuardrail } from "../../brain/timeout.js";
17
22
  import { OFFLOAD_TOOL_NAME } from "../tool-result-store.js";
@@ -39,6 +44,7 @@ import { parkContaminationMarker } from "./park-commit.js";
39
44
  export { gatedCallIdOf, USAGE_WINDOW_REAP_MARGIN_MS } from "./park-commit.js";
40
45
  import { prepareOffloadWrappers } from "./prepare-offload-wrappers.js";
41
46
  import { prepareFileHistory } from "./prepare-file-history.js";
47
+ import { resolveFileHistoryRequest } from "./file-history-request.js";
42
48
  import { prepareExecutionEnv } from "./prepare-execution-env.js";
43
49
  import { prepareRunRefs } from "./prepare-run-refs.js";
44
50
  import { prepareInheritedGate } from "./prepare-inherited-gate.js";
@@ -260,13 +266,21 @@ function refuseRegistryScopeMismatch(spec, internals) {
260
266
  function validateInputFaultNotice(onError, sessionId) {
261
267
  return ({ toolName, toolCallId, error }) => onError?.(new Error(`tool input pre-validation (validateInput) threw for ${toolName} (call ${toolCallId}) — read as no verdict; the call proceeded to the gate: ${thrownText(error)}`), { phase: "hook", sessionId });
262
268
  }
263
- function refuseInvalidAutoModeSeat(spec) {
264
- if (spec.autoModeRequested !== undefined && spec.autoModeRequested !== true) {
265
- const e = new Error(`autoModeRequested must be the literal true when present (got ${JSON.stringify(spec.autoModeRequested)}) — omit it for a non-auto task; a non-boolean or false value is refused rather than read as either polarity.`);
266
- e.code = "config.auto_mode_requested_invalid";
269
+ function refusePermissionModeDoors(spec) {
270
+ assertNoRetiredKeys(spec, RETIRED_TASK_SPEC_KEYS, "TaskSpec");
271
+ const mode = spec.permissionMode;
272
+ if (mode !== undefined && !isPermissionMode(mode)) {
273
+ const e = new Error(`permissionMode must be one of ${PERMISSION_MODES.map((w) => JSON.stringify(w)).join(", ")} when present (got ${JSON.stringify(mode)}) — omit it for the default mode; an unrecognized word is refused rather than read as any mode.`);
274
+ e.code = "config.permission_mode_invalid";
267
275
  throw e;
268
276
  }
269
- return spec.autoModeRequested === true;
277
+ const floor = spec.shellGate;
278
+ if (floor !== undefined && floor !== "always") {
279
+ const e = new Error(`shellGate must be the floor word "always" when present (got ${JSON.stringify(floor)}) — the doctrine values "off" / "classify" are retired: the shell's shape tier is installed on every full shell and the permission mode (permissionMode) decides what its asks become; omit shellGate for no operator floor.`);
280
+ e.code = "config.shell_gate_value_retired";
281
+ throw e;
282
+ }
283
+ return mode;
270
284
  }
271
285
  export async function prepareTask(spec, deps, sessions, resume, internals, runnerSelf, runIdSink) {
272
286
  const runId = uuidv7();
@@ -276,15 +290,22 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
276
290
  const doors = prepareConfigDoors({ spec, deps, sessions, resume, internals });
277
291
  spec = doors.spec;
278
292
  const checkpointStore = resolveCheckpointStore(spec, deps), checkpointSeat = checkpointSeatOf(spec, checkpointStore);
279
- const autoModeSeat = refuseInvalidAutoModeSeat(spec);
280
- const approverPostureSeat = refuseInvalidApproverPosture(spec);
293
+ const permissionModeSeat = refusePermissionModeDoors(spec);
294
+ const declaredModeAtDoor = (() => {
295
+ const requested = modeRequestAfterResume(permissionModeSeat, migrateLegacyModeSeed(resume?.seed.inheritedGate), resume?.outcome);
296
+ const ceiling = internals?.inheritedGate?.permissionModeCeiling?.current ?? internals?.inheritedGate?.permissionMode;
297
+ return ceiling === undefined ? (requested ?? "default") : clampPermissionMode(requested, ceiling).effective;
298
+ })();
299
+ if (modePolicyOf(declaredModeAtDoor).enablePlanMode)
300
+ spec = { ...spec, enablePlanMode: true };
281
301
  const { toolFaceSnapshot, promptProfile, lockedPreflight, resolvedInteractionPosture, microCompactKnob, resolvedRole, model, thinking, compModel, fableMitigations, memoryDelegationEvidence, memoryProvenance, memoryCapturePolicy, usageWindows, brainCallGuardrailRef, brainCallGuardrailMs } = doors;
282
302
  announceToolModelGate(deps.onNotice, model.id, doors.modelGate);
283
303
  const modelGateManifest = toolModelGateManifest(doors.modelGate);
284
- const { toolEffects, egressTools, irreversibleTools, irreversibilityTier, axisExplicitNegatives, reversibilityProbes } = prepareSafetyScan({ spec, deps });
304
+ const { toolEffects, egressTools, irreversibleTools, irreversibilityTier, axisExplicitNegatives, reversibilityProbes, toolMarkSources } = prepareSafetyScan({ spec, deps });
285
305
  let shellGatedTools = new Set();
286
306
  refuseRequireExistingWithoutSession(spec);
287
307
  refuseRegistryScopeMismatch(spec, internals);
308
+ const fileHistoryRequest = resolveFileHistoryRequest(spec, deps);
288
309
  const { acquired, session, conflictRef, wakeRecovered } = await prepareAcquireReconcile({ sessions, spec, resume, toolEffects, ...(internals?.sessionPlacement !== undefined ? { placement: internals.sessionPlacement } : {}), ...(() => { const g = durableParkGapFor(deps, spec); return g !== undefined ? { durableParkGap: g } : {}; })() });
289
310
  const sessionId = acquired.sessionId;
290
311
  const onceLedger = await loadAnnounceOnceLedger(session, resume?.seed.announcedListings, deps.onError);
@@ -384,16 +405,23 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
384
405
  const ownedEnv = ownedEnvSeat.current;
385
406
  const { taskRootFinal, rebaseRestoredPath } = await prepareWorkspaceRestore({ ownedEnv, deps, spec, internals, resume, taskRootInitial, abortController, sessionId });
386
407
  const { canonical: taskRootCanonical } = await resolveTaskRootCanonical({ env: executionEnv, given: taskRootFinal, onNotice: deps.onNotice, sessionId, runId });
387
- const { trackFileEdit, fileHistoryBoundary, restoredFilePaths, rewindNotes, fileHistoryEnabled, fileHistoryFact, historyScope, historyRoot, historyFs } = await prepareFileHistory({ deps, spec, internals, executionEnv, taskRootFinal, handsEnabled, sessionId, session, abortController });
388
- const { noteFileEdited, editedFilesSnapshot, harnessRef, toolProgressRef, skillScope, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, planModeRef, enterPlanMode, subagentRetain, worktreeIsolation } = prepareRunRefs({ spec, internals, executionEnv, taskRootFinal });
408
+ const { trackFileEdit, fileHistoryBoundary, restoredFilePaths, rewindNotes, fileHistoryEnabled, fileHistoryFact, historyScope, historyRoot, historyFs } = await prepareFileHistory({ deps, spec, request: fileHistoryRequest, internals, executionEnv, taskRootFinal, handsEnabled, sessionId, session, abortController });
409
+ const { noteFileEdited, editedFilesSnapshot, harnessRef, toolProgressRef, skillScope, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, bindPermissionModeSeat, enterPlanMode, subagentRetain, worktreeIsolation } = prepareRunRefs({ spec, internals, executionEnv, taskRootFinal });
389
410
  const legContextOverlay = () => runnerSelf?.contextOverlayOf?.(harnessRef.current?.getModelRef() ?? resolvedRole.modelRef);
390
411
  let resolvedReadFace;
391
412
  let resolvedWriteFace;
392
413
  let readDenyAdditionsNormalized = [];
393
414
  let handsLessResolvedFace;
394
415
  let handsLessResolvedWriteFace;
395
- const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, approverPosture: approverPostureResolved, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, autoModeSeat, approverPostureSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDecider, autoModeDenialTracking: () => autoModeDenialTracking, autoModeArming: () => autoModeArming, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
396
- const { roster, blockedRef, runtimeCaps, runtimeCapsFaulted, complianceDenies, complianceDegraded, agentForkDenial, observersActive, autoModeArmReason, autoModeDecider, autoModeDenialTracking, autoModeArming, selfOrchestrationActive, workflowToolsActive, workflowSizeGuideline } = await prepareCapsAndWorkflow({ spec, deps, internals, session, sessionId, runId, hostTaskId, taskScope, taskRootFinal, model, thinking, lockedPreflight, checkpointSeat, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, parentSpecSeat, carrierReadFace, carrierWriteFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoptionRef.current, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
416
+ const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, permissionMode, commitModeSeats, endModeCeiling, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, permissionModeSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => 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 });
417
+ bindPermissionModeSeat(permissionMode);
418
+ const announceRuleStoreSkipped = ruleStoreUnreadableNotice(deps.onNotice, { sessionId, runId });
419
+ 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 });
420
+ const persistedPathRows = persistedPathRowsRead === undefined ? { readDirectories: [], denyRows: [] } : await persistedPathRowsRead;
421
+ const effectivePathDenyRules = [...toolPolicyPathDenyRules([lockedPreflight.toolPolicy, ...(inheritedParentConstraints ?? []).map((c) => c.policy)]), ...persistedPathRows.denyRows];
422
+ const writeProtectionJudgeRef = {};
423
+ const writeGuards = { protection: () => writeProtectionJudgeRef.current, target: toolPolicyWriteTargetGuard([lockedPreflight.toolPolicy, ...(inheritedParentConstraints ?? []).map((c) => c.policy)]) };
424
+ const { roster, blockedRef, runtimeCaps, runtimeCapsFaulted, complianceDenies, complianceDegraded, agentForkDenial, observersActive, autoModeArmReason, autoModeDeciderRef, autoModeDenialTrackingRef, autoModeArmingRef, armAutoMode, selfOrchestrationActive, workflowToolsActive, workflowSizeGuideline } = await prepareCapsAndWorkflow({ spec, deps, internals, session, sessionId, runId, hostTaskId, taskScope, taskRootFinal, model, thinking, lockedPreflight, checkpointSeat, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, parentSpecSeat, carrierReadFace, carrierWriteFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, permissionMode, readDenyEntries: () => ({ entries: readDenyMatcher?.entries ?? [], ruleRows: effectivePathDenyRules }), peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoptionRef.current, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
397
425
  const tools = roster.tools;
398
426
  const pausedRef = {};
399
427
  const undeliveredContinuationRef = { current: { parkedSteers: [], orphanTasks: [] } };
@@ -454,7 +482,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
454
482
  roster.mount(createOutputTool(outputRef, spec.outputSchema, compiled.strict ? compiled.modelSchema : undefined), { source: "synthetic", mountedBy: "optional" });
455
483
  }
456
484
  const rosterSeat = new ToolRosterDeltaSeat();
457
- const { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef, mcpRedial } = await prepareProtocolTools({ lockedPreflight, spec, deps, reminderMark, reminderDisclosureCounts, runId, sessionId, onceLedger, roster, rosterSeat, remoteToolOffload, toolFaceSnapshot, toolEffects, axisExplicitNegatives, irreversibilityTier, irreversibleTools, egressTools, abortController, rollback });
485
+ const { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef, mcpRedial } = await prepareProtocolTools({ lockedPreflight, spec, deps, reminderMark, reminderDisclosureCounts, runId, sessionId, onceLedger, roster, rosterSeat, remoteToolOffload, toolFaceSnapshot, toolEffects, axisExplicitNegatives, irreversibilityTier, irreversibleTools, egressTools, toolMarkSources, abortController, rollback });
458
486
  const callIssuedAtRef = {};
459
487
  const workCutRef = { continuation: false, abortedBeforeTurnEnd: false };
460
488
  const advertisedWritableDirs = handsEnabled
@@ -530,7 +558,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
530
558
  if (internals?.ownOrgAdmissionRef !== undefined && ownOrgVerdictRef.current !== undefined) {
531
559
  internals.ownOrgAdmissionRef.current = ownOrgVerdictRef.current;
532
560
  }
533
- const handsReadFaceInput = { handsEnabled, executionEnv, taskRootFinal, taskRootCanonical, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals, memoryWriteGate, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), firstPartyOffload, roster, egressTools, irreversibilityTier, irreversibleTools, reversibilityProbes, reminderMark, reminderDisclosureCounts, ...(trackFileEdit !== undefined ? { trackFileEdit } : {}), onFileEdited: noteFileEdited, harnessRef, toolProgressRef, ...(restoredFilePaths.length > 0 ? { restoredFilePaths } : {}) };
561
+ 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 } : {}) };
534
562
  const handsReadFace = handsEnabled ? await prepareHandsMount(handsReadFaceInput) : resolveHandsLessReadFace(handsReadFaceInput);
535
563
  const { readFileStateForCheckpoint, seedContextFiles, handsCwdRef, workspaceStateSettle, wsSnapshot, rebaseWsPath, backgroundTaskToolsActive, shellReadCheck, shellReadAdmission, additionalRootsCanonical, additionalReadRootsCanonical, attachmentRootCanonical, readDenyMatcher, envHandToolNames, sealReadStateSeat } = handsReadFace;
536
564
  resolvedReadFace = handsReadFace.resolvedReadFace;
@@ -539,6 +567,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
539
567
  handsLessResolvedFace = handsReadFace.handsLessResolvedFace;
540
568
  handsLessResolvedWriteFace = handsReadFace.handsLessResolvedWriteFace;
541
569
  shellGatedTools = handsReadFace.shellGatedTools;
570
+ await armAutoMode();
542
571
  const { worktreeSessionRef } = prepareDelegationSurface({ spec, deps, internals, resume, sessionId, hostTaskId, taskScope, taskRootFinal, taskRootCanonical, executionEnv, runnerSelf, backgroundTaskToolsActive, shellReadAdmission, workflowToolsActive, peerLaneActive, peerSendMessageBuiltIn, peerLaneRefs, sessionBox, roster, toolEffects, axisExplicitNegatives, envHandToolNames, toolFaceSnapshot, firstPartyOffload, offloadStore, subagentRetain, enrichSpecToolCtx, handsCwdRef, wsSnapshot, rebaseWsPath, rebaseRestoredPath, workspaceStateSettle });
543
572
  const { liveQuestionFace, contentAskBindings, contentAskRoutable, lateStrandedAnswers, discloseStrandedAnswers, settleContentAskBindings, durableQuestionFace, questionToolMounted, mountedQuestionTool } = prepareQuestionFace({ frozenOnQuestion, spec, deps, checkpointStore, runtimeCaps, resolvedInteractionPosture, resume, sessionId, roster, inheritedUnavailableAsks: () => inheritedUnavailableAsks });
544
573
  if (spec.handsReadOnly !== true) {
@@ -559,7 +588,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
559
588
  `(per-principal entitlement governance, not a misconfiguration — Agent(subagent_type:"fork") will refuse honestly)`), { phase: "config", sessionId });
560
589
  }
561
590
  const { lspDiagnostics, nudgeLspOnEdit, lspRunIdent, managerWired: lspManagerWired } = prepareLsp({ spec, deps, executionEnv, taskRootFinal, handsEnabled, sessionId, handsCwdRef, roster, envHandToolNames });
562
- prepareArtifact({ deps, runId, taskId: hostTaskId, sessionId, agentName: internals?.explicitAgentName, onAsk: frozenOnAsk, executionEnv, rootCanonical: attachmentRootCanonical, additionalRootsCanonical: () => [...additionalRootsCanonical, ...additionalReadRootsCanonical], readDenyMatcher, resolvedReadFace, handsCwdRef, roster, firstPartyOffload, envHandToolNames, ...(handsReadFace.artifactStoreRef !== undefined ? { artifactStoreRef: handsReadFace.artifactStoreRef } : {}) });
591
+ prepareArtifact({ deps, runId, taskId: hostTaskId, sessionId, agentName: internals?.explicitAgentName, onAsk: frozenOnAsk, permissionMode, executionEnv, rootCanonical: attachmentRootCanonical, additionalRootsCanonical: () => [...additionalRootsCanonical, ...additionalReadRootsCanonical], readDenyMatcher, resolvedReadFace, handsCwdRef, roster, firstPartyOffload, envHandToolNames, ...(handsReadFace.artifactStoreRef !== undefined ? { artifactStoreRef: handsReadFace.artifactStoreRef } : {}) });
563
592
  if (internals?.delegationProvenance !== undefined && resume !== undefined) {
564
593
  const live = internals.delegationProvenance.ref.current;
565
594
  const seed = resume.seed.delegationProvenance;
@@ -598,7 +627,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
598
627
  }
599
628
  const projectContext = await prepareProjectContext({ deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillScope, roster, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume, skillListingBudget: doors.skillListingBudget });
600
629
  const { memoryBlock: memoryBlockWithProject, instructionSources, projectInstructionContent, hasSkillManifest, skillsListing, sharedMemoryPairMounted, memoryEnginePairMounted } = projectContext;
601
- const { provider, featureFlags, userSystemPrompt, userAppendSystemPrompt, mcpInstructionsBlock, envFacts, tzValid, userTz, dateAnchor, gitStatusRef } = await preparePromptInputs({ spec, deps, contextOverlay: legContextOverlay(), failClosedReason: selfOrchestrationFailClosedReason(spec, deps), lockedPreflight, selfOrchestrationActive, executionEnv, reminderMark, resolvedReadFace, resolvedWriteFace, toolFaceSnapshot, promptProfile, fableMitigations, internals, thinking, ownedEnv, delegation, resolvedRole, mcp, model, externalContentTargetActive, resume, handsEnabled, taskRootFinal, additionalRootsCanonical, additionalReadRootsCanonical, sessionId, session, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}) });
630
+ const { provider, featureFlags, userSystemPrompt, userAppendSystemPrompt, mcpInstructionsBlock, envFacts, tzValid, userTz, dateAnchor, gitStatusRef } = await preparePromptInputs({ spec, deps, contextOverlay: legContextOverlay(), failClosedReason: selfOrchestrationFailClosedReason(spec, deps), lockedPreflight, selfOrchestrationActive, executionEnv, reminderMark, resolvedReadFace, resolvedWriteFace: resolvedWriteFace?.inForce(), toolFaceSnapshot, promptProfile, fableMitigations, internals, thinking, ownedEnv, delegation, resolvedRole, mcp, model, externalContentTargetActive, resume, handsEnabled, taskRootFinal, additionalRootsCanonical, additionalReadRootsCanonical, sessionId, session, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}) });
602
631
  const { deferred, memoryBlock, toolSearch: toolSearchPolicy } = prepareDeferClassify({ spec, deps, roster, toolFaceSnapshot, handsEnabled, fullShellReachable, promptProfile, model, mcp, a2a, sharedMemoryPairMounted, memoryEnginePairMounted, memoryBlock: memoryBlockWithProject, memoryRecallSegment, onceLedger, sessionId });
603
632
  prepareMemoryEngineSession({ memoryEngineSession, internals, memoryDelegationEvidence, externalContentTargetActive, lockedPreflight, mcp, sessionId, runId, deps, provenanceForChildrenRef, delegationSettlementRef, spec, envHandToolNames, roster, contentOriginWrapRef });
604
633
  const { systemPromptSeat, renderWithDate, systemBlocks, promptManifest, epochDeclaredSections, epochArtifactDigestForSnapshot, buildAssembleInputs, centerAdoptionRef } = await preparePromptAssembly({ session, deps, internals, resume, provider, userSystemPrompt, userAppendSystemPrompt, featureFlags, mcpInstructionsBlock, memoryBlock, envFacts, promptProfile, fableMitigations, tools, model, sessionId, reminderMark, onceLedger });
@@ -710,26 +739,29 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
710
739
  releaseSignal = () => sig.removeEventListener("abort", onExternalAbort);
711
740
  }
712
741
  }
713
- const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, envHandToolNames, handsCwdRef, readDenyMatcher, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
714
- const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtectionCheck, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace, sessionBoxBound } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, shellReadStationArmed: shellReadCheck !== undefined, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, frameSettlement, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive: handsReadFace.readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined, fileHistoryFact, sessionBox });
742
+ const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, 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, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider: () => autoModeDeciderRef.current, permissionMode, scratchpadDir: spec.envFacts?.scratchpadDir, toolRosterDeltas: rosterSeat, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
743
+ const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtection, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace, sessionBoxBound } = prepareWiringManifest({ spec, deps, 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, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive: handsReadFace.readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined, fileHistoryFact, sessionBox });
744
+ const writeProtectionCheck = writeProtection?.check;
745
+ writeProtectionJudgeRef.current = writeProtection?.judgeTarget;
715
746
  if (fileHistoryBoundary !== undefined) {
716
747
  harness.on("tool_call", async () => {
717
748
  await fileHistoryBoundary.settle();
718
749
  return undefined;
719
750
  });
720
751
  }
721
- const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot: handsReadFace.grantReadRoot });
722
- const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, autoModeIntent, approverPosture: approverPostureResolved, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
752
+ const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking: autoModeDenialTrackingRef, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot: handsReadFace.grantReadRoot });
753
+ const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, permissionMode, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
723
754
  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 });
724
- const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas: rosterSeat, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef });
725
- prepareGateStations({ askLane, parkAsk, tools, toolRosterDeltas: rosterSeat, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, planModeRef, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, writeProtectionCheck, shellReadCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement });
755
+ 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, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef });
756
+ 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, permissionMode, toolMarkSources, ...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}), ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement });
726
757
  const postureFrameRef = {};
727
758
  const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, contextOverlay: legContextOverlay, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
728
759
  const preparedHolder = {};
729
760
  const { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved } = prepareTurnWiring({ deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, additionalRootsCanonical, additionalReadRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
730
761
  await onceLedger.settle(session, announcedListingsRef);
731
- const buildPrepared = () => ({ harness, session, sessionId, runId, registryAccess: { owner: hostTaskId, scope: taskScope }, reminderMark, reminderDisclosureCounts, fullShellReachable, approvalPosture: { autoMode: autoModeIntent, ...(approverPostureResolved !== undefined ? { approverPosture: approverPostureResolved } : {}) }, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, 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 } } : {}), planModeRef, stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, frameReceipts: { pending: [], listingSnapshotOwed: false }, 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 } : {}) });
762
+ const buildPrepared = () => ({ harness, session, sessionId, runId, registryAccess: { owner: hostTaskId, scope: taskScope }, reminderMark, reminderDisclosureCounts, fullShellReachable, approvalPosture: { get mode() { return permissionMode.current; }, askEffective: wiringManifest.ask.effective ?? "unresolved" }, 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, ...(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 } : {}) });
732
763
  rollback.commit();
764
+ commitModeSeats(abortController.signal);
733
765
  const prepared = buildPrepared();
734
766
  preparedHolder.current = prepared;
735
767
  return prepared;
@@ -29,10 +29,11 @@ import { type ToolResultStore } from "../tool-result-store.js";
29
29
  import { type OnAsk, type ToolPolicy } from "../tool-policy.js";
30
30
  import type { GateOutcome } from "../gate-outcome.js";
31
31
  import type { RunnerDeps, RuntimeCaps, TaskSpec, ToolEffect, ToolExecuteContext } from "../types.js";
32
- import type { WriteProtectionCheck } from "../write-protect.js";
32
+ import { type LegWriteProtection } from "./write-protection-station.js";
33
33
  import { type UsageWindow } from "../usage-window-store.js";
34
34
  import type { ToolRoster } from "../tool-roster.js";
35
35
  import { type AutoModeArmFact, type WiringManifest, type WiringManifestMcpEntry } from "../wiring-manifest.js";
36
+ import type { PermissionModeController } from "../mode-policy.js";
36
37
  import type { AnnounceOnceSink, Prepared, PrepareResume, RunInternals, UsageGovernance } from "./contracts.js";
37
38
  export interface PrepareWiringManifestInput {
38
39
  /** borrowed-readonly — the REBOUND spec. Read: `onAsk` (the own seat), `onQuestion` (the question seam), `handsReadOnly` / `tools` (the ungated scan),
@@ -119,8 +120,11 @@ export interface PrepareWiringManifestInput {
119
120
  /** borrowed-readonly — from the LSP phase's manager fold: gates the manifest's LSP section (absent ⇒ byte-identical manifest). */ lspManagerWired: boolean;
120
121
  /** borrowed-readonly — from the LSP phase: is the diagnostics lane live? The `lane` word's only input. */ lspDiagnosticsLive: boolean;
121
122
  /** borrowed-readonly — from the file-history phase's one fold of the two off switches: the manifest's word, relayed. */ fileHistoryFact: NonNullable<WiringManifest["fileHistory"]>;
122
- /** borrowed-readonly — the arming outcome the caps phase minted (the manifest's auto-mode face). */
123
- autoModeArmReason: AutoModeArmFact;
123
+ /** borrowed-readonly — the arming outcome the caps phase reads live (the manifest's auto-mode face at this leg's start). */
124
+ autoModeArmReason: () => AutoModeArmFact;
125
+ /** borrowed-readonly — the leg's live permission-mode seat: its receipt at this leg's start rides the manifest, and the
126
+ * peer lane reads the effective mode through the refs filled here. */
127
+ permissionMode: PermissionModeController;
124
128
  /** borrowed-readonly — the frozen preflight; only `mcp` (the declared servers the manifest face joins). */
125
129
  lockedPreflight: Pick<LockedPreflight, "mcp">;
126
130
  /** borrowed-mutable — the peer lane's late-bound facts. Writer here: `askEffective` ← the manifest's ask derivation (the
@@ -176,9 +180,9 @@ export interface PrepareWiringManifestResult {
176
180
  durableApproval: TaskSpec["durableApproval"];
177
181
  /** owned — this leg's EFFECTIVE wiring manifest (`Prepared.wiringManifest`). */
178
182
  wiringManifest: WiringManifest;
179
- /** owned — the leg's write-protection gate input, or undefined when the table was emptied (no judge, no
180
- * tighten mounted); handed to the gate station rather than compiled a second time there. */
181
- writeProtectionCheck: WriteProtectionCheck | undefined;
183
+ /** owned — the leg's write-protection judge, or undefined when the table was emptied (no judge, no tighten mounted):
184
+ * its gate input (`check`) for the gate station and its target judge for the shell station — one compile, never two. */
185
+ writeProtection: LegWriteProtection | undefined;
182
186
  /** owned — THE park predicate as one named const (`parkLane.effective === true`). */
183
187
  parkLaneArmed: boolean;
184
188
  /** owned — THE gate-machinery activation predicate as one named const: a policy, a screening hook, an egress or
@@ -116,7 +116,7 @@ function announceDurableGateUnavailable(args) {
116
116
  });
117
117
  }
118
118
  export function prepareWiringManifest(input) {
119
- const { spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, frameSettlement, sessionBox, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive, lspManagerWired, shellReadStationArmed, lspDiagnosticsLive, fileHistoryFact } = input;
119
+ const { spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, permissionMode, lockedPreflight, peerLaneRefs, frameSettlement, sessionBox, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive, lspManagerWired, shellReadStationArmed, lspDiagnosticsLive, fileHistoryFact } = input;
120
120
  const onAsk = spec.onAsk ?? deps.onAsk;
121
121
  const handWriteTools = toolRoster.entries.filter((e) => e.mountedBy === "hands" && (e.effect === "write" || e.effect === "idempotent")).map((e) => e.name);
122
122
  const hasEffectAwareGate = Boolean(policyLayers.length > 0 || (hooks?.preToolUse !== undefined && !preToolUseObservational));
@@ -210,7 +210,8 @@ export function prepareWiringManifest(input) {
210
210
  memoryAdmissionWired: deps.memoryScopeAdmission !== undefined,
211
211
  retentionPolicyWired: deps.retentionPolicy !== undefined,
212
212
  ...(modelGateManifest !== undefined ? { modelGate: modelGateManifest } : {}),
213
- autoMode: { ...autoModeArmReason },
213
+ autoMode: { ...autoModeArmReason() },
214
+ permissionMode: permissionMode.receipt(),
214
215
  mcp: mcpManifestEntries(lockedPreflight.mcp, mcp.statuses),
215
216
  ...(writeProtection !== undefined ? { writeProtectionTargetView: writeProtection.targetView } : {}),
216
217
  readDenyBuiltinTiersActive,
@@ -222,6 +223,7 @@ export function prepareWiringManifest(input) {
222
223
  fileHistory: fileHistoryFact,
223
224
  });
224
225
  peerLaneRefs.askEffective = wiringManifest.ask.effective;
226
+ peerLaneRefs.permissionMode = () => permissionMode.current;
225
227
  const sessionBoxBound = sessionBox !== undefined && internals?.onTaskNotification !== undefined ? sessionBox : undefined;
226
228
  if (sessionBoxBound !== undefined && internals?.onTaskNotification !== undefined) {
227
229
  bindPeerLaneDrain(harness, {
@@ -261,5 +263,5 @@ export function prepareWiringManifest(input) {
261
263
  });
262
264
  const hookContextConsumerWired = hooks?.preToolUse !== undefined || hooks?.postToolUse !== undefined || hooks?.postToolUseFailure !== undefined;
263
265
  const hookEnvFace = hookContextConsumerWired && (ownedEnv ?? deps.executionEnv) != null ? createHookEnvCapabilities(executionEnv) : undefined;
264
- return { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, platformSuspendArmed, durableApproval, wiringManifest, writeProtectionCheck: writeProtection?.check, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace, sessionBoxBound };
266
+ return { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, platformSuspendArmed, durableApproval, wiringManifest, writeProtection, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace, sessionBoxBound };
265
267
  }
@@ -25,7 +25,16 @@ export declare function rootAdmittingArtifactStore(storePhysical: string, rootsC
25
25
  * reader of the hosting seat) once the declaration validates. */
26
26
  export interface ArtifactStoreRef {
27
27
  current: string | undefined;
28
+ /** The person's STANDING read grants — their persisted `Read(//dir/**)` rows, joined to the live read set at prepare —
29
+ * held to the in-flight grant's store rule by {@link withdrawStandingGrantsAdmitting} once the store is known. */
30
+ standing?: {
31
+ readonly keys: readonly string[];
32
+ readonly readRoots: string[];
33
+ };
28
34
  }
35
+ /** The store rule applied to the standing grants: each one that admits `storePhysical` leaves the live read set (so no
36
+ * read face reaches the store through it); returns the withdrawn keys, for the caller to announce. */
37
+ export declare function withdrawStandingGrantsAdmitting(storePhysical: string, ref: ArtifactStoreRef): string[];
29
38
  /** Why the run's station applied no grant: the spelling did not resolve (or its lookup was cut), it no longer
30
39
  * resolves to itself (a directory swapped for a link while the card waited), or it would admit the local artifact
31
40
  * store (the store-isolation rule the Artifact phase judges its mount by). */
@@ -7,6 +7,18 @@ export function rootAdmittingArtifactStore(storePhysical, rootsCanonical) {
7
7
  const storeFamily = pathFamilyOf({ root: storePhysical });
8
8
  return rootsCanonical.find((root) => pathFamilyOf({ root }) === storeFamily && (withinAnyRoot([root], storePhysical) || withinAnyRoot([storePhysical], root)));
9
9
  }
10
+ export function withdrawStandingGrantsAdmitting(storePhysical, ref) {
11
+ const standing = ref.standing;
12
+ if (standing === undefined)
13
+ return [];
14
+ const withdrawn = standing.keys.filter((key) => rootAdmittingArtifactStore(storePhysical, [key]) !== undefined);
15
+ for (const key of withdrawn) {
16
+ const at = standing.readRoots.indexOf(key);
17
+ if (at >= 0)
18
+ standing.readRoots.splice(at, 1);
19
+ }
20
+ return withdrawn;
21
+ }
10
22
  export function readRootGrantOver(seats) {
11
23
  const { env, writeRoots, readRoots, artifactStore } = seats;
12
24
  return async (candidate, signal) => {
@@ -2,6 +2,7 @@ import { assertNoRetiredKeys } from "../retired-keys.js";
2
2
  const RETIRED_RUNNER_DEPS_KEYS = {
3
3
  legacyRewindBoundaryProbe: "the rewind refusal always names both possibilities (never existed / retired whole-tree era); a deployment that holds that era's table answers the question on its own surface",
4
4
  peerAdmission: "the peer lane's knob table is `RunnerDeps.peerLane` (the same fields: admission gate, delivery budgets, listing window)",
5
+ graderEnvFactory: "the grader env reaches the repair loop as `RepairLoopConfig.graderEnv`, minted by the trusted control plane (`assertOracleIsolation` / `GraderEnv` unchanged)",
5
6
  };
6
7
  export function assertNoRetiredRunnerDeps(deps) {
7
8
  assertNoRetiredKeys(deps, RETIRED_RUNNER_DEPS_KEYS, "RunnerDeps");
@@ -54,7 +54,9 @@ export function runBrainSinks(input) {
54
54
  ...(t.nextDelayMs !== undefined ? { nextDelayMs: t.nextDelayMs } : {}),
55
55
  ts: Date.now(),
56
56
  }
57
- : { kind: "vision.placeholder", version: 1, taskId: rs.telemetry.taskId, count: t.count, ts: Date.now() });
57
+ : t.kind === "echo_dropped"
58
+ ? { kind: "brain.echo_dropped", version: 1, taskId: rs.telemetry.taskId, field: t.field, ts: Date.now() }
59
+ : { kind: "vision.placeholder", version: 1, taskId: rs.telemetry.taskId, count: t.count, ts: Date.now() });
58
60
  };
59
61
  const withBrainSinks = (fn) => runWithStatusSink(statusEmit, () => runWithBrainTelemetry(telemetryEmit, () => runWithReasoningWireFacts(observeReasoningWireFacts, fn)));
60
62
  return { subagentName, withBrainSinks };
@@ -111,7 +111,7 @@ export function runIdentityWiring(input) {
111
111
  }
112
112
  }
113
113
  const loopLatch = { ended: false, userInterrupted: false, userHalted: false };
114
- onReady({ harness: prepared.harness, abortController: prepared.abortController, loop: loopLatch, reminderMark: prepared.reminderMark, sessionId: prepared.sessionId, runId: prepared.runId, hookTimeoutMs: prepared.hookTimeoutMs, hookIdentity: prepared.hookIdentity });
114
+ onReady({ harness: prepared.harness, abortController: prepared.abortController, loop: loopLatch, reminderMark: prepared.reminderMark, sessionId: prepared.sessionId, runId: prepared.runId, hookTimeoutMs: prepared.hookTimeoutMs, hookIdentity: prepared.hookIdentity, permissionMode: prepared.permissionMode });
115
115
  const stats = { turns: 0, tokens: 0, toolCalls: 0, promptTokens: 0, totalInputTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: 0, costMicroUsd: 0 };
116
116
  return { runSourceTaskId, parentToolCallId, ident, emitDelegationLifecycle, loopLatch, stats, internalsNotifier, undrainedUserAtEnd: { get current() { return undrainedUserAtEnd; } }, userInputLostAtEnd: { get current() { return userInputLostAtEnd; } } };
117
117
  }