@sema-agent/core 7.24.4 → 7.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/dist/agents/peer-session-drain.d.ts +16 -1
  3. package/dist/agents/peer-session-drain.js +16 -1
  4. package/dist/agents/send-message-tool.d.ts +10 -2
  5. package/dist/agents/send-message-tool.js +18 -7
  6. package/dist/agents/subagent.js +13 -10
  7. package/dist/core/background-agent-store.d.ts +3 -3
  8. package/dist/core/checkpoint-store.d.ts +1 -0
  9. package/dist/core/delegation-frames.d.ts +5 -0
  10. package/dist/core/gate-fold.js +85 -0
  11. package/dist/core/gate-lanes.js +23 -1
  12. package/dist/core/gate-outcome.d.ts +8 -2
  13. package/dist/core/gate-outcome.js +3 -1
  14. package/dist/core/hooks.d.ts +29 -0
  15. package/dist/core/hooks.js +2 -0
  16. package/dist/core/mailbox-store.d.ts +5 -0
  17. package/dist/core/mailbox-store.js +1 -1
  18. package/dist/core/parent-spec-seat.d.ts +7 -1
  19. package/dist/core/parent-spec-seat.js +2 -0
  20. package/dist/core/permission-rule-consent.js +29 -47
  21. package/dist/core/permission-rule-provider.js +4 -3
  22. package/dist/core/permission-rule-store.d.ts +143 -7
  23. package/dist/core/permission-rule-store.js +165 -15
  24. package/dist/core/runner/contracts.d.ts +36 -2
  25. package/dist/core/runner/gate-exit.d.ts +14 -0
  26. package/dist/core/runner/gate-exit.js +4 -0
  27. package/dist/core/runner/prepare-delegation-surface.d.ts +4 -4
  28. package/dist/core/runner/prepare-delegation-surface.js +2 -1
  29. package/dist/core/runner/prepare-gate-stations.d.ts +4 -0
  30. package/dist/core/runner/prepare-gate-stations.js +7 -4
  31. package/dist/core/runner/prepare-hands-readface.d.ts +8 -4
  32. package/dist/core/runner/prepare-hands-readface.js +25 -10
  33. package/dist/core/runner/prepare-inherited-gate.d.ts +23 -0
  34. package/dist/core/runner/prepare-inherited-gate.js +11 -6
  35. package/dist/core/runner/prepare-policy-chain.d.ts +15 -1
  36. package/dist/core/runner/prepare-policy-chain.js +14 -4
  37. package/dist/core/runner/prepare-suspend-saga.d.ts +5 -2
  38. package/dist/core/runner/prepare-suspend-saga.js +4 -3
  39. package/dist/core/runner/prepare-task.js +10 -9
  40. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -4
  41. package/dist/core/runner/prepare-wiring-manifest.js +8 -4
  42. package/dist/core/runner/resume-apply.js +2 -0
  43. package/dist/core/runner/run-harness-handlers.js +1 -0
  44. package/dist/core/runner/run-identity-wiring.d.ts +2 -2
  45. package/dist/core/runner/run-leg.js +26 -0
  46. package/dist/core/runner/run-notification-lane.d.ts +2 -2
  47. package/dist/core/runner/run-notification-lane.js +2 -1
  48. package/dist/core/runner/run-settle-and-teardown.js +1 -0
  49. package/dist/core/runner/session-rule-policy.d.ts +3 -4
  50. package/dist/core/runner/session-rule-policy.js +11 -3
  51. package/dist/core/session-policy-store.d.ts +27 -3
  52. package/dist/core/session-policy-store.js +12 -4
  53. package/dist/core/store-contracts/background-agent-store-contract.js +10 -0
  54. package/dist/core/store-contracts/peer-directory-contract.d.ts +23 -0
  55. package/dist/core/store-contracts/peer-directory-contract.js +82 -0
  56. package/dist/core/task-event.d.ts +6 -0
  57. package/dist/core/task-notification.d.ts +79 -4
  58. package/dist/core/task-notification.js +90 -1
  59. package/dist/core/task-registry-agent.js +1 -2
  60. package/dist/core/task-registry-monitor.js +2 -1
  61. package/dist/core/task-registry-shared.d.ts +53 -5
  62. package/dist/core/task-registry-shared.js +13 -2
  63. package/dist/core/task-registry.js +8 -5
  64. package/dist/core/task-spec.d.ts +12 -5
  65. package/dist/core/tool-catalog-entries.js +1 -0
  66. package/dist/core/tool-spec.d.ts +1 -1
  67. package/dist/core/wiring-manifest.d.ts +2 -2
  68. package/dist/engine/loop/agent-loop.js +1 -1
  69. package/dist/engine/loop/types.d.ts +4 -0
  70. package/dist/index.d.ts +6 -3
  71. package/dist/index.js +4 -3
  72. package/dist/prompts/coordinator.d.ts +2 -1
  73. package/dist/stores/file/adoption/adopt.js +23 -1
  74. package/dist/stores/file/permission-rule-store.js +3 -1
  75. package/dist/stores/file/session-policy-store.d.ts +31 -0
  76. package/dist/stores/file/session-policy-store.js +87 -7
  77. package/dist/tools/fs/bash-program-position.d.ts +4 -26
  78. package/dist/tools/fs/bash-program-position.js +0 -11
  79. package/dist/tools/fs/bash-readonly-classifier.d.ts +20 -309
  80. package/dist/tools/fs/bash-readonly-classifier.js +28 -766
  81. package/dist/tools/fs/fs-bash.d.ts +43 -207
  82. package/dist/tools/fs/fs-bash.js +20 -341
  83. package/dist/tools/fs/index.d.ts +12 -13
  84. package/dist/tools/fs/index.js +3 -4
  85. package/dist/tools/fs/read-deny.d.ts +25 -0
  86. package/dist/tools/fs/read-deny.js +19 -0
  87. package/dist/tools/fs/safety.js +2 -6
  88. package/dist/tools/fs/shell-read-check.d.ts +99 -0
  89. package/dist/tools/fs/shell-read-check.js +229 -0
  90. package/dist/tools/fs/shell-read-establish.d.ts +70 -115
  91. package/dist/tools/fs/shell-read-establish.js +216 -102
  92. package/dist/tools/fs/shell-read-judge.d.ts +231 -0
  93. package/dist/tools/fs/shell-read-judge.js +170 -0
  94. package/dist/tools/fs/shell-read-plan.d.ts +3 -0
  95. package/dist/tools/fs/shell-read-plan.js +82 -19
  96. package/dist/tools/monitor.d.ts +5 -5
  97. package/dist/tools/monitor.js +3 -5
  98. package/dist/tools/task-list.d.ts +4 -0
  99. package/package.json +1 -1
  100. package/test/export-surface.snapshot.json +19 -3
  101. package/wire-changes/core@7.25.0.json +671 -0
@@ -2,163 +2,32 @@ import type { AgentTool, ExecutionEnv } from "../../internal/harness-types.js";
2
2
  import { type TaskRegistry } from "../../core/task-registry.js";
3
3
  import { type CwdRef } from "./fs-shared.js";
4
4
  import type { ReversibilityVerdict } from "../../core/types.js";
5
- import { type BashReadonlyRootBoundary } from "./bash-readonly-classifier.js";
6
- import { type ShellScanDecline } from "./bash-program-position.js";
7
- import { type ReadDenyMatcher } from "./read-deny.js";
5
+ import type { ShellReadAdmission } from "./shell-read-check.js";
8
6
  /**
9
- * A parsed-command classifier for the `bash` tool, exposed as a
10
- * `ToolSpec.reversibilityProbe`. A full shell is treated as egress+irreversible by DEFAULT; this probe is the
11
- * "real parsed classifier" that lets a deployment safely auto-allow the provably-benign subset (the
12
- * doctrine: shell⇒always-gate UNLESS a parsed classifier is wired). It is COMPOUND-aware
13
- * ({@link classifyCompoundReadonly}): a command is reversible ONLY if every `;`/`&&`/`||`/`|`
14
- * connected segment is a bare command (no redirects/substitution/subshells/escapes/`&` anywhere — those
15
- * whole-string reject, so it can't chain to `curl`/`git push`/`rm` or background past TaskStop) whose
16
- * `argv[0]` is in the reversible allowlist (default
17
- * {@link BASH_READONLY_DEFAULT_ALLOW} — `ls`/`cat`/`grep`/…, curated to commands with NO write/mutation mode
18
- * under ANY args; the final-council MAJOR removed `date`/`hostname`/`file`, which mutate with args). EVERYTHING
19
- * else (a write command, an egress command, an operator/pipe, a path-prefixed or env-assigned command, a
20
- * non-string arg) is NOT reversible → the gate tightens it to an `irreversible_ask` durable suspend. Fail-closed
21
- * by construction: the allowlist is the only path to `reversible: true`. It is an argv[0]-NAME filter, so the
22
- * remaining residual is the OTHER axis — a listed reader with crafted args can still READ a secret (a DATA side
23
- * channel, NOT irreversibility/egress); a deployment that widens `allow` owns that (and the mutation tradeoff).
24
- *
25
- * Wire it on the `bash` tool with `irreversibility: "maybe"` (or via `TaskSpec.shellGate: "classify"`); without
26
- * a classifier a deployment should mark `bash` `irreversibility: "always"` (`shellGate: "always"`) — fail-closed.
27
- *
28
- * The DATA-side residual named above is closed for the read direction: pass `boundary` and a listed
29
- * reader whose path arguments leave the allowed directories stops being reversible, so the gate asks
30
- * instead of auto-allowing. Pass it as a FUNCTION wherever the working directory is observable (an
31
- * in-task `cd` moves the base a relative operand resolves against, and the probe is called per command).
32
- * Omitted ⇒ name-only classification, exactly as before.
33
- *
34
- * The BOUNDED READ-ONLY POLL LOOP arm: a monitoring consumer's whole idiom is `for i in $(seq 1 8);
35
- * do tail -n 5 x.log; sleep 2; done`, which the compound face rejects as a class (`for` is not an
36
- * allowlisted name) — making the classify gate behave like `always` for exactly the tool that polls
37
- * the most. When the plain face rejects, the probe consults
38
- * {@link classifyBoundedReadonlyPollLoop} — a narrow exact grammar (literal bound, `;`-separated
39
- * allowlisted readers + literal `sleep`, the same read boundary, everything else banned; the full
40
- * safety argument lives on that function). Additive by construction: the plain verdict is tried
41
- * first and is never overridden.
42
- *
43
- * The RECURSIVE-READ demotion returns a STRUCTURED cause alongside the verdict. That
44
- * demotion is the one arm whose reason is genuinely unrecoverable downstream: a recursive read is
45
- * refused for what the traversal REACHES, and the reached set is by definition not in the command text
46
- * an approval card shows. The cause is minted from the SAME classification pass that produced the
47
- * verdict, so nothing re-parses the command to recover it.
48
- *
49
- * It is a `cause` (code + operand arrays), not a `reason` sentence, and that is a correction rather than
50
- * a preference: every sentence tried here was true of one call and false of another — "a recursive read
51
- * of X" is false for the zero-operand over-fire, "operand the check treated as a root" is false when the
52
- * check SYNTHESIZES the root, and any of them, flattened into one length-capped string, silently dropped
53
- * whichever family sorted last. The consumer now writes the sentence, from a code that cannot go stale
54
- * and arrays that cannot be truncated into a lie.
55
- *
56
- * The other non-reversible arms stay bare on purpose — their cause is legible in the command itself (a
57
- * write verb, a pipe, an unlisted name), and manufacturing an account for them would put text on a card
58
- * that adds nothing the reader cannot already see.
59
- *
60
- * What the boundary half of this probe is — and what it is not — is stated once on the seat that owns it
61
- * ({@link bashReadBoundaryProbe}, `@contract shell.read_boundary.friction_floor`); composing it with the
62
- * shape classifier here changes what happens to the residual shell risk, never that reading.
63
- */
64
- export declare function bashReversibilityProbe(allow?: readonly string[], boundary?: BashReadonlyRootBoundary | (() => BashReadonlyRootBoundary | undefined)): (args: unknown) => ReversibilityVerdict;
65
- /**
66
- * ONE rule, TWO TABLES: which {@link ShellScanDecline} a SEAT reads as read evidence. A decline names no word,
67
- * so it is never the boundary DECLARING anything about a path.
68
- *
69
- * BOTH tables are ROW-PER-MEMBER objects behind a compile-time fence ({@link BOUNDARY_SEAT_READS_DECLINE} /
70
- * {@link CLASSIFY_SEAT_READS_DECLINE}); the sets below are DERIVED from those rows and the ARGUMENT for the
71
- * rows is this block. "Empty by the argument" is a row saying `false`, not a literal nobody can judge.
72
- *
73
- * The READ-BOUNDARY seat is what the default/off doctrine and a bypass mode mount ALONE: there "not mandated"
74
- * is VOUCHED, so a decline must count as evidence or a read nobody judged runs silently (`<out-of-root> |
75
- * xargs cat` at zero asks — a closed P0). It keeps both. The CLASSIFY seat composes the same boundary with the
76
- * SHAPE classifier, which refuses every one of these shapes: what survives is a CARD, and the only question
77
- * this table answers is who may retire it. It keeps NEITHER — an ANSWERER may (a person, the auto-mode
78
- * classifier, a person's stored allow rule) — empty by the argument, not by omission.
79
- *
80
- * WHAT AN EMPTY TABLE DOES NOT BUY, corrected where it was once claimed: "the shape classifier refuses these
81
- * shapes anyway, so the only question left is the stored answer" was true of the PROBE and false of the OUTCOME
82
- * — the allow layer's other members are not that classifier, and both cleared exactly these cards at ZERO asks. An
83
- * empty table says only "no MANDATE HERE"; that the boundary DECLINED is a separate fact, carried on every ask this
84
- * seat raises ({@link BoundaryAnswer}'s weaker rung), standing every ANSWERLESS arm down while every answerer's lane stays open.
85
- * THE COST, stated rather than mitigated: once that ask is retired BY AN ANSWERER, an out-of-root or deny-listed
86
- * read HIDDEN inside such a command is not asked again. A command's words are not the argv its programs receive (a
87
- * stream is re-split by its consumer, an unread option may move the working directory first, stdin text is a
88
- * command line nobody parsed), so a scan of them cannot be the guarantee it was claimed to be. "A read the boundary
89
- * objects to is never retired by a stored rule" does NOT hold on the classify seat for this class, and holds
90
- * unchanged elsewhere. One word-level check survives on both seats and is not part of the trade: a token the scan
91
- * DID read naming a path outside the roots still gates the call ({@link classifyOutOfRootReadGate}).
7
+ * The classify doctrine's SHAPE classifier for the `bash` tool, exposed as a `ToolSpec.reversibilityProbe`
8
+ * (the "maybe" tier's probe under `shellGate:"classify"`). A command is reversible ONLY if every connected
9
+ * segment is a bare command whose program is on the reversible allowlist (default
10
+ * {@link bashClassifyDefaultAllow} — curated to commands with NO write/mutation mode under ANY args) and whose
11
+ * SHAPE the compound classifier accepts (no redirects, substitutions, subshells, escapes or backgrounding; no
12
+ * blocking stdin read, follow mode or device read; a guarded verb form in its safe spelling) — or a bounded
13
+ * read-only poll loop of such commands ({@link classifyBoundedReadonlyPollLoop}). EVERYTHING else — a write
14
+ * command, an egress command, an operator, a path-prefixed or env-assigned command, a backgrounded call, a
15
+ * non-string arg — is NOT reversible → the gate tightens a surviving allow to an ordinary ask. Fail-closed by
16
+ * construction: the allowlist is the only path to `reversible: true`.
17
+ *
18
+ * WHERE a command reads is not this probe's question. The read boundary — deny rows, containment, symlinks,
19
+ * patterns, traversals, the working directory a `cd` moves — is the gate's shell READ STATION
20
+ * (`shell-read-check.ts`), judged on every shell call under every doctrine, ahead of this probe; a call the
21
+ * station refused never reaches it, and a call the station asks about is asked about whatever this probe
22
+ * says. `boundary` supplies only the environment's declared home, so the `$HOME`/`~` spellings fold here the
23
+ * way the station folds them; nothing else of it is read. A caller-supplied `allow` is honored verbatim, so a
24
+ * narrowed deployment is never widened by the default superset.
92
25
  */
93
- export declare const BOUNDARY_SEAT_DECLINE_EVIDENCE: ReadonlySet<ShellScanDecline>;
94
- /** The classify doctrine's table in the shape its readers take — see above; empty because every row says `false`. */
95
- export declare const CLASSIFY_SEAT_DECLINE_EVIDENCE: ReadonlySet<ShellScanDecline>;
96
- export declare function fitMiddle(s: string, budget: number): string;
97
- /** `s` shortened to `budget` by removing its MIDDLE, marked — cut through the repo's surrogate-safe
98
- * primitive, never a raw `slice` (a raw cut leaves half an astral code point on a card and in a durable
99
- * row). Under a dozen characters the HEAD is taken. */
100
- export declare function elideMiddle(s: string, budget: number): string;
101
- /**
102
- * The READ-BOUNDARY seat of the full shell: a `ToolSpec.reversibilityProbe` that answers exactly ONE
103
- * question — does the deployment's read boundary object to where this command reads? — and nothing about
104
- * the command's shape. It is the shell face of the rule every read face obeys: the structured readers
105
- * (Read/Grep/Glob) refuse a deny-listed or out-of-root target; the shell reader gets a MANDATED ask for the
106
- * same target. The engine installs it on Bash and Monitor whenever a real writable shell is mounted and the
107
- * shellGate doctrine adds no residual-risk classifier of its own (`shellGate:"off"`, the default); under
108
- * `"classify"` the doctrine installs {@link bashReversibilityProbe} instead, which composes THIS judgment
109
- * with the read-only shape classifier, and under `"always"` the tier itself is the mandate and no probe runs.
110
- * The boundary is therefore judged under every doctrine; the doctrine decides only what happens to the
111
- * residual shell risk.
112
- *
113
- * The verdict, in the probe vocabulary the gate reads (`reversible` here means "the boundary raised no
114
- * question — the seat vouches the call may run"):
115
- * · the boundary DECLARED an objection — a deny-listed operand, a recursive walk under the deny judge, an
116
- * operand outside the roots — ⇒ `{ reversible: false, mandated: true }` (+ the recursive-reach `cause`);
117
- * · the boundary could NOT READ where the command reads — an unexpanded glob operand (the compound face
118
- * reports it as undecided), a `~`-rooted operand under an adapter that declares no home, a working-directory
119
- * move the walk cannot model — ⇒ `{ reversible: false }` (fail-closed; a plain ask, which a stored allow rule
120
- * may retire — the seat did not declare anything, it declined to guess). The engine wires the environment's
121
- * declared home, so `~/x`, `$HOME/x` and `${HOME}/x` RESOLVE and are judged like the absolute spelling;
122
- * · otherwise ⇒ `{ reversible: true }`. This includes every command whose SHAPE the read faces refuse whole
123
- * (an unlisted program, a redirection, a substitution, a segment that is not a reader): those shapes carry
124
- * no read-face verdict, so this seat has no question to ask — the residual is the doctrine's, and under
125
- * `"off"` the doctrine asks nothing. A listed reader refused for a PER-SEGMENT shape (follow mode, a device,
126
- * a blocking stdin read) is different: the compound face still walks its operands and carries the boundary's
127
- * structural findings beside the sentence, so `tail -f <denied>` is the declared ask `cat <denied>` is.
128
- * `run_in_background` is likewise not this seat's question (it changes WHEN the command runs, never WHERE it
129
- * reads).
130
- *
131
- * Reach = the read faces' reach, and nothing wider: the compound face walks the operands of a command whose
132
- * every segment is a listed reader, and the bounded poll-loop face walks a loop body of listed readers. A
133
- * read hidden in a shape the faces refuse whole (`cat x | <unlisted>`, `$(cat x)`, `cat $DIR/x` — a variable other
134
- * than the home's own deterministic spellings — a launcher `env cat x`, a path-prefixed `/bin/cat x`) is not
135
- * seen here — deny-listed or out-of-root alike — exactly
136
- * the shapes the `"classify"` doctrine exists to ask about. The text-level containment gate the classify
137
- * seat consults on its demotion arms is deliberately not consulted here ({@link boundaryDeclared}: it may
138
- * add a mandate to an ask, never originate one). The poll-loop face's structured verdict
139
- * ({@link classifyBoundedReadonlyPollLoopDetailed}) is read the same three ways as the compound face's.
140
- *
141
- * The reader set is the engine's classify set ({@link bashClassifyDefaultAllow}) — here it is NOT an
142
- * auto-allow list but the set of programs whose operands the faces read; a wider set means more reads are
143
- * judged, never that more runs are vouched.
144
- *
145
- * @contract shell.read_boundary.friction_floor — what this seat answers is a question about TEXT: which
146
- * files the words of a command may name, resolved against the declared roots, the declared home and the
147
- * observed working directory. It is not a question about what the process will open, and no amount of
148
- * parsing makes it one. A deployment's containment for what a shell may reach is the `ExecutionEnv` the
149
- * command runs in (a sandbox, a container, a remote adapter); everything here is the friction floor under
150
- * that containment — it raises the cost of an accidental or careless read, and it is not a defence against
151
- * a command written to defeat it. The floor's last cell belongs to the full shell's own execute leg: once
152
- * the gate has allowed a call and before anything spawns, the candidates this seat accepted are resolved
153
- * on the filesystem, and a target that leaves the roots only after a symlink is followed is REFUSED —
154
- * refused rather than asked, because by then the approval step is behind the call (see `createBashTool`'s
155
- * `readBoundary` option). The leg runs where an environment can resolve a link locally; a remote adapter
156
- * keeps the textual answer alone. A program FILE named in the slot of a verb this floor has no row for
157
- * (`awk -f prog.awk`, `jq -f filter.jq`, `python3 script.py`) is judged as the in-root operand it is, and the
158
- * program inside it is text nobody here parsed — vouched as any in-root operand is, which is the floor's
159
- * stated reach, not a hole in the sandbox.
160
- */
161
- export declare function bashReadBoundaryProbe(boundary: BashReadonlyRootBoundary | (() => BashReadonlyRootBoundary | undefined)): (args: unknown) => ReversibilityVerdict;
26
+ export declare function bashReversibilityProbe(allow?: readonly string[], boundary?: {
27
+ readonly homeDir?: string;
28
+ } | (() => {
29
+ readonly homeDir?: string;
30
+ } | undefined)): (args: unknown) => ReversibilityVerdict;
162
31
  /**
163
32
  * The FULL shell's contract id, single-sourced: both shell faces share the wire
164
33
  * name "Bash", so this id is the ONE structural discriminator between the write-capable shell and
@@ -267,16 +136,14 @@ export declare function createBashTool(env: ExecutionEnv, rootCanonical: string,
267
136
  */
268
137
  additionalRoots?: readonly string[];
269
138
  /**
270
- * The deployment's shell READ BOUNDARY — the same seat the gate's shell probes read
271
- * ({@link bashReadBoundaryProbe}), handed to the tool so its execute leg can close the one gap a
272
- * synchronous seat structurally cannot: the seat judges the WRITTEN spelling, the kernel opens the
273
- * symlink-resolved target. Passed as a FUNCTION wherever the working directory is observable (an
274
- * in-task `cd` moves the base a relative operand resolves against); evaluated per call.
275
- *
276
- * Absent ⇒ no recheck and no I/O: a deployment with no read boundary declared has no roots for a
277
- * read to be outside of, exactly as the seat itself reads an absent boundary.
139
+ * The gate's shell read station's pre-spawn ADMISSION (`ShellReadAdmission`, shell-read-check.ts): what
140
+ * this leg runs is what the station judged before approval, and the admission re-establishes the SAME
141
+ * candidates right before the spawn — a symlink re-pointed or a pattern re-matched while the approver
142
+ * was thinking refuses the call instead of running it. It parses nothing and judges no word the
143
+ * station did not. Absent ⇒ no station judged this mount (a library-direct toolkit with no gate):
144
+ * nothing is re-established, and the full shell runs unconfined as it always did.
278
145
  */
279
- readBoundary?: BashReadonlyRootBoundary | (() => BashReadonlyRootBoundary | undefined);
146
+ shellReadAdmission?: ShellReadAdmission;
280
147
  /** Override of the `timeout` parameter's DEFAULT budget, in ms — see
281
148
  * {@link import("./index.js").HandsToolkitOptions.bashDefaultTimeoutMs} for the resolution
282
149
  * contract (options over env over constant; invalid values discarded). */
@@ -301,46 +168,16 @@ export declare function createBashTool(env: ExecutionEnv, rootCanonical: string,
301
168
  * Absent ⇒ historic bare tag. */
302
169
  reminderMark?: string;
303
170
  }): AgentTool;
304
- type ShellCanonicalRefusal = {
305
- content: string;
306
- isError: true;
307
- details?: unknown;
308
- };
309
- /** What a face contributes to the shared refusal cards: the TOOL refusing, and the three prose slots. */
310
- export interface ShellFaceProse {
311
- readonly tool: string;
312
- readonly subject: string;
313
- readonly verdictPhrase: string;
314
- readonly note: string;
315
- }
316
- export type ShellCanonicalRecheck = (command: string, signal?: AbortSignal) => Promise<ShellCanonicalRefusal | undefined>;
317
- /**
318
- * THE execute-time canonical recheck, mounted by every face that spawns a shell command — the full `Bash`
319
- * tool and `Monitor`, whose approval-time seats judge the WRITTEN spelling while this re-judges where the
320
- * kernel takes those words. The SAME leg, not a second one; the contract below is the leg's own. */
321
- export declare function createShellCanonicalRecheck(env: ExecutionEnv, readBoundary: BashReadonlyRootBoundary | (() => BashReadonlyRootBoundary | undefined) | undefined, face?: ShellFaceProse): ShellCanonicalRecheck;
322
- /** The `Monitor` face's prose — the full shell's with its own tool name: one leg, one finding, two tools. */
323
- export declare const MONITOR_ESCAPE_PROSE: ShellFaceProse;
324
171
  /**
325
172
  * `bash_readonly` (effect:read) — a restricted shell for the verifier read-only boundary: a single
326
173
  * allowlisted, bare command with no shell operators. effect:read lets it survive the verifier's
327
174
  * read-only filter, but it is NOT a policy circumvention — it still goes through the tool-policy gate,
328
- * because a read can still be a side channel (dumping a secret file). The allowlist is a coarse pre-filter;
329
- * the policy gate is the authoritative control.
330
- *
331
- * Hard refusal, not a demotion: the allowlist vets argv[0] and nothing else, so without a path
332
- * boundary an allowlisted reader could name ANY file on the host and get it (`cat /etc/passwd` would go
333
- * straight through). The full `bash` leg carries the same boundary as a DEMOTION — that leg's verdict
334
- * feeds an approval gate, so "outside the roots" can become a prompt. This face has no
335
- * such channel: `effect:"read"` is precisely what lets it run where the write shell may not (the verifier's
336
- * read-only band), so an out-of-root read here is not something to escalate, it is outside what the tool
337
- * is. It is refused, fail-closed, before anything executes — including the conservative direction, where
338
- * the operand cannot be resolved statically (there is nothing to degrade to).
339
- *
340
- * The boundary has TWO halves and this face wires both: roots answer WHERE a command may read and the
341
- * allowlist WHICH verb runs, neither answers WHICH FILES are off the table (a deny row holds INSIDE the
342
- * roots). The judge is the structured read faces' own, asked at both judging moments; every finding lands
343
- * on the standing rule above — a demotion here is a refusal.
175
+ * because a read can still be a side channel (dumping a secret file). The allowlist is a coarse pre-filter
176
+ * over the command's SHAPE; WHERE it reads is the gate's shell read station's judgment (`shell-read-check.ts`,
177
+ * the `readonly` seat), which REFUSES a deny-listed read, an out-of-root read and a read it cannot establish
178
+ * — this face has no approval channel to degrade to, so every finding is a refusal, before anything runs.
179
+ * The execute leg judges nothing: it admits what the station established (`shellReadAdmission` re-establishes
180
+ * the same candidates right before the spawn) and runs the command.
344
181
  */
345
182
  export declare function createBashReadonlyTool(env: ExecutionEnv, rootCanonical: string, allow: ReadonlySet<string>, opts?: {
346
183
  /** Same timeout-cap overrides the full `bash` leg takes — see
@@ -352,13 +189,12 @@ export declare function createBashReadonlyTool(env: ExecutionEnv, rootCanonical:
352
189
  /** Structured sink for the timeout-cap discard announcements — same seat the full `bash` leg
353
190
  * takes (both foreground legs share the resolver, so both must share the sink). */
354
191
  onNotice?: (n: import("../../core/types.js").EngineNotice) => void;
355
- /** The extra containment roots the structured file tools got (the CC `--add-dir` family,
356
- * canonical). A read this deployment already sanctions for Read/Grep/Glob is equally sanctioned
357
- * here — the two faces must not disagree about which directories exist for this session. */
358
- additionalRoots?: readonly string[];
359
- /** The deployment's compiled read deny set — the SAME matcher object the structured read faces of this
360
- * mount hold (one compile, one table). Absent ⇒ no deny judge, and both judging moments are unchanged. */
361
- readDeny?: ReadDenyMatcher;
192
+ /** The gate's shell read station's pre-spawn ADMISSION (`ShellReadAdmission`, shell-read-check.ts): what
193
+ * this leg runs is what the station judged, re-established right before the spawn. Absent ⇒ no station
194
+ * judged this mount (a library-direct toolkit with no gate): this face then confines NOTHING — its
195
+ * containment IS the station, and a consumer mounting it without one must run `runToolGate` with the
196
+ * station's `shellReadCheck` (the Runner does). */
197
+ shellReadAdmission?: ShellReadAdmission;
362
198
  /** The run's tool-progress sink — the SAME seat the full shell leg takes, for the same reason its
363
199
  * two neighbours above are shared: both foreground legs run through `runShell`, so a long command
364
200
  * must breathe on both or a person watching one of them is told nothing. This face has no detach