@gaunt-sloth/core 2.0.0-alpha.35 → 2.0.0-alpha.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/config/configDiscovery.d.ts +79 -0
  2. package/dist/config/configDiscovery.js +80 -0
  3. package/dist/config/configDiscovery.js.map +1 -0
  4. package/dist/config/loader.d.ts +6 -5
  5. package/dist/config/loader.js +229 -92
  6. package/dist/config/loader.js.map +1 -1
  7. package/dist/config/schema.d.ts +3 -2
  8. package/dist/config/schema.js +57 -9
  9. package/dist/config/schema.js.map +1 -1
  10. package/dist/config/shell-policy.d.ts +58 -26
  11. package/dist/config/shell-policy.js +60 -27
  12. package/dist/config/shell-policy.js.map +1 -1
  13. package/dist/config/tool-descriptions.d.ts +6 -6
  14. package/dist/config/tool-descriptions.js +7 -7
  15. package/dist/config/tool-descriptions.js.map +1 -1
  16. package/dist/config/types.d.ts +34 -31
  17. package/dist/config/types.js.map +1 -1
  18. package/dist/config.d.ts +3 -0
  19. package/dist/config.js +3 -0
  20. package/dist/config.js.map +1 -1
  21. package/dist/core/GthAbstractAgent.d.ts +11 -13
  22. package/dist/core/GthAbstractAgent.js +9 -11
  23. package/dist/core/GthAbstractAgent.js.map +1 -1
  24. package/dist/core/GthAgentRunner.d.ts +17 -33
  25. package/dist/core/GthAgentRunner.js +31 -41
  26. package/dist/core/GthAgentRunner.js.map +1 -1
  27. package/dist/core/GthLangChainAgent.js +25 -34
  28. package/dist/core/GthLangChainAgent.js.map +1 -1
  29. package/dist/core/approvals/toolAnnotationSources.d.ts +4 -4
  30. package/dist/core/approvals/toolAnnotationSources.js +8 -8
  31. package/dist/core/debugCapture.d.ts +2 -3
  32. package/dist/core/debugCapture.js +1 -2
  33. package/dist/core/debugCapture.js.map +1 -1
  34. package/dist/core/gthLeanAgentFactory.d.ts +4 -4
  35. package/dist/core/gthLeanAgentFactory.js +4 -4
  36. package/dist/core/reasoningBlocks.d.ts +7 -2
  37. package/dist/core/reasoningBlocks.js +7 -2
  38. package/dist/core/reasoningBlocks.js.map +1 -1
  39. package/dist/core/shell/ShellCommandFailedError.d.ts +3 -4
  40. package/dist/core/shell/ShellCommandFailedError.js +3 -4
  41. package/dist/core/shell/ShellCommandFailedError.js.map +1 -1
  42. package/dist/core/shell/approvalStop.d.ts +68 -29
  43. package/dist/core/shell/approvalStop.js +178 -28
  44. package/dist/core/shell/approvalStop.js.map +1 -1
  45. package/dist/core/shell/hardline.d.ts +22 -0
  46. package/dist/core/shell/hardline.js +81 -0
  47. package/dist/core/shell/hardline.js.map +1 -1
  48. package/dist/core/shell/negotiation.d.ts +81 -2
  49. package/dist/core/shell/negotiation.js +144 -11
  50. package/dist/core/shell/negotiation.js.map +1 -1
  51. package/dist/core/shell/rater.d.ts +7 -69
  52. package/dist/core/shell/rater.js +5 -24
  53. package/dist/core/shell/rater.js.map +1 -1
  54. package/dist/core/shell/raterVocabulary.d.ts +119 -0
  55. package/dist/core/shell/raterVocabulary.js +114 -0
  56. package/dist/core/shell/raterVocabulary.js.map +1 -0
  57. package/dist/core/types.d.ts +17 -3
  58. package/dist/providers/anthropic.js +12 -0
  59. package/dist/providers/anthropic.js.map +1 -1
  60. package/dist/providers/configurationPassthrough.d.ts +107 -0
  61. package/dist/providers/configurationPassthrough.js +148 -0
  62. package/dist/providers/configurationPassthrough.js.map +1 -0
  63. package/dist/providers/geminiThinking.d.ts +8 -0
  64. package/dist/providers/geminiThinking.js +33 -13
  65. package/dist/providers/geminiThinking.js.map +1 -1
  66. package/dist/providers/google-genai.js +11 -0
  67. package/dist/providers/google-genai.js.map +1 -1
  68. package/dist/providers/groq.js +12 -0
  69. package/dist/providers/groq.js.map +1 -1
  70. package/dist/providers/ollama.js +12 -0
  71. package/dist/providers/ollama.js.map +1 -1
  72. package/dist/providers/openrouter.d.ts +23 -0
  73. package/dist/providers/openrouter.js +71 -4
  74. package/dist/providers/openrouter.js.map +1 -1
  75. package/dist/providers/vertexai.js +12 -0
  76. package/dist/providers/vertexai.js.map +1 -1
  77. package/dist/providers/xai.js +20 -0
  78. package/dist/providers/xai.js.map +1 -1
  79. package/dist/runtime/conversation.d.ts +2 -2
  80. package/dist/runtime/conversation.js +19 -4
  81. package/dist/runtime/conversation.js.map +1 -1
  82. package/dist/runtime/singleShot.d.ts +2 -3
  83. package/dist/runtime/singleShot.js +22 -7
  84. package/dist/runtime/singleShot.js.map +1 -1
  85. package/dist/utils/systemPromptNotes.d.ts +17 -20
  86. package/dist/utils/systemPromptNotes.js +22 -24
  87. package/dist/utils/systemPromptNotes.js.map +1 -1
  88. package/dist/utils/untrustedText.d.ts +24 -4
  89. package/dist/utils/untrustedText.js +25 -4
  90. package/dist/utils/untrustedText.js.map +1 -1
  91. package/package.json +4 -4
  92. package/schema/gsloth-config.schema.json +0 -1
@@ -23,18 +23,145 @@
23
23
  * thrown run as `ok: false`, and each command entry point turns that into `setExitCode(1)` — so
24
24
  * "immediate non-zero exit carrying the explanation" is what a caller already gets, without a new
25
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.
26
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
+ }
27
149
  /**
28
150
  * Base class for the two run-ending approvals outcomes, so a caller that wants to present them as
29
- * an ending rather than a crash can catch both with one `instanceof`. Every surface shows the
30
- * message as-is, which is already the whole explanation.
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.
31
155
  */
32
156
  export class ApprovalStopError extends Error {
33
- /** The command that ended the run. */
157
+ /** The command that ended the run, exactly as the agent proposed it. */
34
158
  command;
35
- constructor(message, command) {
36
- super(message);
159
+ /** The message's pieces, tagged with who wrote each — see {@link ApprovalStopPart}. */
160
+ parts;
161
+ constructor(parts, command) {
162
+ super(composeMessage(parts));
37
163
  this.command = command;
164
+ this.parts = parts;
38
165
  // Restore the prototype chain across the ES5 `extends Error` downlevel, so `instanceof` works.
39
166
  Object.setPrototypeOf(this, new.target.prototype);
40
167
  this.name = new.target.name;
@@ -45,11 +172,11 @@ export class ApprovalStopError extends Error {
45
172
  * credential targeting, privilege escalation, persistence, deception, obfuscation). Ends the agent
46
173
  * loop; the model is told nothing and offered nothing.
47
174
  *
48
- * **This is what reaches a surface that cannot ask.** An interactive surface is offered §6.1's red
49
- * banner first — `GthAgentRunner.setAttackHaltCallback`, where typing `run anyway` runs this one
50
- * command and everything else stops the run — and this error is thrown when no banner is wired, or
51
- * when the banner is answered with anything but that phrase. A non-interactive session wires
52
- * nothing and so gets this message directly (§6.2).
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).
53
180
  *
54
181
  * The recovery this message names is deliberately the **allow-list**, not `bypass`. §4.2 makes
55
182
  * `approvals.allow` the supported way to run such a command unattended (it is consulted before the
@@ -61,13 +188,21 @@ export class AttackHaltError extends ApprovalStopError {
61
188
  /** The rater's explanation of what the command's structure showed. */
62
189
  reason;
63
190
  constructor(command, reason) {
64
- super(`Run halted: the auto-rater rated this command as an attack, which ends the run.\n` +
65
- ` Command: ${command}\n` +
66
- ` Reason: ${reason}\n` +
67
- `This is not negotiable. If this command is legitimate and you need it to run, declare ` +
68
- `it in approvals.allow — that list is consulted before the auto-rater, so it never ` +
69
- `reaches a halt. Dropping to approvals "bypass" also works, but it turns off the rater, ` +
70
- `the prompts and the halt for every command in the run.`, command);
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);
71
206
  this.reason = reason;
72
207
  }
73
208
  }
@@ -96,20 +231,35 @@ export class NonInteractiveEscalationError extends ApprovalStopError {
96
231
  */
97
232
  negotiation;
98
233
  constructor(command, outcome, reason, escalatedBy, negotiation) {
99
- super(`Approval required, but this session has no one to ask.\n` +
100
- ` Command: ${command}\n` +
101
- (outcome ? ` Rating: ${outcome}\n` : '') +
102
- (reason ? ` Reason: ${reason}\n` : '') +
103
- (negotiation ? `${negotiation}\n` : '') +
104
- (escalatedBy
105
- ? ` Matched approvals.escalate: ${escalatedBy}\n` +
106
- `An escalate entry always asks a human, whatever the rung would have done, so no ` +
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 ` +
107
249
  `entry in approvals.allow can answer it. Remove the escalate entry if this command ` +
108
- `should run unattended.`
109
- : `Declare the commands this run is allowed to execute in approvals.allow — write each ` +
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 ` +
110
257
  `one as an explicit entry, for example { "type": "shell", "matcher": "exact", ` +
111
258
  `"pattern": "npm test" }. That list is consulted before the auto-rater and never ` +
112
- `escalates.`), command);
259
+ `escalates.`,
260
+ });
261
+ }
262
+ super(parts, command);
113
263
  this.outcome = outcome;
114
264
  this.reason = reason;
115
265
  this.escalatedBy = escalatedBy;
@@ -1 +1 @@
1
- {"version":3,"file":"approvalStop.js","sourceRoot":"","sources":["../../../src/core/shell/approvalStop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;;GAIG;AACH,MAAM,OAAgB,iBAAkB,SAAQ,KAAK;IACnD,sCAAsC;IAC7B,OAAO,CAAS;IAEzB,YAAsB,OAAe,EAAE,OAAe;QACpD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,+FAA+F;QAC/F,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,eAAgB,SAAQ,iBAAiB;IACpD,sEAAsE;IAC7D,MAAM,CAAS;IAExB,YAAY,OAAe,EAAE,MAAc;QACzC,KAAK,CACH,mFAAmF;YACjF,cAAc,OAAO,IAAI;YACzB,aAAa,MAAM,IAAI;YACvB,wFAAwF;YACxF,oFAAoF;YACpF,yFAAyF;YACzF,wDAAwD,EAC1D,OAAO,CACR,CAAC;QACF,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,6BAA8B,SAAQ,iBAAiB;IAClE,gFAAgF;IACvE,OAAO,CAAqB;IACrC,sDAAsD;IAC7C,MAAM,CAAqB;IACpC;;;;;OAKG;IACM,WAAW,CAAqB;IAEzC;;;;;;OAMG;IACM,WAAW,CAAqB;IAEzC,YACE,OAAe,EACf,OAAgB,EAChB,MAAe,EACf,WAAoB,EACpB,WAAoB;QAEpB,KAAK,CACH,0DAA0D;YACxD,cAAc,OAAO,IAAI;YACzB,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,MAAM,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACvC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,WAAW,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACvC,CAAC,WAAW;gBACV,CAAC,CAAC,iCAAiC,WAAW,IAAI;oBAChD,kFAAkF;oBAClF,oFAAoF;oBACpF,wBAAwB;gBAC1B,CAAC,CAAC,sFAAsF;oBACtF,+EAA+E;oBAC/E,kFAAkF;oBAClF,YAAY,CAAC,EACnB,OAAO,CACR,CAAC;QACF,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;CACF"}
1
+ {"version":3,"file":"approvalStop.js","sourceRoot":"","sources":["../../../src/core/shell/approvalStop.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,28 @@
24
24
  * fork-bomb literal (the string *is* the fork bomb).
25
25
  */
26
26
  export declare const HARDLINE_PATTERNS: ReadonlyArray<readonly [RegExp, string]>;
27
+ /**
28
+ * **Every string a §8 decision is made of, in one record — exported ONLY for the snapshot gate**
29
+ * (`shellHardlinePatternSurface.spec.ts`), which freezes it as a literal in its own source. Nothing
30
+ * in production reads it, and nothing should: it is the module's pattern surface made addressable so
31
+ * that a narrowing cannot land silently. **Any new pattern constant joins it**, and the gate's
32
+ * key-set check fails if one does not.
33
+ *
34
+ * **Membership is decided by what the patterns are BUILT FROM, not by what this file declares** —
35
+ * which is why {@link COMMAND_SEPARATOR_CLASS}, imported from `core/shell/normalize`, is a member.
36
+ * Floor behaviour depends on it and it is edited in another module, so a change there is exactly the
37
+ * silent narrowing this exists to make loud.
38
+ *
39
+ * A value composed at module load carries its parts with it: {@link CMD_POS} expands
40
+ * {@link WRAPPER_ARMS}, and {@link ROOT_TARGET} expands `quotedOrBare`, so those parts are frozen
41
+ * whether or not they are also members. The parts are listed anyway, because a diff that names
42
+ * `H_TOKEN_EXCLUSIONS` is actionable where one that names only `CHOWN_HEAD` is a wall of regex.
43
+ *
44
+ * Regexes are recorded as `String(re)` rather than `re.source` **because the flags are part of the
45
+ * behaviour**: a `g` flag makes `.test()` stateful across calls, a change no source-only record
46
+ * would show.
47
+ */
48
+ export declare const HARDLINE_PATTERN_SURFACE: Readonly<Record<string, string | readonly string[]>>;
27
49
  /**
28
50
  * Whether one pipeline both reads credential material and transmits data off the machine.
29
51
  * Exported for tests, which pin BOTH directions: the credential-upload shapes must match, and
@@ -39,6 +39,34 @@
39
39
  * escalation behind it at every rung but `bypass`.** {@link CMD_POS} carries the worked example of
40
40
  * a case measured and dropped.
41
41
  *
42
+ * **How it may SHRINK — a narrowing ships with its removal set pinned.** Any edit that makes the
43
+ * floor refuse strictly fewer commands is a narrowing: a new exclusion, a loosened bound, a
44
+ * tightened anchor, a narrower character class. **The suite cannot review one.** If the edit had
45
+ * removed a refusal that any spec asserts, that spec would be red — so a green run proves only that
46
+ * everything the edit removed was unasserted, which is the same set as "uncovered". Green is
47
+ * structurally silent about a narrowing, and an AI review of the diff is no better: both read what
48
+ * the change still does, and the whole risk is in what it quietly stopped doing.
49
+ *
50
+ * So the change states its **removal set** — the commands that matched before and do not match
51
+ * after — characterised from the shape of the edit, never sampled from the corpus
52
+ * (`shellHardlineCorpus.spec.ts` sees only its own cases *by construction*; its docblock says so). A
53
+ * regex's removal set cannot be enumerated exactly, so the requirement is a characterisation plus
54
+ * probes pinned at its dangerous end. **Derived by the reviewer independently, not by the author** —
55
+ * an author who missed the removal while writing the edit will miss it again while describing it.
56
+ * {@link HARDLINE_PATTERN_SURFACE} is what makes a narrowing ASK for that declaration: every pattern
57
+ * constant this module binds at module scope is frozen in `shellHardlinePatternSurface.spec.ts`, so
58
+ * moving one turns a cell red and only a deliberate edit to that spec's literal turns it green
59
+ * again. A pattern built inside a function body, and any narrowing that lives in control flow rather
60
+ * than in a string, are outside it — that spec's docblock names them.
61
+ *
62
+ * **The asymmetry above flips at the exfiltration boundary — check which half you are editing.**
63
+ * For the destructive-verb arms a miss still has the confirmation dialog and the rater behind it,
64
+ * which is what makes "prefer fewer false positives" safe there. For the exfiltration arms,
65
+ * removing a refusal **is** the harm, and "a miss still has the rater behind it" is the precise
66
+ * reliance §3 forbids: the deterministic subset exists *because* the `attack` outcome must not
67
+ * depend on the rater alone. "Strictly subtractive — it can only remove refusals, never add one" is
68
+ * a safety property in the first half and a description of the vulnerability in the second.
69
+ *
42
70
  * §8.1 — **the floor is never advertised.** It is documented for people reading the code and the
43
71
  * spec, never offered to a user as a reason to feel safe; user-facing copy cites only protections
44
72
  * the user can inspect and extend (the deny list).
@@ -634,6 +662,59 @@ const CREDENTIAL_SOURCE_PATTERNS = [
634
662
  // The whole environment, piped somewhere.
635
663
  new RegExp(CMD_POS + '(?:printenv|env)\\s*(?=\\||$)'),
636
664
  ];
665
+ /**
666
+ * **Every string a §8 decision is made of, in one record — exported ONLY for the snapshot gate**
667
+ * (`shellHardlinePatternSurface.spec.ts`), which freezes it as a literal in its own source. Nothing
668
+ * in production reads it, and nothing should: it is the module's pattern surface made addressable so
669
+ * that a narrowing cannot land silently. **Any new pattern constant joins it**, and the gate's
670
+ * key-set check fails if one does not.
671
+ *
672
+ * **Membership is decided by what the patterns are BUILT FROM, not by what this file declares** —
673
+ * which is why {@link COMMAND_SEPARATOR_CLASS}, imported from `core/shell/normalize`, is a member.
674
+ * Floor behaviour depends on it and it is edited in another module, so a change there is exactly the
675
+ * silent narrowing this exists to make loud.
676
+ *
677
+ * A value composed at module load carries its parts with it: {@link CMD_POS} expands
678
+ * {@link WRAPPER_ARMS}, and {@link ROOT_TARGET} expands `quotedOrBare`, so those parts are frozen
679
+ * whether or not they are also members. The parts are listed anyway, because a diff that names
680
+ * `H_TOKEN_EXCLUSIONS` is actionable where one that names only `CHOWN_HEAD` is a wall of regex.
681
+ *
682
+ * Regexes are recorded as `String(re)` rather than `re.source` **because the flags are part of the
683
+ * behaviour**: a `g` flag makes `.test()` stateful across calls, a change no source-only record
684
+ * would show.
685
+ */
686
+ export const HARDLINE_PATTERN_SURFACE = Object.freeze({
687
+ // Structural / shared.
688
+ COMMAND_SEPARATOR_CLASS,
689
+ WRAPPER_FLAGS,
690
+ WRAPPER_ARMS: Object.freeze([...WRAPPER_ARMS]),
691
+ CMD_POS,
692
+ TARGET_TOKEN_END,
693
+ H_SPACE,
694
+ H_TOKEN_EXCLUSIONS,
695
+ H_TOKEN_CHAR,
696
+ H_OPERAND_CHAR,
697
+ // The destructive-verb arms and the fragments they share.
698
+ ROOT_TARGET,
699
+ SYSTEM_DIR_TARGET,
700
+ RECURSIVE_FLAG,
701
+ CHOWN_SKIPPABLE_ARG,
702
+ CHOWN_HEAD,
703
+ // The pattern list itself, sources only: the human descriptions beside them are prose, and a
704
+ // reworded one is not a narrowing, so they stay out rather than train a reader to update the
705
+ // frozen literal without reading why the cell went red.
706
+ HARDLINE_PATTERNS: Object.freeze(HARDLINE_PATTERNS.map(([pattern]) => String(pattern))),
707
+ // The exfiltration arms.
708
+ PIPELINE_SPLIT_RE: String(PIPELINE_SPLIT_RE),
709
+ NETWORK_SINK_RE: String(NETWORK_SINK_RE),
710
+ RSYNC_REMOTE_TARGET,
711
+ RSYNC_REMOTE_SINK_RE: String(RSYNC_REMOTE_SINK_RE),
712
+ TOKEN_END,
713
+ NOT_PUBLIC_KEY,
714
+ DOTENV_RE: String(DOTENV_RE),
715
+ DOTENV_AS_OUTPUT_TARGET: String(DOTENV_AS_OUTPUT_TARGET),
716
+ CREDENTIAL_SOURCE_PATTERNS: Object.freeze(CREDENTIAL_SOURCE_PATTERNS.map((pattern) => String(pattern))),
717
+ });
637
718
  /**
638
719
  * Whether one pipeline both reads credential material and transmits data off the machine.
639
720
  * Exported for tests, which pin BOTH directions: the credential-upload shapes must match, and
@@ -1 +1 @@
1
- {"version":3,"file":"hardline.js","sourceRoot":"","sources":["../../../src/core/shell/hardline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hardline.js","sourceRoot":"","sources":["../../../src/core/shell/hardline.ts"],"names":[],"mappings":"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"}
@@ -137,13 +137,22 @@ export declare class ShellNegotiationState {
137
137
  * **The round is appended BEFORE either bound is tested**, so the rating that escalates is itself
138
138
  * on the transcript the human sees. §5.6's escalation example turns on this: what matters on the
139
139
  * screen is that the agent proposed the same command three times, and the third proposal is the
140
- * one being escalated.
140
+ * one being escalated. {@link renderNegotiationRows} marks that last round as the pending request
141
+ * rather than as a prior one, which is the half of this the reader needs and cannot derive.
141
142
  */
142
143
  recordRejection(round: RaterNegotiationRound): NegotiationVerdict;
143
144
  /**
144
145
  * §5.3 — a tool call the gate let through. Resets the consecutive counter **and clears the
145
146
  * transcript with it**; the reachability bound is deliberately untouched.
146
147
  *
148
+ * **What it must NOT truncate is the human's record of how hard the agent pushed.** The counter's
149
+ * reset is §5.3's and is sound; the reader at an escalation is asking a different question, and
150
+ * the answer to theirs is {@link NegotiationCounters.rejectionsSinceHuman}, which this leaves
151
+ * standing. {@link renderNegotiationRows} reports that count rather than the surviving rounds, so
152
+ * an argument this erased is still declared even though its rounds are gone; every erased round
153
+ * itself survives whole in [[TUI-C27]]'s archive, which captures per RATING CALL and so is not
154
+ * truncated by anything here.
155
+ *
147
156
  * **"Approved" is what the gate can observe, and it is not quite §5.3's "successful".** The
148
157
  * decision site sees whether a call was allowed to run, never whether it then exited zero — and
149
158
  * the honest alternatives were worse: a tool-result stream carries the *rejected* call's own
@@ -190,6 +199,10 @@ export declare class ShellNegotiationState {
190
199
  * was rejected"* and *"the call was the third consecutive rejection, so the next one goes to a
191
200
  * person"* are different facts, and a bug report carrying only the first invites the wrong
192
201
  * conclusion about why a session started interrupting.
202
+ *
203
+ * **`rejectionsSinceHuman` is also what the escalation prompt COUNTS**, and that is the one place
204
+ * it faces a person rather than a debugging archive. It is read at the escalation site before
205
+ * {@link humanReached} spends it; reading it after would report zero.
193
206
  */
194
207
  counters(): NegotiationCounters;
195
208
  /** Drop everything, including the user messages — the TUI's `/clear` rotates the thread. */
@@ -211,6 +224,31 @@ export interface NegotiationRow {
211
224
  voice: NegotiationVoice;
212
225
  text: string;
213
226
  }
227
+ /**
228
+ * Terminal rows one element of a round — the command, the justification, the rater's answer — may
229
+ * occupy on a screen before the rest of it is elided.
230
+ *
231
+ * **MEASURED, and it is the height bound this block needs rather than a cap on rounds.** At 80
232
+ * columns three rounds of paragraph-length argument cost 37 rows, of which one round was 12; the
233
+ * whole prompt was 64 rows against a 20-row budget, so the human saw the command and the verdict
234
+ * and neither the later rounds nor the menu line. A cap on the NUMBER of rounds would have changed
235
+ * none of that — §5.3 already bounds the transcript at
236
+ * {@link MAX_CONSECUTIVE_REJECTIONS} — because the cost is per row, not per round. Bounding each
237
+ * element keeps every round structurally on the screen, which is what §5.6 calls the most important
238
+ * thing on it, and pays for it out of the tail of a paragraph the reader was never going to need in
239
+ * full: the archive keeps every round whole ([[TUI-C27]]'s capture is per rating call).
240
+ *
241
+ * Two rather than one so a wrapped command keeps the continuation that shows how it differs from
242
+ * the round above it — the comparison the block exists to make.
243
+ */
244
+ export declare const NEGOTIATION_MAX_ROWS_PER_ELEMENT = 2;
245
+ /**
246
+ * Rounds a screen shows, newest last. A backstop rather than today's binding constraint: §5.3
247
+ * escalates at {@link MAX_CONSECUTIVE_REJECTIONS}, so a transcript reaching a human is never longer
248
+ * than this — and an unscrollable prompt must not acquire an unbounded section the moment that
249
+ * number is raised. What is dropped is said out loud in the heading, which carries the true count.
250
+ */
251
+ export declare const NEGOTIATION_MAX_ROUNDS_SHOWN = 3;
214
252
  /**
215
253
  * §6/§5.4 — render a negotiation for the human being asked to rule on it, one terminal row per
216
254
  * element, each tagged with the voice speaking it.
@@ -233,9 +271,46 @@ export interface NegotiationRow {
233
271
  * A wrapped row keeps the voice of the row it continues. A continuation painted as chrome would put
234
272
  * the rater's words in the agent's colour at exactly the width where a long argument is hardest to
235
273
  * read, which is the confusion §5.4 exists to remove.
274
+ *
275
+ * ## The three things this block must not get wrong about its own exchange
276
+ *
277
+ * Every label here sits over data that is already correct, so a label that lies does so silently —
278
+ * and each of these lied toward approving, at the moment a human was deciding whether to overrule
279
+ * a refusal.
280
+ *
281
+ * - **`attempts` is the count, and it is not `rounds.length`.** §5.3 clears the transcript on an
282
+ * approved call, so the rounds handed over are the attempts since the last *approval*, while the
283
+ * fact the reader is weighing is how hard the agent pushed since the last *human* — the caller's
284
+ * `rejectionsSinceHuman`. A measured escalation attempted the same command five times and
285
+ * rendered three. Omit it and this falls back to `rounds.length`, which is the honest reading of
286
+ * a caller that has no better number rather than a claim that none were erased.
287
+ * - **The last round IS the pending rating, not a prior one.** {@link
288
+ * ShellNegotiationState.recordRejection} appends before either bound is tested, deliberately, so
289
+ * that the rating being escalated is on the transcript the human sees. The old heading called
290
+ * them all prior rounds, which both under-reported the argument and put the pending command on
291
+ * the screen twice with nothing saying they were the same call.
292
+ * - **The first round was rated on the command alone.** A cleared transcript IS the round-1 case
293
+ * (see {@link ShellNegotiationState.contextFor}), so §5.1 withheld the justification and the user
294
+ * messages from that rating — while {@link ShellNegotiationState.recordRejection} stores the
295
+ * justification the agent supplied whatever the round. Printed unmarked, the round reads as a
296
+ * rater brushing past a direct answer to its own objection, which is the opposite of what
297
+ * happened and makes the rater look stubborn exactly where the reader is deciding whether to
298
+ * overrule it. The withholding is correct and is not what this marks. The same marker answers
299
+ * *"were the user's own words in view when this was rated?"* — a justification claiming the user
300
+ * asked for a command is exactly what a round-1 rating cannot check, and the user's messages are
301
+ * several rows on a surface with none to spare.
302
+ *
303
+ * **The prompt this feeds does not scroll and nothing else on it can give up rows**, so the height
304
+ * bound is {@link NEGOTIATION_MAX_ROWS_PER_ELEMENT} and every fact above is carried by a row that
305
+ * already exists.
236
306
  */
237
307
  export declare function renderNegotiationRows(rounds: readonly RaterNegotiationRound[], options?: {
238
308
  width?: number;
309
+ /**
310
+ * Rejected attempts since a human was last involved — `rejectionsSinceHuman` from {@link
311
+ * ShellNegotiationState.counters}, read BEFORE the escalation spends it.
312
+ */
313
+ attempts?: number;
239
314
  }): NegotiationRow[];
240
315
  /**
241
316
  * The same transcript as one string, for a consumer with no screen to lay it out on: §6.2's
@@ -245,5 +320,9 @@ export declare function renderNegotiationRows(rounds: readonly RaterNegotiationR
245
320
  * Defined as {@link renderNegotiationRows} joined, so the string and the rows can never come to
246
321
  * describe one exchange two ways — the whole reason there is one renderer and two surfaces.
247
322
  * `null` when there is nothing to show.
323
+ *
324
+ * `attempts` carries the same fact it carries on a screen. It is the one thing a caller must pass
325
+ * for this message to be true, because the count it defaults to is the one an approved call
326
+ * truncated.
248
327
  */
249
- export declare function renderNegotiationTranscript(rounds: readonly RaterNegotiationRound[]): string | null;
328
+ export declare function renderNegotiationTranscript(rounds: readonly RaterNegotiationRound[], attempts?: number): string | null;