gaunt-sloth 2.0.0-beta.13 → 2.0.0-beta.14
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/dist/cli.js +37 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands/prCommand.js +32 -15
- package/dist/commands/prCommand.js.map +1 -1
- package/dist/commands/reviewCommand.js +10 -2
- package/dist/commands/reviewCommand.js.map +1 -1
- package/dist/tui/approvalBridges.d.ts +86 -0
- package/dist/tui/approvalBridges.js +147 -0
- package/dist/tui/approvalBridges.js.map +1 -0
- package/dist/tui/components/App.js +23 -3
- package/dist/tui/components/App.js.map +1 -1
- package/dist/tui/components/DebugPanel.js +17 -7
- package/dist/tui/components/DebugPanel.js.map +1 -1
- package/dist/tui/components/LaunchBanner.d.ts +3 -2
- package/dist/tui/components/LaunchBanner.js +7 -5
- package/dist/tui/components/LaunchBanner.js.map +1 -1
- package/dist/tui/components/LiveTurn.d.ts +9 -4
- package/dist/tui/components/LiveTurn.js +1 -1
- package/dist/tui/components/LiveTurn.js.map +1 -1
- package/dist/tui/debugRender.js +13 -1
- package/dist/tui/debugRender.js.map +1 -1
- package/dist/tui/keyBindings.js +7 -1
- package/dist/tui/keyBindings.js.map +1 -1
- package/dist/tui/tuiSessionModule.js +72 -189
- package/dist/tui/tuiSessionModule.js.map +1 -1
- package/dist/tui/types.d.ts +32 -11
- package/package.json +5 -5
package/dist/cli.js
CHANGED
|
@@ -16,11 +16,11 @@ import { historyCommand } from '#src/commands/historyCommand.js';
|
|
|
16
16
|
import { RESUMABLE_COMMANDS, resumeOption, rootResumeRefusalMessage, } from '#src/commands/resumeOption.js';
|
|
17
17
|
import { insightsCommand } from '#src/commands/insightsCommand.js';
|
|
18
18
|
import { modelsCommand } from '#src/commands/modelsCommand.js';
|
|
19
|
-
import { argv, exit, getSlothVersion, readStdin } from '@gaunt-sloth/core/utils/systemUtils.js';
|
|
19
|
+
import { argv, exit, getSlothVersion, readStdin, willWaitForPipedStdin, } from '@gaunt-sloth/core/utils/systemUtils.js';
|
|
20
20
|
import { commandSkipsStdin, resolveInvokedCommandName } from '#src/utils/stdinPolicy.js';
|
|
21
21
|
import { guardProgramConfigErrors } from '#src/utils/configErrorGuard.js';
|
|
22
|
-
import { CONFLICTING_CONFIG_SOURCES_MESSAGE } from '@gaunt-sloth/core/config.js';
|
|
23
|
-
import { coerceBooleanOrString, displayError } from '@gaunt-sloth/core/utils/consoleUtils.js';
|
|
22
|
+
import { CONFLICTING_CONFIG_SOURCES_MESSAGE, loadConfiguredConsoleLevel, } from '@gaunt-sloth/core/config.js';
|
|
23
|
+
import { coerceBooleanOrString, displayError, setConsoleLevel, } from '@gaunt-sloth/core/utils/consoleUtils.js';
|
|
24
24
|
import { installCrashHandler } from '@gaunt-sloth/core/utils/crashHandler.js';
|
|
25
25
|
// GS2-48 — install the process-level crash handler as early as possible, so an uncaughtException /
|
|
26
26
|
// unhandledRejection anywhere in the run writes a redacted debug snapshot to ~/.gsloth/debug-dumps/
|
|
@@ -147,6 +147,40 @@ if (rootResume !== undefined &&
|
|
|
147
147
|
displayError(rootResumeRefusalMessage(invokedCommand, rootResume));
|
|
148
148
|
exit(1);
|
|
149
149
|
}
|
|
150
|
+
// REL-25 — resolve `consoleLevel` from the config BEFORE the parse, because a piped run writes
|
|
151
|
+
// before the parse. `readStdin` blocks until the pipe closes and draws its `reading STDIN` line
|
|
152
|
+
// while it waits; the level a run finally uses is applied from the merged config inside the command
|
|
153
|
+
// action, i.e. after `program.parseAsync()`. Without this the only level in force when that line is
|
|
154
|
+
// written is the INFO default, so the one progress line a piped run draws was the one line no
|
|
155
|
+
// `consoleLevel` could quieten.
|
|
156
|
+
//
|
|
157
|
+
// **The level is read twice on purpose, and there is only ever one value.** This reads the same key
|
|
158
|
+
// from the same files through the same discovery and the same narrowing as the config load that
|
|
159
|
+
// follows, and sets nothing else. Where that load runs it re-applies the identical value and has
|
|
160
|
+
// the last word (its defaults always carry a `consoleLevel`, so it never leaves the setting alone);
|
|
161
|
+
// where it never runs at all — `insights` and `models` resolve no config, and a `--help`/`--version`
|
|
162
|
+
// run exits before one is built — the value read here is the only one, and it is still the level the
|
|
163
|
+
// user configured. So the two readings cannot hold different answers; one of them can only be
|
|
164
|
+
// absent. That does mean a PIPED `gth insights` now filters its own output by `consoleLevel` while
|
|
165
|
+
// a non-piped one still does not: those surfaces sit outside the config load, not outside the level
|
|
166
|
+
// system, and closing that asymmetry is a change to them rather than to this line.
|
|
167
|
+
//
|
|
168
|
+
// It is also silent: a config it cannot read answers "no level", leaving the run at the default and
|
|
169
|
+
// leaving the report of a broken config where it already is, at the CLI's top-level guard after the
|
|
170
|
+
// parse.
|
|
171
|
+
//
|
|
172
|
+
// Asked only on the branch that writes early, and asked through `readStdin`'s own predicate rather
|
|
173
|
+
// than a restatement of it: a run that will not wait on a pipe gains nothing here, and would pay a
|
|
174
|
+
// config read (and, for a `configure()`-style config, a second module evaluation) for it.
|
|
175
|
+
//
|
|
176
|
+
// It must come after the option-derived overrides above — `-c`, `-g` and `--profile` decide WHICH
|
|
177
|
+
// config this reads — and after the `--no-pipe` implication, which decides whether it is read.
|
|
178
|
+
if (willWaitForPipedStdin(program)) {
|
|
179
|
+
const earlyConsoleLevel = await loadConfiguredConsoleLevel(cliConfigOverrides);
|
|
180
|
+
if (earlyConsoleLevel !== undefined) {
|
|
181
|
+
setConsoleLevel(earlyConsoleLevel);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
150
184
|
// CFG-35 — the config loader raises a catchable error when a provider has no resolvable API key,
|
|
151
185
|
// so programmatic callers can classify it. For a person at a terminal there is nothing to classify,
|
|
152
186
|
// so the `gth` CLI makes the terminating choice here, once, instead of in every command action.
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,7 @@ import { wrapContent } from '@gaunt-sloth/core/utils/llmUtils.js';
|
|
|
9
9
|
import { runPrDiscovery } from '#src/commands/prDiscovery.js';
|
|
10
10
|
import { readMultipleFilesFromProjectDir } from '@gaunt-sloth/review/utils/fileUtils.js';
|
|
11
11
|
import { extractChangedPathsFromDiff } from '@gaunt-sloth/review/utils/diffPaths.js';
|
|
12
|
+
import { writeReviewFailureReport } from '@gaunt-sloth/review/modules/reviewFailureReport.js';
|
|
12
13
|
export function prCommand(program, commandLineConfigOverrides) {
|
|
13
14
|
program
|
|
14
15
|
.command('pr')
|
|
@@ -27,6 +28,23 @@ export function prCommand(program, commandLineConfigOverrides) {
|
|
|
27
28
|
const content = [];
|
|
28
29
|
const requirementSource = getEffectiveRequirementSource('pr', config, options.requirementsSource);
|
|
29
30
|
const contentSource = getEffectiveContentSource('pr', config);
|
|
31
|
+
// REL-20 — the label `review()` would have used, hoisted because the failure report has to
|
|
32
|
+
// land at the same path the successful run would have written.
|
|
33
|
+
const reportSource = prId ? `PR-${prId}` : 'PR-discovery';
|
|
34
|
+
/**
|
|
35
|
+
* REL-20 — every exit from this action that happens BEFORE `review()` runs goes through
|
|
36
|
+
* here, so a caller that asked for a report file gets one saying why there is none of the
|
|
37
|
+
* usual content in it. The exit code is unchanged: the run still failed.
|
|
38
|
+
*
|
|
39
|
+
* This is an enumeration of exits, which is exactly the shape that rots by omission — a new
|
|
40
|
+
* guard added below without this call silently reopens the hole for one more input. Anything
|
|
41
|
+
* here that `return`s before the `review()` call must call this first.
|
|
42
|
+
*/
|
|
43
|
+
const failBeforeReview = (message) => {
|
|
44
|
+
displayError(message);
|
|
45
|
+
writeReviewFailureReport(config, reportSource, 'pr', message);
|
|
46
|
+
setExitCode(1);
|
|
47
|
+
};
|
|
30
48
|
if (options.file) {
|
|
31
49
|
content.push(readMultipleFilesFromProjectDir(options.file));
|
|
32
50
|
}
|
|
@@ -42,10 +60,9 @@ export function prCommand(program, commandLineConfigOverrides) {
|
|
|
42
60
|
const isDiscovery = !prId && !requirementsId;
|
|
43
61
|
const looksLikeRequirementsOnlyMode = contentSource === 'github' && Boolean(prId) && !requirementsId && !/^\d+$/.test(prId);
|
|
44
62
|
if (looksLikeRequirementsOnlyMode) {
|
|
45
|
-
|
|
63
|
+
failBeforeReview(`Unsupported PR command arguments: "${prId}" was provided as the pull request ID. ` +
|
|
46
64
|
'`gth pr <requirementsId>` requirements-only mode is not supported. ' +
|
|
47
65
|
'Use `gth pr` with no arguments to discover change requirements automatically, or provide both a numeric PR ID and requirements ID: `gth pr <prId> <requirementsId>`.');
|
|
48
|
-
setExitCode(1);
|
|
49
66
|
return;
|
|
50
67
|
}
|
|
51
68
|
// With the GitHub content source, prId ends up interpolated into `gh pr diff <prId>` and
|
|
@@ -53,14 +70,12 @@ export function prCommand(program, commandLineConfigOverrides) {
|
|
|
53
70
|
// depth), but reject garbage upfront with a clear error instead of a downstream warning.
|
|
54
71
|
// Non-GitHub content sources (file/text) accept arbitrary content ids and stay untouched.
|
|
55
72
|
if (contentSource === 'github' && prId && !/^\d+$/.test(prId)) {
|
|
56
|
-
|
|
57
|
-
setExitCode(1);
|
|
73
|
+
failBeforeReview(`Invalid pull request ID "${prId}"; expected a numeric PR number, e.g. \`gth pr 42\`.`);
|
|
58
74
|
return;
|
|
59
75
|
}
|
|
60
76
|
if (isDiscovery) {
|
|
61
77
|
if (config.commands?.pr?.discovery?.enabled === false) {
|
|
62
|
-
|
|
63
|
-
setExitCode(1);
|
|
78
|
+
failBeforeReview('Change requirements discovery is disabled. Provide a pull request ID to run `gth pr`.');
|
|
64
79
|
return;
|
|
65
80
|
}
|
|
66
81
|
try {
|
|
@@ -69,8 +84,7 @@ export function prCommand(program, commandLineConfigOverrides) {
|
|
|
69
84
|
content.push(wrapContent(discoveryResult.requirements, 'discovered-requirements', 'requirements'));
|
|
70
85
|
}
|
|
71
86
|
if (!discoveryResult.diff) {
|
|
72
|
-
|
|
73
|
-
setExitCode(1);
|
|
87
|
+
failBeforeReview('Change requirements discovery did not produce a diff. Cannot continue with review.');
|
|
74
88
|
return;
|
|
75
89
|
}
|
|
76
90
|
changedPaths = extractChangedPathsFromDiff(discoveryResult.diff);
|
|
@@ -85,11 +99,16 @@ export function prCommand(program, commandLineConfigOverrides) {
|
|
|
85
99
|
for (const row of approvalStopRows(error.parts, { columns: stdout.columns })) {
|
|
86
100
|
displayError(row);
|
|
87
101
|
}
|
|
102
|
+
// REL-20 — the report gets the stop's own message, not the rows above. Those rows are
|
|
103
|
+
// the terminal rendering: a gutter and a wrap computed against this terminal's width,
|
|
104
|
+
// which is meaningless in a file someone posts as a PR comment. The message is already
|
|
105
|
+
// neutralised at construction, so it is safe to write raw.
|
|
106
|
+
writeReviewFailureReport(config, reportSource, 'pr', error.message);
|
|
107
|
+
setExitCode(1);
|
|
88
108
|
}
|
|
89
109
|
else {
|
|
90
|
-
|
|
110
|
+
failBeforeReview(error instanceof Error ? error.message : String(error));
|
|
91
111
|
}
|
|
92
|
-
setExitCode(1);
|
|
93
112
|
return;
|
|
94
113
|
}
|
|
95
114
|
}
|
|
@@ -106,16 +125,14 @@ export function prCommand(program, commandLineConfigOverrides) {
|
|
|
106
125
|
// returns null (with a warning) for an invalid PR number. Without this guard the review
|
|
107
126
|
// would silently proceed against no diff; fail loudly as the throwing path used to.
|
|
108
127
|
if (!prContent) {
|
|
109
|
-
|
|
110
|
-
setExitCode(1);
|
|
128
|
+
failBeforeReview(`Could not retrieve PR content for "${prId}". Cannot continue with review.`);
|
|
111
129
|
return;
|
|
112
130
|
}
|
|
113
131
|
changedPaths = extractChangedPathsFromDiff(prContent);
|
|
114
132
|
content.push(prContent);
|
|
115
133
|
}
|
|
116
134
|
catch (error) {
|
|
117
|
-
|
|
118
|
-
setExitCode(1);
|
|
135
|
+
failBeforeReview(error instanceof Error ? error.message : String(error));
|
|
119
136
|
return;
|
|
120
137
|
}
|
|
121
138
|
}
|
|
@@ -124,7 +141,7 @@ export function prCommand(program, commandLineConfigOverrides) {
|
|
|
124
141
|
}
|
|
125
142
|
const { review } = await import('@gaunt-sloth/review/modules/reviewModule.js');
|
|
126
143
|
const { createResolvers } = await import('@gaunt-sloth/agent/resolvers.js');
|
|
127
|
-
await review(
|
|
144
|
+
await review(reportSource, getReviewSystemPrompt(config), content.join('\n'), config, 'pr', createResolvers(),
|
|
128
145
|
// Bind GitHub-only review tools (gth_gh_read_file) to this PR's repo/ref, so they read
|
|
129
146
|
// the PR under review rather than letting the model guess owner/repo. Undefined prId =
|
|
130
147
|
// discovery mode (current branch's PR), which `gh pr view` resolves on its own.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prCommand.js","sourceRoot":"","sources":["../../src/commands/prCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,wCAAwC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AACnG,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,6BAA6B,EAC7B,qBAAqB,GACtB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAA8B,MAAM,mBAAmB,CAAC;AACrF,OAAO,WAAW,MAAM,kCAAkC,CAAC;AAG3D,OAAO,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,+BAA+B,EAAE,MAAM,wCAAwC,CAAC;AACzF,OAAO,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;
|
|
1
|
+
{"version":3,"file":"prCommand.js","sourceRoot":"","sources":["../../src/commands/prCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,9 @@ import { readMultipleFilesFromProjectDir } from '@gaunt-sloth/review/utils/fileU
|
|
|
9
9
|
// `commands/commandUtils`, and this helper lives only in review's (GS2-45).
|
|
10
10
|
import { resolvePrIdFromArg } from '@gaunt-sloth/review/commands/commandUtils.js';
|
|
11
11
|
import { extractChangedPathsFromDiff } from '@gaunt-sloth/review/utils/diffPaths.js';
|
|
12
|
+
import { writeReviewFailureReport } from '@gaunt-sloth/review/modules/reviewFailureReport.js';
|
|
13
|
+
/** The source label this command passes to `review()`, and therefore names its report file with. */
|
|
14
|
+
const REVIEW_SOURCE = 'REVIEW';
|
|
12
15
|
export function reviewCommand(program, cliConfigOverrides) {
|
|
13
16
|
program
|
|
14
17
|
.command('review')
|
|
@@ -50,7 +53,12 @@ export function reviewCommand(program, cliConfigOverrides) {
|
|
|
50
53
|
providedContent = await getCommandSourceInput('review', 'content', contentId, config, contentSource);
|
|
51
54
|
}
|
|
52
55
|
catch (error) {
|
|
53
|
-
|
|
56
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
57
|
+
displayError(message);
|
|
58
|
+
// REL-20 — `gth review` fails before inference the same way `gth pr` does (the git source
|
|
59
|
+
// outside a repository, the GitHub source on a diff too large to fetch), so it owes the
|
|
60
|
+
// same report. The two commands share this exit; they must not diverge on it.
|
|
61
|
+
writeReviewFailureReport(config, REVIEW_SOURCE, 'review', message);
|
|
54
62
|
setExitCode(1);
|
|
55
63
|
return;
|
|
56
64
|
}
|
|
@@ -79,7 +87,7 @@ export function reviewCommand(program, cliConfigOverrides) {
|
|
|
79
87
|
}
|
|
80
88
|
const { review } = await import('@gaunt-sloth/review/modules/reviewModule.js');
|
|
81
89
|
const { createResolvers } = await import('@gaunt-sloth/agent/resolvers.js');
|
|
82
|
-
await review(
|
|
90
|
+
await review(REVIEW_SOURCE, getReviewSystemPrompt(config), content.join('\n'), config, 'review', createResolvers(),
|
|
83
91
|
// Bind GitHub-only review tools (gth_gh_read_file) to the pull request this run's DIFF
|
|
84
92
|
// came from. Without it they resolve the PR from the checked-out branch instead, so a
|
|
85
93
|
// `gth review 42 --content-source github` reviews PR 42's diff against a different pull
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reviewCommand.js","sourceRoot":"","sources":["../../src/commands/reviewCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAC;AACzF,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,6BAA6B,EAC7B,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,oBAAoB,EACpB,eAAe,GAGhB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AAElE,OAAO,EAAE,+BAA+B,EAAE,MAAM,wCAAwC,CAAC;AACzF,iFAAiF;AACjF,4EAA4E;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAC;AAClF,OAAO,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;
|
|
1
|
+
{"version":3,"file":"reviewCommand.js","sourceRoot":"","sources":["../../src/commands/reviewCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two promise-based fan-outs between the runner's human-in-the-loop seams and the mounted Ink
|
|
3
|
+
* `<App>`: the tool-approval prompt, and [[TUI-C68]]'s attack banner.
|
|
4
|
+
*
|
|
5
|
+
* ## Why they live in a module of their own
|
|
6
|
+
*
|
|
7
|
+
* [[EXT-110]] — because `abortPending` is the part of a session nobody can reach from a test while
|
|
8
|
+
* it sits inside `tuiSessionModule`. That module pulls in Ink, `initConfig`, the runner, the
|
|
9
|
+
* history store and the mouse plumbing, so a spec for the teardown answer has to mock a session in
|
|
10
|
+
* order to ask a question about eighty lines of plain TypeScript — and the one spec that tried
|
|
11
|
+
* instead kept a COPY of this bridge beside it and asserted against that. A copy passes whatever
|
|
12
|
+
* the original does, which is exactly how a teardown came to be archived as a human refusal with a
|
|
13
|
+
* green suite either side of it. These are the objects production wires; the specs import them
|
|
14
|
+
* from here.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import type { ApprovalOutcome, PendingAttackHalt, PendingToolInterrupt, ToolApprovalReply, AttackHaltReply } from '@gaunt-sloth/core/core/types.js';
|
|
19
|
+
import type { PendingApproval, PendingAttackBanner } from '#src/tui/types.js';
|
|
20
|
+
/**
|
|
21
|
+
* Fan-out so the runner's tool-approval callback can reach the mounted React app. Modeled on
|
|
22
|
+
* `createStatusBridge`, but promise-based: when the runner suspends on a
|
|
23
|
+
* `run_shell_command` interrupt and calls the approval callback, the bridge creates a pending
|
|
24
|
+
* record (the {@link @gaunt-sloth/core!core/types.PendingToolInterrupt | PendingToolInterrupt} plus a `resolve`), emits it to the subscribed
|
|
25
|
+
* `<App>`, and hands the callback a Promise it awaits until the app resolves a decision.
|
|
26
|
+
*
|
|
27
|
+
* Fail-closed: if the session ends / the app unmounts while an approval is still pending, every
|
|
28
|
+
* outstanding record is answered by `abortPending`, so a suspended run can never hang — and the
|
|
29
|
+
* tool does not run, matching the readline path's "anything not o/s/a → reject" default.
|
|
30
|
+
*
|
|
31
|
+
* [[EXT-150]] — **and the answer comes back the other way.** The runner reports what a decision
|
|
32
|
+
* LANDED as only after the callback above has returned (the write is attempted then), so the
|
|
33
|
+
* decision promise cannot carry it. `report` is wired to `runner.setApprovalOutcomeCallback` and
|
|
34
|
+
* settles a second promise per request, which is what `resolve` hands the App. The two are paired by
|
|
35
|
+
* the `pending` object's identity rather than by arrival order: this bridge holds a SET of
|
|
36
|
+
* outstanding records and the App a queue, so ordering is not a property either of them keeps, and a
|
|
37
|
+
* mis-paired outcome would confirm one call's persistence on another call's dialog.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createApprovalBridge(): {
|
|
40
|
+
/** Wired to `runner.setToolApprovalCallback`: returns a Promise the runner awaits. */
|
|
41
|
+
request: (pending: PendingToolInterrupt) => Promise<ToolApprovalReply>;
|
|
42
|
+
/**
|
|
43
|
+
* [[EXT-150]] — wired to `runner.setApprovalOutcomeCallback`: hand the waiting request what its
|
|
44
|
+
* answer actually landed as. An outcome for a request nobody is waiting on (teardown got there
|
|
45
|
+
* first) is dropped, which is why the map entry is removed as it settles.
|
|
46
|
+
*/
|
|
47
|
+
report: (outcome: ApprovalOutcome) => void;
|
|
48
|
+
subscribe: (cb: (record: PendingApproval) => void) => () => void;
|
|
49
|
+
/**
|
|
50
|
+
* Answer every still-pending approval on teardown, so a suspended run can never hang.
|
|
51
|
+
*
|
|
52
|
+
* [[EXT-110]] — **and answer it as a teardown, not as a reject.** The call is still refused;
|
|
53
|
+
* what changed is what the archive is told about WHO refused it. This used to send
|
|
54
|
+
* `{ type: 'reject', message: 'Session ended before approval.' }`, which is the same shape a
|
|
55
|
+
* person pressing *reject* sends — so a terminal closed with a prompt on screen was written
|
|
56
|
+
* into the `/debug-dump` archive as a human having looked at that command and said no. That is
|
|
57
|
+
* the strongest statement the archive makes, and it was being made about nobody.
|
|
58
|
+
*/
|
|
59
|
+
abortPending: () => void;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* [[TUI-C68]] §6.1 — the same fan-out for **attack halts**, so the runner's halt seam can reach the
|
|
63
|
+
* mounted React app and a human can type their way past one.
|
|
64
|
+
*
|
|
65
|
+
* A second bridge rather than a widened approval one: the answers are different types, and keeping
|
|
66
|
+
* them apart is what stops a surface returning an approval `scope` for a halt that must never carry
|
|
67
|
+
* one.
|
|
68
|
+
*
|
|
69
|
+
* Fail-closed the same way: if the session ends while a banner is still up, `abortPending` answers
|
|
70
|
+
* it so the suspended run cannot hang — and it answers with a value that does not run the command.
|
|
71
|
+
*/
|
|
72
|
+
export declare function createAttackHaltBridge(): {
|
|
73
|
+
/** Wired to `runner.setAttackHaltCallback`: returns a Promise the runner awaits. */
|
|
74
|
+
request: (halt: PendingAttackHalt) => Promise<AttackHaltReply>;
|
|
75
|
+
subscribe: (cb: (record: PendingAttackBanner) => void) => () => void;
|
|
76
|
+
/**
|
|
77
|
+
* Answer every still-open banner on teardown.
|
|
78
|
+
*
|
|
79
|
+
* [[EXT-110]] — with `teardown` rather than `stop`, for the reason the approval bridge above
|
|
80
|
+
* sends a teardown reply. The run still ends: `run-anyway` is the only value that runs
|
|
81
|
+
* anything, so the §6.1 polarity is untouched and every other value halts. What no longer
|
|
82
|
+
* happens is the archive recording a person as having read an attack banner and refused it
|
|
83
|
+
* when the session had already gone.
|
|
84
|
+
*/
|
|
85
|
+
abortPending: () => void;
|
|
86
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fan-out so the runner's tool-approval callback can reach the mounted React app. Modeled on
|
|
3
|
+
* `createStatusBridge`, but promise-based: when the runner suspends on a
|
|
4
|
+
* `run_shell_command` interrupt and calls the approval callback, the bridge creates a pending
|
|
5
|
+
* record (the {@link @gaunt-sloth/core!core/types.PendingToolInterrupt | PendingToolInterrupt} plus a `resolve`), emits it to the subscribed
|
|
6
|
+
* `<App>`, and hands the callback a Promise it awaits until the app resolves a decision.
|
|
7
|
+
*
|
|
8
|
+
* Fail-closed: if the session ends / the app unmounts while an approval is still pending, every
|
|
9
|
+
* outstanding record is answered by `abortPending`, so a suspended run can never hang — and the
|
|
10
|
+
* tool does not run, matching the readline path's "anything not o/s/a → reject" default.
|
|
11
|
+
*
|
|
12
|
+
* [[EXT-150]] — **and the answer comes back the other way.** The runner reports what a decision
|
|
13
|
+
* LANDED as only after the callback above has returned (the write is attempted then), so the
|
|
14
|
+
* decision promise cannot carry it. `report` is wired to `runner.setApprovalOutcomeCallback` and
|
|
15
|
+
* settles a second promise per request, which is what `resolve` hands the App. The two are paired by
|
|
16
|
+
* the `pending` object's identity rather than by arrival order: this bridge holds a SET of
|
|
17
|
+
* outstanding records and the App a queue, so ordering is not a property either of them keeps, and a
|
|
18
|
+
* mis-paired outcome would confirm one call's persistence on another call's dialog.
|
|
19
|
+
*/
|
|
20
|
+
export function createApprovalBridge() {
|
|
21
|
+
const listeners = new Set();
|
|
22
|
+
// Records that have been emitted but not yet resolved (used by abortPending on teardown).
|
|
23
|
+
const outstanding = new Set();
|
|
24
|
+
// [[EXT-150]] — how to settle the outcome promise of a request still waiting to hear one, keyed by
|
|
25
|
+
// the interrupt the runner will name. Cleared as each is settled, so nothing accumulates.
|
|
26
|
+
const awaitingOutcome = new Map();
|
|
27
|
+
return {
|
|
28
|
+
/** Wired to `runner.setToolApprovalCallback`: returns a Promise the runner awaits. */
|
|
29
|
+
request: (pending) => new Promise((resolve) => {
|
|
30
|
+
let settled = false;
|
|
31
|
+
// Created before the record so `resolve` can hand it back; the executor runs synchronously,
|
|
32
|
+
// so `settleOutcome` is the real resolver by the time anything can call it.
|
|
33
|
+
let settleOutcome = () => { };
|
|
34
|
+
const outcome = new Promise((settleIt) => {
|
|
35
|
+
settleOutcome = settleIt;
|
|
36
|
+
});
|
|
37
|
+
awaitingOutcome.set(pending, (reported) => {
|
|
38
|
+
awaitingOutcome.delete(pending);
|
|
39
|
+
settleOutcome(reported);
|
|
40
|
+
});
|
|
41
|
+
const record = {
|
|
42
|
+
pending,
|
|
43
|
+
resolve: (reply) => {
|
|
44
|
+
if (!settled) {
|
|
45
|
+
settled = true;
|
|
46
|
+
outstanding.delete(record);
|
|
47
|
+
resolve(reply);
|
|
48
|
+
}
|
|
49
|
+
// The same promise on every call, so the idempotent second `resolve` of a race is still
|
|
50
|
+
// told the outcome rather than handed one that can never settle.
|
|
51
|
+
return outcome;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
outstanding.add(record);
|
|
55
|
+
for (const l of listeners)
|
|
56
|
+
l(record);
|
|
57
|
+
}),
|
|
58
|
+
/**
|
|
59
|
+
* [[EXT-150]] — wired to `runner.setApprovalOutcomeCallback`: hand the waiting request what its
|
|
60
|
+
* answer actually landed as. An outcome for a request nobody is waiting on (teardown got there
|
|
61
|
+
* first) is dropped, which is why the map entry is removed as it settles.
|
|
62
|
+
*/
|
|
63
|
+
report: (outcome) => {
|
|
64
|
+
awaitingOutcome.get(outcome.pending)?.(outcome);
|
|
65
|
+
},
|
|
66
|
+
subscribe: (cb) => {
|
|
67
|
+
listeners.add(cb);
|
|
68
|
+
return () => {
|
|
69
|
+
listeners.delete(cb);
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
/**
|
|
73
|
+
* Answer every still-pending approval on teardown, so a suspended run can never hang.
|
|
74
|
+
*
|
|
75
|
+
* [[EXT-110]] — **and answer it as a teardown, not as a reject.** The call is still refused;
|
|
76
|
+
* what changed is what the archive is told about WHO refused it. This used to send
|
|
77
|
+
* `{ type: 'reject', message: 'Session ended before approval.' }`, which is the same shape a
|
|
78
|
+
* person pressing *reject* sends — so a terminal closed with a prompt on screen was written
|
|
79
|
+
* into the `/debug-dump` archive as a human having looked at that command and said no. That is
|
|
80
|
+
* the strongest statement the archive makes, and it was being made about nobody.
|
|
81
|
+
*/
|
|
82
|
+
abortPending: () => {
|
|
83
|
+
for (const record of [...outstanding]) {
|
|
84
|
+
record.resolve({ type: 'teardown', message: 'Session ended before approval.' });
|
|
85
|
+
}
|
|
86
|
+
// [[EXT-150]] — and tell everything still waiting that no outcome is coming. Fail-closed here
|
|
87
|
+
// means `null`, which the App renders as *no persistence claim*: a surface that hung waiting
|
|
88
|
+
// would be as bad as one that guessed, and guessing is what this node removes.
|
|
89
|
+
for (const settle of [...awaitingOutcome.values()])
|
|
90
|
+
settle(null);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* [[TUI-C68]] §6.1 — the same fan-out for **attack halts**, so the runner's halt seam can reach the
|
|
96
|
+
* mounted React app and a human can type their way past one.
|
|
97
|
+
*
|
|
98
|
+
* A second bridge rather than a widened approval one: the answers are different types, and keeping
|
|
99
|
+
* them apart is what stops a surface returning an approval `scope` for a halt that must never carry
|
|
100
|
+
* one.
|
|
101
|
+
*
|
|
102
|
+
* Fail-closed the same way: if the session ends while a banner is still up, `abortPending` answers
|
|
103
|
+
* it so the suspended run cannot hang — and it answers with a value that does not run the command.
|
|
104
|
+
*/
|
|
105
|
+
export function createAttackHaltBridge() {
|
|
106
|
+
const listeners = new Set();
|
|
107
|
+
const outstanding = new Set();
|
|
108
|
+
return {
|
|
109
|
+
/** Wired to `runner.setAttackHaltCallback`: returns a Promise the runner awaits. */
|
|
110
|
+
request: (halt) => new Promise((resolve) => {
|
|
111
|
+
let settled = false;
|
|
112
|
+
const record = {
|
|
113
|
+
halt,
|
|
114
|
+
resolve: (reply) => {
|
|
115
|
+
if (settled)
|
|
116
|
+
return;
|
|
117
|
+
settled = true;
|
|
118
|
+
outstanding.delete(record);
|
|
119
|
+
resolve(reply);
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
outstanding.add(record);
|
|
123
|
+
for (const l of listeners)
|
|
124
|
+
l(record);
|
|
125
|
+
}),
|
|
126
|
+
subscribe: (cb) => {
|
|
127
|
+
listeners.add(cb);
|
|
128
|
+
return () => {
|
|
129
|
+
listeners.delete(cb);
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
/**
|
|
133
|
+
* Answer every still-open banner on teardown.
|
|
134
|
+
*
|
|
135
|
+
* [[EXT-110]] — with `teardown` rather than `stop`, for the reason the approval bridge above
|
|
136
|
+
* sends a teardown reply. The run still ends: `run-anyway` is the only value that runs
|
|
137
|
+
* anything, so the §6.1 polarity is untouched and every other value halts. What no longer
|
|
138
|
+
* happens is the archive recording a person as having read an attack banner and refused it
|
|
139
|
+
* when the session had already gone.
|
|
140
|
+
*/
|
|
141
|
+
abortPending: () => {
|
|
142
|
+
for (const record of [...outstanding])
|
|
143
|
+
record.resolve('teardown');
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=approvalBridges.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvalBridges.js","sourceRoot":"","sources":["../../src/tui/approvalBridges.ts"],"names":[],"mappings":"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"}
|
|
@@ -1245,6 +1245,10 @@ export function App(props) {
|
|
|
1245
1245
|
// TUI-C19 — the actual validation warnings so `/config` renders the details the
|
|
1246
1246
|
// standing advisory line points at.
|
|
1247
1247
|
configWarnings: props.advisories,
|
|
1248
|
+
// GS2-88 — the three summaries AND the reason they may be empty, so a command with
|
|
1249
|
+
// nothing to show names the cause the session actually established rather than the
|
|
1250
|
+
// config. Both surfaces pass all four, from the one core builder.
|
|
1251
|
+
historyAvailability: props.historyAvailability,
|
|
1248
1252
|
historySummary: props.historySummary,
|
|
1249
1253
|
insightsSummary: props.insightsSummary,
|
|
1250
1254
|
historySearch: props.historySearch,
|
|
@@ -1283,7 +1287,12 @@ export function App(props) {
|
|
|
1283
1287
|
// thread intact (the LangGraph checkpointer replays it on the next turn), so the
|
|
1284
1288
|
// model would still "remember" everything. Reset the agent's thread too so the
|
|
1285
1289
|
// model context truly matches the now-empty transcript (TUI-C8).
|
|
1286
|
-
|
|
1290
|
+
//
|
|
1291
|
+
// [[EXT-109]] — and the agent's own record of the conversation goes with it: the
|
|
1292
|
+
// approvals gate's capture log is what the Auto-mode tab above and a later
|
|
1293
|
+
// `/debug-dump` read, and every other pane cleared here would otherwise sit beside one
|
|
1294
|
+
// that still quotes the user's pre-`/clear` messages.
|
|
1295
|
+
agent.clearConversation?.();
|
|
1287
1296
|
// The status-bar turn counter is part of the conversation state we just wiped, so
|
|
1288
1297
|
// reset it too — a cleared session starts back at "turns: 0".
|
|
1289
1298
|
turnCountRef.current = 0;
|
|
@@ -1395,7 +1404,7 @@ export function App(props) {
|
|
|
1395
1404
|
// path never registers gsloth's readline Esc handler, so Ink owns raw mode cleanly).
|
|
1396
1405
|
// 3. Tab while the panel is visible and idle → focus/cycle the panel.
|
|
1397
1406
|
// 4. While the panel is focused → Tab/Shift+Tab cycle sections, ↑/↓ scroll one line,
|
|
1398
|
-
// PageUp/PageDown page-step, m maximises, Esc
|
|
1407
|
+
// PageUp/PageDown page-step, m maximises, Esc leaves the pane (restoring a maximised one).
|
|
1399
1408
|
useInput((input, key) => {
|
|
1400
1409
|
// [[TUI-C79]] — **Ctrl+C is this app's, not Ink's**, and it is answered here before anything
|
|
1401
1410
|
// else can claim it. `render()` takes `exitOnCtrlC: false` (`tuiSessionModule.tsx`), which is
|
|
@@ -1632,7 +1641,17 @@ export function App(props) {
|
|
|
1632
1641
|
}
|
|
1633
1642
|
return;
|
|
1634
1643
|
}
|
|
1635
|
-
// Esc layering: clear an active search first (less-style), else
|
|
1644
|
+
// Esc layering: clear an active search first (less-style), else LEAVE the pane — and leaving
|
|
1645
|
+
// it drops the maximised view as well as focus, in the one press.
|
|
1646
|
+
//
|
|
1647
|
+
// TUI-C107 — focus and maximisation are one state as far as an exit is concerned, so an exit
|
|
1648
|
+
// that dropped only one of them left the user with nothing to press. The prompt is unmounted
|
|
1649
|
+
// while the pane holds focus (the render's `!debugFocused` guard), and a maximised pane takes
|
|
1650
|
+
// the terminal height minus DEBUG_MAX_CHROME_ROWS, so the prompt has no rows to draw in even
|
|
1651
|
+
// once focus is gone: dropping focus alone left the session with no visible prompt, and `m` —
|
|
1652
|
+
// the key that would undo the maximisation — is handled inside this focused-only branch, so
|
|
1653
|
+
// it no longer answered either. Same pairing, and the same reason, as the /debug toggle's
|
|
1654
|
+
// "focus never lingers on a hidden panel" above.
|
|
1636
1655
|
if (key.escape) {
|
|
1637
1656
|
if (debugSearchQueryRef.current) {
|
|
1638
1657
|
clearDebugSearch();
|
|
@@ -1640,6 +1659,7 @@ export function App(props) {
|
|
|
1640
1659
|
}
|
|
1641
1660
|
setDebugFocused(false);
|
|
1642
1661
|
debugFocusedRef.current = false;
|
|
1662
|
+
setDebugMaximized(false);
|
|
1643
1663
|
return;
|
|
1644
1664
|
}
|
|
1645
1665
|
// `/` opens the search input (scoped to pane focus — the seam).
|