@gaunt-sloth/core 2.0.0-alpha.24 → 2.0.0-alpha.26
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/README.md +1 -1
- package/dist/config/colour.d.ts +38 -0
- package/dist/config/colour.js +36 -0
- package/dist/config/colour.js.map +1 -0
- package/dist/config/defaults.d.ts +1 -0
- package/dist/config/defaults.js +1 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/loader.d.ts +13 -6
- package/dist/config/loader.js +171 -28
- package/dist/config/loader.js.map +1 -1
- package/dist/config/mouse.d.ts +50 -0
- package/dist/config/mouse.js +44 -0
- package/dist/config/mouse.js.map +1 -0
- package/dist/config/schema.d.ts +2372 -114
- package/dist/config/schema.js +845 -16
- package/dist/config/schema.js.map +1 -1
- package/dist/config/shell-policy.d.ts +466 -116
- package/dist/config/shell-policy.js +273 -62
- package/dist/config/shell-policy.js.map +1 -1
- package/dist/config/tool-descriptions.d.ts +165 -0
- package/dist/config/tool-descriptions.js +229 -0
- package/dist/config/tool-descriptions.js.map +1 -0
- package/dist/config/types.d.ts +36 -1
- package/dist/config/types.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/core/GthAbstractAgent.d.ts +59 -1
- package/dist/core/GthAbstractAgent.js +72 -0
- package/dist/core/GthAbstractAgent.js.map +1 -1
- package/dist/core/GthAgentRunner.d.ts +341 -58
- package/dist/core/GthAgentRunner.js +817 -149
- package/dist/core/GthAgentRunner.js.map +1 -1
- package/dist/core/GthLangChainAgent.d.ts +16 -0
- package/dist/core/GthLangChainAgent.js +72 -2
- package/dist/core/GthLangChainAgent.js.map +1 -1
- package/dist/core/approvals/annotations.d.ts +122 -0
- package/dist/core/approvals/annotations.js +137 -0
- package/dist/core/approvals/annotations.js.map +1 -0
- package/dist/core/approvals/grants.d.ts +216 -0
- package/dist/core/approvals/grants.js +469 -0
- package/dist/core/approvals/grants.js.map +1 -0
- package/dist/core/approvals/matcher.d.ts +202 -0
- package/dist/core/approvals/matcher.js +267 -0
- package/dist/core/approvals/matcher.js.map +1 -0
- package/dist/core/approvals/mcpSubjects.d.ts +40 -0
- package/dist/core/approvals/mcpSubjects.js +99 -0
- package/dist/core/approvals/mcpSubjects.js.map +1 -0
- package/dist/core/approvals/toolAnnotationSources.d.ts +105 -0
- package/dist/core/approvals/toolAnnotationSources.js +277 -0
- package/dist/core/approvals/toolAnnotationSources.js.map +1 -0
- package/dist/core/approvals/toolHost.d.ts +46 -0
- package/dist/core/approvals/toolHost.js +108 -0
- package/dist/core/approvals/toolHost.js.map +1 -0
- package/dist/core/launchBanner.d.ts +120 -0
- package/dist/core/launchBanner.js +399 -0
- package/dist/core/launchBanner.js.map +1 -0
- package/dist/core/plainToolIndication.js +13 -7
- package/dist/core/plainToolIndication.js.map +1 -1
- package/dist/core/shell/approvalStop.d.ts +75 -0
- package/dist/core/shell/approvalStop.js +108 -0
- package/dist/core/shell/approvalStop.js.map +1 -0
- package/dist/core/shell/arity.d.ts +6 -0
- package/dist/core/shell/arity.js +20 -6
- package/dist/core/shell/arity.js.map +1 -1
- package/dist/core/shell/denylist.d.ts +11 -0
- package/dist/core/shell/denylist.js +37 -0
- package/dist/core/shell/denylist.js.map +1 -0
- package/dist/core/shell/normalize.d.ts +41 -1
- package/dist/core/shell/normalize.js +58 -4
- package/dist/core/shell/normalize.js.map +1 -1
- package/dist/core/shell/openWorld.d.ts +138 -0
- package/dist/core/shell/openWorld.js +523 -0
- package/dist/core/shell/openWorld.js.map +1 -0
- package/dist/core/shell/rater.d.ts +560 -0
- package/dist/core/shell/rater.js +895 -0
- package/dist/core/shell/rater.js.map +1 -0
- package/dist/core/shell/raterModel.d.ts +41 -0
- package/dist/core/shell/raterModel.js +51 -0
- package/dist/core/shell/raterModel.js.map +1 -0
- package/dist/core/shell/rejection.d.ts +66 -0
- package/dist/core/shell/rejection.js +38 -0
- package/dist/core/shell/rejection.js.map +1 -0
- package/dist/core/types.d.ts +71 -9
- package/dist/core/types.js.map +1 -1
- package/dist/runtime/askStructured.d.ts +8 -7
- package/dist/runtime/askStructured.js +8 -7
- package/dist/runtime/askStructured.js.map +1 -1
- package/dist/runtime/conversation.js +89 -76
- package/dist/runtime/conversation.js.map +1 -1
- package/dist/runtime/singleShot.js +71 -60
- package/dist/runtime/singleShot.js.map +1 -1
- package/dist/utils/ProgressIndicator.d.ts +21 -0
- package/dist/utils/ProgressIndicator.js +30 -3
- package/dist/utils/ProgressIndicator.js.map +1 -1
- package/dist/utils/consoleUtils.d.ts +30 -0
- package/dist/utils/consoleUtils.js +27 -2
- package/dist/utils/consoleUtils.js.map +1 -1
- package/dist/utils/systemUtils.d.ts +17 -0
- package/dist/utils/systemUtils.js +17 -0
- package/dist/utils/systemUtils.js.map +1 -1
- package/package.json +5 -2
- package/schema/gsloth-config.schema.json +1216 -417
- package/dist/core/shell/allowlist.d.ts +0 -75
- package/dist/core/shell/allowlist.js +0 -187
- package/dist/core/shell/allowlist.js.map +0 -1
- package/dist/core/shell/judge.d.ts +0 -161
- package/dist/core/shell/judge.js +0 -261
- package/dist/core/shell/judge.js.map +0 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @packageDocumentation
|
|
3
3
|
* Shell / dev-tools policy: the {@link GthDevToolsConfig} type plus all the resolvers
|
|
4
|
-
* that interpret it (shell enablement, timeouts, output budget,
|
|
5
|
-
*
|
|
4
|
+
* that interpret it (shell enablement, timeouts, output budget, per-command dev-tools
|
|
5
|
+
* selection), and — since CFG-27 — the {@link ApprovalsConfig} value and its resolver
|
|
6
|
+
* {@link resolveApprovals} (the five-rung ladder, the rater's identity profile, and the declared
|
|
7
|
+
* allow/deny lists).
|
|
6
8
|
*
|
|
7
9
|
* CFG-18 — the dev/shell tools are now configured through the unified {@link GthConfig.builtInTools}
|
|
8
10
|
* registry (`string[] | Record<string, boolean | BuiltInToolConfig>`), NOT the removed per-command
|
|
@@ -11,8 +13,8 @@
|
|
|
11
13
|
* `builtInTools` registry, and that {@link GthDevToolkit} + the shell accessors below consume. This
|
|
12
14
|
* keeps the toolkit/accessor surface stable while the single config surface is `builtInTools`.
|
|
13
15
|
*/
|
|
14
|
-
import type
|
|
15
|
-
import type { GthConfig
|
|
16
|
+
import { type GthCommand, StatusLevel } from '#src/core/types.js';
|
|
17
|
+
import type { GthConfig } from '#src/config/types.js';
|
|
16
18
|
/**
|
|
17
19
|
* CFG-18 — the per-tool config object carried as a value in the {@link GthConfig.builtInTools}
|
|
18
20
|
* registry (the object form's values, alongside a bare boolean that enables/force-disables a tool).
|
|
@@ -20,12 +22,16 @@ import type { GthConfig, LLMConfig } from '#src/config/types.js';
|
|
|
20
22
|
* discriminated union so the registry can carry every tool's shape:
|
|
21
23
|
* - the fixed dev-command tools (`run_tests`/`run_lint`/`run_build`/`run_single_test`) read
|
|
22
24
|
* {@link command} — the shell command to run; its presence enables the tool;
|
|
23
|
-
* - `run_shell_command` reads the EXT-9/
|
|
24
|
-
* {@link maxOutputBytes}
|
|
25
|
-
* `yolo` is the folded former `shellYolo`);
|
|
25
|
+
* - `run_shell_command` reads the EXT-9/12 execution knobs ({@link enabled}/{@link timeout}/
|
|
26
|
+
* {@link maxOutputBytes});
|
|
26
27
|
* - `gth_grep` reads {@link fileSet} (GS2-51) — which corpus to search;
|
|
27
28
|
* - a plain built-in tool (`gth_checklist`, `gth_web_fetch`, …) reads {@link enabled} (or is
|
|
28
29
|
* toggled with a bare boolean in the registry).
|
|
30
|
+
*
|
|
31
|
+
* CFG-26 — the APPROVAL knobs (`allowlist`, `persistAllowlist`, `judge`, `yolo`) are gone from
|
|
32
|
+
* here and live in the top-level {@link ApprovalsConfig}. They were fields of the object shared by
|
|
33
|
+
* EVERY built-in tool, so `gth_grep: { yolo: true }` used to validate; approvals are a property of
|
|
34
|
+
* the session, not of one tool's registry entry.
|
|
29
35
|
*/
|
|
30
36
|
export interface BuiltInToolConfig {
|
|
31
37
|
/**
|
|
@@ -41,23 +47,6 @@ export interface BuiltInToolConfig {
|
|
|
41
47
|
timeout?: number;
|
|
42
48
|
/** `run_shell_command`: captured-output byte budget. See {@link SHELL_DEFAULT_MAX_OUTPUT_BYTES}. */
|
|
43
49
|
maxOutputBytes?: number;
|
|
44
|
-
/** `run_shell_command`: EXT-9 Tier-2 scoped allow-list master switch (default `true`). */
|
|
45
|
-
allowlist?: boolean;
|
|
46
|
-
/** `run_shell_command`: persist `always`-scoped approvals to the project file (default `true`). */
|
|
47
|
-
persistAllowlist?: boolean;
|
|
48
|
-
/** `run_shell_command`: EXT-10 LLM-as-judge safety gate (default OFF). */
|
|
49
|
-
judge?: boolean | {
|
|
50
|
-
enabled?: boolean;
|
|
51
|
-
autoApproveLow?: boolean;
|
|
52
|
-
blockHigh?: boolean;
|
|
53
|
-
model?: LLMConfig;
|
|
54
|
-
};
|
|
55
|
-
/**
|
|
56
|
-
* `run_shell_command`: opt out of the per-command approval prompt — the explicit "yolo" bypass
|
|
57
|
-
* (folded former top-level `shellYolo`). Dangerous by design; off by default. Example:
|
|
58
|
-
* `{ "run_shell_command": { "yolo": true } }`.
|
|
59
|
-
*/
|
|
60
|
-
yolo?: boolean;
|
|
61
50
|
/**
|
|
62
51
|
* `gth_grep` (GS2-51): which corpus the content-search tool scans, applied consistently to BOTH
|
|
63
52
|
* execution engines (native ripgrep and the in-process JS fallback):
|
|
@@ -87,7 +76,7 @@ export interface BuiltInToolConfig {
|
|
|
87
76
|
* { "builtInTools": {
|
|
88
77
|
* "gth_checklist": true,
|
|
89
78
|
* "gth_web_fetch": true,
|
|
90
|
-
* "run_shell_command": { "timeout": 300000
|
|
79
|
+
* "run_shell_command": { "timeout": 300000 }
|
|
91
80
|
* } }
|
|
92
81
|
* ```
|
|
93
82
|
* Turn the (code-mode default-on) shell OFF: `{ "builtInTools": { "run_shell_command": false } }`.
|
|
@@ -156,17 +145,16 @@ export interface GthDevToolsConfig {
|
|
|
156
145
|
* lacks (it can read/write files but not run commands).
|
|
157
146
|
*
|
|
158
147
|
* EXT-12 — default: ON in `code` mode, OFF elsewhere. When this is ABSENT/undefined,
|
|
159
|
-
* `code` mode emits the tool (still GATED behind the
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* `{ enabled }` object for symmetry with future per-tool options.
|
|
148
|
+
* `code` mode emits the tool (still GATED behind the approval gate — the absent-config
|
|
149
|
+
* default NEVER implies bypass); `exec` / `ask --write` keep it OFF. An EXPLICIT value
|
|
150
|
+
* always wins: `shell: false` (or `{ enabled: false }`) is a hard escape hatch that fully
|
|
151
|
+
* disables it even in `code`. Accepts a bare boolean or an `{ enabled }` object.
|
|
164
152
|
*
|
|
165
|
-
* Because the model chooses the command, every invocation is gated behind
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
153
|
+
* Because the model chooses the command, every invocation is gated behind the CFG-26
|
|
154
|
+
* approvals gate (LangChain `humanInTheLoopMiddleware`, wired via deepagents' `interruptOn`)
|
|
155
|
+
* UNLESS `approvals.mode: "bypass"` turns the gate off. The gate — not string-filtering — is
|
|
156
|
+
* the guardrail, so the command is passed through verbatim (pipes / `$` / `;` are all
|
|
157
|
+
* legitimate).
|
|
170
158
|
*
|
|
171
159
|
* The object form also tunes the EXT-9 Tier-1 hardening applied to every run
|
|
172
160
|
* (these have safe defaults so bare `shell: true` is already hardened):
|
|
@@ -179,63 +167,21 @@ export interface GthDevToolsConfig {
|
|
|
179
167
|
*
|
|
180
168
|
* A hardcoded hardline blocklist of catastrophic commands (rm -rf /, mkfs, dd
|
|
181
169
|
* to a block device, fork bomb, shutdown/reboot, …) is refused even under
|
|
182
|
-
*
|
|
170
|
+
* `approvals.mode: "bypass"`; that floor is not configurable.
|
|
183
171
|
*
|
|
184
172
|
* On-disk (CFG-18) these live on the `run_shell_command` entry of `builtInTools`, e.g.
|
|
185
173
|
* `{ "builtInTools": { "run_shell_command": true } }` or
|
|
186
174
|
* `{ "builtInTools": { "run_shell_command": { "timeout": 300000, "maxOutputBytes": 200000 } } }`.
|
|
187
175
|
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* `always` scope, flag-variants of the same classified operation auto-approve
|
|
192
|
-
* without re-prompting. Set `false` to require fresh approval for every command.
|
|
193
|
-
* - `persistAllowlist`: whether `always`-scoped approvals are written to the project
|
|
194
|
-
* allow-list file (`.gsloth/.gsloth-settings/shell-allowlist.json`). Default `true`.
|
|
195
|
-
* When `false`, an `always` decision behaves like `session` (in-memory only).
|
|
196
|
-
*
|
|
197
|
-
* The object form also accepts the EXT-10 LLM-as-judge safety gate (default OFF):
|
|
198
|
-
* - `judge`: an opt-in, tiered auto-approve pre-filter that vets each `run_shell_command`
|
|
199
|
-
* with a lightweight judge model BEFORE the human prompt. It auto-approves clearly-safe
|
|
200
|
-
* commands (fatigue reducer), escalates the rest to the existing human prompt, and may
|
|
201
|
-
* reject clearly-catastrophic ones. Default OFF because it costs one LLM call per command.
|
|
202
|
-
* Accepts a bare boolean (`judge: true` → defaults: auto-approve low, escalate medium/high,
|
|
203
|
-
* judge model = `config.llm`) or an object:
|
|
204
|
-
* - `enabled`: turn the gate on.
|
|
205
|
-
* - `autoApproveLow`: auto-approve `low`-risk, statically-resolvable commands. Default true.
|
|
206
|
-
* - `blockHigh`: reject clearly-catastrophic (`high` + destructive) verdicts WITHOUT
|
|
207
|
-
* prompting. Default false (conservative; EXT-9's hardline floor already refuses truly
|
|
208
|
-
* catastrophic commands at exec time).
|
|
209
|
-
* - `model`: an optional separate (e.g. cheaper) judge model config. Defaults to `config.llm`.
|
|
210
|
-
* Hardening (always on when the judge runs): the command is normalized + XML-tagged as
|
|
211
|
-
* UNTRUSTED input in the judge prompt; a judge throw/timeout/parse-failure fails CLOSED
|
|
212
|
-
* (escalate, never auto-approve); commands whose target can't be statically resolved
|
|
213
|
-
* (shell composition / substitution / redirection) and interpreter+script invocations that
|
|
214
|
-
* leak ALL_CAPS env vars are NEVER auto-approved.
|
|
176
|
+
* CFG-26 — the approval knobs that used to live here (`allowlist`, `persistAllowlist`,
|
|
177
|
+
* `judge`, `yolo`) moved to the top-level `approvals` block ({@link ApprovalsConfig}); read
|
|
178
|
+
* them through {@link resolveApprovals}, never from this object.
|
|
215
179
|
*/
|
|
216
180
|
shell?: boolean | {
|
|
217
181
|
enabled?: boolean;
|
|
218
182
|
timeout?: number;
|
|
219
183
|
maxOutputBytes?: number;
|
|
220
|
-
allowlist?: boolean;
|
|
221
|
-
persistAllowlist?: boolean;
|
|
222
|
-
judge?: boolean | {
|
|
223
|
-
enabled?: boolean;
|
|
224
|
-
autoApproveLow?: boolean;
|
|
225
|
-
blockHigh?: boolean;
|
|
226
|
-
model?: LLMConfig;
|
|
227
|
-
};
|
|
228
184
|
};
|
|
229
|
-
/**
|
|
230
|
-
* Opt-out of the per-command confirmation dialog for {@link shell}
|
|
231
|
-
* (`run_shell_command`) — the explicit "yolo" bypass. When `true` AND `shell`
|
|
232
|
-
* is enabled, the shell tool runs without any approval interrupt: the model's
|
|
233
|
-
* commands execute immediately. Dangerous by design; off by default.
|
|
234
|
-
*
|
|
235
|
-
* On-disk (CFG-18) this is the `yolo` knob of the `run_shell_command` entry, e.g.
|
|
236
|
-
* `{ "builtInTools": { "run_shell_command": { "yolo": true } } }`.
|
|
237
|
-
*/
|
|
238
|
-
shellYolo?: boolean;
|
|
239
185
|
}
|
|
240
186
|
/**
|
|
241
187
|
* Default per-command shell timeout (ms) when {@link GthDevToolsConfig.shell}
|
|
@@ -282,50 +228,454 @@ export declare function getShellTimeoutMs(devTools: GthDevToolsConfig | undefine
|
|
|
282
228
|
*/
|
|
283
229
|
export declare function getShellMaxOutputBytes(devTools: GthDevToolsConfig | undefined): number;
|
|
284
230
|
/**
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
231
|
+
* Resolve the {@link GthDevToolsConfig} that applies to the active command from the unified
|
|
232
|
+
* {@link GthConfig.builtInTools} registry (CFG-18 — replaces the removed per-command `devTools`).
|
|
233
|
+
* Mirrors the per-command selection used by `builtInToolsConfig.getDefaultTools`: `exec` →
|
|
234
|
+
* `commands.exec`, `ask --write` → `commands.ask`, `code` → `commands.code`; `undefined` elsewhere
|
|
235
|
+
* (the dev/shell tools are inert there). The effective registry for the scope is the per-command
|
|
236
|
+
* `builtInTools` if set, else the root `builtInTools` — matching `getEffectiveConfig`'s replace
|
|
237
|
+
* merge. Shared in core so the runner's allow-list/judge gates stay in lockstep with where the
|
|
238
|
+
* shell tool is actually emitted.
|
|
239
|
+
*/
|
|
240
|
+
export declare function getEffectiveDevToolsConfig(config: Pick<GthConfig, 'commands' | 'builtInTools' | 'askWriteMode'> | undefined, command: GthCommand | undefined): GthDevToolsConfig | undefined;
|
|
241
|
+
/**
|
|
242
|
+
* CFG-27 (spec §1, §2) — **the ladder**. There is ONE approvals setting and it is a single ordered
|
|
243
|
+
* ladder of five rungs; each rung fully determines behaviour. There are no severity thresholds, no
|
|
244
|
+
* strictness levels and no independent rater on/off switch.
|
|
245
|
+
*
|
|
246
|
+
* | # | Rung | Rater | LLM cost |
|
|
247
|
+
* |---|---|---|---|
|
|
248
|
+
* | 1 | `read-only` | no | none |
|
|
249
|
+
* | 2 | `write` | no | none |
|
|
250
|
+
* | 3 | `auto-safe` | yes | 1 call per gated call |
|
|
251
|
+
* | 4 | `full-auto` | yes | 1–2 calls per gated call |
|
|
252
|
+
* | 5 | `bypass` | no | none |
|
|
253
|
+
*
|
|
254
|
+
* Rungs 1, 2 and 5 are fully deterministic: no model is consulted, so behaviour is reproducible
|
|
255
|
+
* and costs nothing.
|
|
256
|
+
*
|
|
257
|
+
* **`bypass` is NOT a higher-autonomy rung than `full-auto`** (§2.5). Both let the agent act
|
|
258
|
+
* without asking; `bypass` is the same autonomy with the checks removed. The ordering below is the
|
|
259
|
+
* order the rungs are *offered* in, and must never be presented as though `full-auto` were an
|
|
260
|
+
* incomplete `bypass`.
|
|
261
|
+
*
|
|
262
|
+
* Identifiers are kebab-case (§9.1) because the same token must work as a config value, a
|
|
263
|
+
* slash-command argument and a CLI flag — a space breaks the last two. Display names keep their
|
|
264
|
+
* spaces; see {@link APPROVAL_RUNG_LABELS}.
|
|
265
|
+
*/
|
|
266
|
+
export declare const APPROVAL_RUNGS: readonly ['read-only', 'write', 'auto-safe', 'full-auto', 'bypass'];
|
|
267
|
+
/** One rung of {@link APPROVAL_RUNGS}. */
|
|
268
|
+
export type ApprovalRung = (typeof APPROVAL_RUNGS)[number];
|
|
269
|
+
/**
|
|
270
|
+
* §9.1 rule / §10 rule 4 — the display spelling of each rung, with spaces. An identifier and a
|
|
271
|
+
* label do not have to match and only one of them has to survive a shell, so user-facing prose
|
|
272
|
+
* uses these and never the kebab-case identifiers.
|
|
273
|
+
*/
|
|
274
|
+
export declare const APPROVAL_RUNG_LABELS: Record<ApprovalRung, string>;
|
|
275
|
+
/**
|
|
276
|
+
* §10 — the one sentence shown wherever a rung is chosen or displayed. **Copied verbatim from the
|
|
277
|
+
* specification**; the wording is constrained by four normative rules there (state what the rung
|
|
278
|
+
* PERMITS, state the allow-list carve-out, never claim safety this system cannot deliver, use the
|
|
279
|
+
* display spelling) plus §8.1 (the hardline floor is real but is NEVER advertised — descriptions
|
|
280
|
+
* cite only protections the user can inspect and extend, i.e. the deny list). Do not "improve"
|
|
281
|
+
* these: `auto-safe` in particular MUST keep the sentence saying files are still rewritten and
|
|
282
|
+
* deleted without asking.
|
|
283
|
+
*
|
|
284
|
+
* The only departure from the source text is that §10's markdown emphasis markers (`**not**` in
|
|
285
|
+
* *Full auto*) are dropped, since these strings are rendered as plain terminal copy.
|
|
286
|
+
*/
|
|
287
|
+
export declare const APPROVAL_RUNG_DESCRIPTIONS: Record<ApprovalRung, string>;
|
|
288
|
+
/** Narrowing type guard for a raw string that may name a rung. */
|
|
289
|
+
export declare function isApprovalRung(value: unknown): value is ApprovalRung;
|
|
290
|
+
/** The rungs at which every gated call is rated by the model (§2.3, §2.4). */
|
|
291
|
+
export declare function isRatedRung(rung: ApprovalRung): boolean;
|
|
292
|
+
/**
|
|
293
|
+
* EXT-71 §3.1 — the **subject** axis of a rule entry, and only that. The schema twin is
|
|
294
|
+
* `APPROVAL_ENTRY_TYPES` in `config/schema.ts`. What holds the two together is
|
|
295
|
+
* `approvalEntrySchema.spec.ts`, where a list of `ApprovalEntry`-typed literals is parsed by that
|
|
296
|
+
* schema: a value either side stops accepting fails there. That is a weaker pin than a direct
|
|
297
|
+
* equality assertion — it catches a narrowing, not a widening on one side alone.
|
|
288
298
|
*/
|
|
289
|
-
export
|
|
299
|
+
export type ApprovalEntryType = 'shell' | 'tool' | 'mcpTool';
|
|
300
|
+
/** EXT-71 §3.1 — the **comparison** axis of a rule entry, and only that. */
|
|
301
|
+
export type ApprovalMatcher = 'exact' | 'glob' | 'regexp' | 'hint';
|
|
290
302
|
/**
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
303
|
+
* §4.7 — the four MCP `ToolAnnotations` hint names, and the whole vocabulary. It is the same list
|
|
304
|
+
* on both sides of the design: what a `hint` pattern may name ({@link ApprovalHintPattern}) and what
|
|
305
|
+
* a user may believe from a server ({@link McpServerApprovalsConfig.trustAnnotations}).
|
|
306
|
+
*
|
|
307
|
+
* **The schema twin `HINT_ANNOTATION_KEYS` in `config/schema.ts` is a deliberate duplicate, and the
|
|
308
|
+
* reason is layering, not oversight.** Neither file may import the other. `schema.ts` states in its
|
|
309
|
+
* own header that it must stay pure and cwd/fs-independent because it feeds `z.toJSONSchema()`, and
|
|
310
|
+
* importing this module would pull `core/types.js` and the whole runtime policy surface into it;
|
|
311
|
+
* importing `schema.ts` here would in turn pull zod into every module that only wanted a policy
|
|
312
|
+
* type. So the vocabulary is written once per layer on purpose — do not "simplify" it by making one
|
|
313
|
+
* import the other.
|
|
314
|
+
*
|
|
315
|
+
* What keeps the two honest instead is the equality assertion in `mcpApprovalsBlock.spec.ts`, which
|
|
316
|
+
* fails the moment they drift. Drift matters in one direction especially: a name the config accepts
|
|
317
|
+
* but the derivation never reads fails silently, and it fails toward trusting. Change one list,
|
|
318
|
+
* change the other.
|
|
294
319
|
*/
|
|
295
|
-
export declare
|
|
320
|
+
export declare const TOOL_ANNOTATION_HINTS: readonly ['readOnlyHint', 'destructiveHint', 'idempotentHint', 'openWorldHint'];
|
|
321
|
+
/** One of {@link TOOL_ANNOTATION_HINTS}. */
|
|
322
|
+
export type ToolAnnotationHint = (typeof TOOL_ANNOTATION_HINTS)[number];
|
|
296
323
|
/**
|
|
297
|
-
*
|
|
324
|
+
* EXT-71 §3.1 — a `hint` pattern: the four MCP `ToolAnnotations` booleans, each mapped to the
|
|
325
|
+
* value it must EFFECTIVELY hold (§4.7.1). All named hints must match (AND within the entry);
|
|
326
|
+
* hints not named are unconstrained; `false` is the spelling of negation. At least one must be
|
|
327
|
+
* named — an empty object is a config error, never a match-everything.
|
|
298
328
|
*/
|
|
299
|
-
export interface
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
329
|
+
export interface ApprovalHintPattern {
|
|
330
|
+
readOnlyHint?: boolean;
|
|
331
|
+
destructiveHint?: boolean;
|
|
332
|
+
idempotentHint?: boolean;
|
|
333
|
+
openWorldHint?: boolean;
|
|
334
|
+
}
|
|
335
|
+
/** Fields every rule entry may carry, whatever its subject. */
|
|
336
|
+
interface ApprovalEntryCommon {
|
|
337
|
+
/**
|
|
338
|
+
* §3.2 — whether the rater still reviews a call this entry matched, honored at the rater rungs
|
|
339
|
+
* and inert at the deterministic ones. Valid on EVERY type. Absent takes the §3.2 default: an
|
|
340
|
+
* entry skips the rater only to the extent that it recorded what the rater would have seen, so
|
|
341
|
+
* `shell` + `exact` defaults to `false` and everything else to `true`.
|
|
342
|
+
*/
|
|
343
|
+
rate?: boolean;
|
|
344
|
+
}
|
|
345
|
+
/** §3.1 — a `shell` entry: a command, compared against the normalized command string. */
|
|
346
|
+
export interface ShellApprovalEntry extends ApprovalEntryCommon {
|
|
347
|
+
type: 'shell';
|
|
348
|
+
/** `hint` is absent on purpose — a command carries no tool annotations. */
|
|
349
|
+
matcher: 'exact' | 'glob' | 'regexp';
|
|
350
|
+
pattern: string;
|
|
351
|
+
}
|
|
352
|
+
/** §3.1 — a `tool` entry: a built-in or custom in-process tool, compared against the tool name. */
|
|
353
|
+
export interface ToolApprovalEntry extends ApprovalEntryCommon {
|
|
354
|
+
type: 'tool';
|
|
355
|
+
matcher: ApprovalMatcher;
|
|
356
|
+
pattern: string | ApprovalHintPattern;
|
|
357
|
+
/** §4.7.4 — optional exact-match bound on the call's host. Tool subjects only. */
|
|
358
|
+
host?: string;
|
|
359
|
+
}
|
|
360
|
+
/** §3.1 — an `mcpTool` entry: one server's tool. */
|
|
361
|
+
export interface McpToolApprovalEntry extends ApprovalEntryCommon {
|
|
362
|
+
type: 'mcpTool';
|
|
363
|
+
matcher: ApprovalMatcher;
|
|
364
|
+
pattern: string | ApprovalHintPattern;
|
|
365
|
+
/**
|
|
366
|
+
* §4.7.5 — **required**, and the user's own key in `mcpServers`: the only identity a server has
|
|
367
|
+
* that is stable, unique and user-authored. The literal `*` means every server, which is why a
|
|
368
|
+
* configured server may not be named `*`.
|
|
369
|
+
*/
|
|
370
|
+
server: string;
|
|
371
|
+
/** §4.7.4 — optional exact-match bound on the call's host. */
|
|
372
|
+
host?: string;
|
|
308
373
|
}
|
|
309
374
|
/**
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
375
|
+
* EXT-71 §3.1 — **one entry** in `allow`, `deny` or `escalate`. All three lists take the same
|
|
376
|
+
* shape; the list an entry sits in decides only what a match DOES (deny over escalate over allow).
|
|
377
|
+
*
|
|
378
|
+
* `type`, `matcher` and `pattern` are always required — no field is inferred and no entry reads
|
|
379
|
+
* two ways. The runtime validator is `approvalEntrySchema` in `config/schema.ts`, which is
|
|
380
|
+
* stricter than TypeScript can be: it rejects unknown fields, an empty or unknown-key `hint`
|
|
381
|
+
* pattern, and a `regexp` that does not compile or is over the length cap.
|
|
314
382
|
*/
|
|
315
|
-
export
|
|
383
|
+
export type ApprovalEntry = ShellApprovalEntry | ToolApprovalEntry | McpToolApprovalEntry;
|
|
316
384
|
/**
|
|
317
|
-
*
|
|
318
|
-
* (
|
|
385
|
+
* EXT-70 §4.7.1/§9 — the approvals relationship with ONE MCP server, keyed by the user's own
|
|
386
|
+
* `mcpServers` config key (§4.7.5 — the only identity a server has that is stable, unique and
|
|
387
|
+
* user-authored; nothing a server declares about itself ever participates).
|
|
388
|
+
*
|
|
389
|
+
* `trustAnnotations` names the hints that are BELIEVED from that server. It is a list rather than a
|
|
390
|
+
* boolean because trusting `readOnlyHint` while distrusting `openWorldHint` is a coherent position
|
|
391
|
+
* and the common one, and because a single "trusted server" flag throws that distinction away for
|
|
392
|
+
* nothing. **Absent or empty means what the default means: nothing external is believed** — every
|
|
393
|
+
* hint of that server's collapses to the MCP fail-closed default, so its declarations cannot
|
|
394
|
+
* perturb any rule.
|
|
319
395
|
*/
|
|
320
|
-
export
|
|
396
|
+
export interface McpServerApprovalsConfig {
|
|
397
|
+
/** §4.7.1 — the hints believed from this server. Absent or empty trusts nothing. */
|
|
398
|
+
trustAnnotations?: ToolAnnotationHint[];
|
|
399
|
+
}
|
|
321
400
|
/**
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
* `
|
|
326
|
-
*
|
|
327
|
-
* `
|
|
328
|
-
*
|
|
329
|
-
*
|
|
401
|
+
* EXT-70 §4.7/§9 — the `approvals.mcp` block: the per-server relationship, keyed by the user's own
|
|
402
|
+
* `mcpServers` config key.
|
|
403
|
+
*
|
|
404
|
+
* **`defaults` applies to servers NOT named under `servers`** — §9's own gloss. A server that names
|
|
405
|
+
* itself states its own relationship in full, so `{"jira": {}}` trusts nothing however permissive
|
|
406
|
+
* `defaults` is: trust by omission is exactly the failure §4.7.1 exists to prevent, and the fail-
|
|
407
|
+
* closed direction is the one a silent config edit must fall in.
|
|
408
|
+
*
|
|
409
|
+
* A key here is **not** validated against `mcpServers`. A user may write the policy before adding
|
|
410
|
+
* the server, and coupling the two would make config ORDER matter.
|
|
411
|
+
*
|
|
412
|
+
* This block cannot live inside `mcpServers`, which is modelled permissively because it carries
|
|
413
|
+
* runtime objects.
|
|
330
414
|
*/
|
|
331
|
-
export
|
|
415
|
+
export interface McpApprovalsConfig {
|
|
416
|
+
/** The relationship with every server not named under {@link servers}. */
|
|
417
|
+
defaults?: McpServerApprovalsConfig;
|
|
418
|
+
/** Per-server relationships, keyed by the user's own `mcpServers` config key (§4.7.5). */
|
|
419
|
+
servers?: Record<string, McpServerApprovalsConfig>;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* On-disk `approvals` object form (root or per command). The **scalar form is exactly sugar for
|
|
423
|
+
* `{ mode: <value> }`** (§9.1) — the union exists so the extras have a home when they are needed,
|
|
424
|
+
* not so there are two ways to say the same thing.
|
|
425
|
+
*/
|
|
426
|
+
export interface ApprovalsObjectConfig {
|
|
427
|
+
/** The rung. Absent = {@link DEFAULT_APPROVAL_RUNG}. */
|
|
428
|
+
mode?: ApprovalRung;
|
|
429
|
+
/**
|
|
430
|
+
* §9.1 — the identity profile the rater runs under, as a **bare name** (strict resolution,
|
|
431
|
+
* GS2-62: a name that does not resolve is a hard config error, never a silent fallback).
|
|
432
|
+
* Omitted = the main model. It is the only rater knob; nesting a one-field object is what this
|
|
433
|
+
* design removed.
|
|
434
|
+
*/
|
|
435
|
+
rater?: string;
|
|
436
|
+
/** §3 — declared allow-list: what the human has trusted. Read-only input. */
|
|
437
|
+
allow?: ApprovalEntry[];
|
|
438
|
+
/** §3 — declared deny-list: what never runs. Read-only input; applies under `bypass` too. */
|
|
439
|
+
deny?: ApprovalEntry[];
|
|
440
|
+
/**
|
|
441
|
+
* §3/§3.2 — declared escalate list: a match always asks the human, whatever the rung would have
|
|
442
|
+
* done, and with no rating call. Read-only input; inert under `bypass` (§2.5).
|
|
443
|
+
*/
|
|
444
|
+
escalate?: ApprovalEntry[];
|
|
445
|
+
/**
|
|
446
|
+
* EXT-66 — wall-clock budget (ms) for ONE rating call. Absent = {@link RATER_DEFAULT_TIMEOUT_MS}
|
|
447
|
+
* (30s), which is a hosted-model number: a local rater is knowably slower, and when it runs out
|
|
448
|
+
* of time the gate escalates, so an unreachable timeout turns the permissive rung into one that
|
|
449
|
+
* asks about everything while every layer reports success.
|
|
450
|
+
*/
|
|
451
|
+
raterTimeoutMs?: number;
|
|
452
|
+
/**
|
|
453
|
+
* EXT-70 §4.7/§9 — the per-server MCP relationship. Read through
|
|
454
|
+
* `createEffectiveToolAnnotationSource` (`core/approvals/annotations.ts`), which is the ONE place
|
|
455
|
+
* an effective annotation set is derived; nothing else re-reads this block.
|
|
456
|
+
*/
|
|
457
|
+
mcp?: McpApprovalsConfig;
|
|
458
|
+
}
|
|
459
|
+
/** On-disk `approvals` value: the rung on its own, or the object when the extras are needed. */
|
|
460
|
+
export type ApprovalsConfig = ApprovalRung | ApprovalsObjectConfig;
|
|
461
|
+
/**
|
|
462
|
+
* The fully-defaulted approvals posture for one command. `allow`/`deny`/`escalate` are the
|
|
463
|
+
* **declared** lists straight from config — read-only input that the runner merges with the
|
|
464
|
+
* runtime stores the escalation menu writes, and that is never written back to config (§9.1).
|
|
465
|
+
*/
|
|
466
|
+
export interface ResolvedApprovals {
|
|
467
|
+
/** The rung in force. */
|
|
468
|
+
rung: ApprovalRung;
|
|
469
|
+
/** Identity profile the rater runs under, or `undefined` for the session model. */
|
|
470
|
+
rater?: string;
|
|
471
|
+
/** Declared allow-list entries (§3.1). Empty when none are declared. */
|
|
472
|
+
allow: ApprovalEntry[];
|
|
473
|
+
/** Declared deny-list entries (§3.1). Empty when none are declared. */
|
|
474
|
+
deny: ApprovalEntry[];
|
|
475
|
+
/** Declared escalate-list entries (§3.1). Empty when none are declared. */
|
|
476
|
+
escalate: ApprovalEntry[];
|
|
477
|
+
/**
|
|
478
|
+
* EXT-66 — wall-clock budget (ms) for one rating call, or `undefined` to let the rater apply
|
|
479
|
+
* `RATER_DEFAULT_TIMEOUT_MS`. Left `undefined` rather than defaulted here so the effective-config
|
|
480
|
+
* snapshot does not churn, exactly as `rater` is.
|
|
481
|
+
*/
|
|
482
|
+
raterTimeoutMs?: number;
|
|
483
|
+
/**
|
|
484
|
+
* EXT-70 §4.7 — the declared per-server MCP relationship, or `undefined` when no scope states
|
|
485
|
+
* one (which reads the same as an empty block: nothing external is believed). Left `undefined`
|
|
486
|
+
* rather than defaulted here for the same reason `rater` is — so the effective-config snapshot
|
|
487
|
+
* does not churn.
|
|
488
|
+
*/
|
|
489
|
+
mcp?: McpApprovalsConfig;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* CFG-26 — how many command prefixes the allow-list holds, for the `/approvals` display.
|
|
493
|
+
* `always: undefined` means the persisted store has not been loaded — rendered `—` rather than a
|
|
494
|
+
* misleading `0`, since a display must never create the store.
|
|
495
|
+
*/
|
|
496
|
+
export interface AllowlistCounts {
|
|
497
|
+
session: number;
|
|
498
|
+
always: number | undefined;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* EXT-70 §4.7.1 — what one server's annotations are believed on, for display.
|
|
502
|
+
*
|
|
503
|
+
* `configured` says whether {@link server} is a key under `mcpServers` in the loaded config. It is
|
|
504
|
+
* an advisory rather than a validity test: §9 deliberately does NOT check `approvals.mcp` keys
|
|
505
|
+
* against `mcpServers`, so a user may write the policy before adding the server and config ORDER
|
|
506
|
+
* never matters. What it buys interactively is that a mistyped key — which trusts nothing while
|
|
507
|
+
* reading as though it did — can be pointed out rather than swallowed.
|
|
508
|
+
*/
|
|
509
|
+
export interface McpServerAnnotationTrust {
|
|
510
|
+
/** §4.7.5 — the user's own `mcpServers` config key. */
|
|
511
|
+
server: string;
|
|
512
|
+
/** The hints believed from this server, resolved through `defaults` where it is not named. */
|
|
513
|
+
trusted: ToolAnnotationHint[];
|
|
514
|
+
/** Whether this key names a server in the loaded config's `mcpServers`. */
|
|
515
|
+
configured: boolean;
|
|
516
|
+
}
|
|
517
|
+
/** EXT-70 §4.7.1 — the whole believed-annotation picture, for the `/approvals` display. */
|
|
518
|
+
export interface McpAnnotationTrustView {
|
|
519
|
+
/** §9 — the hints believed from servers NOT named under `servers`. */
|
|
520
|
+
defaults: ToolAnnotationHint[];
|
|
521
|
+
/** Per-server, for every key either the config names or the policy does. */
|
|
522
|
+
servers: McpServerAnnotationTrust[];
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* EXT-70 §4.7.1/§4.7.4 — the outcome of believing (or ceasing to believe) hints from one server,
|
|
526
|
+
* so the surface that asked can report exactly what landed and what it costs.
|
|
527
|
+
*
|
|
528
|
+
* **`weakening` is the field that keeps the human un-surprised.** Withdrawing trust pushes a hint
|
|
529
|
+
* back to its fail-closed default, which for three of the four is a *weakening*, so the saved
|
|
530
|
+
* approvals made for that server while the hint was believed will be invalidated (§4.7.4) at the
|
|
531
|
+
* next call to that tool. That has to be said where the user withdraws trust, not only in the
|
|
532
|
+
* notice that arrives later.
|
|
533
|
+
*/
|
|
534
|
+
export interface McpAnnotationTrustChange extends McpServerAnnotationTrust {
|
|
535
|
+
/** Hints this change started believing (absent from the previous set). */
|
|
536
|
+
added: ToolAnnotationHint[];
|
|
537
|
+
/** Hints this change stopped believing (present in the previous set). */
|
|
538
|
+
removed: ToolAnnotationHint[];
|
|
539
|
+
/**
|
|
540
|
+
* §4.7.4 — the subset of {@link removed} whose withdrawal can weaken an effective set, and
|
|
541
|
+
* therefore invalidate a grant. Empty on a grant of trust, which can never weaken: every
|
|
542
|
+
* weakening move ends at the fail-closed default, and believing a hint only ever moves away
|
|
543
|
+
* from it.
|
|
544
|
+
*/
|
|
545
|
+
weakening: ToolAnnotationHint[];
|
|
546
|
+
/**
|
|
547
|
+
* §4.7.4 — the saved approvals for this server that the *resulting* trust actually weakens, each
|
|
548
|
+
* rendered by `describeApprovalEntry`. They are the ones that will be withdrawn, with the §4.7.4
|
|
549
|
+
* notice, at the next call to that tool.
|
|
550
|
+
*
|
|
551
|
+
* **A prediction, never a deletion.** Invalidation stays scoped to the call being decided, because
|
|
552
|
+
* a sweep would read every held grant against a source that can only answer for the tools
|
|
553
|
+
* registered right now — a server that happened to be offline would read as having weakened
|
|
554
|
+
* everything it ever declared, and the grants would go. Listing them is safe where deleting them
|
|
555
|
+
* is not.
|
|
556
|
+
*
|
|
557
|
+
* **It over-reports in two distinct ways, and both are the same trade.** The comparison is each
|
|
558
|
+
* grant's recorded snapshot against the set in force *now*, not the set before this change against
|
|
559
|
+
* the set after it. So (1) a server that is offline when trust moves declares nothing, resolves to
|
|
560
|
+
* the fail-closed constant, and every grant it holds is named; and (2) a grant already weakened
|
|
561
|
+
* for some other reason — an earlier withdrawal, a `tools/list` that took a hint back — is named
|
|
562
|
+
* under whichever withdrawal happens to run next, including one that moved nothing relevant to it.
|
|
563
|
+
* A named grant is therefore one that the trust now in force weakens, which is what the user needs
|
|
564
|
+
* to know; it is not a claim that *this* withdrawal is what weakened it. Reading it as the latter
|
|
565
|
+
* is how a test comes to assert causation the field never promised.
|
|
566
|
+
*
|
|
567
|
+
* Empty is likewise not "nothing is at risk" but "nothing this session can see is": with
|
|
568
|
+
* {@link weakening} non-empty the rule still holds for any grant made while those hints were
|
|
569
|
+
* believed, which is what the notice says in that case. Counted read-only, like
|
|
570
|
+
* {@link AllowlistCounts}: the persisted store is consulted only when it is already loaded.
|
|
571
|
+
*/
|
|
572
|
+
invalidates: string[];
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* §1.1 — **the default rung is `auto-safe`, everywhere.** It is the default in every interactive
|
|
576
|
+
* context, it does NOT vary with the configured model, and there is no separate non-interactive
|
|
577
|
+
* default. What changes without a human is what an escalation *does* (§6.2: an immediate non-zero
|
|
578
|
+
* exit, never an approval), not which rung the session starts on. A context-dependent default
|
|
579
|
+
* would reintroduce exactly the hidden branching this ladder exists to remove.
|
|
580
|
+
*/
|
|
581
|
+
export declare const DEFAULT_APPROVAL_RUNG: ApprovalRung;
|
|
582
|
+
/**
|
|
583
|
+
* CFG-27 — resolve the effective {@link ResolvedApprovals} for the active command.
|
|
584
|
+
*
|
|
585
|
+
* There is no defaults *matrix*: §1.1 makes `auto-safe` the default in every context, so this
|
|
586
|
+
* resolver neither detects nor accepts a "context". Precedence is the only thing it decides, and
|
|
587
|
+
* §9.1 splits it in two:
|
|
588
|
+
*
|
|
589
|
+
* - **The scalars — `mode`, `rater`, `raterTimeoutMs` — are replaced** when the per-command value
|
|
590
|
+
* states them and **inherited from the root when it does not**. So the scalar sugar
|
|
591
|
+
* `"code": { "approvals": "bypass" }` is exactly `{ mode: 'bypass' }` merged over the root: it
|
|
592
|
+
* sets the rung and nothing else.
|
|
593
|
+
* - **`deny` and `escalate` never replace: they CONCATENATE across every scope.** A
|
|
594
|
+
* command-specific `deny` *adds to* the root's. Removing an inherited prohibition for one command
|
|
595
|
+
* is deliberately not expressible.
|
|
596
|
+
* - **`allow` is REPLACED when the per-command value states its own, and inherited when it does
|
|
597
|
+
* not.** A per-command scope may therefore narrow what runs unprompted, and may never widen what
|
|
598
|
+
* is prohibited.
|
|
599
|
+
* - **`mcp` (EXT-70 §4.7) follows `allow`, not the restrictive lists**: replaced when the
|
|
600
|
+
* per-command value states it, inherited when it does not. Believing a hint is a PERMISSIVE act
|
|
601
|
+
* in both directions — it can make an `allow` hint entry fire and can make a `deny` hint entry
|
|
602
|
+
* stop firing — so it merges the way the permissive list does, and a per-command scope can
|
|
603
|
+
* narrow the session's trust (`"mcp": {}` believes nothing) but never inherits half of it by
|
|
604
|
+
* accident. Deep-merging the two scopes' `servers` maps was rejected for the same reason: it
|
|
605
|
+
* would leave a deliberately distrustful per-command block silently carrying the root's trust.
|
|
606
|
+
*
|
|
607
|
+
* **The two halves differ because the costs differ (§3.1), not for tidiness.** A missed allow entry
|
|
608
|
+
* escalates and a missed deny entry falls through to the rater — neither is an execution — while a
|
|
609
|
+
* too-broad allow entry *runs, unrated and unprompted*. Concatenating the restrictive lists fails
|
|
610
|
+
* toward a prompt; concatenating the permissive one fails toward an execution, and would leave a
|
|
611
|
+
* deliberately restrictive per-command rung with no way to shed the root's standing grants. Do not
|
|
612
|
+
* "regularize" these three into one policy: the direction each list fails in is the whole design.
|
|
613
|
+
*
|
|
614
|
+
* On the restrictive side the pressure runs the other way (§11.1f). Were the per-command value to
|
|
615
|
+
* replace the root wholesale, the friendliest spelling of "stop asking me about `code`" would also
|
|
616
|
+
* delete every `deny` entry — at the one rung where the deny list and the §8 floor are the only
|
|
617
|
+
* checks left. A prohibition a nested config key can quietly delete is not a hardline.
|
|
618
|
+
*
|
|
619
|
+
* Concatenation order cannot change any outcome (`resolveApprovalRules` consults every deny entry
|
|
620
|
+
* before any escalate entry and every escalate entry before any allow entry), so root-first is a
|
|
621
|
+
* convention for readability — matching `GthAgentRunner.approvalRuleLists`, where the declared
|
|
622
|
+
* entries precede the runtime grants — and never a precedence.
|
|
623
|
+
*
|
|
624
|
+
* Defaults are applied HERE, at the read site, rather than in `DEFAULT_CONFIG` — so the
|
|
625
|
+
* effective-config snapshot the `/config` panel renders never churns (à la GS2-34
|
|
626
|
+
* `injectModelContext` / GS2-63 `output.header`).
|
|
627
|
+
*
|
|
628
|
+
* This is the per-command half. The cross-LAYER half (a project config's lists adding to a global
|
|
629
|
+
* config's rather than replacing them) is the additive-array policy in `config/loader.ts`; both are
|
|
630
|
+
* needed, since either alone still loses a list silently.
|
|
631
|
+
*
|
|
632
|
+
* @param command The active command; selects the per-command block.
|
|
633
|
+
*/
|
|
634
|
+
export declare function resolveApprovals(config: Pick<GthConfig, 'commands' | 'approvals'> | undefined, command: GthCommand | undefined): ResolvedApprovals;
|
|
635
|
+
/** A status notice a backend should surface after resolving the shell approval gate. */
|
|
636
|
+
export interface ShellApprovalGateNotice {
|
|
637
|
+
/** Severity to pass to the agent's `statusUpdate` callback. */
|
|
638
|
+
level: StatusLevel;
|
|
639
|
+
/** The user-facing message. */
|
|
640
|
+
message: string;
|
|
641
|
+
}
|
|
642
|
+
/** The resolved shell approval-gate policy: whether to gate, and what to tell the user. */
|
|
643
|
+
export interface ShellApprovalGateDecision {
|
|
644
|
+
/**
|
|
645
|
+
* Whether `run_shell_command` must be wired behind the per-command approval interrupt
|
|
646
|
+
* (langchain's `humanInTheLoopMiddleware` — installed directly on the lean backend, via
|
|
647
|
+
* deepagents' `interruptOn` on the deep one).
|
|
648
|
+
*/
|
|
649
|
+
gateShell: boolean;
|
|
650
|
+
/** The notice to surface, when this configuration warrants one. */
|
|
651
|
+
notice?: ShellApprovalGateNotice;
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* EXT-52 — the ONE shell approval-gate policy both agent backends resolve
|
|
655
|
+
* (`GthLangChainAgent` = lean/default, `GthDeepAgent` = deep). It decides whether the opt-in
|
|
656
|
+
* `run_shell_command` tool is gated behind the per-command approval interrupt, and which status
|
|
657
|
+
* notice (if any) the backend should surface. The backends differ only in HOW they install the
|
|
658
|
+
* interrupt; the policy and its user-facing copy live here so the two can never drift (and so a
|
|
659
|
+
* later rename of this config surface has one place to change).
|
|
660
|
+
*
|
|
661
|
+
* CFG-27 — **the tool is gated whenever it is enabled, at every rung including `bypass`.** CFG-26
|
|
662
|
+
* used to leave it UNGATED under `bypass` outside interactive `code`, which the ladder cannot
|
|
663
|
+
* afford: §2.5 makes the declared **deny list the one check `bypass` keeps**, and a deny entry can
|
|
664
|
+
* only fire if the call reaches `GthAgentRunner.decideToolApproval` — an ungated call never does.
|
|
665
|
+
* Gating unconditionally also keeps the rung switchable mid-session (`/approvals <rung>`), since a
|
|
666
|
+
* tool wired without the interrupt cannot be re-gated without rebuilding the agent.
|
|
667
|
+
*
|
|
668
|
+
* What each rung then does is decided in `decideToolApproval`, not here:
|
|
669
|
+
* • `bypass` — deny list, then approve without prompting or rating.
|
|
670
|
+
* • `read-only`/`write` — deny list, allow-list, else escalate to the human.
|
|
671
|
+
* • `auto-safe`/`full-auto` — deny list, allow-list, then the rater.
|
|
672
|
+
* • With the shell tool disabled — or on a non-dev-tools command (chat/api/…) — nothing is gated
|
|
673
|
+
* and nothing is announced.
|
|
674
|
+
*
|
|
675
|
+
* Shell enablement itself is resolved through {@link getEffectiveDevToolsConfig} +
|
|
676
|
+
* {@link isShellToolEnabled}, so the gate stays in lockstep with where `GthDevToolkit` actually
|
|
677
|
+
* emits the tool; the posture comes from {@link resolveApprovals}, so this and the runner can
|
|
678
|
+
* never disagree about which rung is in force.
|
|
679
|
+
*/
|
|
680
|
+
export declare function resolveShellApprovalGate(config: Pick<GthConfig, 'commands' | 'builtInTools' | 'askWriteMode' | 'approvals'> | undefined, command: GthCommand | undefined): ShellApprovalGateDecision;
|
|
681
|
+
export {};
|