@gaunt-sloth/core 2.0.0-alpha.9 → 2.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gsloth.review.md +2 -0
- package/README.md +71 -20
- 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/configDiscovery.d.ts +79 -0
- package/dist/config/configDiscovery.js +80 -0
- package/dist/config/configDiscovery.js.map +1 -0
- package/dist/config/defaults.d.ts +20 -20
- package/dist/config/defaults.js +10 -8
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/filesystem-tools.d.ts +41 -0
- package/dist/config/filesystem-tools.js +56 -0
- package/dist/config/filesystem-tools.js.map +1 -0
- package/dist/config/loader.d.ts +171 -19
- package/dist/config/loader.js +1054 -144
- 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/profiles.d.ts +68 -0
- package/dist/config/profiles.js +93 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/providerKeys.d.ts +69 -0
- package/dist/config/providerKeys.js +69 -0
- package/dist/config/providerKeys.js.map +1 -0
- package/dist/config/schema.d.ts +2671 -138
- package/dist/config/schema.js +1361 -85
- package/dist/config/schema.js.map +1 -1
- package/dist/config/shell-policy.d.ts +899 -111
- package/dist/config/shell-policy.js +800 -70
- package/dist/config/shell-policy.js.map +1 -1
- package/dist/config/tool-descriptions.d.ts +211 -0
- package/dist/config/tool-descriptions.js +272 -0
- package/dist/config/tool-descriptions.js.map +1 -0
- package/dist/config/types.d.ts +352 -41
- package/dist/config/types.js +1 -0
- package/dist/config/types.js.map +1 -1
- package/dist/config.d.ts +35 -1
- package/dist/config.js +16 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +28 -1
- package/dist/constants.js +28 -1
- package/dist/constants.js.map +1 -1
- package/dist/core/GthAbstractAgent.d.ts +166 -11
- package/dist/core/GthAbstractAgent.js +484 -45
- package/dist/core/GthAbstractAgent.js.map +1 -1
- package/dist/core/GthAgentRunner.d.ts +543 -57
- package/dist/core/GthAgentRunner.js +1494 -140
- package/dist/core/GthAgentRunner.js.map +1 -1
- package/dist/core/GthLangChainAgent.d.ts +117 -2
- package/dist/core/GthLangChainAgent.js +602 -29
- 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/promptHeader.d.ts +28 -0
- package/dist/core/approvals/promptHeader.js +62 -0
- package/dist/core/approvals/promptHeader.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/debugCapture.d.ts +74 -0
- package/dist/core/debugCapture.js +100 -0
- package/dist/core/debugCapture.js.map +1 -0
- package/dist/core/gthLeanAgentFactory.d.ts +4 -4
- package/dist/core/gthLeanAgentFactory.js +4 -4
- package/dist/core/launchBanner.d.ts +127 -0
- package/dist/core/launchBanner.js +414 -0
- package/dist/core/launchBanner.js.map +1 -0
- package/dist/core/modelLabel.d.ts +19 -0
- package/dist/core/modelLabel.js +26 -0
- package/dist/core/modelLabel.js.map +1 -0
- package/dist/core/plainToolIndication.d.ts +15 -0
- package/dist/core/plainToolIndication.js +174 -0
- package/dist/core/plainToolIndication.js.map +1 -0
- package/dist/core/reasoningBlocks.d.ts +65 -0
- package/dist/core/reasoningBlocks.js +103 -0
- package/dist/core/reasoningBlocks.js.map +1 -0
- package/dist/core/refusal.d.ts +53 -0
- package/dist/core/refusal.js +133 -0
- package/dist/core/refusal.js.map +1 -0
- package/dist/core/runHeader.d.ts +38 -0
- package/dist/core/runHeader.js +42 -0
- package/dist/core/runHeader.js.map +1 -0
- package/dist/core/runStats.d.ts +14 -3
- package/dist/core/runStats.js +48 -3
- package/dist/core/runStats.js.map +1 -1
- package/dist/core/shell/ShellCommandFailedError.d.ts +3 -4
- package/dist/core/shell/ShellCommandFailedError.js +3 -4
- package/dist/core/shell/ShellCommandFailedError.js.map +1 -1
- package/dist/core/shell/abstention.d.ts +88 -0
- package/dist/core/shell/abstention.js +184 -0
- package/dist/core/shell/abstention.js.map +1 -0
- package/dist/core/shell/approvalCapture.d.ts +271 -0
- package/dist/core/shell/approvalCapture.js +108 -0
- package/dist/core/shell/approvalCapture.js.map +1 -0
- package/dist/core/shell/approvalStop.d.ts +123 -0
- package/dist/core/shell/approvalStop.js +269 -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/escalationSeverity.d.ts +141 -0
- package/dist/core/shell/escalationSeverity.js +89 -0
- package/dist/core/shell/escalationSeverity.js.map +1 -0
- package/dist/core/shell/framing.d.ts +190 -0
- package/dist/core/shell/framing.js +633 -0
- package/dist/core/shell/framing.js.map +1 -0
- package/dist/core/shell/hardline.d.ts +103 -0
- package/dist/core/shell/hardline.js +780 -0
- package/dist/core/shell/hardline.js.map +1 -0
- package/dist/core/shell/negotiation.d.ts +328 -0
- package/dist/core/shell/negotiation.js +488 -0
- package/dist/core/shell/negotiation.js.map +1 -0
- package/dist/core/shell/normalize.d.ts +44 -4
- package/dist/core/shell/normalize.js +61 -7
- package/dist/core/shell/normalize.js.map +1 -1
- package/dist/core/shell/openWorld.d.ts +263 -0
- package/dist/core/shell/openWorld.js +1188 -0
- package/dist/core/shell/openWorld.js.map +1 -0
- package/dist/core/shell/rater.d.ts +873 -0
- package/dist/core/shell/rater.js +1454 -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/raterVocabulary.d.ts +121 -0
- package/dist/core/shell/raterVocabulary.js +116 -0
- package/dist/core/shell/raterVocabulary.js.map +1 -0
- package/dist/core/shell/rejection.d.ts +69 -0
- package/dist/core/shell/rejection.js +38 -0
- package/dist/core/shell/rejection.js.map +1 -0
- package/dist/core/toolCallRepair/grammar.d.ts +41 -0
- package/dist/core/toolCallRepair/grammar.js +116 -0
- package/dist/core/toolCallRepair/grammar.js.map +1 -0
- package/dist/core/toolCallRepair/index.d.ts +2 -0
- package/dist/core/toolCallRepair/index.js +7 -0
- package/dist/core/toolCallRepair/index.js.map +1 -0
- package/dist/core/toolCallRepair/payload.d.ts +36 -0
- package/dist/core/toolCallRepair/payload.js +341 -0
- package/dist/core/toolCallRepair/payload.js.map +1 -0
- package/dist/core/toolCallRepair/promote.d.ts +45 -0
- package/dist/core/toolCallRepair/promote.js +90 -0
- package/dist/core/toolCallRepair/promote.js.map +1 -0
- package/dist/core/toolDisplay.d.ts +123 -0
- package/dist/core/toolDisplay.js +451 -0
- package/dist/core/toolDisplay.js.map +1 -0
- package/dist/core/toolOutputChannel.d.ts +95 -0
- package/dist/core/toolOutputChannel.js +165 -0
- package/dist/core/toolOutputChannel.js.map +1 -0
- package/dist/core/types.d.ts +349 -16
- package/dist/core/types.js.map +1 -1
- package/dist/history/historyFormat.d.ts +12 -3
- package/dist/history/historyFormat.js +50 -8
- package/dist/history/historyFormat.js.map +1 -1
- package/dist/history/historyStore.d.ts +77 -0
- package/dist/history/historyStore.js +173 -6
- package/dist/history/historyStore.js.map +1 -1
- package/dist/history/recordSession.d.ts +10 -1
- package/dist/history/recordSession.js +27 -0
- package/dist/history/recordSession.js.map +1 -1
- package/dist/providers/anthropic.js +12 -0
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/configurationPassthrough.d.ts +107 -0
- package/dist/providers/configurationPassthrough.js +148 -0
- package/dist/providers/configurationPassthrough.js.map +1 -0
- package/dist/providers/geminiSchemaSanitizer.d.ts +55 -0
- package/dist/providers/geminiSchemaSanitizer.js +347 -0
- package/dist/providers/geminiSchemaSanitizer.js.map +1 -0
- package/dist/providers/geminiThinking.d.ts +60 -0
- package/dist/providers/geminiThinking.js +92 -0
- package/dist/providers/geminiThinking.js.map +1 -0
- package/dist/providers/google-genai.js +18 -1
- package/dist/providers/google-genai.js.map +1 -1
- package/dist/providers/groq.js +12 -0
- package/dist/providers/groq.js.map +1 -1
- package/dist/providers/huggingface.d.ts +25 -0
- package/dist/providers/huggingface.js +69 -0
- package/dist/providers/huggingface.js.map +1 -0
- package/dist/providers/modelCatalog.d.ts +109 -0
- package/dist/providers/modelCatalog.js +245 -0
- package/dist/providers/modelCatalog.js.map +1 -0
- package/dist/providers/modelDiscovery.d.ts +99 -5
- package/dist/providers/modelDiscovery.js +191 -35
- package/dist/providers/modelDiscovery.js.map +1 -1
- package/dist/providers/ollama.d.ts +18 -4
- package/dist/providers/ollama.js +67 -37
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.js +34 -0
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +26 -4
- package/dist/providers/openrouter.js +83 -26
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/vertexai.js +19 -1
- package/dist/providers/vertexai.js.map +1 -1
- package/dist/providers/xai.js +20 -0
- package/dist/providers/xai.js.map +1 -1
- package/dist/runtime/askStructured.d.ts +105 -0
- package/dist/runtime/askStructured.js +120 -0
- package/dist/runtime/askStructured.js.map +1 -0
- package/dist/runtime/conversation.d.ts +64 -0
- package/dist/runtime/conversation.js +171 -0
- package/dist/runtime/conversation.js.map +1 -0
- package/dist/runtime/singleShot.d.ts +37 -6
- package/dist/runtime/singleShot.js +113 -67
- package/dist/runtime/singleShot.js.map +1 -1
- package/dist/runtime/structuredOutput.d.ts +104 -0
- package/dist/runtime/structuredOutput.js +393 -0
- package/dist/runtime/structuredOutput.js.map +1 -0
- 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/aiignoreUtils.js.map +1 -1
- package/dist/utils/binaryOutputUtils.js.map +1 -1
- package/dist/utils/consoleUtils.d.ts +95 -0
- package/dist/utils/consoleUtils.js +112 -2
- package/dist/utils/consoleUtils.js.map +1 -1
- package/dist/utils/crashHandler.d.ts +87 -0
- package/dist/utils/crashHandler.js +128 -0
- package/dist/utils/crashHandler.js.map +1 -0
- package/dist/utils/debugDump.d.ts +134 -0
- package/dist/utils/debugDump.js +381 -0
- package/dist/utils/debugDump.js.map +1 -0
- package/dist/utils/debugUtils.d.ts +13 -4
- package/dist/utils/debugUtils.js +36 -13
- package/dist/utils/debugUtils.js.map +1 -1
- package/dist/utils/displayWidth.d.ts +53 -0
- package/dist/utils/displayWidth.js +195 -0
- package/dist/utils/displayWidth.js.map +1 -0
- package/dist/utils/fileUtils.js.map +1 -1
- package/dist/utils/globalConfigUtils.d.ts +14 -2
- package/dist/utils/globalConfigUtils.js +22 -4
- package/dist/utils/globalConfigUtils.js.map +1 -1
- package/dist/utils/llmUtils.d.ts +39 -8
- package/dist/utils/llmUtils.js +76 -8
- package/dist/utils/llmUtils.js.map +1 -1
- package/dist/utils/redactSecrets.d.ts +63 -0
- package/dist/utils/redactSecrets.js +286 -0
- package/dist/utils/redactSecrets.js.map +1 -0
- package/dist/utils/systemPromptNotes.d.ts +222 -0
- package/dist/utils/systemPromptNotes.js +338 -0
- package/dist/utils/systemPromptNotes.js.map +1 -0
- package/dist/utils/systemUtils.d.ts +18 -1
- package/dist/utils/systemUtils.js +38 -3
- package/dist/utils/systemUtils.js.map +1 -1
- package/dist/utils/toolMatching.d.ts +30 -0
- package/dist/utils/toolMatching.js +44 -0
- package/dist/utils/toolMatching.js.map +1 -0
- package/dist/utils/untrustedText.d.ts +86 -0
- package/dist/utils/untrustedText.js +101 -0
- package/dist/utils/untrustedText.js.map +1 -0
- package/package.json +21 -7
- package/schema/gsloth-config.schema.json +1921 -326
- 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
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/shell/approvalStop
|
|
3
|
+
*
|
|
4
|
+
* CFG-27 — the two ways the approvals gate **ends a run** rather than answering a tool call.
|
|
5
|
+
* Both are thrown from `GthAgentRunner.decideToolApproval`, both carry the command and the reason
|
|
6
|
+
* a person needs to see, and both are re-thrown UNCHANGED by `processMessages` (which otherwise
|
|
7
|
+
* wraps failures as `Agent processing failed: …`) so the explanation reaches the user intact.
|
|
8
|
+
*
|
|
9
|
+
* They are errors rather than {@link ../types.js ToolApprovalDecision}s on purpose. A decision is
|
|
10
|
+
* something the model observes as a `ToolMessage` and can respond to; these two are precisely the
|
|
11
|
+
* cases where the spec says the model gets no move at all:
|
|
12
|
+
*
|
|
13
|
+
* - **{@link AttackHaltError}** (§4.2) — an `attack` outcome *halts the run*. "A halt ends the
|
|
14
|
+
* agent loop. It is not a rejection the model can respond to, and no rung except `bypass` can
|
|
15
|
+
* turn it into anything else."
|
|
16
|
+
* - **{@link NonInteractiveEscalationError}** (§6.2) — where no human can answer, *every*
|
|
17
|
+
* escalation is an immediate non-zero exit carrying the command, the rating and its reason.
|
|
18
|
+
* There is no prompt, no waiting, and never a timeout into approval. Teams that need specific
|
|
19
|
+
* commands to run unattended declare them in `approvals.allow` (§3), which is consulted before
|
|
20
|
+
* the rater and therefore never escalates.
|
|
21
|
+
*
|
|
22
|
+
* Exit code: neither class sets one. The single-shot runtime (`runSingleShot`) already reports a
|
|
23
|
+
* thrown run as `ok: false`, and each command entry point turns that into `setExitCode(1)` — so
|
|
24
|
+
* "immediate non-zero exit carrying the explanation" is what a caller already gets, without a new
|
|
25
|
+
* exit path that could diverge from the existing one.
|
|
26
|
+
*
|
|
27
|
+
* ## [[TUI-C71]] — a stop message is UNTRUSTED TEXT, and it is made safe in two places
|
|
28
|
+
*
|
|
29
|
+
* Every string a stop carries except this module's own sentences is written by something other
|
|
30
|
+
* than the user: the command the agent proposed, the rater's outcome and reason, the
|
|
31
|
+
* `approvals.escalate` entry that matched, and the whole §5 negotiation. On an `attack` verdict
|
|
32
|
+
* that is the *worst* text in the system by construction — the rater judged the command's own
|
|
33
|
+
* structure to evidence deception or obfuscation — so a stop is the one message most likely to
|
|
34
|
+
* carry a payload crafted to forge terminal output, and the last one that may be printed raw.
|
|
35
|
+
*
|
|
36
|
+
* The defence [[TUI-C26]] built has two halves, and they belong at different times:
|
|
37
|
+
*
|
|
38
|
+
* 1. **Neutralisation is done HERE, at construction, unconditionally.** It needs no width and no
|
|
39
|
+
* screen, it is idempotent, and it is the half every consumer needs — a thrown stop reaches the
|
|
40
|
+
* Ink transcript, `--no-tui` stderr, a session log, the approvals archive (`record.error`), an
|
|
41
|
+
* eval turn record, AG-UI and a CI job's output, and only some of those are terminals. Doing it
|
|
42
|
+
* at each render site would mean every present and future consumer had to remember; doing it
|
|
43
|
+
* once here means none of them can forget. After it, `{@link message}` contains no control or
|
|
44
|
+
* format character at all: a carriage return is the five printable characters `\x0d`, and —
|
|
45
|
+
* because LF is a control character too — untrusted text cannot even open a new line, let alone
|
|
46
|
+
* reach column 0 on one.
|
|
47
|
+
* 2. **The gutter and the width bound are done at RENDER, by the surface** ({@link
|
|
48
|
+
* approvalStopRows} for the plain surfaces, `<ApprovalStopMessage>` for the Ink TUI). They
|
|
49
|
+
* cannot be done here: framing is arithmetic against a terminal width, an `Error.message` has
|
|
50
|
+
* no width, and a block wrapped for an 80-column terminal is wrong for every other one. What
|
|
51
|
+
* they buy over neutralisation alone is the last case it cannot close — one very long
|
|
52
|
+
* neutralised line that the *terminal* wraps, whose continuation starts at column 0 carrying
|
|
53
|
+
* whatever the attacker chose to put at that offset. That route is measured, not theorised: on
|
|
54
|
+
* a 120-column terminal the neutralised `Command:` line of a hostile command is ~164 columns.
|
|
55
|
+
*
|
|
56
|
+
* **Because half of it is the surface's job, the surfaces are enumerated rather than assumed.**
|
|
57
|
+
* Every place that catches an `ApprovalStopError` and prints it calls {@link approvalStopRows} (or
|
|
58
|
+
* the Ink component): `runtime/singleShot.ts`, `runtime/conversation.ts`,
|
|
59
|
+
* `agent/modules/interactiveSessionModule.ts` (both its per-turn catch and its outermost one, which
|
|
60
|
+
* is where the `-m` path lands), `review/modules/reviewModule.ts`, `app/commands/prCommand.ts`
|
|
61
|
+
* (which catches what the PR-discovery agent throws) and `app/tui/components/App.tsx`.
|
|
62
|
+
* **Adding a surface that catches a stop means adding it to that list.**
|
|
63
|
+
*
|
|
64
|
+
* **That list is over the ERROR, and the error is not the only way a stop reaches a screen.** Some
|
|
65
|
+
* consumers take {@link message} and hand the *string* onward, at which point no `instanceof` can
|
|
66
|
+
* find it again: `runConversation` records it as a turn's `error`, and `evalRunner` folds that into
|
|
67
|
+
* an eval case's `reasons`, which `batch/reporters/textReporter.ts` prints — a terminal, reached
|
|
68
|
+
* without any of its code ever seeing this class. That one frames the text itself. So the honest
|
|
69
|
+
* statement is not "every other consumer is a non-terminal" but: **a consumer that prints to a
|
|
70
|
+
* terminal frames what it prints, whether it holds the error or only its text**, and the ones that
|
|
71
|
+
* legitimately do neither are the file and protocol sinks (the approvals archive, the JUnit/JSON
|
|
72
|
+
* reports, the AG-UI event) plus `reviewModule`'s `displayDebug` of the raw error, which is a debug
|
|
73
|
+
* channel. All of them still inherit the neutralisation, which is what keeps the worst case a
|
|
74
|
+
* wrapped line rather than a repainted screen.
|
|
75
|
+
*
|
|
76
|
+
* **The structured fields stay RAW; only the message is neutralised.** That is what answers the
|
|
77
|
+
* obvious objection to construction-time work — that it stops the error being a faithful record of
|
|
78
|
+
* what was proposed. `{@link ApprovalStopError.command}`, `reason`, `outcome`, `escalatedBy` and
|
|
79
|
+
* `negotiation` are the command as the agent wrote it, byte for byte, for any consumer that needs
|
|
80
|
+
* the truth rather than a screen; the message is the *presentation*, and a presentation that can
|
|
81
|
+
* repaint the terminal is not one. The render path frames from those raw fields, so its gutter
|
|
82
|
+
* numbers the command's real lines rather than one escaped line.
|
|
83
|
+
*
|
|
84
|
+
* **One parts list, two renderings** ({@link ApprovalStopPart}). The message string and the framed
|
|
85
|
+
* block are both derived from {@link ApprovalStopError.parts}, so they cannot come to describe one
|
|
86
|
+
* stop two ways — the same argument `renderNegotiationTranscript` makes for its own two surfaces.
|
|
87
|
+
* A part is tagged with *who wrote it*, which is the only distinction any of this rests on: the
|
|
88
|
+
* gate's own prose can be painted as it is because nothing can forge it, and everything else goes
|
|
89
|
+
* through the shared renderer.
|
|
90
|
+
*/
|
|
91
|
+
import { frameUntrustedCommand, frameUntrustedText, frameWidthFor, narrowTerminalNotice, neutralizeUntrustedText, } from '#src/core/shell/framing.js';
|
|
92
|
+
/**
|
|
93
|
+
* The parts as the one string an `Error.message` has to be, with every untrusted part neutralised.
|
|
94
|
+
*
|
|
95
|
+
* A `block` is neutralised **line by line**, so the renderer-owned breaks that carry its meaning
|
|
96
|
+
* survive while its content still cannot introduce one. Everything else is neutralised whole,
|
|
97
|
+
* which — LF being a control character — is what collapses an untrusted value onto the single line
|
|
98
|
+
* its label put it on.
|
|
99
|
+
*/
|
|
100
|
+
function composeMessage(parts) {
|
|
101
|
+
return parts.map(messageLineFor).join('\n');
|
|
102
|
+
}
|
|
103
|
+
function messageLineFor(part) {
|
|
104
|
+
switch (part.kind) {
|
|
105
|
+
case 'own':
|
|
106
|
+
return part.text;
|
|
107
|
+
case 'block':
|
|
108
|
+
return part.text.split('\n').map(neutralizeUntrustedText).join('\n');
|
|
109
|
+
default:
|
|
110
|
+
return ` ${part.label}: ${neutralizeUntrustedText(part.text)}`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The stop as terminal rows, for a surface that paints lines rather than components.
|
|
115
|
+
*
|
|
116
|
+
* Every row is painted **verbatim, one row per line, and never re-wrapped** — that is the
|
|
117
|
+
* condition `core/shell/framing` states its column-0 guarantee over, and a caller that joins these
|
|
118
|
+
* and hands them to something that wraps has undone the whole point.
|
|
119
|
+
*
|
|
120
|
+
* `columns` is what the surface reports (`stdout.columns`), not a frame width: it is resolved
|
|
121
|
+
* through {@link frameWidthFor} here so the plain surfaces, the Ink TUI and the approval dialog
|
|
122
|
+
* cannot come to disagree about how much of a command a human was shown. When it is below core's
|
|
123
|
+
* floor the frame is wider than the terminal and the guarantee lapses — {@link narrowTerminalNotice}
|
|
124
|
+
* leads the block in that case, so it lapses out loud.
|
|
125
|
+
*/
|
|
126
|
+
export function approvalStopRows(parts, options) {
|
|
127
|
+
const width = frameWidthFor(options?.columns);
|
|
128
|
+
const rows = [];
|
|
129
|
+
const tooNarrow = narrowTerminalNotice(options?.columns);
|
|
130
|
+
if (tooNarrow)
|
|
131
|
+
rows.push(tooNarrow);
|
|
132
|
+
for (const part of parts) {
|
|
133
|
+
if (part.kind === 'own') {
|
|
134
|
+
rows.push(part.text);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
// The label is the gate's OWN word for what follows, and it stays on the gate's own row rather
|
|
138
|
+
// than being prefixed to the first framed row — a label sharing a row with untrusted text is a
|
|
139
|
+
// row a reader cannot tell apart from one the model wrote all of.
|
|
140
|
+
if (part.kind !== 'block')
|
|
141
|
+
rows.push(` ${part.label}:`);
|
|
142
|
+
const framed = part.kind === 'command'
|
|
143
|
+
? frameUntrustedCommand(part.text, { width })
|
|
144
|
+
: frameUntrustedText(part.text, { width });
|
|
145
|
+
rows.push(...framed.notices, ...framed.lines);
|
|
146
|
+
}
|
|
147
|
+
return rows;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Base class for the two run-ending approvals outcomes, so a caller that wants to present them as
|
|
151
|
+
* an ending rather than a crash can catch both with one `instanceof`.
|
|
152
|
+
*
|
|
153
|
+
* A surface that can frame should render {@link parts} (see {@link approvalStopRows}); one that
|
|
154
|
+
* cannot shows {@link message}, which is already neutralised and is already the whole explanation.
|
|
155
|
+
*/
|
|
156
|
+
export class ApprovalStopError extends Error {
|
|
157
|
+
/** The command that ended the run, exactly as the agent proposed it. */
|
|
158
|
+
command;
|
|
159
|
+
/** The message's pieces, tagged with who wrote each — see {@link ApprovalStopPart}. */
|
|
160
|
+
parts;
|
|
161
|
+
constructor(parts, command) {
|
|
162
|
+
super(composeMessage(parts));
|
|
163
|
+
this.command = command;
|
|
164
|
+
this.parts = parts;
|
|
165
|
+
// Restore the prototype chain across the ES5 `extends Error` downlevel, so `instanceof` works.
|
|
166
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
167
|
+
this.name = new.target.name;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* §4.2 — an `attack` outcome: the command's own **structure** evidenced compromise (§4.1.1 —
|
|
172
|
+
* credential targeting, privilege escalation, persistence, deception, obfuscation). Ends the agent
|
|
173
|
+
* loop; the model is told nothing and offered nothing.
|
|
174
|
+
*
|
|
175
|
+
* **This is what reaches a surface that cannot ask.** An interactive surface is offered [[TUI-C68]]
|
|
176
|
+
* §6.1's red banner first — `GthAgentRunner.setAttackHaltCallback`, where typing `run anyway` runs
|
|
177
|
+
* this one command and everything else stops the run — and this error is thrown when no banner is
|
|
178
|
+
* wired, or when the banner is answered with anything but that phrase. A non-interactive session
|
|
179
|
+
* wires nothing and so gets this message directly (§6.2).
|
|
180
|
+
*
|
|
181
|
+
* The recovery this message names is deliberately the **allow-list**, not `bypass`. §4.2 makes
|
|
182
|
+
* `approvals.allow` the supported way to run such a command unattended (it is consulted before the
|
|
183
|
+
* rater, so it never reaches a halt at all); `bypass` also works and is far blunter — it turns off
|
|
184
|
+
* the rater, the escalation and the halt together, for every command, for the whole run. It is a
|
|
185
|
+
* last resort, not the answer, and the wording says so in that order.
|
|
186
|
+
*/
|
|
187
|
+
export class AttackHaltError extends ApprovalStopError {
|
|
188
|
+
/** The rater's explanation of what the command's structure showed. */
|
|
189
|
+
reason;
|
|
190
|
+
constructor(command, reason) {
|
|
191
|
+
super([
|
|
192
|
+
{
|
|
193
|
+
kind: 'own',
|
|
194
|
+
text: 'Run halted: the auto-rater rated this command as an attack, which ends the run.',
|
|
195
|
+
},
|
|
196
|
+
{ kind: 'command', label: 'Command', text: command },
|
|
197
|
+
{ kind: 'value', label: 'Reason', text: reason },
|
|
198
|
+
{
|
|
199
|
+
kind: 'own',
|
|
200
|
+
text: `This is not negotiable. If this command is legitimate and you need it to run, declare ` +
|
|
201
|
+
`it in approvals.allow — that list is consulted before the auto-rater, so it never ` +
|
|
202
|
+
`reaches a halt. Dropping to approvals "bypass" also works, but it turns off the rater, ` +
|
|
203
|
+
`the prompts and the halt for every command in the run.`,
|
|
204
|
+
},
|
|
205
|
+
], command);
|
|
206
|
+
this.reason = reason;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* §6.2 — an escalation with nobody to ask (CI, one-shot runs, servers). Fails the build loudly,
|
|
211
|
+
* with everything a person needs in order to see why.
|
|
212
|
+
*/
|
|
213
|
+
export class NonInteractiveEscalationError extends ApprovalStopError {
|
|
214
|
+
/** The rater's outcome, when a rating existed (the unrated rungs have none). */
|
|
215
|
+
outcome;
|
|
216
|
+
/** The rater's explanation, when a rating existed. */
|
|
217
|
+
reason;
|
|
218
|
+
/**
|
|
219
|
+
* EXT-71 §3.2 — the declared `approvals.escalate` entry that sent this call to a human, when one
|
|
220
|
+
* did. It changes the recovery the message names: pointing someone at `approvals.allow` when they
|
|
221
|
+
* themselves wrote an escalate entry sends them to a list that cannot win, since a match on
|
|
222
|
+
* `escalate` outranks a match on `allow`.
|
|
223
|
+
*/
|
|
224
|
+
escalatedBy;
|
|
225
|
+
/**
|
|
226
|
+
* [[EXT-29]] §6 — the §5 negotiation that preceded this escalation, rendered, when there was one.
|
|
227
|
+
*
|
|
228
|
+
* §6.2's message is the ONLY thing a person sees on this path — there is no prompt to attach a
|
|
229
|
+
* transcript to — so an unattended run that ended after three rejections would otherwise report
|
|
230
|
+
* the last command and give no hint that the agent had already been told twice what to fix.
|
|
231
|
+
*/
|
|
232
|
+
negotiation;
|
|
233
|
+
constructor(command, outcome, reason, escalatedBy, negotiation) {
|
|
234
|
+
const parts = [
|
|
235
|
+
{ kind: 'own', text: 'Approval required, but this session has no one to ask.' },
|
|
236
|
+
{ kind: 'command', label: 'Command', text: command },
|
|
237
|
+
];
|
|
238
|
+
if (outcome)
|
|
239
|
+
parts.push({ kind: 'value', label: 'Rating', text: outcome });
|
|
240
|
+
if (reason)
|
|
241
|
+
parts.push({ kind: 'value', label: 'Reason', text: reason });
|
|
242
|
+
if (negotiation)
|
|
243
|
+
parts.push({ kind: 'block', text: negotiation });
|
|
244
|
+
if (escalatedBy) {
|
|
245
|
+
parts.push({ kind: 'value', label: 'Matched approvals.escalate', text: escalatedBy });
|
|
246
|
+
parts.push({
|
|
247
|
+
kind: 'own',
|
|
248
|
+
text: `An escalate entry always asks a human, whatever the rung would have done, so no ` +
|
|
249
|
+
`entry in approvals.allow can answer it. Remove the escalate entry if this command ` +
|
|
250
|
+
`should run unattended.`,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
parts.push({
|
|
255
|
+
kind: 'own',
|
|
256
|
+
text: `Declare the commands this run is allowed to execute in approvals.allow — write each ` +
|
|
257
|
+
`one as an explicit entry, for example { "type": "shell", "matcher": "exact", ` +
|
|
258
|
+
`"pattern": "npm test" }. That list is consulted before the auto-rater and never ` +
|
|
259
|
+
`escalates.`,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
super(parts, command);
|
|
263
|
+
this.outcome = outcome;
|
|
264
|
+
this.reason = reason;
|
|
265
|
+
this.escalatedBy = escalatedBy;
|
|
266
|
+
this.negotiation = negotiation;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=approvalStop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvalStop.js","sourceRoot":"","sources":["../../../src/core/shell/approvalStop.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,6 +18,12 @@
|
|
|
18
18
|
* redirections. Such commands NEVER auto-match an allow-list entry — they always go to
|
|
19
19
|
* fresh human approval. This is what stops `git checkout x; rm -rf /` from matching an
|
|
20
20
|
* approved `git checkout *`.
|
|
21
|
+
*
|
|
22
|
+
* EXT-55: "newlines" in that list was aspirational until this node. `normalizeCommand` folded
|
|
23
|
+
* a line break to a SPACE, so the check below never saw one and `ls -la\nrm -rf /` classified
|
|
24
|
+
* as the single command `ls`. The separator set now lives in ONE place
|
|
25
|
+
* ({@link import('./normalize.js').COMMAND_SEPARATOR_CLASS}) and a line break survives
|
|
26
|
+
* normalization, so a multi-line command is categorically ambiguous — exactly like `;`.
|
|
21
27
|
*/
|
|
22
28
|
/**
|
|
23
29
|
* Result of classifying a command for allow-list matching.
|
package/dist/core/shell/arity.js
CHANGED
|
@@ -18,7 +18,14 @@
|
|
|
18
18
|
* redirections. Such commands NEVER auto-match an allow-list entry — they always go to
|
|
19
19
|
* fresh human approval. This is what stops `git checkout x; rm -rf /` from matching an
|
|
20
20
|
* approved `git checkout *`.
|
|
21
|
+
*
|
|
22
|
+
* EXT-55: "newlines" in that list was aspirational until this node. `normalizeCommand` folded
|
|
23
|
+
* a line break to a SPACE, so the check below never saw one and `ls -la\nrm -rf /` classified
|
|
24
|
+
* as the single command `ls`. The separator set now lives in ONE place
|
|
25
|
+
* ({@link import('./normalize.js').COMMAND_SEPARATOR_CLASS}) and a line break survives
|
|
26
|
+
* normalization, so a multi-line command is categorically ambiguous — exactly like `;`.
|
|
21
27
|
*/
|
|
28
|
+
import { COMMAND_SEPARATOR_RE, LINE_BREAK_RE } from '#src/core/shell/normalize.js';
|
|
22
29
|
/**
|
|
23
30
|
* Arity table: command-prefix string → number of leading tokens (binary + subcommands,
|
|
24
31
|
* flags excluded) that define the "human-understandable command". Longest matching
|
|
@@ -181,11 +188,9 @@ const ARITY = {
|
|
|
181
188
|
* Note: `&&`/`||`/`|` are covered by the bare `&`/`|` character scan; listed conceptually.
|
|
182
189
|
*/
|
|
183
190
|
function hasUnsafeComposition(normalized) {
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
// Shell control / separator operators and background.
|
|
188
|
-
if (/[;|&]/.test(normalized))
|
|
191
|
+
// Shell control / separator operators, background, AND line breaks — one shared definition of
|
|
192
|
+
// "a new command begins here" (EXT-55), so this can never drift from the hardline floor's.
|
|
193
|
+
if (COMMAND_SEPARATOR_RE.test(normalized))
|
|
189
194
|
return true;
|
|
190
195
|
// Command substitution: $(...) or `...`.
|
|
191
196
|
if (/\$\(/.test(normalized))
|
|
@@ -235,7 +240,9 @@ export function tokenize(command) {
|
|
|
235
240
|
inToken = true;
|
|
236
241
|
continue;
|
|
237
242
|
}
|
|
238
|
-
|
|
243
|
+
// EXT-55: line breaks are whitespace here too, so a token can never be glued across a line
|
|
244
|
+
// (`ls\n-o` must tokenize as `ls` + `-o`, or a caller inspecting the argv would miss the flag).
|
|
245
|
+
if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r') {
|
|
239
246
|
if (inToken) {
|
|
240
247
|
tokens.push(current);
|
|
241
248
|
current = '';
|
|
@@ -294,6 +301,13 @@ export function meaningfulPrefixTokens(argv) {
|
|
|
294
301
|
* @param normalize Normalizer to apply for the detection form (inject normalizeCommand).
|
|
295
302
|
*/
|
|
296
303
|
export function classifyCommand(command, normalize) {
|
|
304
|
+
// EXT-55 — the boundary question is answered HERE, not delegated to the injected normalizer.
|
|
305
|
+
// `normalize` is a parameter, so this function cannot assume it preserved the command
|
|
306
|
+
// separators (folding them away is exactly the bug this node fixed). A line break anywhere
|
|
307
|
+
// INSIDE the command means more than one command → never classifiable. `.trim()` first: a
|
|
308
|
+
// purely leading/trailing break separates nothing, and models routinely emit `"npm test\n"`.
|
|
309
|
+
if (LINE_BREAK_RE.test(command.trim()))
|
|
310
|
+
return null;
|
|
297
311
|
const normalized = normalize(command);
|
|
298
312
|
if (!normalized)
|
|
299
313
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arity.js","sourceRoot":"","sources":["../../../src/core/shell/arity.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"arity.js","sourceRoot":"","sources":["../../../src/core/shell/arity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split a raw command into the segments a shell would run, normalized and **case preserved**.
|
|
3
|
+
*
|
|
4
|
+
* Substitution bodies are segments in their own right: `echo $(npm publish)` runs `npm publish`,
|
|
5
|
+
* so a deny entry for `npm publish` must see it. Splitting on `$(`, `` ` `` and `)` yields the
|
|
6
|
+
* body as its own segment (and leaves harmless empty fragments, which are dropped).
|
|
7
|
+
*
|
|
8
|
+
* Case is preserved because the matcher folds it per list and per matcher — a `regexp` entry is
|
|
9
|
+
* compiled exactly as the user wrote it, and folding here would decide that for it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function commandSegments(command: string): string[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/shell/denylist
|
|
3
|
+
*
|
|
4
|
+
* **Command segmentation** — splitting a command into every command a shell would actually run.
|
|
5
|
+
*
|
|
6
|
+
* This is what lets a restrictive rule (`deny`, `escalate`) see inside a compound command. The
|
|
7
|
+
* asymmetry it serves lives in `core/approvals/matcher.ts`, which is the one comparison engine:
|
|
8
|
+
*
|
|
9
|
+
* - **No allow entry matches a command that does not statically resolve.** Composition,
|
|
10
|
+
* substitution and redirection are a non-match for every allow entry, so a grant can never be
|
|
11
|
+
* extended with a `; rm -rf /`.
|
|
12
|
+
* - **A deny or escalate entry MAY match one**, and is compared against every segment as well as
|
|
13
|
+
* the whole string, because a prohibition that catches something unresolvable errs in the
|
|
14
|
+
* direction that costs nothing. Without that, `git push --force; ls` would sail straight past a
|
|
15
|
+
* declared deny entry for `git push --force` — a prohibition any trailing `; ls` defeats is not a
|
|
16
|
+
* prohibition.
|
|
17
|
+
*/
|
|
18
|
+
import { COMMAND_SEPARATOR_CLASS, normalizeCommand } from '#src/core/shell/normalize.js';
|
|
19
|
+
/** Splits the normalized command at every point where a shell would begin a NEW command. */
|
|
20
|
+
const SEGMENT_SPLIT_RE = new RegExp(`[${COMMAND_SEPARATOR_CLASS}]|\\$\\(|\\)|\``, 'g');
|
|
21
|
+
/**
|
|
22
|
+
* Split a raw command into the segments a shell would run, normalized and **case preserved**.
|
|
23
|
+
*
|
|
24
|
+
* Substitution bodies are segments in their own right: `echo $(npm publish)` runs `npm publish`,
|
|
25
|
+
* so a deny entry for `npm publish` must see it. Splitting on `$(`, `` ` `` and `)` yields the
|
|
26
|
+
* body as its own segment (and leaves harmless empty fragments, which are dropped).
|
|
27
|
+
*
|
|
28
|
+
* Case is preserved because the matcher folds it per list and per matcher — a `regexp` entry is
|
|
29
|
+
* compiled exactly as the user wrote it, and folding here would decide that for it.
|
|
30
|
+
*/
|
|
31
|
+
export function commandSegments(command) {
|
|
32
|
+
return normalizeCommand(command)
|
|
33
|
+
.split(SEGMENT_SPLIT_RE)
|
|
34
|
+
.map((segment) => segment.trim())
|
|
35
|
+
.filter((segment) => segment.length > 0);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=denylist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"denylist.js","sourceRoot":"","sources":["../../../src/core/shell/denylist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEzF,4FAA4F;AAC5F,MAAM,gBAAgB,GAAG,IAAI,MAAM,CAAC,IAAI,uBAAuB,iBAAiB,EAAE,GAAG,CAAC,CAAC;AAEvF;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,gBAAgB,CAAC,OAAO,CAAC;SAC7B,KAAK,CAAC,gBAAgB,CAAC;SACvB,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;SAChC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/shell/escalationSeverity
|
|
3
|
+
*
|
|
4
|
+
* [[TUI-C26]] (spec §6) — **how severe the escalation is, said in words.**
|
|
5
|
+
*
|
|
6
|
+
* Every escalation used to look identical: the same yellow line, the same
|
|
7
|
+
* `⚠ Auto-rater (<outcome>):`, on both surfaces. So `npm install lodash` and a typosquatted
|
|
8
|
+
* `curl | bash` produced the same dialog, and a dialog that looks the same for everything trains
|
|
9
|
+
* the reader to answer it the same way — which costs more than having no dialog at all.
|
|
10
|
+
*
|
|
11
|
+
* ## Why the words carry it, not the colour
|
|
12
|
+
*
|
|
13
|
+
* Colour is not reliably available. `NO_COLOR` is set on plenty of machines, output gets piped, a
|
|
14
|
+
* monochrome or high-contrast terminal renders every tone identically, and a reader may simply not
|
|
15
|
+
* be looking at hue. So each outcome carries **three** independent signals — a glyph, a tone and a
|
|
16
|
+
* sentence — and the sentence is the one that always arrives. A change that makes two outcomes read
|
|
17
|
+
* the same in words has removed the signal even if the colours still differ.
|
|
18
|
+
*
|
|
19
|
+
* **What the sentence says is the consequence, not the severity word again.** `catastrophic` is
|
|
20
|
+
* defined by needing something from *outside* the session to undo (rescue media, a backup, a
|
|
21
|
+
* re-provision), and that — not the adjective — is what a person can act on.
|
|
22
|
+
*
|
|
23
|
+
* ## The map is total, and `attack` is in it
|
|
24
|
+
*
|
|
25
|
+
* A `Record<RaterOutcome, …>` rather than a lookup with a default: an outcome added to
|
|
26
|
+
* {@link RATER_OUTCOMES} must be given words here, and cannot silently inherit another outcome's.
|
|
27
|
+
*
|
|
28
|
+
* `attack` is present for that totality and **not because the approval prompt renders it**. An
|
|
29
|
+
* `attack` verdict halts the run (`AttackHaltError`) on both rating paths, so it never reaches an
|
|
30
|
+
* approval dialog: it is answered at [[TUI-C68]]'s §6.1 banner, whose copy is
|
|
31
|
+
* {@link attackBannerCopy} — here, beside the heading it reuses, so the two cannot come to describe
|
|
32
|
+
* one verdict two ways.
|
|
33
|
+
*
|
|
34
|
+
* `attack` shares the `danger` tone and glyph with `catastrophic` deliberately: the two are not
|
|
35
|
+
* ranked against each other (they answer different questions — *can this be undone?* versus *is
|
|
36
|
+
* something hostile acting here?*), so distinguishing them by loudness would assert an ordering the
|
|
37
|
+
* rater's own schema refuses. They are distinguished where it matters, in what they say.
|
|
38
|
+
*/
|
|
39
|
+
import type { RaterOutcome } from '#src/core/shell/rater.js';
|
|
40
|
+
/**
|
|
41
|
+
* How loud a surface should be about an outcome. A surface maps this to its own vocabulary — Ink
|
|
42
|
+
* colours on the TUI, a `DialogTone` on the readline prompt — so neither has to hold its own
|
|
43
|
+
* opinion about which outcome is worse than which.
|
|
44
|
+
*/
|
|
45
|
+
export type EscalationTone = 'notice' | 'warn' | 'danger';
|
|
46
|
+
/** Everything a surface needs to render one outcome, so no surface invents its own wording. */
|
|
47
|
+
export interface RaterOutcomeDisplay {
|
|
48
|
+
/**
|
|
49
|
+
* The heading a surface paints above the rater's reason, glyph and sentence included.
|
|
50
|
+
*
|
|
51
|
+
* It keeps the `Auto-rater (<outcome>):` opening the dialog has always had — that is the
|
|
52
|
+
* attribution, and the reason underneath it is the rater's, not the gate's — and adds the
|
|
53
|
+
* consequence after it.
|
|
54
|
+
*/
|
|
55
|
+
heading: string;
|
|
56
|
+
/** The tone the heading and the framed reason are painted in. */
|
|
57
|
+
tone: EscalationTone;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The label above the framed reason.
|
|
61
|
+
*
|
|
62
|
+
* The heading now carries a sentence of the gate's own, so without this the model-authored prose
|
|
63
|
+
* beneath it could be read as a continuation of what the gate said. §6 wants the opposite: the
|
|
64
|
+
* reason is the rater's, quoted, and it is framed exactly like the command for the same reason.
|
|
65
|
+
*/
|
|
66
|
+
export declare const RATER_REASON_LABEL = " the rater's own words:";
|
|
67
|
+
/** How to render {@link RaterOutcome} on an approval dialog. */
|
|
68
|
+
export declare function describeRaterOutcome(outcome: RaterOutcome): RaterOutcomeDisplay;
|
|
69
|
+
/**
|
|
70
|
+
* [[TUI-C68]] §6.1 — **the phrase that runs an `attack`-rated command anyway.** The only string on
|
|
71
|
+
* the banner that is not a refusal.
|
|
72
|
+
*
|
|
73
|
+
* A typed phrase rather than a key, because an irreversible decision must not be reachable by the
|
|
74
|
+
* muscle memory built answering routine prompts. A key merely *disjoint from today's menu* stops
|
|
75
|
+
* being disjoint the moment someone binds one; a phrase is immune to any future binding. `run`
|
|
76
|
+
* alone was considered and rejected as the leading token of half of what anyone types into a
|
|
77
|
+
* terminal.
|
|
78
|
+
*
|
|
79
|
+
* The label is never `bypass`: that is a rung (§2.5) and a far broader thing — it turns off the
|
|
80
|
+
* rater, the escalation and the halt together, for every command, for the whole run — and a user
|
|
81
|
+
* must never read this banner as switching to it.
|
|
82
|
+
*/
|
|
83
|
+
export declare const RUN_ANYWAY_PHRASE = "run anyway";
|
|
84
|
+
/**
|
|
85
|
+
* §6.1 — does what the human typed grant this one command?
|
|
86
|
+
*
|
|
87
|
+
* **The whole matching rule, in one place both surfaces call**, so what the banner *says* is
|
|
88
|
+
* answerable and what a surface *accepts* cannot drift. Trimmed, lower-cased, compared whole:
|
|
89
|
+
* `RUN ANYWAY` and a phrase with surrounding spaces grant; `run`, `runanyway`, `run anyway please`
|
|
90
|
+
* and the phrase with a doubled inner space do not. No prefix, no initial, no `y`, no alias.
|
|
91
|
+
*
|
|
92
|
+
* **Everything that is not the phrase is a refusal, and that is the property the banner rests on.**
|
|
93
|
+
* An approval prompt grants only on an offered key and treats every other keystroke as a rejection;
|
|
94
|
+
* a text buffer inverts that by accumulating keystrokes instead of rejecting them, and a matcher
|
|
95
|
+
* this narrow is what puts it back — the buffer may hold anything at all, and only one value of it
|
|
96
|
+
* runs the command.
|
|
97
|
+
*
|
|
98
|
+
* Deliberately NOT whitespace-normalising the middle: a rule that repairs what the user typed is a
|
|
99
|
+
* rule that grants on something they did not type, and retyping a phrase costs nothing next to
|
|
100
|
+
* running this command by accident.
|
|
101
|
+
*/
|
|
102
|
+
export declare function grantsRunAnyway(typed: string): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* §6.1 — the banner's own words, shared so no surface invents its own.
|
|
105
|
+
*
|
|
106
|
+
* It carries what every surface must say and nothing about how any one of them is driven: the
|
|
107
|
+
* readline prompt reads a line in cooked mode and has no `q` or `Esc` to bind, so a keyboard line
|
|
108
|
+
* here would be false on one of the two surfaces. A surface adds its own keys beside these.
|
|
109
|
+
*/
|
|
110
|
+
export interface AttackBannerCopy {
|
|
111
|
+
/** The banner's title row — what happened, above everything else. */
|
|
112
|
+
title: string;
|
|
113
|
+
/** The `attack` heading from {@link describeRaterOutcome}, glyph and consequence included. */
|
|
114
|
+
heading: string;
|
|
115
|
+
/**
|
|
116
|
+
* **The irreversibility line, and it is UNCONDITIONAL** — on every attack banner, whatever the
|
|
117
|
+
* rating said, and not only where the command was also rated `catastrophic`.
|
|
118
|
+
*
|
|
119
|
+
* The banner is rare by construction (§4.1.1 forbids `attack` firing on ordinary work), so the
|
|
120
|
+
* line cannot become noise; and a static string cannot fail the way a model's explanation can. It
|
|
121
|
+
* is the last thing between a human and an action nothing else will stop.
|
|
122
|
+
*/
|
|
123
|
+
irreversible: string;
|
|
124
|
+
/** What each control does, in the phrase's own words. */
|
|
125
|
+
controls: readonly string[];
|
|
126
|
+
/** The label a surface puts in front of the buffer the human types into. */
|
|
127
|
+
prompt: string;
|
|
128
|
+
/**
|
|
129
|
+
* What a surface says once the phrase has been accepted.
|
|
130
|
+
*
|
|
131
|
+
* It states the scope of what was just granted, because that is the half a user cannot observe:
|
|
132
|
+
* the command running is visible, and *only this one running* is not. Shared so neither surface
|
|
133
|
+
* can promise a persistence the runner does not perform.
|
|
134
|
+
*/
|
|
135
|
+
granted: string;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* §6.1 — the banner copy. A function rather than an exported object so one surface cannot mutate
|
|
139
|
+
* the strings another is about to paint.
|
|
140
|
+
*/
|
|
141
|
+
export declare function attackBannerCopy(): AttackBannerCopy;
|