@sema-agent/core 7.8.0 → 7.9.1
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.
- package/CHANGELOG.md +75 -1
- package/dist/agents/subagent.d.ts +1 -1
- package/dist/core/ask-origin.d.ts +16 -1
- package/dist/core/ask-origin.js +5 -1
- package/dist/core/checkpoint-store.d.ts +36 -35
- package/dist/core/effective-path-target.d.ts +43 -0
- package/dist/core/effective-path-target.js +56 -0
- package/dist/core/engine-notice.d.ts +8 -0
- package/dist/core/fs-write-gate-policy.js +2 -1
- package/dist/core/gate-lanes.d.ts +1 -0
- package/dist/core/gate-lanes.js +60 -28
- package/dist/core/gate-outcome.d.ts +8 -4
- package/dist/core/gate-outcome.js +2 -1
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hooks.d.ts +66 -32
- package/dist/core/mcp-injection-drop.d.ts +74 -0
- package/dist/core/mcp-injection-drop.js +27 -0
- package/dist/core/permission-rule-consent.d.ts +29 -36
- package/dist/core/permission-rule-consent.js +110 -60
- package/dist/core/permission-rule-model.d.ts +311 -96
- package/dist/core/permission-rule-model.js +309 -50
- package/dist/core/permission-rule-org.d.ts +19 -12
- package/dist/core/permission-rule-org.js +13 -16
- package/dist/core/permission-rule-provider.d.ts +10 -6
- package/dist/core/permission-rule-provider.js +12 -8
- package/dist/core/permission-rule-session.d.ts +7 -6
- package/dist/core/permission-rule-session.js +29 -10
- package/dist/core/permission-rule-store.d.ts +40 -18
- package/dist/core/permission-rule-store.js +68 -42
- package/dist/core/permission-rule-sync.d.ts +9 -3
- package/dist/core/permission-rule-sync.js +29 -25
- package/dist/core/permission-rule-syntax.d.ts +30 -0
- package/dist/core/permission-rule-syntax.js +44 -0
- package/dist/core/permission-rules.d.ts +56 -33
- package/dist/core/permission-rules.js +88 -55
- package/dist/core/persisted-rule-arms.d.ts +61 -0
- package/dist/core/persisted-rule-arms.js +50 -0
- package/dist/core/runner/active-skill-scope.js +2 -1
- package/dist/core/runner/contracts.d.ts +21 -2
- package/dist/core/runner/permission-rule-lanes.d.ts +42 -27
- package/dist/core/runner/permission-rule-lanes.js +25 -21
- package/dist/core/runner/prepare-gate-stations.js +1 -1
- package/dist/core/runner/prepare-safety-scan.js +8 -2
- package/dist/core/runner/prepare-task.js +1 -1
- package/dist/core/runner/resume-admission.d.ts +53 -0
- package/dist/core/runner/resume-admission.js +83 -0
- package/dist/core/runner/resume-apply.d.ts +50 -0
- package/dist/core/runner/resume-apply.js +184 -0
- package/dist/core/runner/resume-checkpoint-screen.d.ts +18 -0
- package/dist/core/runner/resume-checkpoint-screen.js +108 -0
- package/dist/core/runner/resume-claim.d.ts +32 -0
- package/dist/core/runner/resume-claim.js +27 -0
- package/dist/core/runner/resume-internals-and-config.d.ts +33 -0
- package/dist/core/runner/resume-internals-and-config.js +50 -0
- package/dist/core/runner/resume-policy-outcome.d.ts +31 -0
- package/dist/core/runner/resume-policy-outcome.js +127 -0
- package/dist/core/runner/resume-preflight.d.ts +40 -0
- package/dist/core/runner/resume-preflight.js +122 -0
- package/dist/core/runner/resume-review-outcome.d.ts +30 -0
- package/dist/core/runner/resume-review-outcome.js +88 -0
- package/dist/core/runner/run-harness-handlers.js +1 -1
- package/dist/core/runner/runtask.d.ts +20 -0
- package/dist/core/runner/runtask.js +85 -734
- package/dist/core/runner/session-rule-policy.js +2 -1
- package/dist/core/runner/tool-end-body.d.ts +12 -5
- package/dist/core/runner/tool-end-body.js +5 -5
- package/dist/core/runner/tool-face-overlay.js +22 -3
- package/dist/core/runner/tool-output-projection.d.ts +9 -6
- package/dist/core/runner/tool-output-projection.js +1 -18
- package/dist/core/runner/turn-attachments.d.ts +2 -2
- package/dist/core/sensitive-path-policy.js +5 -3
- package/dist/core/shell-lexer.d.ts +47 -0
- package/dist/core/shell-lexer.js +478 -0
- package/dist/core/shell-scan.d.ts +60 -0
- package/dist/core/shell-scan.js +183 -0
- package/dist/core/shell-wrapper-table.d.ts +297 -0
- package/dist/core/shell-wrapper-table.js +58 -0
- package/dist/core/store-contracts/permission-rule-sync-contract.js +29 -10
- package/dist/core/tool-catalog-entries.js +6 -6
- package/dist/core/tool-face.d.ts +80 -4
- package/dist/core/tool-face.js +10 -0
- package/dist/core/tool-policy.d.ts +1 -6
- package/dist/core/tool-registry.d.ts +8 -11
- package/dist/core/tool-registry.js +5 -2
- package/dist/core/tool-roster.d.ts +20 -2
- package/dist/core/tool-roster.js +36 -8
- package/dist/index.d.ts +7 -4
- package/dist/index.js +6 -3
- package/dist/stores/file/checkpoint-store.js +1 -1
- package/dist/stores/file/permission-rule-store.d.ts +28 -23
- package/dist/stores/file/permission-rule-store.js +64 -16
- package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
- package/dist/tools/fs/fs-pdf.d.ts +1 -1
- package/dist/tools/fs/fs-search-tools.d.ts +3 -2
- package/dist/tools/fs/fs-search-tools.js +17 -9
- package/dist/tools/fs/index.js +1 -1
- package/dist/tools/fs/notebook.d.ts +1 -1
- package/dist/tools/fs/safety.d.ts +1 -1
- package/dist/tools/fs/search.d.ts +0 -8
- package/dist/tools/fs/search.js +0 -23
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +99 -11
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* design/179 §2 — the persisted
|
|
3
|
-
*
|
|
4
|
-
* A persisted
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
2
|
+
* design/179 §2 — the persisted permission-rule model and its matchers.
|
|
3
|
+
*
|
|
4
|
+
* A persisted rule is one person's standing instruction about a tool call, in one of THREE behaviors
|
|
5
|
+
* ({@link RULE_BEHAVIORS}): an `allow` is the standing form of ONE recorded human approval (the person
|
|
6
|
+
* confirmed a command once on an approval card and asked not to be asked again); a `deny` and an `ask`
|
|
7
|
+
* are the standing forms of "never run this" and "ask me every time" — the same content-form rules
|
|
8
|
+
* upstream reads out of its settings' deny/ask lists. ONE grammar, ONE canonical spelling and ONE
|
|
9
|
+
* matcher family serve all three; the behavior is a FIELD beside the text, never part of it, so
|
|
10
|
+
* `Bash(rm -r:*)` is one spelling whether it allows, denies or asks. This module owns the rule's SHAPE
|
|
11
|
+
* (text grammar, canonical spelling, scope, behavior) and its MATCH semantics; who may mint one, where
|
|
12
|
+
* it is stored, and where in the gate it is consumed live in `permission-rule-store.ts`,
|
|
13
|
+
* `permission-rule-consent.ts` and `gate-lanes.ts` respectively.
|
|
14
|
+
*
|
|
15
|
+
* The two DIRECTIONS read a command differently, on purpose: an allow must cover the WHOLE call
|
|
16
|
+
* ({@link ruleAdmitsCommand} — one recorded yes clears exactly what it spells), while a deny/ask
|
|
17
|
+
* speaks about any PROGRAM RUN the call may perform ({@link programRunReachOf} — whole command or
|
|
18
|
+
* any segment of a chain, read with a real shell lexer, `shell-lexer.ts`, and answering `unreadable`
|
|
19
|
+
* for what it cannot read rather than silence). Precedence across behaviors is the upstream one,
|
|
20
|
+
* deny > ask > allow ({@link RULE_BEHAVIOR_PRECEDENCE}, the one table every adjudication reads).
|
|
21
|
+
*
|
|
22
|
+
* ## The floor: what a rule may name, and what a rule may match (the ALLOW direction)
|
|
11
23
|
*
|
|
12
24
|
* Everything a shell can use to run a program the text does not NAME — substitution, subshells,
|
|
13
25
|
* backgrounding, escapes, line breaks, and **redirection** — puts the command outside this lane
|
|
@@ -41,7 +53,9 @@
|
|
|
41
53
|
* The floor is `parseLeadingCommandName` + `splitShellCompoundSegments` — the one simple-command parser
|
|
42
54
|
* and the one segmentation, both already shared with the read-only classifier, the reversibility probe,
|
|
43
55
|
* the coarse command-name policy and the skill tool specifier. A second tokenizer would drift, and drift
|
|
44
|
-
* on a
|
|
56
|
+
* on a LOOSENING face shows up as a circumvention rather than as a test failure. The tightening
|
|
57
|
+
* direction's lexer is not that second tokenizer: it reads MORE than the floor (never less), and a
|
|
58
|
+
* drift there costs an approval prompt, never a standing approval.
|
|
45
59
|
*
|
|
46
60
|
* argv[0] may carry a PATH prefix here (`./gradlew`, `/usr/bin/git`) — opt-in at the shared parser and
|
|
47
61
|
* used by this lane alone. The bare-name requirement belongs to the argv[0]-NAME filters, which compare
|
|
@@ -58,6 +72,48 @@
|
|
|
58
72
|
* here) and no leading-env-assignment stripping (`FOO=1 git status` simply does not match
|
|
59
73
|
* `Bash(git status:*)`) — the last two are strict-side divergences from upstream, registered as such.
|
|
60
74
|
*/
|
|
75
|
+
import type { AssertAllKeysHandled } from "./ask-origin.js";
|
|
76
|
+
/**
|
|
77
|
+
* The closed set of rule BEHAVIORS — what a matching rule says about the call. The upstream vocabulary
|
|
78
|
+
* verbatim (`allow` / `deny` / `ask`, the three settings lists), in PRECEDENCE order: when rules of more
|
|
79
|
+
* than one behavior speak for one call, the earliest member here decides ({@link RULE_BEHAVIOR_PRECEDENCE}
|
|
80
|
+
* is that order as a table, and {@link adjudicatePersistedRules} is the one place that walks it).
|
|
81
|
+
*
|
|
82
|
+
* The type is derived FROM the array so a member added to either side is a compile error; a value read
|
|
83
|
+
* off a wire or a file goes through {@link isRuleBehavior} (present but outside the set = refused, never
|
|
84
|
+
* defaulted) or {@link readRowBehavior} (the ONE reading of a row that predates the field).
|
|
85
|
+
*/
|
|
86
|
+
export declare const RULE_BEHAVIORS: readonly ["deny", "ask", "allow"];
|
|
87
|
+
export type RuleBehavior = (typeof RULE_BEHAVIORS)[number];
|
|
88
|
+
/** Membership test for {@link RuleBehavior}. Exported because the word crosses process boundaries (a
|
|
89
|
+
* store row, a sync payload, a settings entry) and a host validating one must not hand-roll the set. */
|
|
90
|
+
export declare function isRuleBehavior(v: unknown): v is RuleBehavior;
|
|
91
|
+
/**
|
|
92
|
+
* The precedence table: the rank at which each behavior decides a call when several rules speak (lower
|
|
93
|
+
* decides first). Upstream's arbiter walks deny, then ask, then allow — an explicit deny is never
|
|
94
|
+
* outvoted by an allow, and a standing "ask me each time" is never silenced by a remembered yes. Every
|
|
95
|
+
* adjudication over a mixed rule table orders its walk by THIS table (never by a hand-written if-chain),
|
|
96
|
+
* so the rank of a behavior is stated once.
|
|
97
|
+
*/
|
|
98
|
+
export declare const RULE_BEHAVIOR_PRECEDENCE: {
|
|
99
|
+
readonly deny: 0;
|
|
100
|
+
readonly ask: 1;
|
|
101
|
+
readonly allow: 2;
|
|
102
|
+
};
|
|
103
|
+
/** Compile-time fence over the precedence table: `never` while every behavior has a rank. */
|
|
104
|
+
export type PrecedenceTableCoversEveryBehavior = AssertAllKeysHandled<Exclude<RuleBehavior, keyof typeof RULE_BEHAVIOR_PRECEDENCE>>;
|
|
105
|
+
/** The behaviors in precedence order — the walk every mixed-table adjudication takes. */
|
|
106
|
+
export declare const RULE_BEHAVIORS_BY_PRECEDENCE: readonly RuleBehavior[];
|
|
107
|
+
/**
|
|
108
|
+
* The ONE reading of a persisted row's behavior at every seam that may meet a row written before the
|
|
109
|
+
* field existed — the file store's schema migration, the sync wire's inbound picker, a checkpoint's
|
|
110
|
+
* rehydrated session rows. An ABSENT behavior reads `"allow"`: every row an engine without the field
|
|
111
|
+
* ever wrote was an allow, so that reading is the row's own meaning, not a default. A PRESENT value
|
|
112
|
+
* outside the closed set is refused (`undefined`) — a word this engine cannot read must not become an
|
|
113
|
+
* allow by falling through (the loud-bad-value doctrine), and must not become a deny either (a
|
|
114
|
+
* fabricated tightening is still a fabrication).
|
|
115
|
+
*/
|
|
116
|
+
export declare function readRowBehavior(v: unknown): RuleBehavior | undefined;
|
|
61
117
|
/** The tools the rule lane speaks for. `"Bash"` is the historical member; `"Read"` (design/382 §2.2)
|
|
62
118
|
* speaks EXACTLY ONE grammar — the double-slash directory form `Read(//abs/**)`, the directory
|
|
63
119
|
* read-authorization a compound card's cd segment mints — and its admission is the CLOSED two-point
|
|
@@ -70,7 +126,19 @@
|
|
|
70
126
|
* silence the classifier's questions, never a mandated one), so a recorded human yes is exactly what
|
|
71
127
|
* clears it. The org/hook/matchedAskRule immunities stay: those conjuncts live in the gate's
|
|
72
128
|
* persisted-rule lane and do not loosen with this set. */
|
|
73
|
-
export type PersistedRuleTool =
|
|
129
|
+
export type PersistedRuleTool = string;
|
|
130
|
+
/** The one shell tool the command grammar speaks for. */
|
|
131
|
+
export declare const COMMAND_RULE_TOOL = "Bash";
|
|
132
|
+
/** The one tool whose path family has an ALLOW form (the `Read(//abs-dir/**)` directory grant). */
|
|
133
|
+
export declare const READ_RULE_TOOL = "Read";
|
|
134
|
+
/**
|
|
135
|
+
* Which tools a rule may name — the model's tool set is DERIVED, never a hand-kept list: the shell tool
|
|
136
|
+
* takes the COMMAND grammar; every tool the catalog declares a path target for (Read / Edit / Write /
|
|
137
|
+
* Glob / NotebookEdit / …, `pathTargetOf`) takes the PATH grammar. A rule on any other tool has no
|
|
138
|
+
* content this model can judge and is refused (`unsupported.tool`). The tool set therefore moves with the
|
|
139
|
+
* catalog's declarations, exactly as the protective fences' covered set does.
|
|
140
|
+
*/
|
|
141
|
+
export declare function ruleToolGrammarOf(toolName: string): "command" | "path" | undefined;
|
|
74
142
|
/**
|
|
75
143
|
* Match forms. Four members, two of them one predicate:
|
|
76
144
|
* · `exact` — admits the one string the rule spells.
|
|
@@ -82,13 +150,19 @@ export type PersistedRuleTool = "Bash" | "Read";
|
|
|
82
150
|
* on the single-command body. Kept as a fully supported COMPATIBILITY READ — stored rules are never
|
|
83
151
|
* migrated or rewritten — and it remains the ONLY spelling of the compound-prefix form
|
|
84
152
|
* (`Bash(cd /tmp && adb pull:*)`): the trailing-star form deliberately has no compound reading
|
|
85
|
-
* (see {@link
|
|
153
|
+
* (see {@link parseRuleText}), because upstream's own trailing-star pattern is inert on a
|
|
86
154
|
* compound and importing/minting one as a chain licence would grant what nobody approved.
|
|
87
|
-
* · `subpath` — the
|
|
88
|
-
* one absolute directory. Never admits any command; its one predicate is
|
|
89
|
-
*
|
|
155
|
+
* · `subpath` — the directory form (design/382 §2.2): `Tool(//abs-dir/**)`, reaching every path under
|
|
156
|
+
* one absolute directory. Never admits any command; its one predicate is {@link directoryRuleAdmits}.
|
|
157
|
+
* As an ALLOW it exists for the Read tool only (a standing yes to reads under a directory); as a
|
|
158
|
+
* deny/ask it is legal on every path-targeting tool.
|
|
159
|
+
* · `path` — the PATH PATTERN form, deny/ask only: upstream's file-rule spelling over a tool's path
|
|
160
|
+
* target (`Edit(~/.sema/settings.json)`, `Write(src/**)`, `Read(//etc/*)`), stored AS SPELLED and
|
|
161
|
+
* resolved at match time against the call's bases (`//` absolute, `~/` home, `/` project root,
|
|
162
|
+
* otherwise the working directory). Its predicate is {@link pathRuleReaches}. Not an allow form: a
|
|
163
|
+
* standing approval by path pattern would be a wider grant than any card ever shows a person.
|
|
90
164
|
*/
|
|
91
|
-
export type PersistedRuleMatch = "exact" | "prefix" | "wildcard" | "subpath";
|
|
165
|
+
export type PersistedRuleMatch = "exact" | "prefix" | "wildcard" | "subpath" | "path";
|
|
92
166
|
/**
|
|
93
167
|
* Where a rule applies — design/382 §4.1, the THREE consent dimensions.
|
|
94
168
|
*
|
|
@@ -143,13 +217,24 @@ export interface RuleAdd {
|
|
|
143
217
|
createdAt: string;
|
|
144
218
|
}
|
|
145
219
|
/**
|
|
146
|
-
* A persisted
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
220
|
+
* A persisted rule for one tool, in one of the three behaviors.
|
|
221
|
+
*
|
|
222
|
+
* The logical identity of a rule is the TRIPLE (behavior, rule text, scope): the same text may stand as
|
|
223
|
+
* an allow and as a deny in one bucket (upstream keeps the three lists side by side and lets precedence
|
|
224
|
+
* decide), so a tombstone, a quarantine row and a join group all key on the triple — a key on (text,
|
|
225
|
+
* scope) alone would let a delete aimed at the allow take the deny with it.
|
|
226
|
+
*
|
|
227
|
+
* `adds` is a real set, not a scalar: two concurrently approved records for the same identity each
|
|
228
|
+
* redeem their own dot, and a rule is live iff at least one of its adds survives the tombstones.
|
|
229
|
+
* Organization rules are a separate partition (a server-published snapshot, no CRDT) and never enter
|
|
230
|
+
* this type; the personal deny/ask rows here are the PERSON's own standing instructions (their settings'
|
|
231
|
+
* deny/ask lists, imported through the same consent door the allow list takes).
|
|
151
232
|
*/
|
|
152
|
-
export interface
|
|
233
|
+
export interface PersistedRule {
|
|
234
|
+
/** What a match of this rule says about the call — REQUIRED on every row this engine writes. A row
|
|
235
|
+
* read without one is a pre-field row and reads `allow` through {@link readRowBehavior}, at the
|
|
236
|
+
* reading seams only (the store's schema migration, the sync picker, a checkpoint's session rows). */
|
|
237
|
+
behavior: RuleBehavior;
|
|
153
238
|
/** Canonical rule text: `Bash(git status)` (exact) or `Bash(git status:*)` (prefix). A prefix body
|
|
154
239
|
* may itself be a connector chain (`Bash(cd /tmp && adb pull:*)`) — see the module note on
|
|
155
240
|
* connectors for what such a rule reaches. */
|
|
@@ -170,6 +255,8 @@ export interface PersistedAllowRule {
|
|
|
170
255
|
* in no tombstone for the same (rule, scope).
|
|
171
256
|
*/
|
|
172
257
|
export interface RuleTombstone {
|
|
258
|
+
/** The identity's behavior member — a tombstone deletes adds of ONE (behavior, text, scope). */
|
|
259
|
+
behavior: RuleBehavior;
|
|
173
260
|
rule: string;
|
|
174
261
|
scope: RuleScope;
|
|
175
262
|
removedDots: RuleDot[];
|
|
@@ -185,8 +272,11 @@ export interface RuleReject {
|
|
|
185
272
|
code: RuleRejectCode;
|
|
186
273
|
message: string;
|
|
187
274
|
}
|
|
188
|
-
/** The parsed, canonical shape of one rule text — everything but the scope and the
|
|
189
|
-
|
|
275
|
+
/** The parsed, canonical shape of one rule text under one behavior — everything but the scope and the
|
|
276
|
+
* provenance. The behavior is the parser's INPUT (it decides which validator floor applies), carried
|
|
277
|
+
* out so a consumer holding the parse never re-derives which list the text came from. */
|
|
278
|
+
export interface ParsedRule {
|
|
279
|
+
behavior: RuleBehavior;
|
|
190
280
|
rule: string;
|
|
191
281
|
tool: PersistedRuleTool;
|
|
192
282
|
match: PersistedRuleMatch;
|
|
@@ -209,7 +299,7 @@ export declare const MAX_RULE_TEXT_CHARS = 512;
|
|
|
209
299
|
* an EXACT rule naming a whole interpreter command line stays legal, since it authorizes one command.
|
|
210
300
|
*
|
|
211
301
|
* TWO READERS, and they consult this table with DIFFERENT questions — do not read either as the other.
|
|
212
|
-
* {@link
|
|
302
|
+
* {@link parseRuleText} asks "is this rule TEXT's own head a row?" and compares the basename as
|
|
213
303
|
* SPELLED, so an alias spelling (`SSH host:*`, `ssh.exe host:*`) is a different name to it and enters
|
|
214
304
|
* the store like any other rule. {@link genericPrefixBody} asks "should the engine PROPOSE a body with
|
|
215
305
|
* this head?" and normalizes first ({@link screenedHeadName}: case, trailing periods, executable
|
|
@@ -250,18 +340,16 @@ export declare const BARE_INTERPRETER_NAMES: ReadonlySet<string>;
|
|
|
250
340
|
* Shell KEYWORDS, refused as a segment's `argv[0]` everywhere this lane reads a command.
|
|
251
341
|
*
|
|
252
342
|
* The floor this lane stands on extracts the first TOKEN of a segment and calls it the command name.
|
|
253
|
-
* That identification is what every comparison here rests on — the interpreter refusal
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
* structure, so a keyword is never mistaken for a program); a token-level lane cannot, and pretending
|
|
264
|
-
* otherwise is the loosening direction.
|
|
343
|
+
* That identification is what every comparison here rests on — the interpreter refusal and a person's
|
|
344
|
+
* reading of the rule text. For a control structure it is simply false: `for x in once; do curl
|
|
345
|
+
* evil.example; done` splits into three segments whose first tokens are `for`, `do` and `done`; all
|
|
346
|
+
* three are ordinary bare words, so the floor accepts each, and a rule text could name a keyword as
|
|
347
|
+
* though it were a program.
|
|
348
|
+
*
|
|
349
|
+
* Refusing the keyword puts the whole command outside the ALLOW lane, which is the honest answer: no rule
|
|
350
|
+
* can be minted for it and no allow matches it, so it asks. The DENY/ASK direction reads these shapes
|
|
351
|
+
* with its own lexer (`shell-lexer.ts`: a prefixing keyword is stripped so `do curl evil.example` runs
|
|
352
|
+
* `curl`; a `for`/`case` header, whose commands need a syntax tree, is `unreadable` and asks).
|
|
265
353
|
*
|
|
266
354
|
* `!` is here for the same reason with a sharper edge: it is a keyword that PREFIXES a real command, so
|
|
267
355
|
* `! node -e …` is the `node` licence the interpreter table exists to refuse, wearing one extra token.
|
|
@@ -391,23 +479,46 @@ export declare function hasUnrenderableCharacters(text: string): boolean;
|
|
|
391
479
|
* product's settings, reloading a store file, and any future backend. A shape refused here cannot enter
|
|
392
480
|
* the store through any door.
|
|
393
481
|
*
|
|
394
|
-
* `
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
482
|
+
* `behavior` is REQUIRED — there is no default, because the two directions have different floors and
|
|
483
|
+
* a defaulted allow is exactly the silent-widening shape a validator must not have: the
|
|
484
|
+
* bare-interpreter-prefix refusal is an ALLOW-side argument (one approval click cannot be read as
|
|
485
|
+
* authorizing arbitrary programs), while a deny/ask has the opposite polarity — `Bash(node:*)` as a DENY
|
|
486
|
+
* is a legitimately wide tightening, and refusing it would silently disarm exactly the rule a person
|
|
487
|
+
* most wants. The tightening behaviors therefore admit the interpreter-headed prefix form and change
|
|
488
|
+
* nothing else; the allow floor is unchanged.
|
|
399
489
|
*/
|
|
400
|
-
export declare function
|
|
401
|
-
|
|
402
|
-
}): {
|
|
403
|
-
rule: ParsedAllowRule;
|
|
490
|
+
export declare function parseRuleText(text: string, behavior: RuleBehavior): {
|
|
491
|
+
rule: ParsedRule;
|
|
404
492
|
} | {
|
|
405
493
|
reject: RuleReject;
|
|
406
494
|
};
|
|
407
|
-
/** The canonical spelling of a rule for a (already folded) command and match form
|
|
408
|
-
* command is the lexical-normal absolute directory (one leading slash), re-spelled in the
|
|
409
|
-
* double-slash directory form. */
|
|
410
|
-
export declare function
|
|
495
|
+
/** The canonical spelling of a rule for a (already folded) command and match form on `tool`. For
|
|
496
|
+
* `subpath` the command is the lexical-normal absolute directory (one leading slash), re-spelled in the
|
|
497
|
+
* double-slash directory form; for `path` it is the pattern as spelled. */
|
|
498
|
+
export declare function formatRuleText(command: string, match: PersistedRuleMatch, tool: PersistedRuleTool): string;
|
|
499
|
+
/**
|
|
500
|
+
* The bases a path pattern resolves against at match time — facts of the CALL, never of the rule:
|
|
501
|
+
* `home` for `~/…` (absent ⇒ `os.homedir()`), `root` for `/…` (the task's project root), `cwd` for a
|
|
502
|
+
* relative pattern (the live working directory; absent ⇒ `root`). A pattern whose base is absent
|
|
503
|
+
* resolves to nothing and reaches nothing.
|
|
504
|
+
*/
|
|
505
|
+
export interface PathRuleBases {
|
|
506
|
+
readonly root?: string;
|
|
507
|
+
readonly cwd?: string;
|
|
508
|
+
readonly home?: string;
|
|
509
|
+
}
|
|
510
|
+
/** Is this base usable by {@link resolvePathPattern} — an absolute path with a lexical normal form? A rule
|
|
511
|
+
* compiler asks this at compile time so a pattern is refused rather than compiled inert. */
|
|
512
|
+
export declare function isUsablePathBase(base: string | undefined): boolean;
|
|
513
|
+
/**
|
|
514
|
+
* The PATH family's tightening predicate: does this deny/ask rule reach the call's target path? The
|
|
515
|
+
* target is the caller's ALREADY-RESOLVED lexical-normal absolute path (the same identity the read/write
|
|
516
|
+
* fences judge with). A `subpath` rule reaches the directory and everything under it
|
|
517
|
+
* ({@link directoryRuleAdmits}); a `path` rule resolves its spelling against the call's bases and
|
|
518
|
+
* matches segment-wise (`*` within a segment, `**` across segments), reaching the path it names and
|
|
519
|
+
* everything under a directory it names. A command-family rule reaches no path.
|
|
520
|
+
*/
|
|
521
|
+
export declare function pathRuleReaches(rule: Pick<PersistedRule, "match" | "command">, target: string, bases: PathRuleBases): boolean;
|
|
411
522
|
/**
|
|
412
523
|
* Does this rule's command pattern admit `command`?
|
|
413
524
|
*
|
|
@@ -425,22 +536,74 @@ export declare function formatAllowRuleText(command: string, match: PersistedRul
|
|
|
425
536
|
* same count equality is the containment: `Bash(a && b:*)` admits `a && b x`, never `a && b x && c`.
|
|
426
537
|
* An EXACT rule has no such reach by construction: it admits one string, the one it spells.
|
|
427
538
|
*/
|
|
428
|
-
export declare function ruleAdmitsCommand(rule: Pick<
|
|
539
|
+
export declare function ruleAdmitsCommand(rule: Pick<PersistedRule, "match" | "command">, command: string): boolean;
|
|
429
540
|
/**
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
* is
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
541
|
+
* What one TIGHTENING (deny/ask) rule can say about a call — the closed three-word answer of
|
|
542
|
+
* {@link programRunReachOf}:
|
|
543
|
+
* - `reached` — the text PROVABLY performs a run the rule names (the whole chain, or any one segment);
|
|
544
|
+
* - `not_reached` — the text provably does not (every compared word is literal and differs, or the
|
|
545
|
+
* command is shorter than the rule's body);
|
|
546
|
+
* - `unreadable` — the text MAY perform it but cannot be read: an expansion sits where the rule reads a
|
|
547
|
+
* word, a quote or substitution never closes, a syntax error, a control structure whose commands need
|
|
548
|
+
* a syntax tree, a wrapper handed its command as one string.
|
|
549
|
+
* The three lanes that consume it (the org partition, the personal deny/ask rows, the rule DSL's
|
|
550
|
+
* content-form rules) dispose of the words identically: `reached` speaks with the rule's behavior,
|
|
551
|
+
* `not_reached` is silence, and `unreadable` is a FAIL-CLOSED ask — never silence, which is what once let
|
|
552
|
+
* `rm -r x; echo "$HOME"` walk past a deny on `rm -r` because one expansion took the whole line out of
|
|
553
|
+
* the reader. Registered in docs/CLOSED-SETS.md.
|
|
554
|
+
*/
|
|
555
|
+
export declare const PROGRAM_RUN_REACHES: readonly ["reached", "not_reached", "unreadable"];
|
|
556
|
+
export type ProgramRunReach = (typeof PROGRAM_RUN_REACHES)[number];
|
|
557
|
+
/** The answer of {@link programRunReachOf}: the word, and for `unreadable` the reason a surface can show. */
|
|
558
|
+
export type ProgramRunReachOutcome = {
|
|
559
|
+
readonly reach: "reached" | "not_reached";
|
|
560
|
+
} | {
|
|
561
|
+
readonly reach: "unreadable";
|
|
562
|
+
readonly reason: string;
|
|
563
|
+
};
|
|
564
|
+
/**
|
|
565
|
+
* Does one TIGHTENING (deny/ask) rule speak about this call — as a whole, or about any single program
|
|
566
|
+
* run in it — or can the call not be read against it at all? The ONE reach predicate of the tightening
|
|
567
|
+
* direction, shared by the org partition, the personal deny/ask rows and the rule DSL's content-form
|
|
568
|
+
* deny/ask rules, so no two of them can disagree about what a deny reaches.
|
|
569
|
+
*
|
|
570
|
+
* The allow lane asks a different question and gets a different answer, deliberately. An allow rule is
|
|
571
|
+
* one recorded human yes and must cover the WHOLE call to clear it ({@link ruleAdmitsCommand}:
|
|
572
|
+
* `Bash(npm test)` does not clear `npm test && curl evil.example`, because nobody said yes to the second
|
|
573
|
+
* half), and it refuses every shell construct outright. A deny/ask rule is a standing statement about a
|
|
574
|
+
* program run, and a call that MAY perform that run is a call the rule speaks about — so this direction
|
|
575
|
+
* reads the command with a real lexer ({@link readShellCommand}): redirections are stripped at the
|
|
576
|
+
* token level, quotes are removed on BOTH sides before words are compared (`rm "-r" x` runs `rm -r x`),
|
|
577
|
+
* wrappers are peeled (`sudo env X=1 nohup rm -r x` runs `rm -r x`), every connector — `&` and a newline
|
|
578
|
+
* included — bounds a segment, and a comment is not a run. Deliberately an OVER-approximation, in the
|
|
579
|
+
* tightening direction: the segments are the runs the text can reach, not the runs that will happen, so
|
|
580
|
+
* a short-circuited branch (`true || curl evil.example`) is judged as though it ran. Answering "does this
|
|
581
|
+
* definitely execute" needs evaluation, not parsing; over-firing costs an approval prompt, and the
|
|
582
|
+
* opposite error costs the rule.
|
|
583
|
+
*
|
|
584
|
+
* Words are compared ARGV-wise, position by position, against the rule body read by the same lexer —
|
|
585
|
+
* over every run CANDIDATE a segment carries: as spelled, then each peeled layer (`sudo rm -r x` is a run
|
|
586
|
+
* of `sudo …` AND of `rm -r x`; a deny on either reaches; a rule body is never peeled, so
|
|
587
|
+
* `Bash(sudo rm -r x)` denies exactly that). A literal word that differs settles `not_reached`; a word
|
|
588
|
+
* carrying an expansion at a position the rule reads settles `unreadable` (the text could spell the
|
|
589
|
+
* rule's word there — unless it is the SAME glob/brace spelling the body wrote); a prefix body whose
|
|
590
|
+
* every word is matched is `reached` whatever follows; an exact body needs equal length — a longer
|
|
591
|
+
* command is `not_reached` unless an extra word is an UNQUOTED expansion (or `"$@"` / an array
|
|
592
|
+
* expansion), which may expand to nothing. The commands inside a substitution and an unquoted heredoc
|
|
593
|
+
* body are segments of their own (`echo "$(rm -r x)"` runs `rm`). A compound
|
|
594
|
+
* body (`Bash(echo ok && rm -r:*)`) reaches only a chain of the same shape — same segment count, same
|
|
595
|
+
* connectors, every leading segment equal, the final one by the body's match kind; a single-segment
|
|
596
|
+
* body reaches any segment. A segment the lexer could not read makes the answer `unreadable` when no
|
|
597
|
+
* other segment already reached.
|
|
598
|
+
*
|
|
599
|
+
* A rule body the lexer cannot read (foreign store bytes; every entrance validates against exactly
|
|
600
|
+
* this) reaches nothing. A path-family rule (`subpath` / `path`) reaches no command: its predicates are
|
|
601
|
+
* {@link directoryRuleAdmits} and {@link pathRuleReaches}.
|
|
442
602
|
*/
|
|
443
|
-
export declare function
|
|
603
|
+
export declare function programRunReachOf(rule: Pick<PersistedRule, "match" | "command">, command: string): ProgramRunReachOutcome;
|
|
604
|
+
/** The boolean face of {@link programRunReachOf}: `reached` only. The shadow view and the pins that
|
|
605
|
+
* assert a reach read this; a lane that must fail closed on `unreadable` reads the outcome. */
|
|
606
|
+
export declare function ruleReachesProgramRun(rule: Pick<PersistedRule, "match" | "command">, command: string): boolean;
|
|
444
607
|
/** design/382 §3.3-5 — one BREADTH note about a rule a person is about to persist: which wide shape
|
|
445
608
|
* it has, said in words a surface can show beside the input line. A warning is never a refusal —
|
|
446
609
|
* the closed refusal set is untouched — and never adjudication input. */
|
|
@@ -478,21 +641,7 @@ export interface EditedRuleBreadthWarning {
|
|
|
478
641
|
* The upstream editor validates edited text but shows no breadth evidence at all; this seat is a
|
|
479
642
|
* superset disclosure (registered divergence, design/382 §6-4).
|
|
480
643
|
*/
|
|
481
|
-
export declare function ruleBreadthWarningsOf(rule: Pick<
|
|
482
|
-
/**
|
|
483
|
-
* The segments of `command` as the deny/ask layer must judge them, or `undefined` for a command this
|
|
484
|
-
* lane cannot read.
|
|
485
|
-
*
|
|
486
|
-
* A tightening rule speaks about a PROGRAM RUN, and a compound runs several. `Bash(curl:*)` published
|
|
487
|
-
* as a deny means "this machine does not make that call", and reading `npm test && curl evil.example`
|
|
488
|
-
* as one unmatched blob answered that with silence — the shape the widening above would otherwise make
|
|
489
|
-
* permanently approvable. Exported (rather than folded into a matcher here) because the layer that
|
|
490
|
-
* needs it holds the rules: this module owns what a command IS, `permission-rule-org.ts` owns what the
|
|
491
|
-
* organization says about each part of it.
|
|
492
|
-
*
|
|
493
|
-
* A single simple command yields a one-element list, so a deny that matched before matches identically.
|
|
494
|
-
*/
|
|
495
|
-
export declare function ruleLaneSegmentsOf(command: string): readonly string[] | undefined;
|
|
644
|
+
export declare function ruleBreadthWarningsOf(rule: Pick<PersistedRule, "match" | "command">): readonly EditedRuleBreadthWarning[];
|
|
496
645
|
/**
|
|
497
646
|
* Is `path` inside (or equal to) `root`? Word-boundary containment on the path separator, so `/a` does
|
|
498
647
|
* not contain `/ab`. Both sides are expected to be canonical already.
|
|
@@ -501,8 +650,8 @@ export declare function pathWithinRoot(path: string, root: string): boolean;
|
|
|
501
650
|
/**
|
|
502
651
|
* The LEXICAL NORMALIZER for a CALL's target path (design/382 §2.2 — the rule family's one path
|
|
503
652
|
* identity is the lexical normal form, zero IO): collapse slash runs and `.` segments, resolve `..`
|
|
504
|
-
* segments textually
|
|
505
|
-
*
|
|
653
|
+
* segments textually (a `..` at the root stays at the root, as POSIX resolves it), and answer the
|
|
654
|
+
* one-leading-slash absolute spelling — or `undefined` for a relative path. RULE
|
|
506
655
|
* bodies never come through here (their canonical spelling is unique and non-normal spellings are
|
|
507
656
|
* refused at the validator); this is for the comparison INPUT a caller derives from a live call.
|
|
508
657
|
*/
|
|
@@ -520,13 +669,16 @@ export declare function lexicalNormalAbsolutePathOf(path: string): string | unde
|
|
|
520
669
|
* caller cannot smuggle a second path identity through the comparison. Containment is the
|
|
521
670
|
* word-boundary one ({@link pathWithinRoot}: `/a` never reaches `/ab`).
|
|
522
671
|
*
|
|
523
|
-
* What it never does, stated as loudly as what it does (§2.5): admits READS ONLY —
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
672
|
+
* What it never does, stated as loudly as what it does (§2.5): as an ALLOW it admits READS ONLY — the
|
|
673
|
+
* TOOL conjunct is the caller's (every consumer judges eligibility with the call's tool, and
|
|
674
|
+
* {@link adjudicatePersistedPathRules} takes the allow arm for the Read tool alone), so a directory allow
|
|
675
|
+
* never admits a write, never a whole Bash command, never a non-cd segment — and it clears ASKS only,
|
|
676
|
+
* never a filesystem fence: workspace containment, the sensitive-read deny face and every violation
|
|
677
|
+
* refusal judge exactly as they would without the rule (a fence refusal is structural, not an ask, so
|
|
678
|
+
* there is nothing here for it to clear). As a DENY/ASK the same containment refuses or questions every
|
|
679
|
+
* path under the directory, on whichever path tool the rule names.
|
|
528
680
|
*/
|
|
529
|
-
export declare function directoryRuleAdmits(rule: Pick<
|
|
681
|
+
export declare function directoryRuleAdmits(rule: Pick<PersistedRule, "match" | "command">, path: string): boolean;
|
|
530
682
|
/** Does a rule's scope cover a task running in `cwd` (and, for the session dimension, in the session
|
|
531
683
|
* named by `sessionId`)? A project rule needs a cwd to compare against; without one it covers nothing
|
|
532
684
|
* (fail-closed). A session rule (design/382 §4.3) covers a call iff the call's `sessionId` equals the
|
|
@@ -539,7 +691,7 @@ export declare function directoryRuleAdmits(rule: Pick<PersistedAllowRule, "tool
|
|
|
539
691
|
export declare function scopeCoversCwd(scope: RuleScope, cwd: string | undefined, sessionId?: string): boolean;
|
|
540
692
|
/** Is this rule live — i.e. does it still carry at least one add? Deleted adds are removed by the store
|
|
541
693
|
* when tombstones are applied, so a rule with an empty `adds` is a rule that no longer exists. */
|
|
542
|
-
export declare function isRuleLive(rule:
|
|
694
|
+
export declare function isRuleLive(rule: PersistedRule): boolean;
|
|
543
695
|
/**
|
|
544
696
|
* design/375 §5.1 — MAY this rule speak about this call's CONTEXT? Tool identity plus scope coverage,
|
|
545
697
|
* and nothing about the rule's liveness: the CONTEXT question is the half a not-yet-persisted rule (a
|
|
@@ -551,7 +703,7 @@ export declare function isRuleLive(rule: PersistedAllowRule): boolean;
|
|
|
551
703
|
* cwd to compare against covers nothing (fail-closed, {@link scopeCoversCwd}).
|
|
552
704
|
*
|
|
553
705
|
* `scope` rides beside the rule rather than inside it for the PROPOSED half's sake: a
|
|
554
|
-
* {@link
|
|
706
|
+
* {@link ParsedRule} carries no scope, so a proposal is judged against the scope it would LAND in.
|
|
555
707
|
*
|
|
556
708
|
* design/382 §4.3 — the call context carries a THIRD axis, `sessionId` (optional: an existing caller
|
|
557
709
|
* that threads only `{tool, cwd}` still compiles and still means what it meant — no session rule can
|
|
@@ -576,13 +728,16 @@ export declare function eligibleContext(rule: {
|
|
|
576
728
|
* conjunction arm and its coverage-set selection here, the suggestion face's coverage judgement and the
|
|
577
729
|
* consent prepare's ({@link segmentCoverageOf} is the shared instrument). One predicate, so a rule that
|
|
578
730
|
* one consumer refuses cannot be a rule another counts: the historically inlined trio of checks
|
|
579
|
-
* (live / tool / scope) is this function, verbatim
|
|
731
|
+
* (live / tool / scope) is this function, verbatim — plus the BEHAVIOR conjunct: a mixed table holds
|
|
732
|
+
* allow, deny and ask rows, and a consumer asking "which allow rules cover this" must not be answered
|
|
733
|
+
* by a deny row that happens to spell the same text. The behavior is a REQUIRED argument for the same
|
|
734
|
+
* reason the parser's is: a defaulted allow here is a silent widening.
|
|
580
735
|
*/
|
|
581
|
-
export declare function eligiblePersisted(rule:
|
|
736
|
+
export declare function eligiblePersisted(rule: PersistedRule, call: {
|
|
582
737
|
tool: string;
|
|
583
738
|
cwd: string | undefined;
|
|
584
739
|
sessionId?: string;
|
|
585
|
-
}): boolean;
|
|
740
|
+
}, behavior: RuleBehavior): boolean;
|
|
586
741
|
/**
|
|
587
742
|
* Find the rules that admit this command for a task in `cwd`, or `undefined`.
|
|
588
743
|
*
|
|
@@ -611,8 +766,12 @@ export declare function eligiblePersisted(rule: PersistedAllowRule, call: {
|
|
|
611
766
|
*
|
|
612
767
|
* Order is defined only for reporting (what the decision names); no rule is stronger than another —
|
|
613
768
|
* they are all the same one answer, "the person already said yes to this".
|
|
769
|
+
*
|
|
770
|
+
* ALLOW rows only: this is the allow direction's question, and a deny/ask row in the same table is not
|
|
771
|
+
* coverage (the eligibility predicate's behavior conjunct). The mixed-table walk that orders the three
|
|
772
|
+
* behaviors is {@link adjudicatePersistedRules}.
|
|
614
773
|
*/
|
|
615
|
-
export declare function findAdmittingRule(rules: readonly
|
|
774
|
+
export declare function findAdmittingRule(rules: readonly PersistedRule[], call: {
|
|
616
775
|
tool: string;
|
|
617
776
|
command: string;
|
|
618
777
|
cwd: string | undefined;
|
|
@@ -622,7 +781,63 @@ export declare function findAdmittingRule(rules: readonly PersistedAllowRule[],
|
|
|
622
781
|
* `./x` against the moved cursor, not the task root). Defaults to `cwd`; the scope/eligibility
|
|
623
782
|
* axis stays `cwd` either way. */
|
|
624
783
|
execCwd?: string;
|
|
625
|
-
}): readonly
|
|
784
|
+
}): readonly PersistedRule[] | undefined;
|
|
785
|
+
/** The answer of {@link adjudicatePersistedRules}: which behavior decided, and the rule(s) it stands
|
|
786
|
+
* on — a one-element set for a deny/ask (the first table-order rule of the deciding behavior that
|
|
787
|
+
* reaches the call) and the allow direction's coverage SET ({@link findAdmittingRule}). */
|
|
788
|
+
export interface PersistedRuleVerdict {
|
|
789
|
+
readonly behavior: RuleBehavior;
|
|
790
|
+
readonly rules: readonly PersistedRule[];
|
|
791
|
+
/** The call could not be READ against the person's deny/ask rows (see {@link programRunReachOf}):
|
|
792
|
+
* `behavior` is `ask`, `rules` names the first tightening row that could not be judged, and this is
|
|
793
|
+
* the lexer's reason. The lane projects it as an UNREADABLE answer — the same fail-closed shape an
|
|
794
|
+
* unreadable store takes — never as a matched ask rule. */
|
|
795
|
+
readonly unreadable?: string;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* The ONE adjudication over a MIXED persisted-rule table for a shell call: walk the behaviors in
|
|
799
|
+
* {@link RULE_BEHAVIOR_PRECEDENCE} order and answer with the first behavior whose eligible rules speak
|
|
800
|
+
* for the call — deny before ask before allow, the upstream arbiter's order across its three lists.
|
|
801
|
+
*
|
|
802
|
+
* Each behavior is judged with ITS direction's instrument: a deny/ask row through
|
|
803
|
+
* {@link programRunReachOf} (the wide tightening reading: whole command or any program run in it,
|
|
804
|
+
* three-valued), an allow through {@link findAdmittingRule} (the narrow reading: the whole call, or
|
|
805
|
+
* every segment of it, covered by recorded yeses). The asymmetry is the point and runs one way only: a
|
|
806
|
+
* deny reads MORE of a command than an allow does, so an exact allow naming a whole chain can never
|
|
807
|
+
* outrun a deny on one of its parts. A call the lexer cannot read against a standing deny/ask row is an
|
|
808
|
+
* UNREADABLE verdict (`behavior: "ask"` + `unreadable`), returned before any allow is consulted: the
|
|
809
|
+
* question "does one of my rules forbid this?" is unanswered, and a recorded yes must not answer it.
|
|
810
|
+
* Eligibility (live, this tool, scope covers the call, THIS behavior) is the one predicate every
|
|
811
|
+
* consumer shares. `undefined` ⇒ no rule of any behavior speaks.
|
|
812
|
+
*/
|
|
813
|
+
export declare function adjudicatePersistedRules(rules: readonly PersistedRule[], call: {
|
|
814
|
+
tool: string;
|
|
815
|
+
command: string;
|
|
816
|
+
cwd: string | undefined;
|
|
817
|
+
sessionId?: string;
|
|
818
|
+
execCwd?: string;
|
|
819
|
+
}): PersistedRuleVerdict | undefined;
|
|
820
|
+
/**
|
|
821
|
+
* The path-family sibling of {@link adjudicatePersistedRules}: the same precedence walk over the rules
|
|
822
|
+
* of one path-targeting tool, judged on the call's resolved target path.
|
|
823
|
+
*
|
|
824
|
+
* TWO targets, because the two directions read the call's path slot differently (design/388 B6/B17): the
|
|
825
|
+
* tightening arms judge the PROTECTIVE target (`protectivePathTargetOf` — the object face's declaration,
|
|
826
|
+
* the catalog's for the name as the floor: reading the catalog for an undeclared same-name tool can only
|
|
827
|
+
* tighten a deny) with {@link pathRuleReaches}; the allow arm judges the DECLARED target
|
|
828
|
+
* (`declaredPathTargetOf` — the face's OWN declaration only: a same-name tool that declares no path
|
|
829
|
+
* target is unconfirmable, and no grant admits it) with {@link directoryRuleAdmits}, and for the Read tool
|
|
830
|
+
* ONLY — a directory allow is a standing yes to reads, never to a write. Either target may be absent
|
|
831
|
+
* (no resolvable path in that reading); the arm that needs it then speaks for nothing.
|
|
832
|
+
*/
|
|
833
|
+
export declare function adjudicatePersistedPathRules(rules: readonly PersistedRule[], call: {
|
|
834
|
+
tool: string;
|
|
835
|
+
cwd: string | undefined;
|
|
836
|
+
sessionId?: string;
|
|
837
|
+
}, targets: {
|
|
838
|
+
readonly tighten?: string;
|
|
839
|
+
readonly allow?: string;
|
|
840
|
+
}, bases: PathRuleBases): PersistedRuleVerdict | undefined;
|
|
626
841
|
/**
|
|
627
842
|
* design/375 §5.2 — the per-segment coverage table for `command`: which segments an eligible rule
|
|
628
843
|
* already admits, judged with the SAME split and the same eligibility predicates as the gate's
|
|
@@ -654,9 +869,9 @@ export declare function findAdmittingRule(rules: readonly PersistedAllowRule[],
|
|
|
654
869
|
* adjudication inputs.
|
|
655
870
|
*/
|
|
656
871
|
export declare function segmentCoverageOf(command: string, rules: {
|
|
657
|
-
persisted: readonly
|
|
872
|
+
persisted: readonly PersistedRule[];
|
|
658
873
|
proposed?: readonly {
|
|
659
|
-
rule:
|
|
874
|
+
rule: ParsedRule;
|
|
660
875
|
scope: RuleScope;
|
|
661
876
|
}[];
|
|
662
877
|
}, call: {
|