@sema-agent/core 7.14.0 → 7.16.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 (136) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/core/ask-origin.d.ts +2 -2
  3. package/dist/core/checkpoint-store.d.ts +14 -15
  4. package/dist/core/governance-codes.js +2 -0
  5. package/dist/core/hooks.d.ts +4 -3
  6. package/dist/core/memory-engine/delegation-settlement.d.ts +37 -12
  7. package/dist/core/memory-engine/delegation-settlement.js +128 -39
  8. package/dist/core/memory-engine/engine.d.ts +9 -8
  9. package/dist/core/memory-engine/engine.js +115 -120
  10. package/dist/core/memory-engine/file-backend.d.ts +17 -5
  11. package/dist/core/memory-engine/file-backend.js +78 -34
  12. package/dist/core/memory-engine/index.d.ts +2 -2
  13. package/dist/core/memory-engine/index.js +2 -2
  14. package/dist/core/memory-engine/layout.d.ts +152 -54
  15. package/dist/core/memory-engine/layout.js +441 -104
  16. package/dist/core/memory-engine/types.d.ts +8 -3
  17. package/dist/core/memory-engine/types.js +5 -0
  18. package/dist/core/runner/abort-race.d.ts +3 -3
  19. package/dist/core/runner/active-skill-scope.d.ts +2 -2
  20. package/dist/core/runner/assemble-result.d.ts +29 -29
  21. package/dist/core/runner/checkpoint-scope.d.ts +4 -4
  22. package/dist/core/runner/clock-and-limits.d.ts +3 -3
  23. package/dist/core/runner/compaction-call-options.d.ts +11 -11
  24. package/dist/core/runner/compaction-knobs.d.ts +4 -4
  25. package/dist/core/runner/compaction-seams.d.ts +5 -5
  26. package/dist/core/runner/contracts.d.ts +224 -226
  27. package/dist/core/runner/decide-continuation.d.ts +6 -6
  28. package/dist/core/runner/denial-limit-arms.d.ts +8 -8
  29. package/dist/core/runner/derived-route-fallback.d.ts +2 -2
  30. package/dist/core/runner/gate-exit.d.ts +6 -6
  31. package/dist/core/runner/git-status-frame.d.ts +7 -7
  32. package/dist/core/runner/inherited-ask-grants.d.ts +6 -6
  33. package/dist/core/runner/memory-capture-optout.d.ts +1 -1
  34. package/dist/core/runner/memory-consolidation-driver.d.ts +4 -4
  35. package/dist/core/runner/memory-consolidation.d.ts +6 -6
  36. package/dist/core/runner/park-commit.d.ts +6 -6
  37. package/dist/core/runner/permission-rule-lanes.d.ts +22 -20
  38. package/dist/core/runner/permission-rule-lanes.js +1 -1
  39. package/dist/core/runner/prepare-acquire-reconcile.d.ts +4 -4
  40. package/dist/core/runner/prepare-ask-lane.d.ts +3 -3
  41. package/dist/core/runner/prepare-boundary-parks.d.ts +3 -3
  42. package/dist/core/runner/prepare-caps-and-workflow.d.ts +5 -5
  43. package/dist/core/runner/prepare-config-doors.d.ts +21 -21
  44. package/dist/core/runner/prepare-context-lane.d.ts +6 -6
  45. package/dist/core/runner/prepare-defer-classify.d.ts +3 -3
  46. package/dist/core/runner/prepare-defer-classify.js +12 -10
  47. package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
  48. package/dist/core/runner/prepare-file-history.d.ts +2 -2
  49. package/dist/core/runner/prepare-gate-stations.d.ts +8 -9
  50. package/dist/core/runner/prepare-gate-stations.js +2 -2
  51. package/dist/core/runner/prepare-hands-readface.d.ts +41 -41
  52. package/dist/core/runner/prepare-hands-readface.js +27 -29
  53. package/dist/core/runner/prepare-inherited-gate.d.ts +6 -6
  54. package/dist/core/runner/prepare-listings.d.ts +3 -3
  55. package/dist/core/runner/prepare-lsp.d.ts +3 -3
  56. package/dist/core/runner/prepare-memory-engine-session.d.ts +5 -5
  57. package/dist/core/runner/prepare-memory.js +6 -7
  58. package/dist/core/runner/prepare-offload-wrappers.d.ts +2 -2
  59. package/dist/core/runner/prepare-park-ask.d.ts +11 -12
  60. package/dist/core/runner/prepare-park-ask.js +3 -3
  61. package/dist/core/runner/prepare-policy-chain.d.ts +6 -6
  62. package/dist/core/runner/prepare-policy-chain.js +2 -2
  63. package/dist/core/runner/prepare-prompt-inputs.d.ts +5 -5
  64. package/dist/core/runner/prepare-protocol-tools.d.ts +1 -1
  65. package/dist/core/runner/prepare-question-face.d.ts +4 -4
  66. package/dist/core/runner/prepare-run-refs.d.ts +11 -11
  67. package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
  68. package/dist/core/runner/prepare-suspend-saga.d.ts +4 -4
  69. package/dist/core/runner/prepare-task.d.ts +12 -12
  70. package/dist/core/runner/prepare-task.js +5 -7
  71. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +2 -3
  72. package/dist/core/runner/prepare-wiring-manifest.d.ts +4 -4
  73. package/dist/core/runner/prepare-workspace-restore.d.ts +1 -1
  74. package/dist/core/runner/remote-env-retry.d.ts +3 -3
  75. package/dist/core/runner/resume-admission.d.ts +1 -1
  76. package/dist/core/runner/resume-claim.d.ts +1 -1
  77. package/dist/core/runner/resume-internals-and-config.d.ts +1 -1
  78. package/dist/core/runner/resume-policy-outcome.d.ts +1 -1
  79. package/dist/core/runner/resume-preflight.d.ts +1 -1
  80. package/dist/core/runner/resume-review-outcome.d.ts +1 -1
  81. package/dist/core/runner/run-brain-sinks.d.ts +1 -1
  82. package/dist/core/runner/run-clock-and-content.d.ts +4 -4
  83. package/dist/core/runner/run-compaction-machinery.d.ts +3 -3
  84. package/dist/core/runner/run-git-lane.d.ts +1 -1
  85. package/dist/core/runner/run-harness-handlers.d.ts +2 -2
  86. package/dist/core/runner/run-identity-wiring.d.ts +4 -4
  87. package/dist/core/runner/run-leg.d.ts +3 -3
  88. package/dist/core/runner/run-notification-lane.d.ts +2 -2
  89. package/dist/core/runner/run-reasoning-seat.d.ts +2 -2
  90. package/dist/core/runner/run-settle-and-teardown.d.ts +4 -4
  91. package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
  92. package/dist/core/runner/run-terminal-adoption.d.ts +5 -5
  93. package/dist/core/runner/runtask.d.ts +14 -14
  94. package/dist/core/runner/session-file-state-replay.d.ts +3 -3
  95. package/dist/core/runner/session-rule-policy.d.ts +2 -2
  96. package/dist/core/runner/steer-admission.d.ts +2 -2
  97. package/dist/core/runner/stream-halt-verbs.d.ts +1 -1
  98. package/dist/core/runner/stream-reap.d.ts +1 -1
  99. package/dist/core/runner/synthetic-tools.d.ts +8 -8
  100. package/dist/core/runner/teardown-bounded.d.ts +2 -2
  101. package/dist/core/runner/tool-defer-gate.d.ts +86 -0
  102. package/dist/core/runner/tool-defer-gate.js +57 -0
  103. package/dist/core/runner/tool-disclosure.d.ts +33 -69
  104. package/dist/core/runner/tool-disclosure.js +0 -43
  105. package/dist/core/runner/tool-end-body.d.ts +3 -3
  106. package/dist/core/runner/tool-face-overlay.d.ts +2 -2
  107. package/dist/core/runner/tool-output-projection.d.ts +2 -2
  108. package/dist/core/runner/turn-attachments.d.ts +53 -53
  109. package/dist/core/runner/usage-accounting.d.ts +4 -4
  110. package/dist/core/runner/workspace-path.d.ts +1 -1
  111. package/dist/core/runner-deps.d.ts +16 -9
  112. package/dist/core/task-spec.d.ts +8 -2
  113. package/dist/core/tool-spec.d.ts +7 -4
  114. package/dist/core/types.d.ts +1 -1
  115. package/dist/engine/llm/types.d.ts +1 -1
  116. package/dist/index.d.ts +4 -4
  117. package/dist/index.js +3 -3
  118. package/dist/tools/fs/bash-readonly-classifier.d.ts +40 -40
  119. package/dist/tools/fs/encoding.d.ts +31 -40
  120. package/dist/tools/fs/fs-bash.d.ts +76 -35
  121. package/dist/tools/fs/fs-bash.js +86 -29
  122. package/dist/tools/fs/fs-pdf.d.ts +8 -8
  123. package/dist/tools/fs/fs-search-tools.d.ts +4 -4
  124. package/dist/tools/fs/fs-shared.d.ts +144 -158
  125. package/dist/tools/fs/fs-write.d.ts +8 -8
  126. package/dist/tools/fs/gh-rate-limit.d.ts +13 -14
  127. package/dist/tools/fs/index.d.ts +74 -78
  128. package/dist/tools/fs/notebook.d.ts +24 -25
  129. package/dist/tools/fs/pdf.d.ts +3 -3
  130. package/dist/tools/fs/read-deny.d.ts +22 -22
  131. package/dist/tools/fs/read-face.d.ts +21 -24
  132. package/dist/tools/fs/repo-map.d.ts +8 -8
  133. package/dist/tools/fs/safety.d.ts +116 -129
  134. package/dist/tools/fs/search.d.ts +63 -71
  135. package/package.json +5 -2
  136. package/test/export-surface.snapshot.json +9 -1
@@ -1,6 +1,6 @@
1
1
  import { type BeforeWriteHook, type CwdRef, type ReadDenyMatcher, type ReadFace } from "../../tools/fs/index.js";
2
2
  import { type ReadFileState } from "../../tools/fs/safety.js";
3
- import type { RunnerDeps, TaskSpec } from "../types.js";
3
+ import type { RunnerDeps, ShellGateDoctrine, TaskSpec } from "../types.js";
4
4
  import type { Model } from "../../internal/llm.js";
5
5
  import type { AgentTool, ExecutionEnv, WorkspaceState } from "../../internal/harness.js";
6
6
  import type { AdvertisedWritableDirs } from "./advertised-writable-dirs.js";
@@ -11,10 +11,10 @@ import { RosterBuilder } from "../tool-roster.js";
11
11
  * Deliberately asymmetric — it resets only the console latch: the WeakSet arm needs no seam
12
12
  * because a test resets it by minting a fresh sink function (identity IS the ledger key), while
13
13
  * the console arm's key is the process itself, which only this seam can refresh. Moved WITH the
14
- * phase (design/238 B-5); prepare-task.ts keeps a re-export so the existing import sites stand. */
14
+ * phase; prepare-task.ts keeps a re-export so the existing import sites stand. */
15
15
  export declare function __resetReadFaceClampAnnouncement(): void;
16
16
  /**
17
- * #483 rung-1 — the Runner's per-session read-file-state seats (CC parity: readFileState is minted
17
+ * The Runner's per-session read-file-state seats (CC parity: readFileState is minted
18
18
  * once per REPL session and mutated across turns; it has NO turn-boundary and NO abort clear site —
19
19
  * its only clears are compaction/fork/subagent-cleanup/dispose). sema's map stays per-task (the §11
20
20
  * ruling-A closure wiring), so the SESSION scope lives here instead: prepare adopts each task's live
@@ -30,17 +30,17 @@ export declare function __resetReadFaceClampAnnouncement(): void;
30
30
  * Bounded: an LRU over sessionIds (insertion-order refresh on read), because the SessionStore's own
31
31
  * eviction is not observable from here — a dropped seat costs one transcript replay, nothing else.
32
32
  * The stored value is the task's LIVE map (object identity), so in-turn mutations — tool write-backs,
33
- * and above all the RB-197 compaction clear — are the seat's truth by construction; a snapshot copy
33
+ * and above all the compaction clear — are the seat's truth by construction; a snapshot copy
34
34
  * here would resurrect summarized-away reads on the next turn.
35
35
  *
36
- * BRANCH BINDING (codex r1+r2, adopted): a sessionId alone does not name a transcript — the same id
36
+ * BRANCH BINDING: a sessionId alone does not name a transcript — the same id
37
37
  * can be rebranched (`resumeAt`), rewound out-of-band, or DELETED and re-created (`release()` + the
38
38
  * default create-on-miss acquire), and the seat's map would then vouch for reads the CURRENT branch
39
39
  * never saw. Each seat therefore records `anchorLeafId` — a leaf on the lineage its task produced —
40
40
  * and inheritance must first prove the CURRENT branch still descends from that anchor
41
41
  * (see {@link sessionSeatAnchorHolds}). The anchor is written twice: at ADOPT (the leaf at prepare;
42
42
  * `null` when the branch was still empty) and SEALED at the task's settle to the task's own tail
43
- * (`reanchor`). A `null` anchor is NEVER inheritable (codex r2: "the branch is non-empty now" proves
43
+ * (`reanchor`). A `null` anchor is NEVER inheritable ("the branch is non-empty now" proves
44
44
  * nothing about WHOSE messages those are — another Runner repopulating a released id, or a session
45
45
  * import, makes a non-empty branch that never contained this seat's reads). An unprovable anchor
46
46
  * falls back to the transcript replay, which reads the current branch by construction — cost, never
@@ -59,14 +59,14 @@ export declare class SessionReadFileStates {
59
59
  * `anchorLeafId` = the session's leaf as read at THIS prepare (null = the branch was empty; a
60
60
  * null-anchored seat is not inheritable until the settle-time {@link reanchor} names a real leaf). */
61
61
  adopt(sessionId: string, state: ReadFileState, anchorLeafId: string | null): void;
62
- /** #483 codex r2 — the settle-time SEAL: re-anchor the seat to the finished task's own tail, so a
62
+ /** The settle-time SEAL: re-anchor the seat to the finished task's own tail, so a
63
63
  * first-task (null-anchor) seat becomes inheritable only through a leaf its task provably wrote.
64
64
  * Identity-guarded: touches the row only while it still holds THIS task's map (a later prepare on
65
65
  * the same session has already replaced the seat and owns its own anchor). */
66
66
  reanchor(sessionId: string, state: ReadFileState, anchorLeafId: string): void;
67
67
  }
68
68
  /**
69
- * #483 (codex r1+r2) — does the session's CURRENT branch still descend from the seat's anchor?
69
+ * Does the session's CURRENT branch still descend from the seat's anchor?
70
70
  * TRUE is the licence to inherit; anything unprovable answers FALSE (the caller then replays the
71
71
  * transcript — the fail-closed direction costs a rebuild, never admits a wrong entry).
72
72
  *
@@ -82,17 +82,17 @@ export declare class SessionReadFileStates {
82
82
  * - a COMPACTION on the walked segment (A-082 idx2): refuses, even though the branch is perfectly
83
83
  * intact. The walked segment is exactly what landed AFTER this seat was anchored, and a
84
84
  * compaction there summarized older Read results OUT of the context the model will be shown —
85
- * the very state RB-197 clears from the live map at the landing site. That clear reaches the seat
85
+ * the very state the compaction clear removes from the live map at the landing site. That clear reaches the seat
86
86
  * only when the SAME Runner did both (the seat IS that map, by object identity); a second Runner
87
87
  * on the shared durable session clears its own map and commits the compaction as an ORDINARY
88
88
  * LINEAR APPEND, which every other test here passes. Inheriting across it would let requireRead
89
89
  * and checkStale both pass on a read whose card the model no longer has — a blind edit from a
90
- * summarized-away recollection, the exact failure the RB-197 clear exists to prevent. Refusing
90
+ * summarized-away recollection, the exact failure the compaction clear exists to prevent. Refusing
91
91
  * costs one transcript replay, and the replay reads the POST-compaction branch by construction,
92
92
  * so the fail-closed direction is also the CORRECT state. A compaction at or ABOVE the anchor is
93
93
  * not on the walked segment and does not refuse: the seat that observed it already carries its
94
94
  * clear (P-483-4), and an ordinary foreign turn with no compaction still inherits (G-483-5) —
95
- * dropping every seat a foreign Runner touched would restore the cross-turn amnesia #483 ended.
95
+ * dropping every seat a foreign Runner touched would restore the cross-turn read-state amnesia this seat-anchor check already ended.
96
96
  */
97
97
  export declare function sessionSeatAnchorHolds(getEntry: (id: string) => Promise<{
98
98
  parentId: string | null;
@@ -118,8 +118,8 @@ export interface PrepareHandsReadFaceInput {
118
118
  * resume; the workspace_state rung is skipped; the face fold goes stricter-wins). */
119
119
  resume: Pick<PrepareResume, "seed"> | undefined;
120
120
  /** borrowed-readonly — the acquired session (B-3 output), as a Pick (相 API 规则件 R-4: receiver
121
- * preserved): `getWorkspaceState` feeds the design/155 continuation rung; `buildContext` feeds
122
- * the transcript replay; `getStorage`/`getEntry` feed the #483 seat-anchor verification (one
121
+ * preserved): `getWorkspaceState` feeds the continuation rung; `buildContext` feeds
122
+ * the transcript replay; `getStorage`/`getEntry` feed the seat-anchor verification (one
123
123
  * leaf read per prepare + a bounded parent walk on the inherit leg only). */
124
124
  session: Pick<StoredSession, "getWorkspaceState" | "buildContext" | "getStorage" | "getEntry">;
125
125
  /** borrowed-readonly — the acquired session's id, for error/notice tags and the band's session
@@ -136,9 +136,9 @@ export interface PrepareHandsReadFaceInput {
136
136
  fullShellReachable: boolean;
137
137
  /** borrowed-readonly — the MAX-RANK fold of spec.shellGate with the inherited chain's doctrine
138
138
  * (computed in the driver's inherited-gate block); the shellGate fold's doctrine value. */
139
- effectiveShellGate: "off" | "classify" | "always";
139
+ effectiveShellGate: ShellGateDoctrine;
140
140
  /** borrowed-readonly — B-1's frozen task-start face snapshot; only `.exclude` is read here (the
141
- * Monitor-mount honesty bit for the gh rate-limit hint, #181-F6). */
141
+ * Monitor-mount honesty bit for the gh rate-limit hint). */
142
142
  toolFaceSnapshot: ToolFaceSnapshot;
143
143
  /** borrowed-readonly — B-1's resolved main model; read ONLY for its pdf capability profile
144
144
  * (the Read tool's document-degradation fork). */
@@ -149,39 +149,39 @@ export interface PrepareHandsReadFaceInput {
149
149
  /** borrowed-readonly — the deployment seats this phase reads, as a Pick over the SAME deps
150
150
  * object (R-4: `deps.onError?.()` keeps its receiver; a new deps read must widen this Pick).
151
151
  * `readDenyBuiltinTiers`/`readDenyBuiltinExclude` are SNAPSHOTTED before the first await that
152
- * precedes the toolkit compile (#245/#244 — the caller retains these arrays by reference). */
152
+ * precedes the toolkit compile (the caller retains these arrays by reference). */
153
153
  deps: Pick<RunnerDeps, "onError" | "onNotice" | "tracer" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude" | "readFace" | "permissionRuleStore" | "hands">;
154
154
  /** borrowed-readonly — the trusted spawn-side seats the band forwards: the finished-command
155
- * notification lane, the mid-flight detach hub, and (#483 rung-1) the Runner's per-session
155
+ * notification lane, the mid-flight detach hub, and the Runner's per-session
156
156
  * read-state seats (absent on a standalone prepare ⇒ the transcript-replay rung covers alone). */
157
157
  internals: Pick<RunInternals, "onTaskNotification" | "detachHub" | "sessionReadStates"> | undefined;
158
- /** borrowed-readonly (A-082 idx7) — ABSOLUTE paths this prepare's design/381 restore wrote, deleted,
158
+ /** borrowed-readonly — ABSOLUTE paths this prepare's restore wrote, deleted,
159
159
  * or failed to write. Every read-state seed above describes the PRE-restore tree, so these keys are
160
160
  * dropped from the map: the engine replaced those bytes itself, and a surviving entry makes the
161
161
  * next refusal blame "the user or a linter" for the engine's own write. Absent (the common case)
162
162
  * when no file restore ran. Fail-closed by construction — dropping a read record only ever
163
163
  * produces refusals. */
164
164
  restoredFilePaths?: readonly string[];
165
- /** borrowed-readonly — (ref cell; the REF itself is never replaced) design/138 S2-C memory write gate:
165
+ /** borrowed-readonly — (ref cell; the REF itself is never replaced) memory write gate:
166
166
  * the band's beforeWrite closure reads `.current` call-time; prepareMemory (driver, BEFORE this phase
167
- * since #691 — the memory root must be known before the toolkit compiles) installs the gate — the one
167
+ * since the memory root must be known before the toolkit compiles) installs the gate — the one
168
168
  * writer; this phase only reads. Declared in the driver because the SAME ref feeds prepareMemory. */
169
169
  memoryWriteGateRef: {
170
170
  current?: BeforeWriteHook;
171
171
  };
172
- /** borrowed-mutable — #691: the advertised-writable-directory seat (see its module header for the one
172
+ /** borrowed-mutable — the advertised-writable-directory seat (see its module header for the one
173
173
  * rule). Present on the hands leg only (a fence exists). The memory phase, which runs BEFORE this one,
174
174
  * has already admitted its write root through it; this phase admits the deployment's scratchpad and
175
175
  * folds `roots` into the write-capable containment allowlist before the toolkit compiles. */
176
176
  advertisedWritableDirs?: AdvertisedWritableDirs;
177
- /** borrowed-readonly — the driver's per-tool offload wrapper (design/158 posture + policy); the
177
+ /** borrowed-readonly — the driver's per-tool offload wrapper (posture + policy); the
178
178
  * band rides through it exactly as caller/MCP tools do. */
179
179
  firstPartyOffload: (tool: AgentTool) => AgentTool;
180
180
  /** borrowed-mutable — the SHARED mount array (§4.4: later refs alias it; identity is the
181
181
  * contract, never map/copy). This phase pushes the hands band IN PLACE (after caller tools /
182
182
  * built-ins / MCP / A2A, before the background-task mounts — T22 order). Mutation owners after
183
183
  * this phase: P11 background/Monitor/worktree pushes, the driver's excludeTools splice, the
184
- * refresh seam's runtime splice/push. Since design/388 the array is builder-private: this phase `mountAll`s the band. */
184
+ * refresh seam's runtime splice/push. The array is builder-private: this phase `mountAll`s the band. */
185
185
  roster: RosterBuilder;
186
186
  /** borrowed-readonly HERE (census divergence #5 vs the design row): this phase only reads
187
187
  * `.has("Bash")`/`.has("Monitor")` for the provenance bits — its mutation owners are B-2/P8/P9
@@ -197,16 +197,16 @@ export interface PrepareHandsReadFaceInput {
197
197
  /** borrowed-mutable — the classify doctrine's probe installs (`.set`, only where the doctrine
198
198
  * owns the seat). Same-object contract as above. */
199
199
  reversibilityProbes: ReversibilityProbes;
200
- /** borrowed-readonly — design/319 (A ticket): the run's reminder provenance mark (minted/adopted
200
+ /** borrowed-readonly — the run's reminder provenance mark (minted/adopted
201
201
  * by the driver's adoption ladder); threaded into the hands band so the fs-side engine mints
202
202
  * (the Read cyber/dedup/offset/empty reminders, the gh rate-limit hint) stamp the same mark the
203
203
  * system-prompt declaration names. */
204
204
  reminderMark: string;
205
- /** borrowed-mutable — design/319 (B ticket): the leg's disclosure trigger counters; the hands
205
+ /** borrowed-mutable — the leg's disclosure trigger counters; the hands
206
206
  * band's Read/notebook/PDF trailer outlets bump keys on it at tool-execute time (the driver owns
207
207
  * the object and folds it into result stats). */
208
208
  reminderDisclosureCounts: import("../reminder-disclosure.js").ReminderDisclosureCounts;
209
- /** borrowed-readonly — design/381: the first-touch history hook prepare built (present iff a fileHistoryStore is
209
+ /** borrowed-readonly — the first-touch history hook prepare built (present iff a fileHistoryStore is
210
210
  * wired and the run mounts a real fs env); threaded to the write band's trackFileEdit seat. */
211
211
  trackFileEdit?: import("../types.js").TrackFileEditHook;
212
212
  /** borrowed-readonly — the mutation lane's LANDED observation seat (the accumulator behind
@@ -233,11 +233,11 @@ export interface PrepareHandsReadFaceResult {
233
233
  path: string;
234
234
  content: string;
235
235
  }>) => Promise<void>) | undefined;
236
- /** owned — design/99 §E13: the per-task logical cwd ref (observable `cd`), minted when a real
236
+ /** owned — the per-task logical cwd ref (observable `cd`), minted when a real
237
237
  * writable shell mounts. The driver stamps it into checkpoints, threads it into worktree tools
238
238
  * and gate cwd reads; tools mutate `.current` at run time. Undefined when no shell / readOnly. */
239
239
  handsCwdRef: CwdRef | undefined;
240
- /** owned — design/155: the settle-side snapshot base (what the cwd/worktree refs were RESTORED
240
+ /** owned — the settle-side snapshot base (what the cwd/worktree refs were RESTORED
241
241
  * to), so runtask's settle write diffs against it. The driver's G4 worktree mount writes
242
242
  * `.restoredWorktreeDir` after this phase — object identity carried. */
243
243
  workspaceStateSettle: {
@@ -246,7 +246,7 @@ export interface PrepareHandsReadFaceResult {
246
246
  restoredWorktreeDir?: string;
247
247
  baselineUnknown?: boolean;
248
248
  } | undefined;
249
- /** owned — design/155: the branch-nearest `workspace_state` entry, fetched ONCE here and shared
249
+ /** owned — the branch-nearest `workspace_state` entry, fetched ONCE here and shared
250
250
  * with the driver's worktree seed (the G4 mount reads `.activeWorktree` through it). */
251
251
  wsSnapshot: WorkspaceState | undefined;
252
252
  /** owned — the wsSnapshot path rebaser (identity when no snapshot / same root); the G4 worktree
@@ -266,7 +266,7 @@ export interface PrepareHandsReadFaceResult {
266
266
  pattern: string;
267
267
  caseSensitive: boolean;
268
268
  }>;
269
- /** write-back (D-3 hold) — the hands-LESS legs' resolved face (#242: the read posture is a
269
+ /** write-back (D-3 hold) — the hands-LESS legs' resolved face (the read posture is a
270
270
  * property of the RUN, not the mount). Driver assigns onto its outer `let`. */
271
271
  handsLessResolvedFace: ReadFace | undefined;
272
272
  /** owned — the canonicalized write-capable extra roots (additionalDirectories + the advertised
@@ -276,19 +276,19 @@ export interface PrepareHandsReadFaceResult {
276
276
  /** owned — the read-only arm of the fence widening (2026-08-05 both-arms ruling); same identity
277
277
  * contract as above. */
278
278
  additionalReadRootsCanonical: string[];
279
- /** owned — RB-159: the canonical containment root, published so the working-file ATTACHMENT
279
+ /** owned — the canonical containment root, published so the working-file ATTACHMENT
280
280
  * reader applies the same containment the hands tools do. Undefined ⇔ hands-less. */
281
281
  attachmentRootCanonical: string | undefined;
282
- /** owned — design/199 件B: the ONE compiled deny judge per prepare (built-ins ∪ deployment ∪
282
+ /** owned — the ONE compiled deny judge per prepare (built-ins ∪ deployment ∪
283
283
  * task additions). Undefined ⇔ hands-less (its consumers only exist with hands). */
284
284
  readDenyMatcher: ReadDenyMatcher | undefined;
285
- /** write-back"the shellGate doctrine is the SOLE POSITIVE source of Bash's tier" provenance
286
- * bit; the driver's `let` (risk grading + the persisted-rule mandate boundary read it
287
- * call-time). Written ONLY by this phase; false when the fold did not run. */
288
- shellGatedBash: boolean;
289
- /** write-back the Monitor twin of the bit above. */
290
- shellGatedMonitor: boolean;
291
- /** owned (design/380 rescan C4) — the ENV-HAND tool names, minted AT THE MOUNT: every tool this
285
+ /** owned — the shell-seat PROVENANCE set: the shell-running tools whose tier the engine's shell seat (read
286
+ * boundary + shellGate doctrine) is the SOLE POSITIVE source of no explicit deployment mark on that name.
287
+ * The one seat every consumer reads (risk grading, the gate's tighten attribution, the persisted-rule mandate
288
+ * boundary): `shellGatedTools.has(toolName)`. Minted here, never grown afterwards; empty when the seat fold
289
+ * did not run (no real writable shell). */
290
+ shellGatedTools: ReadonlySet<string>;
291
+ /** owned — the ENV-HAND tool names, minted AT THE MOUNT: every tool this
292
292
  * phase mounted over the ExecutionEnv (the fs/bash band), later grown by the driver's own env
293
293
  * mounts (Monitor, the worktree pair — the SAME Set identity, mutated at those mount sites).
294
294
  * Consumed by the O9a external-content-target fold: these are the tools whose RESULTS are the
@@ -297,12 +297,12 @@ export interface PrepareHandsReadFaceResult {
297
297
  * misses Monitor/worktree, which are hands — both directions wrong for this consumer). Empty on
298
298
  * a hands-less leg (nothing mounts on the env; the driver's env mounts are hands-gated too). */
299
299
  envHandToolNames: Set<string>;
300
- /** owned (#483 codex r2+r3) — the settle-time SEAT SEAL: re-anchors the session seat to the
300
+ /** owned — the settle-time SEAT SEAL: re-anchors the session seat to the
301
301
  * task's OWN last-committed entry (see {@link SessionReadFileStates.reanchor}). The driver calls
302
302
  * it once at the run's settle (every terminal, suspend included), under the session lock, passing
303
303
  * the last entryId ITS OWN run committed (the message_committed lane) — never a re-read of the
304
304
  * shared session leaf, which a concurrent writer on another Runner could have moved onto a branch
305
- * this task's reads were never on (codex r3). No commits observed ⇒ no-op (the adopt-time anchor
305
+ * this task's reads were never on. No commits observed ⇒ no-op (the adopt-time anchor
306
306
  * stands). Synchronous, never throws. Undefined ⇔ hands-less / no seat channel. */
307
307
  sealReadStateSeat: ((ownCommittedTailEntryId: string | undefined) => void) | undefined;
308
308
  }
@@ -2,7 +2,7 @@ import { emitTrace } from "../trace.js";
2
2
  import { StubExecutionEnv } from "../stub-env.js";
3
3
  import { hasBackgroundShell } from "../background-shell.js";
4
4
  import { defaultTaskRegistry } from "../task-registry.js";
5
- import { bashReversibilityProbe, compileReadDeny, createHandsToolkit, deploymentReadFaceClampNotice, pdfModelCapabilitiesOf, resolveReadDenyBuiltins, inheritSessionReadFileState, resolveReadFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, } from "../../tools/fs/index.js";
5
+ import { bashReadBoundaryProbe, bashReversibilityProbe, compileReadDeny, createHandsToolkit, deploymentReadFaceClampNotice, pdfModelCapabilitiesOf, resolveReadDenyBuiltins, inheritSessionReadFileState, resolveReadFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, } from "../../tools/fs/index.js";
6
6
  import { resolveKey, sha256 } from "../../tools/fs/safety.js";
7
7
  import { normalizeFileText } from "../../tools/fs/encoding.js";
8
8
  import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
@@ -117,8 +117,7 @@ export function resolveHandsLessReadFace(input) {
117
117
  attachmentRootCanonical: undefined,
118
118
  readDenyMatcher: undefined,
119
119
  envHandToolNames: new Set(),
120
- shellGatedBash: false,
121
- shellGatedMonitor: false,
120
+ shellGatedTools: new Set(),
122
121
  sealReadStateSeat: undefined,
123
122
  };
124
123
  }
@@ -140,8 +139,7 @@ export async function prepareHandsMount(input) {
140
139
  let resolvedReadFace;
141
140
  let readDenyAdditionsNormalized = [];
142
141
  let handsLessResolvedFace;
143
- let shellGatedBash = false;
144
- let shellGatedMonitor = false;
142
+ const shellGatedTools = new Set();
145
143
  const additionalRootsCanonical = [];
146
144
  const additionalReadRootsCanonical = [];
147
145
  let attachmentRootCanonical;
@@ -345,34 +343,35 @@ export async function prepareHandsMount(input) {
345
343
  }
346
344
  roster.mountAll(band.map((t) => (envHandToolNames.add(t.name), firstPartyOffload(t))), { source: "builtin", mountedBy: "hands" });
347
345
  const shellGate = effectiveShellGate;
348
- if (shellGate === "off" && !(executionEnv instanceof StubExecutionEnv) && spec.handsReadOnly !== true) {
349
- deps.onError?.(new Error(`shell gate doctrine is "off" while a real writable shell (Bash) is mounted — no shell safety-axis ` +
350
- `fold applies to this run (commands are adjudicated by the ordinary policy/hook chain only). ` +
351
- `Set spec.shellGate to "classify" or "always" if this deployment expects doctrine-gated shell behavior.`), { phase: "config", sessionId, classification: "shell-gate-off" });
352
- }
353
- if (shellGate !== "off" && !(executionEnv instanceof StubExecutionEnv) && spec.handsReadOnly !== true) {
354
- const bashTierBefore = irreversibilityTier.get("Bash");
355
- shellGatedBash = !egressTools.has("Bash") && bashTierBefore !== "always" && bashTierBefore !== "maybe";
356
- const bashEffectiveTier = shellGate === "always" || bashTierBefore === "always" ? "always" : "maybe";
357
- irreversibilityTier.set("Bash", bashEffectiveTier);
358
- irreversibleTools.add("Bash");
346
+ if (!(executionEnv instanceof StubExecutionEnv) && spec.handsReadOnly !== true) {
347
+ if (shellGate === "off") {
348
+ deps.onError?.(new Error(`shell gate doctrine is "off" while a real writable shell (Bash) is mounted the read boundary is ` +
349
+ `still judged on every command (a deny-listed or out-of-root read asks; the ask is mandated), but no ` +
350
+ `residual shell-risk classifier is installed: writes, egress and unknown commands are adjudicated by ` +
351
+ `the ordinary policy/hook chain only. Set spec.shellGate to "classify" or "always" if this deployment ` +
352
+ `expects doctrine-gated shell behavior.`), { phase: "config", sessionId, classification: "shell-gate-off" });
353
+ }
359
354
  const shellReadBoundary = () => ({
360
355
  roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical],
361
356
  ...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}),
362
357
  denyMatch: (p) => readDenyMatcher?.matchPath(p)?.pattern ?? null,
363
358
  ...(resolvedReadFace !== undefined ? { face: resolvedReadFace } : {}),
364
359
  });
365
- if (shellGate === "classify" && shellGatedBash)
366
- reversibilityProbes.set("Bash", bashReversibilityProbe(undefined, shellReadBoundary));
367
- if (backgroundTaskToolsActive) {
368
- const monitorTierBefore = irreversibilityTier.get("Monitor");
369
- shellGatedMonitor = !egressTools.has("Monitor") && monitorTierBefore !== "always" && monitorTierBefore !== "maybe";
370
- const monitorEffectiveTier = shellGate === "always" || monitorTierBefore === "always" ? "always" : "maybe";
371
- irreversibilityTier.set("Monitor", monitorEffectiveTier);
372
- irreversibleTools.add("Monitor");
373
- if (shellGate === "classify" && shellGatedMonitor)
374
- reversibilityProbes.set("Monitor", bashReversibilityProbe(undefined, shellReadBoundary));
375
- }
360
+ const installShellSeat = (name) => {
361
+ const tierBefore = irreversibilityTier.get(name);
362
+ const seatOwned = !egressTools.has(name) && tierBefore !== "always" && tierBefore !== "maybe";
363
+ if (seatOwned)
364
+ shellGatedTools.add(name);
365
+ const tier = shellGate === "always" || tierBefore === "always" ? "always" : "maybe";
366
+ irreversibilityTier.set(name, tier);
367
+ irreversibleTools.add(name);
368
+ if (seatOwned && tier === "maybe") {
369
+ reversibilityProbes.set(name, shellGate === "classify" ? bashReversibilityProbe(undefined, shellReadBoundary) : bashReadBoundaryProbe(shellReadBoundary));
370
+ }
371
+ };
372
+ installShellSeat("Bash");
373
+ if (backgroundTaskToolsActive)
374
+ installShellSeat("Monitor");
376
375
  }
377
376
  }
378
377
  return {
@@ -391,8 +390,7 @@ export async function prepareHandsMount(input) {
391
390
  attachmentRootCanonical,
392
391
  readDenyMatcher,
393
392
  envHandToolNames,
394
- shellGatedBash,
395
- shellGatedMonitor,
393
+ shellGatedTools,
396
394
  sealReadStateSeat,
397
395
  };
398
396
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * design/390 §1.2 M3b — prepareTask's INHERITED-GATE phase, verbatim from the driver: the live/seed fold of the
2
+ * PrepareTask's INHERITED-GATE phase, verbatim from the driver: the live/seed fold of the
3
3
  * chain this run inherits (ancestor rules rev-monotonic per (session, principal), shellGate by max rank, the
4
4
  * re-supplied parent constraints with their rebuilt denial trackers, the auto-mode intent, the org-admission
5
5
  * freeze with its tighten-only intersection and the monotonic governed bit), the effective shellGate against the
@@ -8,11 +8,11 @@
8
8
  * FROZEN ask and question seats (spec over deps), the two delegation refs the memory phase binds late, the ONE
9
9
  * child-chain assembly `inheritedGateForChildren`, the caller-tool ctx enrichment `enrichSpecToolCtx` shared by
10
10
  * the roster wrap and the revival spawner, the denial-limit stop seat, and the full-shell reachability predicate.
11
- * The interface is the dependency list the segment had implicitly (design/238 R-1).
11
+ * The interface is the dependency list the segment had implicitly.
12
12
  *
13
13
  * SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await (the one `await` in it sits inside the
14
14
  * `hostSessionFork` closure a tool calls later), so the phase adds no yield between the run-refs phase before it
15
- * and the caps phase after it (design/390 §1.5: a stretch with no await is extracted as a sync function).
15
+ * and the caps phase after it (a stretch with no await is extracted as a sync function).
16
16
  *
17
17
  * LATE-BOUND SEATS. The closures minted here run long after this phase returns — at spawn time, at tool-call
18
18
  * time, at suspend time — and several of the facts they read are resolved by LATER stations of the same prepare:
@@ -25,7 +25,7 @@
25
25
  * sat behind a short-circuit). Read-stability of the host handles for the call: {@link RunInternals}
26
26
  * (`@contract prepare.deps-read-stable`).
27
27
  *
28
- * Every cell that leaves here as `borrowed-mutable` carries its writer table on the Result member (design/238 R-3).
28
+ * Every cell that leaves here as `borrowed-mutable` carries its writer table on the Result member.
29
29
  */
30
30
  import type { AgentHarness, ExecutionEnv } from "../../internal/harness.js";
31
31
  import type { SubagentWorktreeIsolation } from "../../agents/subagent.js";
@@ -43,7 +43,7 @@ import type { DelegationContentSafety } from "../memory-engine/delegation-proven
43
43
  import type { SessionPermissionRules } from "../session-policy-store.js";
44
44
  import type { SessionStore } from "../session.js";
45
45
  import { type OnAsk } from "../tool-policy.js";
46
- import type { RunnerDeps, RuntimeCaps, TaskEvent, TaskSpec, ToolExecuteContext } from "../types.js";
46
+ import type { RunnerDeps, RuntimeCaps, ShellGateDoctrine, TaskEvent, TaskSpec, ToolExecuteContext } from "../types.js";
47
47
  import type { ActiveSkillScope } from "./active-skill-scope.js";
48
48
  import type { InheritedGate, Prepared, PrepareResume, RunInternals, ToolFaceSnapshot } from "./contracts.js";
49
49
  import { type StopForDenialLimit } from "./denial-limit-arms.js";
@@ -194,7 +194,7 @@ export interface PrepareInheritedGateResult {
194
194
  /** owned — the monotonic governed-provenance bit for this leg. */
195
195
  orgGovernedProvenance: boolean;
196
196
  /** owned — the spec's own shellGate tightened by the inherited doctrine. */
197
- effectiveShellGate: "off" | "classify" | "always";
197
+ effectiveShellGate: ShellGateDoctrine;
198
198
  /** borrowed-mutable — THIS task's own session-rule snapshot for its children's chain. Writers (R-3): the policy-chain
199
199
  * station's session-policy read, once per leg, when a store is wired and the task carries its own sessionId. Read by
200
200
  * `inheritedGateForChildren` at spawn time. */
@@ -1,13 +1,13 @@
1
1
  /**
2
- * design/390 M15 — prepareTask's LISTINGS phase: the per-task agent definitions' tool-name audit
2
+ * PrepareTask's LISTINGS phase: the per-task agent definitions' tool-name audit
3
3
  * (deny entries fail loud, allow entries advise), the agent-listing announce face read off the
4
4
  * delegation tool's roster (with its deferred / excluded / removal-to-zero arms), the two listing
5
5
  * opt-out advisories, the announced-listings mirror seeded from the once-per-session ledger, the
6
6
  * deferred roster's ToolSearch ride, and the date_change seam. The body is the driver's stretch moved
7
- * verbatim; the interface is the dependency list it had implicitly (design/238 R-1).
7
+ * verbatim; the interface is the dependency list it had implicitly.
8
8
  *
9
9
  * SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await, so the phase adds no yield between
10
- * the disclosure mount before it and the context-lane seats after it (design/390 §1.5 S1: a stretch
10
+ * the disclosure mount before it and the context-lane seats after it (a stretch
11
11
  * with no await is extracted as a sync function — an `async` wrapper would open a microtask window).
12
12
  *
13
13
  * Throws pass through unchanged: `config.agent.unknown_tool` (a deny entry naming a tool this
@@ -1,13 +1,13 @@
1
1
  /**
2
- * design/390 §1.2 M8 — prepareTask's LSP phase, verbatim from the driver: the manager fold (spec > deps), the
2
+ * PrepareTask's LSP phase, verbatim from the driver: the manager fold (spec > deps), the
3
3
  * LSP tool mount over the per-task env with its git-ignore filter, the diagnostics lane behind its four
4
4
  * preconditions (a manager whose sessions feed a registry, write-capable hands, not opted out), the run-scoped
5
5
  * registry key, and the edit nudge (raw path → the SAME resolution the tool leg uses → registry reset +
6
6
  * session didChange). No manager ⇒ nothing mounts and both lane seats are undefined. The interface is the
7
- * dependency list the segment had implicitly (design/238 R-1).
7
+ * dependency list the segment had implicitly.
8
8
  *
9
9
  * SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await, so the phase adds no yield between the
10
- * fork-governance note before it and the memory trio's occupancy read after it (design/390 §1.5 S1: a stretch
10
+ * fork-governance note before it and the memory trio's occupancy read after it (a stretch
11
11
  * with no await is extracted as a sync function — an `async` wrapper would open a microtask window).
12
12
  * Read-stability of the host handles for the call: {@link RunInternals} (`@contract prepare.deps-read-stable`).
13
13
  */
@@ -1,16 +1,16 @@
1
1
  /**
2
- * design/390 §1.2 M12 — prepareTask's MEMORY-ENGINE-SESSION phase, verbatim from the driver: the content-origin
3
- * classification + one-way session pollution wrap section (design/178 §3 / design/180 half A / design/324 /
4
- * design/336 §3.3 + §5.5 / design/378 D-4 / design/380 O9a). Its arming predicate (a mounted memory session OR an
2
+ * PrepareTask's MEMORY-ENGINE-SESSION phase, verbatim from the driver: the content-origin
3
+ * classification + one-way session pollution wrap section (consolidating several design threads).
4
+ * Its arming predicate (a mounted memory session OR an
5
5
  * upstream recorder channel), the chain-narrowed classification config, the children's provenance seat, the
6
6
  * declared-MCP-class announcement, the settlement seat, the per-tool wrap with its three record verbs (mark /
7
7
  * recordExternal / recordIncomplete), the Read tool's recall-taint arm and the delegation arm's three-valued
8
8
  * attestation judgment all live here; the announcement helper only this section called moved with it. The
9
- * interface is the dependency list the segment had implicitly (design/238 R-1).
9
+ * interface is the dependency list the segment had implicitly.
10
10
  *
11
11
  * SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await of its own (the awaits sit inside the wrappers it
12
12
  * installs, which run at tool-call time), so the phase adds no yield between the deferred classification before it
13
- * and the prompt assembly after it (design/390 §1.5: a stretch with no await is extracted as a sync function).
13
+ * and the prompt assembly after it (a stretch with no await is extracted as a sync function).
14
14
  * Read-stability of the host handles for the call: {@link RunInternals} (`@contract prepare.deps-read-stable`).
15
15
  *
16
16
  * One read changed spelling and nothing else: the declared-class announcement read `mcp?.statuses` while `mcp` had
@@ -10,6 +10,7 @@ import { readSessionCaptureOptOut } from "../memory-engine/layout.js";
10
10
  import { createMemoryEngineTools } from "../memory-engine/tools.js";
11
11
  import { assertScopeContractPlacement, parseScopeKey, resolveProjectId } from "../memory-engine/scope-contract.js";
12
12
  import { FileMemoryEngineBackend } from "../memory-engine/file-backend.js";
13
+ import { writableRootOf } from "../memory-engine/types.js";
13
14
  let undeclaredCapturePostureAnnounced = false;
14
15
  export async function prepareMemory(input) {
15
16
  const { spec, deps, sessionId, taskRootPath, memoryWriteGateRef, admissionCtx } = input;
@@ -460,20 +461,18 @@ export async function prepareMemory(input) {
460
461
  }
461
462
  const foreignUnderRoot = [];
462
463
  if (input.writeToolsMounted && writeHandle.writeScope !== null && input.advertisedWritableDirs !== undefined) {
463
- for (const d of [...writeEngine.readonlyDirNamesUnderRoot(writeHandle)].sort())
464
- foreignUnderRoot.push(`another scope's directory ${d}`);
465
464
  const controlPlane = canonicalize(writeEngine.controlPlaneDir);
466
- if (isContainedIn(writeHandle.writableRoot, controlPlane))
465
+ if (isContainedIn(writableRootOf(writeHandle), controlPlane))
467
466
  foreignUnderRoot.push(`the engine's control plane ${controlPlane}`);
468
467
  }
469
468
  if (foreignUnderRoot.length > 0) {
470
- deps.onError?.(new Error(`memory advertised writable directory refused: the write scope's root ${writeHandle.writableRoot} contains what is not the model's to touch (${foreignUnderRoot.join("; ")}) — the file tools' root fence is directory-granular, so admitting it would expose those files; memory writes through the file tools stay closed this session and the write instruction is not taught.`), { phase: "config", sessionId });
469
+ deps.onError?.(new Error(`memory advertised writable directory refused: the write scope's root ${writableRootOf(writeHandle)} contains what is not the model's to touch (${foreignUnderRoot.join("; ")}) — the file tools' root fence is directory-granular, so admitting it would expose those files; memory writes through the file tools stay closed this session and the write instruction is not taught.`), { phase: "config", sessionId });
471
470
  }
472
471
  writeChannelOpen =
473
472
  input.writeToolsMounted &&
474
473
  writeHandle.writeScope !== null &&
475
474
  foreignUnderRoot.length === 0 &&
476
- (input.advertisedWritableDirs === undefined || (await input.advertisedWritableDirs.admit({ path: writeHandle.writableRoot, advertiser: "memory", expectCanonical: writeHandle.writableRoot })));
475
+ (input.advertisedWritableDirs === undefined || (await input.advertisedWritableDirs.admit({ path: writableRootOf(writeHandle), advertiser: "memory", expectCanonical: writableRootOf(writeHandle) })));
477
476
  memoryWriteGateRef.current = async (w) => {
478
477
  if (readOnlyEngine !== undefined && readOnlyHandle !== undefined) {
479
478
  const ro = readOnlyEngine.gateWrite(readOnlyHandle, w.key, w.content);
@@ -482,7 +481,7 @@ export async function prepareMemory(input) {
482
481
  }
483
482
  if (input.memoryPersistenceDeclared === false) {
484
483
  const root = writeHandle.writableRoot;
485
- if (w.key === root || w.key.startsWith(`${root}${sep}`)) {
484
+ if (root !== null && (w.key === root || w.key.startsWith(`${root}${sep}`))) {
486
485
  return {
487
486
  ok: false,
488
487
  code: "read_only_layering",
@@ -493,7 +492,7 @@ export async function prepareMemory(input) {
493
492
  }
494
493
  {
495
494
  const root = writeHandle.writableRoot;
496
- if (w.key === root || w.key.startsWith(`${root}${sep}`)) {
495
+ if (root !== null && (w.key === root || w.key.startsWith(`${root}${sep}`))) {
497
496
  const state = captureOptedOut ? { record: { at: 0, reason: "in-run state" }, fault: false } : await writeEngine.sessionCaptureOptOutOrFault(sessionId);
498
497
  if (state.record !== undefined || state.fault) {
499
498
  return {
@@ -1,11 +1,11 @@
1
1
  /**
2
- * design/390 §1.2 M0 (the offload half of the prelude) — prepareTask's nested-usage accumulator, the
2
+ * (The offload half of the prelude) — prepareTask's nested-usage accumulator, the
3
3
  * large-tool-result offload store resolution and the THREE offload wrappers every later mount site
4
4
  * applies (caller tools, first-party hand tools, remote MCP/A2A peers), verbatim from the driver.
5
5
  *
6
6
  * SYNCHRONOUS by construction: the segment held no await in the driver, and a phase that yields where
7
7
  * the driver did not would open a microtask window between two steps that used to be adjacent (the
8
- * design/238 T1 posture). Nothing here touches the store — the wrappers close over it and only the
8
+ * posture). Nothing here touches the store — the wrappers close over it and only the
9
9
  * wrapped tools' `execute` reach it, at run time.
10
10
  *
11
11
  * What stays in the driver: the trust-axis choice of `offloadScope` (`spec.principal ?? "default"`)
@@ -1,11 +1,11 @@
1
1
  /**
2
- * design/390 §1.2 M19 (park ask) — prepareTask's PARK CLOSURE and CONTENT-ASK ROUTING closure, verbatim from the driver's
2
+ * PrepareTask's PARK CLOSURE and CONTENT-ASK ROUTING closure, verbatim from the driver's
3
3
  * gate-machinery block: `resolveContentAsk` (the question face's delivery race, the binding table's cap and redemption
4
4
  * identity, the stranded-answer disclosure) and `suspendAsk` (the sync-first decline and the park-priority mandates, the
5
5
  * args capture / fidelity projection / re-adjudication steps, the pause-then-cut fence, the gate-kind choice, the row
6
6
  * mint, the commit through the saga and the post-commit publication). The one module-scope helper only this segment
7
- * threw (`ParkRefusal`) moved with it. The interface is the dependency list the segment had implicitly (design/238
8
- * R-1) — the two closures used to capture every seat here off the driver's scope, which is the capture surface this
7
+ * threw (`ParkRefusal`) moved with it. The interface is the dependency list the segment had implicitly
8
+ * — the two closures used to capture every seat here off the driver's scope, which is the capture surface this
9
9
  * decomposition exists to make explicit.
10
10
  *
11
11
  * The thirteen-argument park closure is unchanged: twelve positional seats then the structural {@link AskCarry}, the
@@ -13,7 +13,7 @@
13
13
  * carry onto the row exactly as before — this phase mints no origin, changes no word, adds no arm to the closed set.
14
14
  *
15
15
  * SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await of its own (both closures run per tool call), so the
16
- * phase adds no yield between the boundary parks before it and the gate station after it (design/390 §1.5).
16
+ * phase adds no yield between the boundary parks before it and the gate station after it.
17
17
  * Read-stability of the host handles for the call: {@link RunInternals} (`@contract prepare.deps-read-stable`).
18
18
  *
19
19
  * Both closures exist exactly when the ask lane and the saga do (the same predicate) — an absent lane answers two absent
@@ -25,7 +25,7 @@ import { type CheckpointStore } from "../checkpoint-store.js";
25
25
  import type { SessionStore } from "../session.js";
26
26
  import { type OnAsk, type ToolCallRequest, type ToolPolicy } from "../tool-policy.js";
27
27
  import { type ToolResultStore } from "../tool-result-store.js";
28
- import type { RunnerDeps, RuntimeCaps, TaskSpec } from "../types.js";
28
+ import type { RunnerDeps, RuntimeCaps, ShellGateDoctrine, TaskSpec } from "../types.js";
29
29
  import type { CwdRef } from "../../tools/fs/fs-shared.js";
30
30
  import { type ContentAskBinding } from "./content-ask-bindings.js";
31
31
  import type { AskLane, ParkAsk, Prepared, SuspendSaga } from "./contracts.js";
@@ -50,7 +50,7 @@ export interface PrepareParkAskInput {
50
50
  /** borrowed-readonly — the leg's checkpoint store, or undefined (then `suspendAsk` is absent — the park predicate's store
51
51
  * term). Read: the declared fidelity; the saga writes to it. */
52
52
  checkpointStore: CheckpointStore | undefined;
53
- /** borrowed-readonly — the leg's delta seat (design/388 B18): the park reads the gated tool's identity off its
53
+ /** borrowed-readonly — the leg's delta seat (B18): the park reads the gated tool's identity off its
54
54
  * CURRENT roster snapshot so the row carries who the action was minted against. */
55
55
  toolRosterDeltas: ToolRosterDeltaSeat;
56
56
  /** borrowed-readonly — the wiring-manifest phase's park predicate (`parkLane.effective`): arms `suspendAsk`. */
@@ -108,12 +108,11 @@ export interface PrepareParkAskInput {
108
108
  maxSuspends: number;
109
109
  /** borrowed-mutable — the run-scoped remote-lifecycle failure log. Writer here: a refused `suspendVM` (`push`). */
110
110
  remoteEnvFailures: Prepared["remoteEnvFailures"];
111
- /** borrowed-readonly — whether Bash is coarsely shell-gated (the risk descriptor's `shellGated`). */
112
- shellGatedBash: boolean;
113
- /** borrowed-readonly — whether Monitor is coarsely shell-gated (same). */
114
- shellGatedMonitor: boolean;
111
+ /** borrowed-readonly — the shell-seat provenance set: the shell-running tools whose tier is the engine's shell seat's, not a
112
+ * deployment mark (the risk descriptor's `shellGated` is the gated tool's membership). */
113
+ shellGatedTools: ReadonlySet<string>;
115
114
  /** borrowed-readonly — the effective shell-gate doctrine persisted on the descriptor. */
116
- effectiveShellGate: "off" | "classify" | "always";
115
+ effectiveShellGate: ShellGateDoctrine;
117
116
  /** borrowed-readonly — the EFFECTIVE durable-approval opt-in (the caller's, or the synthesized mandate scope): picks the
118
117
  * gate kind, the scope and the ttl. */
119
118
  durableApproval: TaskSpec["durableApproval"];
@@ -156,7 +155,7 @@ export interface PrepareParkAskResult {
156
155
  /** The M19 park-ask phase body — prepareTask's park closure and content-ask routing stretch, verbatim (see the module header). */
157
156
  export declare function prepareParkAsk(input: PrepareParkAskInput): PrepareParkAskResult;
158
157
  /**
159
- * From the resumed/active transcript, the batch position of `currentId` (design/45 §4.ter): the tool-call
158
+ * From the resumed/active transcript, the batch position of `currentId`: the tool-call
160
159
  * ids of the assistant message that issued it (the batch), and the subset already resolved (executed
161
160
  * before the suspend — #1..k-1). ID-based, not positional (council Question #1): immune to reordering.
162
161
  */