gaunt-sloth 2.0.0-beta.11 → 2.0.0-beta.13
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/cli.js +49 -0
- package/dist/commands/adkEvalRunner.d.ts +7 -6
- package/dist/commands/adkEvalRunner.js +3 -3
- package/dist/commands/adkEvalRunner.js.map +1 -1
- package/dist/commands/agUiEvalRunner.d.ts +4 -4
- package/dist/commands/agUiEvalRunner.js +8 -7
- package/dist/commands/agUiEvalRunner.js.map +1 -1
- package/dist/commands/askCommand.js +9 -1
- package/dist/commands/askCommand.js.map +1 -1
- package/dist/commands/batchCommand.d.ts +2 -2
- package/dist/commands/batchCommand.js +2 -2
- package/dist/commands/configCommand.d.ts +1 -1
- package/dist/commands/configCommand.js +1 -1
- package/dist/commands/configSetup.d.ts +1 -1
- package/dist/commands/evalCommand.d.ts +1 -1
- package/dist/commands/evalCommand.js +8 -3
- package/dist/commands/evalCommand.js.map +1 -1
- package/dist/commands/execCommand.d.ts +1 -1
- package/dist/commands/execCommand.js +9 -2
- package/dist/commands/execCommand.js.map +1 -1
- package/dist/commands/firstRunDialog.d.ts +3 -3
- package/dist/commands/firstRunDialog.js +2 -2
- package/dist/commands/historyCommand.d.ts +1 -1
- package/dist/commands/historyCommand.js +16 -4
- package/dist/commands/historyCommand.js.map +1 -1
- package/dist/commands/prDiscovery.d.ts +1 -1
- package/dist/tui/components/App.js +249 -24
- package/dist/tui/components/App.js.map +1 -1
- package/dist/tui/components/DebugPanel.d.ts +6 -3
- package/dist/tui/components/DebugPanel.js +15 -3
- package/dist/tui/components/DebugPanel.js.map +1 -1
- package/dist/tui/components/FetchProgress.d.ts +1 -1
- package/dist/tui/components/LaunchBanner.d.ts +1 -1
- package/dist/tui/components/LaunchBanner.js +1 -1
- package/dist/tui/components/LiveTurn.d.ts +6 -5
- package/dist/tui/components/LiveTurn.js +15 -14
- package/dist/tui/components/LiveTurn.js.map +1 -1
- package/dist/tui/components/PromptInput.d.ts +2 -2
- package/dist/tui/components/PromptInput.js +11 -6
- package/dist/tui/components/PromptInput.js.map +1 -1
- package/dist/tui/components/ResumePicker.d.ts +69 -0
- package/dist/tui/components/ResumePicker.js +93 -0
- package/dist/tui/components/ResumePicker.js.map +1 -0
- package/dist/tui/components/SelectList.d.ts +1 -1
- package/dist/tui/components/SelectList.js +1 -1
- package/dist/tui/components/SlashCommandMenu.d.ts +1 -1
- package/dist/tui/components/SlashCommandMenu.js +1 -1
- package/dist/tui/components/StatusBar.d.ts +27 -1
- package/dist/tui/components/StatusBar.js +13 -2
- package/dist/tui/components/StatusBar.js.map +1 -1
- package/dist/tui/debugRender.d.ts +26 -3
- package/dist/tui/debugRender.js +220 -2
- package/dist/tui/debugRender.js.map +1 -1
- package/dist/tui/fixtureAgent.d.ts +2 -2
- package/dist/tui/fixtureAgent.js +2 -2
- package/dist/tui/keyGuards.d.ts +1 -1
- package/dist/tui/keyGuards.js +1 -1
- package/dist/tui/ruleWidth.d.ts +1 -1
- package/dist/tui/ruleWidth.js +1 -1
- package/dist/tui/selectCancelled.d.ts +1 -1
- package/dist/tui/selectCancelled.js +1 -1
- package/dist/tui/transcriptWindow.js +7 -1
- package/dist/tui/transcriptWindow.js.map +1 -1
- package/dist/tui/tuiSessionModule.js +17 -1
- package/dist/tui/tuiSessionModule.js.map +1 -1
- package/dist/tui/types.d.ts +45 -7
- package/dist/tui/useMouse.d.ts +1 -1
- package/dist/tui/useMouse.js +1 -1
- package/dist/tui/useSlothAnimation.d.ts +1 -1
- package/dist/tui/viewModel.d.ts +64 -12
- package/dist/tui/viewModel.js +38 -11
- package/dist/tui/viewModel.js.map +1 -1
- package/dist/utils/configErrorGuard.d.ts +11 -3
- package/dist/utils/configErrorGuard.js +11 -3
- package/dist/utils/configErrorGuard.js.map +1 -1
- package/package.json +6 -6
|
@@ -37,7 +37,7 @@ export function defaultModelIndex(models) {
|
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* Builds the `.gsloth.config.json` body for a chosen provider + model. Delegates to the shared
|
|
40
|
-
* {@link buildInitConfigContent} so the interactive first-run path stamps the same `$schema`
|
|
40
|
+
* {@link @gaunt-sloth/core!providers/modelDiscovery.buildInitConfigContent | buildInitConfigContent} so the interactive first-run path stamps the same `$schema`
|
|
41
41
|
* pointer (GS2-1) as the per-provider template writers. Previously this local builder omitted
|
|
42
42
|
* `$schema`, so a config created through the first-run dialog got no editor autocomplete/validation.
|
|
43
43
|
* First-run always has an explicit model chosen, so it is written into the config (unlike a bare
|
|
@@ -160,7 +160,7 @@ function makeDefaultSelect(ask) {
|
|
|
160
160
|
/**
|
|
161
161
|
* CFG-21 — the default {@link FirstRunDialogDeps.withProgress}: an Ink spinner while `run` is in
|
|
162
162
|
* flight when the environment can drive interactive Ink (the same activation policy as
|
|
163
|
-
*
|
|
163
|
+
* `makeDefaultSelect`), otherwise a plain printed line via {@link @gaunt-sloth/core!utils/consoleUtils.displayInfo | displayInfo} on the
|
|
164
164
|
* readline / non-TTY path. Either way the wait is visible, never silent. The Ink module is
|
|
165
165
|
* imported lazily so a readline-only run never loads React/Ink.
|
|
166
166
|
*/
|
|
@@ -20,7 +20,7 @@ export declare const NO_HISTORY_MESSAGE: string;
|
|
|
20
20
|
* The first three are READ-ONLY and fail-soft: they open the store with `create: false`, so a
|
|
21
21
|
* missing DB (nothing recorded yet, or history turned off) simply reports "no history yet" instead
|
|
22
22
|
* of materialising an empty file.
|
|
23
|
-
* (Opening still migrates a pre-GS2-19 DB in place — see {@link HistoryStore} migrate.) The DB
|
|
23
|
+
* (Opening still migrates a pre-GS2-19 DB in place — see {@link @gaunt-sloth/core!history/historyStore.HistoryStore | HistoryStore} migrate.) The DB
|
|
24
24
|
* defaults to the global `~/.gsloth/history.db`; `--db <path>` overrides it. Local only — nothing
|
|
25
25
|
* here touches the network.
|
|
26
26
|
*
|
|
@@ -30,7 +30,7 @@ import { sessionConfigFor } from '#src/modules/sessionConfigs.js';
|
|
|
30
30
|
* The first three are READ-ONLY and fail-soft: they open the store with `create: false`, so a
|
|
31
31
|
* missing DB (nothing recorded yet, or history turned off) simply reports "no history yet" instead
|
|
32
32
|
* of materialising an empty file.
|
|
33
|
-
* (Opening still migrates a pre-GS2-19 DB in place — see {@link HistoryStore} migrate.) The DB
|
|
33
|
+
* (Opening still migrates a pre-GS2-19 DB in place — see {@link @gaunt-sloth/core!history/historyStore.HistoryStore | HistoryStore} migrate.) The DB
|
|
34
34
|
* defaults to the global `~/.gsloth/history.db`; `--db <path>` overrides it. Local only — nothing
|
|
35
35
|
* here touches the network.
|
|
36
36
|
*
|
|
@@ -102,8 +102,13 @@ export function historyCommand(program, commandLineConfigOverrides = {}) {
|
|
|
102
102
|
return;
|
|
103
103
|
try {
|
|
104
104
|
const stats = maintenance.stats(dbPath);
|
|
105
|
-
|
|
105
|
+
// GS2-111 — checkpoints are not the whole store. A dropped `put` leaves a thread holding
|
|
106
|
+
// pending writes and no checkpoint: real bytes on disk that only `gth history prune` takes
|
|
107
|
+
// back. A guard on the checkpoint count alone made the one screen that reports the store's
|
|
108
|
+
// size silent about them (DL-1), so this is the pair GS2-108 settled on for `gth insights`.
|
|
109
|
+
if (stats.checkpointCount > 0 || stats.writeOnlyThreadCount > 0) {
|
|
106
110
|
display(formatStoreSizeLine(stats));
|
|
111
|
+
}
|
|
107
112
|
}
|
|
108
113
|
finally {
|
|
109
114
|
maintenance.close();
|
|
@@ -190,11 +195,17 @@ export function historyCommand(program, commandLineConfigOverrides = {}) {
|
|
|
190
195
|
// is how a person finds out how much of the store was never reachable in the first place.
|
|
191
196
|
const unaddressable = maintenance.unaddressable();
|
|
192
197
|
const unaddressableBytes = maintenance.bytesOf(unaddressable);
|
|
198
|
+
// GS2-108 — and so does the write-only set: threads whose pending writes outlived the
|
|
199
|
+
// checkpoint they belonged to. This is the ONLY command that reaches them, because they
|
|
200
|
+
// carry no checkpoint to read an age from and the automatic pass reclaims nothing it
|
|
201
|
+
// cannot date. The reasoning is at `findWriteOnlyThreads`.
|
|
202
|
+
const writeOnly = maintenance.writeOnly();
|
|
203
|
+
const writeOnlyBytes = maintenance.bytesOf(writeOnly);
|
|
193
204
|
displayInfo('History prune:');
|
|
194
|
-
for (const line of formatPrunePlan(candidates, unaddressable.length, unaddressableBytes)) {
|
|
205
|
+
for (const line of formatPrunePlan(candidates, unaddressable.length, unaddressableBytes, writeOnly.length, writeOnlyBytes)) {
|
|
195
206
|
display(line);
|
|
196
207
|
}
|
|
197
|
-
if (candidates.length === 0 && unaddressable.length === 0)
|
|
208
|
+
if (candidates.length === 0 && unaddressable.length === 0 && writeOnly.length === 0)
|
|
198
209
|
return;
|
|
199
210
|
if (!options.yes) {
|
|
200
211
|
displayWarning('Nothing was removed. Re-run with `--yes` to remove it.');
|
|
@@ -204,6 +215,7 @@ export function historyCommand(program, commandLineConfigOverrides = {}) {
|
|
|
204
215
|
const removed = maintenance.remove([
|
|
205
216
|
...candidates.map((c) => c.threadId),
|
|
206
217
|
...unaddressable,
|
|
218
|
+
...writeOnly,
|
|
207
219
|
]);
|
|
208
220
|
const vacuumed = maintenance.vacuum();
|
|
209
221
|
const after = fileBytes(dbPath);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"historyCommand.js","sourceRoot":"","sources":["../../src/commands/historyCommand.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"historyCommand.js","sourceRoot":"","sources":["../../src/commands/historyCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,7 +32,7 @@ export interface PrDiscoveryConfig {
|
|
|
32
32
|
* `set_requirements` is always retained regardless, since it is how the discovery agent
|
|
33
33
|
* records the requirements it found. When omitted, all resolved tools remain available; an
|
|
34
34
|
* empty array keeps only `set_requirements`. The discovery agent never inherits the
|
|
35
|
-
* top-level
|
|
35
|
+
* top-level `GthConfig.allowedTools`; this property is its only allow-list.
|
|
36
36
|
*/
|
|
37
37
|
allowedTools?: string[];
|
|
38
38
|
}
|
|
@@ -5,6 +5,9 @@ import { foldEvents, foldSubagentEvents, initialSubagentTree, initialTurnViewMod
|
|
|
5
5
|
import { buildRejectionMessage } from '@gaunt-sloth/core/core/shell/rejection.js';
|
|
6
6
|
import { ApprovalStopError } from '@gaunt-sloth/core/core/shell/approvalStop.js';
|
|
7
7
|
import { shouldAnnounceTermination } from '@gaunt-sloth/core/core/terminationNotice.js';
|
|
8
|
+
import { runEndReport } from '@gaunt-sloth/core/core/runRecap.js';
|
|
9
|
+
import { providerErrorNotice } from '@gaunt-sloth/core/core/providerErrorNotice.js';
|
|
10
|
+
import { readWaitNarration } from '@gaunt-sloth/core/core/waitNarration.js';
|
|
8
11
|
import { attackBannerCopy, grantsRunAnyway, } from '@gaunt-sloth/core/core/shell/escalationSeverity.js';
|
|
9
12
|
import { TranscriptViewport } from '#src/tui/components/TranscriptViewport.js';
|
|
10
13
|
import { ApprovalPrompt } from '#src/tui/components/ApprovalPrompt.js';
|
|
@@ -22,15 +25,23 @@ import { ClearBanner } from '#src/tui/components/ClearBanner.js';
|
|
|
22
25
|
import { LaunchBanner } from '#src/tui/components/LaunchBanner.js';
|
|
23
26
|
import { MouseProvider } from '#src/tui/useMouse.js';
|
|
24
27
|
import { DebugPanel, debugPanelLines, debugPanelRows, DEBUG_TABS, } from '#src/tui/components/DebugPanel.js';
|
|
28
|
+
import { renderApprovalDetails } from '#src/tui/debugRender.js';
|
|
25
29
|
import { approvalPostureChoices, approvalsRungNotice, approvalsRefusalsNotice, approvalsStatusNotice, approvalsTrustNotice, approvalsUndenyNotice, COMPACTING_LINE, compactionFailedNotice, autocompactNotice, autocompactUnavailableNotice, compactionNotice, compactionUnavailableNotice, createCommandRegistry, dispatchSlashCommand, parseSlashCommand, toolsToggleNotice, } from '@gaunt-sloth/agent/modules/slashCommands.js';
|
|
26
|
-
import { resumedConversationNotice, resumeFailedNotice,
|
|
30
|
+
import { resumableConversationsNotice, resumedConversationNotice, resumeFailedNotice, resumeRefusalNotice, resumeSameConversationNotice, resumeUnavailableNotice, } from '@gaunt-sloth/agent/modules/sessionResume.js';
|
|
27
31
|
import { ApprovalsPicker } from '#src/tui/components/ApprovalsPicker.js';
|
|
32
|
+
import { ResumePicker, resumeCancelledNotice } from '#src/tui/components/ResumePicker.js';
|
|
28
33
|
import { TerminalSizeProvider, useTerminalSize } from '#src/tui/useTerminalSize.js';
|
|
29
34
|
import { findMatches, scrollOffsetForLine, stepMatch } from '#src/tui/debugSearch.js';
|
|
30
35
|
import { useTranscriptScroll } from '#src/tui/useTranscriptScroll.js';
|
|
31
36
|
import { isComposingKeystroke, WHEEL_ROWS_PER_NOTCH } from '#src/tui/transcriptScroll.js';
|
|
32
37
|
import { TUI_HINT_SUFFIX, TUI_KEY_BINDINGS } from '#src/tui/keyBindings.js';
|
|
33
38
|
import { isTypedText, typedText } from '#src/tui/keyGuards.js';
|
|
39
|
+
/**
|
|
40
|
+
* The empty line list a debug tab that is not on screen hands the panel. A module-level constant so
|
|
41
|
+
* its identity is stable across renders: a fresh `[]` each time would make the memo that clamps the
|
|
42
|
+
* scroll offset recompute on every render of every other tab.
|
|
43
|
+
*/
|
|
44
|
+
const NO_DEBUG_LINES = [];
|
|
34
45
|
/** Rows of clipping viewport in the docked debug panel (default / restored size). */
|
|
35
46
|
const DEBUG_VIEWPORT_HEIGHT = 8;
|
|
36
47
|
/** Rows of chrome around the debug viewport we leave for when it is maximised (panel
|
|
@@ -128,6 +139,15 @@ export function App(props) {
|
|
|
128
139
|
const [transcript, setTranscript] = useState(seeded);
|
|
129
140
|
const [live, setLive] = useState(null);
|
|
130
141
|
const [running, setRunning] = useState(false);
|
|
142
|
+
/**
|
|
143
|
+
* [[EXT-92]] scope (a) — what the run is waiting on, when that is not the model answering.
|
|
144
|
+
*
|
|
145
|
+
* Held as the whole rendered sentence rather than an activity code, because the words are
|
|
146
|
+
* `core/waitNarration.ts`'s to choose: it is the module that reaches every surface, and a second
|
|
147
|
+
* spelling of the same wait on this one is the drift the exported prefix exists to prevent. The
|
|
148
|
+
* status bar renders it in place of its own default label, so this costs no row.
|
|
149
|
+
*/
|
|
150
|
+
const [waitActivity, setWaitActivity] = useState(undefined);
|
|
131
151
|
const [turnCount, setTurnCount] = useState(props.resumed?.turns.length ?? 0);
|
|
132
152
|
// Subagent tree, folded from `task` tool calls on the live event stream. No backend dispatches
|
|
133
153
|
// subagents in this release, so nothing fills it today; kept for GS2-25's lean primitive.
|
|
@@ -169,6 +189,11 @@ export function App(props) {
|
|
|
169
189
|
// chrome: unlike the status notice it is never committed to the transcript, and it owns the
|
|
170
190
|
// keyboard while mounted (the prompt is suspended below, as it is for a pending approval).
|
|
171
191
|
const [approvalsPicker, setApprovalsPicker] = useState(null);
|
|
192
|
+
// GS2-112 — the open bare-`/resume` picker's candidates, or null when none is showing. Transient
|
|
193
|
+
// chrome on the same terms as the approvals picker above: never committed to the transcript, and
|
|
194
|
+
// it owns the keyboard while mounted. The list is the snapshot taken when the picker opened; a
|
|
195
|
+
// landed resume replaces the screen, so the picker is closed before the choice is applied.
|
|
196
|
+
const [resumePicker, setResumePicker] = useState(null);
|
|
172
197
|
// Whether to show the post-`/clear` "history cleared" banner. Hidden again the moment the next
|
|
173
198
|
// user turn starts so it doesn't linger above a fresh conversation.
|
|
174
199
|
const [clearedBanner, setClearedBanner] = useState(false);
|
|
@@ -221,6 +246,14 @@ export function App(props) {
|
|
|
221
246
|
// GS2-20 — starts past the seeded items so a later push never reuses a seeded key.
|
|
222
247
|
const idRef = useRef(seeded.length);
|
|
223
248
|
const runningRef = useRef(false);
|
|
249
|
+
/**
|
|
250
|
+
* [[EXT-92]] scope (e) — whether the turn now ending was ended by a throw.
|
|
251
|
+
*
|
|
252
|
+
* A ref rather than state because the only reader is the `finally` of the same async function
|
|
253
|
+
* the catch runs in: a `setState` would not be visible there, and the fact has to be true at the
|
|
254
|
+
* instant the turn is committed, not on the next render.
|
|
255
|
+
*/
|
|
256
|
+
const turnErroredRef = useRef(false);
|
|
224
257
|
/**
|
|
225
258
|
* GS2-23 — whether a `/compact` is awaiting its summary. Read where `runningRef` is read at
|
|
226
259
|
* submit, so a plain message cannot start a turn on a thread that is about to be rewritten, and
|
|
@@ -249,6 +282,22 @@ export function App(props) {
|
|
|
249
282
|
*/
|
|
250
283
|
const conversationIdRef = useRef(props.conversationId);
|
|
251
284
|
const turnCountRef = useRef(props.resumed?.turns.length ?? 0);
|
|
285
|
+
/**
|
|
286
|
+
* [[EXT-178]] — which conversation the outstanding recap request still belongs to.
|
|
287
|
+
*
|
|
288
|
+
* The recap is a model call launched detached at the end of a turn, so up to
|
|
289
|
+
* `RUN_RECAP_TIMEOUT_MS` can pass between asking and answering, and the session does not stand
|
|
290
|
+
* still for it: `/clear` empties the transcript the recap is describing, and a new turn can
|
|
291
|
+
* start and finish inside the window. Appending then puts a summary of a wiped conversation onto
|
|
292
|
+
* a blank screen, or files the previous turn's recap under this one.
|
|
293
|
+
*
|
|
294
|
+
* So the request carries the epoch it was made in and drops itself if the epoch has moved. It is
|
|
295
|
+
* bumped by exactly the three events that make a pending recap wrong: a new turn starting,
|
|
296
|
+
* `/clear`, and this component unmounting. It is deliberately NOT `turnCountRef`, which counts
|
|
297
|
+
* turns for the status bar, is incremented after the recap is launched, and is reset to 0 by
|
|
298
|
+
* `/clear` — a counter that goes backwards cannot be compared for staleness.
|
|
299
|
+
*/
|
|
300
|
+
const recapEpochRef = useRef(0);
|
|
252
301
|
// Per-turn args buffers for the subagent fold (mirrors foldSubagentTree's internal map).
|
|
253
302
|
const subagentBuffersRef = useRef(new Map());
|
|
254
303
|
const debugFocusedRef = useRef(false);
|
|
@@ -260,10 +309,11 @@ export function App(props) {
|
|
|
260
309
|
// inside <PromptInput>, where it lives.
|
|
261
310
|
const promptHandleRef = useRef(null);
|
|
262
311
|
// TUI-C51 — the one thing about the prompt that has to outlive it: the message a command was
|
|
263
|
-
// dispatched beside. The prompt is unmounted in
|
|
264
|
-
// opening
|
|
265
|
-
// here, where nothing unmounts, and the prompt takes it back when
|
|
266
|
-
// rather than state: nothing here renders it, and the prompt empties
|
|
312
|
+
// dispatched beside. The prompt is unmounted in five states below, and two of them (`/approvals`
|
|
313
|
+
// and a bare `/resume` opening their pickers) are entered by a command dispatched FROM the
|
|
314
|
+
// prompt — so the draft is put here, where nothing unmounts, and the prompt takes it back when
|
|
315
|
+
// it returns. It stays a slot rather than state: nothing here renders it, and the prompt empties
|
|
316
|
+
// it as soon as it has it.
|
|
267
317
|
const promptDraftCarryRef = useRef(null);
|
|
268
318
|
// TUI-C79 — set the moment a teardown starts, so a second `quit()` can tell "leaving" from
|
|
269
319
|
// "already leaving" and skip straight to the unmount. See `quit()`.
|
|
@@ -283,6 +333,18 @@ export function App(props) {
|
|
|
283
333
|
const debugViewport = debugViewportHeight(debugMaximized, terminalRows);
|
|
284
334
|
// PageUp/PageDown step tracks the live viewport so maximise pages by (almost) a full screen.
|
|
285
335
|
const debugPageStep = Math.max(1, debugViewport - 1);
|
|
336
|
+
// [[TUI-C27]] — the Auto-mode tab's lines: the approvals gate's own record of every gated tool
|
|
337
|
+
// call, plus the rater config in force (already held here, since `/approvals` moves it).
|
|
338
|
+
//
|
|
339
|
+
// **Read fresh on every render while that tab is on screen, deliberately, and memoised on
|
|
340
|
+
// nothing.** The decision this tab exists for emits no event: a call the rater approves on its
|
|
341
|
+
// first rating relays nothing to any surface — the tool simply runs — so anything keyed on the
|
|
342
|
+
// approval, negotiation or turn signals the App already has would look live and miss exactly it.
|
|
343
|
+
// A pull costs one render of a capped (50-record) buffer, and only while a user is looking at
|
|
344
|
+
// the tab; every other tab keeps the stable empty array and pays nothing.
|
|
345
|
+
const debugAuto = debugVisible && debugTab === 'auto'
|
|
346
|
+
? renderApprovalDetails(props.readApprovalCaptures?.() ?? [], approvals).split('\n')
|
|
347
|
+
: NO_DEBUG_LINES;
|
|
286
348
|
// The active section's line count drives the real maximum scroll offset, so neither the page
|
|
287
349
|
// step nor the arrow step can push the offset past the end (the over-scroll bug that left
|
|
288
350
|
// PgUp/↑ burning through phantom offset before anything moved — TUI-C11). Single-sourced with
|
|
@@ -294,8 +356,9 @@ export function App(props) {
|
|
|
294
356
|
toolsLines: debugTools,
|
|
295
357
|
mcpLines: debugMcp,
|
|
296
358
|
responseLines: debugResponse,
|
|
359
|
+
autoLines: debugAuto,
|
|
297
360
|
activeTab: debugTab,
|
|
298
|
-
}), [subagents, debugHistory, debugSystem, debugTools, debugMcp, debugResponse, debugTab]);
|
|
361
|
+
}), [subagents, debugHistory, debugSystem, debugTools, debugMcp, debugResponse, debugAuto, debugTab]);
|
|
299
362
|
const debugLineCount = debugLines.length;
|
|
300
363
|
const debugMaxOffset = Math.max(0, debugLineCount - debugViewport);
|
|
301
364
|
// Match line-indices for the current query over the active tab's lines — one search, every tab
|
|
@@ -394,6 +457,8 @@ export function App(props) {
|
|
|
394
457
|
// A new exchange supersedes the post-/clear banner; drop it so it doesn't sit above
|
|
395
458
|
// the fresh conversation.
|
|
396
459
|
setClearedBanner(false);
|
|
460
|
+
// [[EXT-178]] — and it supersedes the previous turn's recap, which may still be in flight.
|
|
461
|
+
recapEpochRef.current += 1;
|
|
397
462
|
push({ kind: 'user', text: userInput });
|
|
398
463
|
// [[TUI-C69]] §5.4 — a new user turn is a person being reached, which ENDS any negotiation
|
|
399
464
|
// still standing (the runner clears its own transcript on the same event). Rounds from the
|
|
@@ -403,11 +468,21 @@ export function App(props) {
|
|
|
403
468
|
abortRef.current = ac;
|
|
404
469
|
runningRef.current = true;
|
|
405
470
|
setRunning(true);
|
|
471
|
+
// [[EXT-92]] — both per-turn facts start clean. `turnErroredRef` (scope e) is set by the
|
|
472
|
+
// catch and read by the `finally`; `waitActivity` (scope a) is a label the status bar may
|
|
473
|
+
// still be holding from the previous turn's last wait.
|
|
474
|
+
turnErroredRef.current = false;
|
|
475
|
+
setWaitActivity(undefined);
|
|
406
476
|
setLiveTurn(initialTurnViewModel());
|
|
407
477
|
// Fresh args-buffer map per turn so partial `task` JSON from a prior turn never bleeds in.
|
|
408
478
|
subagentBuffersRef.current = new Map();
|
|
409
479
|
try {
|
|
410
480
|
for await (const event of agent.runTurn(userInput, ac.signal)) {
|
|
481
|
+
// [[EXT-92]] scope (a) — an event arriving IS the wait ending. Clearing here rather than
|
|
482
|
+
// waiting for a paired "done" narration is what keeps the core signal one-way and the
|
|
483
|
+
// plain surface to exactly one line per wait: there is no end message to emit, so there
|
|
484
|
+
// is none to print. The status bar falls back to its own default label.
|
|
485
|
+
setWaitActivity(undefined);
|
|
411
486
|
// [[TUI-C99]] — folded from the REF, not from a local: an approval answered mid-turn
|
|
412
487
|
// writes the outcome onto its tool call from a keystroke, and a local carried across the
|
|
413
488
|
// loop would overwrite it on the very next event the resumed run produces.
|
|
@@ -427,16 +502,43 @@ export function App(props) {
|
|
|
427
502
|
push({ kind: 'stop', parts: err.parts });
|
|
428
503
|
}
|
|
429
504
|
else {
|
|
505
|
+
// [[EXT-92]] scope (b) — a provider error is rendered as prose here, and the raw payload
|
|
506
|
+
// rides along in a field this item's renderer never prints.
|
|
507
|
+
//
|
|
508
|
+
// The whole defect was on this line. `err.message` for an OpenRouter refusal is
|
|
509
|
+
// `Provider returned error | metadata: {…}` — upstream flattens its own structured
|
|
510
|
+
// metadata into the message — so the user read a JSON blob while the provider's own
|
|
511
|
+
// sentence, its remedy hint, and the fact that it was a SHARED pool rather than their
|
|
512
|
+
// quota all sat unread inside it. Nothing had been lost: the same fields are still
|
|
513
|
+
// objects on `err`, which is why this needed a renderer rather than a parser.
|
|
514
|
+
// `providerErrorNotice` returns null for the ordinary runtime errors it has nothing to
|
|
515
|
+
// add to, and those keep exactly the rendering they had.
|
|
516
|
+
const notice = providerErrorNotice(err);
|
|
430
517
|
push({
|
|
431
518
|
kind: 'system',
|
|
432
519
|
level: 'error',
|
|
433
|
-
text: err instanceof Error ? err.message : String(err),
|
|
520
|
+
text: notice?.text ?? (err instanceof Error ? err.message : String(err)),
|
|
521
|
+
...(notice ? { raw: notice.raw } : {}),
|
|
434
522
|
});
|
|
435
523
|
}
|
|
524
|
+
// [[EXT-92]] scope (e) — remember that this turn was ended by the failure above, so the
|
|
525
|
+
// `finally` can commit it as a turn that STOPPED rather than one that finished. An
|
|
526
|
+
// approvals stop is deliberately excluded: it already commits its own `stop` item saying
|
|
527
|
+
// so in full, and marking the turn as well would state the same ending twice.
|
|
528
|
+
if (!(err instanceof ApprovalStopError))
|
|
529
|
+
turnErroredRef.current = true;
|
|
436
530
|
}
|
|
437
531
|
finally {
|
|
532
|
+
// [[EXT-92]] scope (a) — the turn is over, so nothing is being waited on. In the `finally`
|
|
533
|
+
// because the endings that most need it are the ones that reach neither the loop's normal
|
|
534
|
+
// exit nor the catch: a turn cancelled mid-rating would otherwise leave the bar naming a
|
|
535
|
+
// wait that no longer exists.
|
|
536
|
+
setWaitActivity(undefined);
|
|
438
537
|
const vm = liveVmRef.current ?? initialTurnViewModel();
|
|
439
|
-
push({
|
|
538
|
+
push({
|
|
539
|
+
kind: 'assistant',
|
|
540
|
+
turn: turnErroredRef.current ? { ...vm, endedInError: true } : vm,
|
|
541
|
+
});
|
|
440
542
|
// [[EXT-159]] — and, immediately under the answer, why the turn ended when that is not
|
|
441
543
|
// simply "the model finished".
|
|
442
544
|
//
|
|
@@ -454,6 +556,50 @@ export function App(props) {
|
|
|
454
556
|
const reason = agent.getTerminationReason?.() ?? null;
|
|
455
557
|
if (reason && shouldAnnounceTermination(reason))
|
|
456
558
|
push({ kind: 'termination', reason });
|
|
559
|
+
// [[EXT-158]] — and the other silence: a turn that ended cleanly with its own checklist
|
|
560
|
+
// still carrying work. `shouldAnnounceTermination` declines `completed`, correctly — an
|
|
561
|
+
// ordinary answer needs no epitaph — which is exactly why this case had nothing at all.
|
|
562
|
+
//
|
|
563
|
+
// Committed as an ordinary `notice` item rather than a transcript kind of its own. The
|
|
564
|
+
// termination item earns its kind by carrying the REASON value so the transcript holds
|
|
565
|
+
// the classification rather than a rendered line; this fact has no such closed taxonomy
|
|
566
|
+
// to preserve, and a new kind would buy a second row-count case and a second renderer for
|
|
567
|
+
// a title and three lines the generic notice already draws.
|
|
568
|
+
//
|
|
569
|
+
// [[EXT-178]] — and its successor when the user has switched the recap on. The two speak
|
|
570
|
+
// for the same population, so exactly one of them is drawn: `runEndReport` decides, with
|
|
571
|
+
// the recap already resolved, and a recap that never arrived leaves the notice standing.
|
|
572
|
+
//
|
|
573
|
+
// **Detached from the turn's teardown, and that is the whole reason it is down here.**
|
|
574
|
+
// A recap is a model call made after the answer is already on screen; awaiting it in this
|
|
575
|
+
// `finally` would keep the spinner running and the prompt away for as long as a provider
|
|
576
|
+
// takes to reply, which is a worse thing to do to a finished turn than a late paragraph.
|
|
577
|
+
// The transcript is append-only and this lands at the bottom, where the reader is. With
|
|
578
|
+
// the default `off` rung nothing is contacted and the notice lands as it always did.
|
|
579
|
+
const work = agent.getOutstandingWork?.() ?? null;
|
|
580
|
+
const ending = reason;
|
|
581
|
+
// Detached means the session can move on underneath it, so the request carries the epoch
|
|
582
|
+
// it was made in and says nothing if `recapEpochRef` has moved on — see that ref.
|
|
583
|
+
const epoch = recapEpochRef.current;
|
|
584
|
+
void (async () => {
|
|
585
|
+
try {
|
|
586
|
+
const recap = (await agent.requestRunRecap?.(ending)) ?? null;
|
|
587
|
+
if (recapEpochRef.current !== epoch)
|
|
588
|
+
return;
|
|
589
|
+
const report = runEndReport(recap, work, ending);
|
|
590
|
+
if (report.kind !== 'silent') {
|
|
591
|
+
push({
|
|
592
|
+
kind: 'notice',
|
|
593
|
+
title: report.notice.title,
|
|
594
|
+
lines: report.notice.lines,
|
|
595
|
+
tone: report.kind === 'recap' ? 'info' : 'warn',
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
catch {
|
|
600
|
+
/* fail-soft: explaining a turn must never be what breaks the session */
|
|
601
|
+
}
|
|
602
|
+
})();
|
|
457
603
|
}
|
|
458
604
|
catch {
|
|
459
605
|
/* fail-soft: explaining a turn must never be what breaks the session */
|
|
@@ -715,7 +861,31 @@ export function App(props) {
|
|
|
715
861
|
// replaces the screen: the transcript on it belonged to the conversation being left, and the
|
|
716
862
|
// model is now continuing another one, so the banner and the restored turns take its place —
|
|
717
863
|
// the same wipe `/clear` does, without the cleared banner, because nothing was discarded (the
|
|
718
|
-
// conversation left keeps everything recorded under it). Bare,
|
|
864
|
+
// conversation left keeps everything recorded under it). Bare, this surface offers the picker
|
|
865
|
+
// below; the readline session prints the list.
|
|
866
|
+
//
|
|
867
|
+
// GS2-112 — **why the interactive picker is one branch here and not a new protocol field.**
|
|
868
|
+
//
|
|
869
|
+
// `/resume` returns `resume?: { id?: number }`, and the missing id is not an omission to be
|
|
870
|
+
// filled in later: the command is pure and cannot reach the store or the runner, so the no-id
|
|
871
|
+
// case has always meant *the surface fulfils this*, which is what every other surface already
|
|
872
|
+
// does with it. The Ink TUI can show a list and read arrow keys; the readline session can print
|
|
873
|
+
// one; a server surface has no user at a keyboard at all. Choosing is therefore the surface's
|
|
874
|
+
// answer to the same request, and nothing in the request needs to change for this surface to
|
|
875
|
+
// give a better one.
|
|
876
|
+
//
|
|
877
|
+
// The alternative was weighed and rejected rather than overlooked. A cross-package chooser — a
|
|
878
|
+
// new result field carrying candidates, or a `chooseConversation` callback threaded from the
|
|
879
|
+
// agent down into the component — would put a widget's contract in the module both surfaces
|
|
880
|
+
// read from, to serve one surface, and would still leave this branch deciding when to open it.
|
|
881
|
+
// It buys nothing: the candidates are already here (`props.listResumeCandidates`, wired in
|
|
882
|
+
// `tuiSessionModule`), and the id the user picks re-enters the with-id path below, which is the
|
|
883
|
+
// same code `--resume <id>` runs at boot. One branch, one new component, no new protocol.
|
|
884
|
+
//
|
|
885
|
+
// **The readline surface keeps the printed list deliberately.** It is not a fallback that has
|
|
886
|
+
// been left behind: with no Ink tree there is nothing to arrow through, and the printed ids
|
|
887
|
+
// remain the only way in on that surface — so `resumableConversationsNotice` stays its list
|
|
888
|
+
// builder, and no picker wording is allowed to leak into it (pinned by a spec).
|
|
719
889
|
const applyResume = useCallback(async (id) => {
|
|
720
890
|
const commit = (notice) => push({
|
|
721
891
|
kind: 'notice',
|
|
@@ -724,11 +894,23 @@ export function App(props) {
|
|
|
724
894
|
tone: notice.tone ?? 'info',
|
|
725
895
|
});
|
|
726
896
|
if (id === undefined) {
|
|
897
|
+
// A session with no store behind it cannot list anything, let alone offer it: this prop is
|
|
898
|
+
// wired only where history is on and a store opened, so its absence is the same "resume is
|
|
899
|
+
// unavailable here" the with-id path reports below.
|
|
727
900
|
if (!props.listResumeCandidates) {
|
|
728
901
|
commit(resumeUnavailableNotice());
|
|
729
902
|
return;
|
|
730
903
|
}
|
|
731
|
-
|
|
904
|
+
const candidates = props.listResumeCandidates();
|
|
905
|
+
// With nothing to offer, say so and open nothing. An empty <SelectList> renders a "no
|
|
906
|
+
// matches" line and its Enter is inert, so a picker here would be a modal the user can
|
|
907
|
+
// only escape from — strictly worse than the sentence, which also explains what makes a
|
|
908
|
+
// conversation resumable.
|
|
909
|
+
if (candidates.length === 0) {
|
|
910
|
+
commit(resumableConversationsNotice(candidates));
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
setResumePicker(candidates);
|
|
732
914
|
return;
|
|
733
915
|
}
|
|
734
916
|
if (!agent.resumeConversation) {
|
|
@@ -1049,6 +1231,10 @@ export function App(props) {
|
|
|
1049
1231
|
conversationId: conversationIdRef.current,
|
|
1050
1232
|
toolsExpanded: toolsExpandedRef.current,
|
|
1051
1233
|
debugVisible: debugVisibleRef.current,
|
|
1234
|
+
// TUI-C96 — this surface has the slash-command menu (<PromptInput> renders it
|
|
1235
|
+
// unconditionally), so the unknown-command notice may explain a shortened name by
|
|
1236
|
+
// pointing at it. The readline surface says false and gets the other sentence.
|
|
1237
|
+
hasSlashMenu: true,
|
|
1052
1238
|
// TUI-C37 — undefined (not false) when this surface has no mouse layer, so `/mouse`
|
|
1053
1239
|
// says it is unavailable rather than silently reporting "off".
|
|
1054
1240
|
mouseEnabled: props.mouseEnabled === undefined ? undefined : mouseEnabledRef.current,
|
|
@@ -1102,6 +1288,9 @@ export function App(props) {
|
|
|
1102
1288
|
// reset it too — a cleared session starts back at "turns: 0".
|
|
1103
1289
|
turnCountRef.current = 0;
|
|
1104
1290
|
setTurnCount(0);
|
|
1291
|
+
// [[EXT-178]] — a recap still in flight describes the conversation just wiped, so drop
|
|
1292
|
+
// it rather than append a summary of nothing onto a cleared screen.
|
|
1293
|
+
recapEpochRef.current += 1;
|
|
1105
1294
|
}
|
|
1106
1295
|
if (result.toggleTools) {
|
|
1107
1296
|
// Flip the fold mode and commit the notice via the shared helper (single-sourced with
|
|
@@ -1227,13 +1416,19 @@ export function App(props) {
|
|
|
1227
1416
|
// bare `exit` keyword all take it too).
|
|
1228
1417
|
//
|
|
1229
1418
|
// **The modal states are answered first, and they exit.** While an attack banner, an approval
|
|
1230
|
-
// prompt or the approvals
|
|
1231
|
-
//
|
|
1232
|
-
//
|
|
1233
|
-
//
|
|
1234
|
-
//
|
|
1419
|
+
// prompt or one of the pickers (`/approvals`, bare `/resume`) owns the keyboard, the prompt is
|
|
1420
|
+
// unmounted, so rung 1 has no buffer to scrap; and where that modal state was reached during a
|
|
1421
|
+
// turn, rung 2 would read "stop the turn" while the banner's own controls line says `Ctrl+C
|
|
1422
|
+
// exits gth` and the user is looking for the way out of an irreversible action. So it exits, as
|
|
1423
|
+
// the screen promises — and it goes through `quit()`, which resolves both bridges fail-closed
|
|
1424
|
+
// on the way out rather than leaving the halt unanswered.
|
|
1425
|
+
//
|
|
1426
|
+
// GS2-112 — the resume picker is in this set for coherence rather than for a case it changes
|
|
1427
|
+
// today: `/resume` is idle-only and the prompt is unmounted while its picker is up, so no turn
|
|
1428
|
+
// can be started under it and the ladder would fall to rung 3 and exit anyway. Naming it here
|
|
1429
|
+
// is what keeps that answer from depending on which rung happens to catch it.
|
|
1235
1430
|
if (key.ctrl && input === 'c') {
|
|
1236
|
-
if (pendingAttack || pendingApproval || approvalsPicker) {
|
|
1431
|
+
if (pendingAttack || pendingApproval || approvalsPicker || resumePicker) {
|
|
1237
1432
|
quit();
|
|
1238
1433
|
return;
|
|
1239
1434
|
}
|
|
@@ -1369,11 +1564,11 @@ export function App(props) {
|
|
|
1369
1564
|
resolveApproval('reject');
|
|
1370
1565
|
return;
|
|
1371
1566
|
}
|
|
1372
|
-
// CFG-39 — while
|
|
1373
|
-
//
|
|
1374
|
-
//
|
|
1375
|
-
//
|
|
1376
|
-
if (approvalsPicker)
|
|
1567
|
+
// CFG-39, GS2-112 — while a picker is open it OWNS the keyboard, exactly as a pending approval
|
|
1568
|
+
// does. <SelectList> is a useInput subscriber too and Ink runs every subscriber, so without
|
|
1569
|
+
// this the arrow keys would also scroll the transcript and Esc would both cancel the picker and
|
|
1570
|
+
// jump to the newest output. Returning here leaves the picker the only claimant.
|
|
1571
|
+
if (approvalsPicker || resumePicker)
|
|
1377
1572
|
return;
|
|
1378
1573
|
if (key.escape && runningRef.current) {
|
|
1379
1574
|
abortRef.current?.abort();
|
|
@@ -1588,6 +1783,12 @@ export function App(props) {
|
|
|
1588
1783
|
};
|
|
1589
1784
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1590
1785
|
}, []);
|
|
1786
|
+
// [[EXT-178]] — the third thing that invalidates a recap still in flight: this component going
|
|
1787
|
+
// away. A quit unmounts while a provider may still be holding the request open, and the reply
|
|
1788
|
+
// would then land on a session the user has left.
|
|
1789
|
+
useEffect(() => () => {
|
|
1790
|
+
recapEpochRef.current += 1;
|
|
1791
|
+
}, []);
|
|
1591
1792
|
// Route agent status updates (warnings/errors) into the transcript instead of stdout,
|
|
1592
1793
|
// which would otherwise corrupt Ink's frame. INFO/DEBUG system lines are suppressed in the
|
|
1593
1794
|
// TUI: the agent's per-turn chatter (`Requested tools`, `Loaded tools`, `Workdir`, `Model`,
|
|
@@ -1598,6 +1799,17 @@ export function App(props) {
|
|
|
1598
1799
|
if (!props.subscribeStatus)
|
|
1599
1800
|
return;
|
|
1600
1801
|
return props.subscribeStatus((level, message) => {
|
|
1802
|
+
// [[EXT-92]] scope (a) — a wait narration is INFO, and INFO is exactly what this handler
|
|
1803
|
+
// drops, so it is picked out BEFORE the filter and routed to the status bar instead of the
|
|
1804
|
+
// transcript. That is the right destination on both counts: it is a transient fact about the
|
|
1805
|
+
// present moment, which the transcript is the wrong shape for, and the bar is already the
|
|
1806
|
+
// TUI's wait renderer. Recognised by the prefix `core/waitNarration.ts` exports for this,
|
|
1807
|
+
// rather than by a copy of the wording here.
|
|
1808
|
+
const narration = readWaitNarration(message);
|
|
1809
|
+
if (narration !== null) {
|
|
1810
|
+
setWaitActivity(narration);
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1601
1813
|
if (level === 'INFO' || level === 'DEBUG')
|
|
1602
1814
|
return;
|
|
1603
1815
|
if (message.trim())
|
|
@@ -1679,10 +1891,19 @@ export function App(props) {
|
|
|
1679
1891
|
: 0) -
|
|
1680
1892
|
(activeChecklist ? checklistPanelRows(activeChecklist) : 0) -
|
|
1681
1893
|
negotiationPanelRows(negotiationRounds, terminalColumns);
|
|
1682
|
-
return (_jsx(TerminalSizeProvider, { size: terminalSize, children: _jsx(MouseProvider, { subscribe: props.subscribeMouse, enabled: mouseEnabled, children: _jsxs(Box, { flexDirection: "column", height: terminalRows, children: [_jsxs(TranscriptViewport, { items: transcript, budgetRows: terminalRows, columns: terminalColumns, toolsExpanded: toolsExpanded, scroll: transcriptScroll, regionRows: transcriptRegionRows, geometry: transcriptGeometry, children: [clearedBanner ? _jsx(ClearBanner, {}) : null, showIntro && props.showLaunchBanner ? (_jsx(LaunchBanner, { model: modelDisplayName, provider: props.modelProviderType })) : null, showIntro ? _jsx(Text, { dimColor: true, children: readyMessage.trim() }) : null, live && transcript.length > 0 ? _jsx(BlankRow, {}) : null, live ? (_jsx(LiveTurn, { turn: live, toolsExpanded: toolsExpanded, streaming: true, columns: terminalColumns })) : null, pendingApproval ? (_jsxs(_Fragment, { children: [live || transcript.length > 0 ? _jsx(BlankRow, {}) : null, _jsx(ApprovalRequestPanel, { pending: pendingApproval.pending, columns: terminalColumns })] })) : null] }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [debugVisible ? (_jsx(DebugPanel, { subagents: subagents, historyLines: debugHistory, systemLines: debugSystem, toolsLines: debugTools, mcpLines: debugMcp, responseLines: debugResponse, activeTab: debugTab, scrollOffset: debugScroll, focused: debugFocused, viewportHeight: debugViewport, maximized: debugMaximized, searchQuery: debugSearchQuery, searchActive: debugSearchInput, matchCount: debugMatches.length, currentMatchIndex: debugSearchCurrent, currentMatchLine: currentMatchLine })) : null, activeChecklist ? _jsx(ChecklistPanel, { items: activeChecklist }) : null, _jsx(NegotiationPanel, { rounds: negotiationRounds }), pendingAttack ? _jsx(AttackBanner, { halt: pendingAttack.halt, typed: attackTyped }) : null, pendingApproval && !pendingAttack ? (_jsx(ApprovalPrompt, { pending: pendingApproval.pending })) : null, approvalsPicker && !pendingApproval && !pendingAttack ? (_jsx(ApprovalsPicker, { choices: approvalsPicker, onSelect: (rung) => {
|
|
1894
|
+
return (_jsx(TerminalSizeProvider, { size: terminalSize, children: _jsx(MouseProvider, { subscribe: props.subscribeMouse, enabled: mouseEnabled, children: _jsxs(Box, { flexDirection: "column", height: terminalRows, children: [_jsxs(TranscriptViewport, { items: transcript, budgetRows: terminalRows, columns: terminalColumns, toolsExpanded: toolsExpanded, scroll: transcriptScroll, regionRows: transcriptRegionRows, geometry: transcriptGeometry, children: [clearedBanner ? _jsx(ClearBanner, {}) : null, showIntro && props.showLaunchBanner ? (_jsx(LaunchBanner, { model: modelDisplayName, provider: props.modelProviderType })) : null, showIntro ? _jsx(Text, { dimColor: true, children: readyMessage.trim() }) : null, live && transcript.length > 0 ? _jsx(BlankRow, {}) : null, live ? (_jsx(LiveTurn, { turn: live, toolsExpanded: toolsExpanded, streaming: true, columns: terminalColumns })) : null, pendingApproval ? (_jsxs(_Fragment, { children: [live || transcript.length > 0 ? _jsx(BlankRow, {}) : null, _jsx(ApprovalRequestPanel, { pending: pendingApproval.pending, columns: terminalColumns })] })) : null] }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [debugVisible ? (_jsx(DebugPanel, { subagents: subagents, historyLines: debugHistory, systemLines: debugSystem, toolsLines: debugTools, mcpLines: debugMcp, responseLines: debugResponse, autoLines: debugAuto, activeTab: debugTab, scrollOffset: debugScroll, focused: debugFocused, viewportHeight: debugViewport, maximized: debugMaximized, searchQuery: debugSearchQuery, searchActive: debugSearchInput, matchCount: debugMatches.length, currentMatchIndex: debugSearchCurrent, currentMatchLine: currentMatchLine })) : null, activeChecklist ? _jsx(ChecklistPanel, { items: activeChecklist }) : null, _jsx(NegotiationPanel, { rounds: negotiationRounds }), pendingAttack ? _jsx(AttackBanner, { halt: pendingAttack.halt, typed: attackTyped }) : null, pendingApproval && !pendingAttack ? (_jsx(ApprovalPrompt, { pending: pendingApproval.pending })) : null, approvalsPicker && !pendingApproval && !pendingAttack ? (_jsx(ApprovalsPicker, { choices: approvalsPicker, onSelect: (rung) => {
|
|
1683
1895
|
setApprovalsPicker(null);
|
|
1684
1896
|
applyApprovalRung(rung);
|
|
1685
|
-
}, onCancel: () => setApprovalsPicker(null) })) : null,
|
|
1897
|
+
}, onCancel: () => setApprovalsPicker(null) })) : null, resumePicker && !approvalsPicker && !pendingApproval && !pendingAttack ? (_jsx(ResumePicker, { candidates: resumePicker, onSelect: (id) => {
|
|
1898
|
+
// Close BEFORE applying: a landed resume replaces the transcript and re-keys its
|
|
1899
|
+
// ids, and this list is a snapshot of the conversations as they were before it.
|
|
1900
|
+
setResumePicker(null);
|
|
1901
|
+
void applyResume(id);
|
|
1902
|
+
}, onCancel: () => {
|
|
1903
|
+
setResumePicker(null);
|
|
1904
|
+
const notice = resumeCancelledNotice();
|
|
1905
|
+
push({ kind: 'notice', title: notice.title, lines: notice.lines, tone: 'info' });
|
|
1906
|
+
} })) : null, _jsx(BlankRow, {}), _jsx(Rule, {}), _jsx(NoticeBar, { advisories: props.advisories }), _jsx(McpFailureBar, { failures: props.mcpFailures }), _jsx(StatusBar, { running: running, activity: waitActivity, mode: mode, modelDisplayName: modelDisplayName,
|
|
1686
1907
|
// CFG-38 — the provider beside the model, and the live width it has to fit in: the
|
|
1687
1908
|
// bar drops the provider rather than overflowing a narrow terminal.
|
|
1688
1909
|
modelProviderType: props.modelProviderType, columns: terminalColumns, turnCount: turnCount, debugHint: debugVisible && !debugFocused, approvals: approvals
|
|
@@ -1690,7 +1911,11 @@ export function App(props) {
|
|
|
1690
1911
|
rung: approvals.rung,
|
|
1691
1912
|
raterProfile: approvals.rater,
|
|
1692
1913
|
}
|
|
1693
|
-
: undefined }), !debugFocused &&
|
|
1914
|
+
: undefined }), !debugFocused &&
|
|
1915
|
+
!pendingApproval &&
|
|
1916
|
+
!pendingAttack &&
|
|
1917
|
+
!approvalsPicker &&
|
|
1918
|
+
!resumePicker ? (_jsxs(_Fragment, { children: [_jsx(BlankRow, {}), _jsx(PromptInput, { onSubmit: handleSubmit, commands: registry, onMenuStateChange: (active) => {
|
|
1694
1919
|
slashMenuActiveRef.current = active;
|
|
1695
1920
|
}, handleRef: promptHandleRef, draftCarryRef: promptDraftCarryRef, promptBlockRows: promptBlockRows }), _jsx(BlankRow, {})] })) : null, _jsx(Text, { dimColor: true, wrap: "truncate-end", children: `${exitMessage.trim()}${TUI_HINT_SUFFIX}` }), _jsx(Rule, {})] })] }) }) }));
|
|
1696
1921
|
}
|