@wrongstack/tui 1.0.8 → 1.0.9
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/app-props.d.ts +12 -0
- package/dist/components/autonomy-picker.d.ts +10 -1
- package/dist/components/design-picker.d.ts +10 -1
- package/dist/components/resume-picker.d.ts +10 -1
- package/dist/components/status-bar-format.d.ts +1 -0
- package/dist/hooks/use-app-execution-pipeline.d.ts +1 -1
- package/dist/hooks/use-leader-auto-wake.d.ts +41 -0
- package/dist/index.js +260 -119
- package/dist/run-blocks-controller.d.ts +2 -2
- package/dist/run-tui-options.d.ts +6 -0
- package/dist/tui-host-capabilities.d.ts +16 -0
- package/package.json +13 -13
package/dist/app-props.d.ts
CHANGED
|
@@ -22,6 +22,16 @@ import type { AgentTranscriptReader, McpPickerItem, PluginPickerItem, ProviderOp
|
|
|
22
22
|
* own. `app.tsx` re-exports `AppProps` for consumers importing it from
|
|
23
23
|
* '@wrongstack/tui' / '../src/app.js'.
|
|
24
24
|
*/
|
|
25
|
+
/**
|
|
26
|
+
* The TUI's view of core's `LeaderAutoWakeController` (structural, so tests
|
|
27
|
+
* can pass a fake). The TUI binds a port for its foreground session.
|
|
28
|
+
*/
|
|
29
|
+
export interface TuiLeaderAutoWake {
|
|
30
|
+
attachPort(port: import('@wrongstack/core/coordination').LeaderWakePort): () => void;
|
|
31
|
+
onRunFinished(sessionId: string): unknown;
|
|
32
|
+
onSessionDisplayed(sessionId: string): unknown;
|
|
33
|
+
noteUserInput(sessionId: string): void;
|
|
34
|
+
}
|
|
25
35
|
export interface AppProps {
|
|
26
36
|
agent: Agent;
|
|
27
37
|
slashRegistry: SlashCommandRegistry;
|
|
@@ -50,6 +60,8 @@ export interface AppProps {
|
|
|
50
60
|
* awareness — see core's queued-messages.ts). Display state is unaffected.
|
|
51
61
|
*/
|
|
52
62
|
onQueueChange?: ((items: string[]) => void) | undefined;
|
|
63
|
+
/** Background-delegation auto-wake controller; see {@link TuiLeaderAutoWake}. */
|
|
64
|
+
leaderAutoWake?: TuiLeaderAutoWake | undefined;
|
|
53
65
|
/** Reflects the policy's --yolo flag for the status bar's "⚠ YOLO" chip. */
|
|
54
66
|
yolo?: boolean | undefined;
|
|
55
67
|
/** Play terminal bell when an agent run completes. */
|
|
@@ -9,7 +9,16 @@ export interface AutonomyPickerProps {
|
|
|
9
9
|
options: AutonomyOption[];
|
|
10
10
|
selected: number;
|
|
11
11
|
hint?: string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Measured vertical budget for the whole picker box (terminal rows minus
|
|
14
|
+
* status bar, input, and margins — see `pickerMaxRows` in app-view.tsx).
|
|
15
|
+
* When provided the window math uses it instead of the
|
|
16
|
+
* `rows - shellReservedRows` guess, which under-reserves whenever the
|
|
17
|
+
* status bar or input bar grows — the same overflow class the resume
|
|
18
|
+
* panel had before it consumed this budget.
|
|
19
|
+
*/
|
|
20
|
+
maxRows?: number | undefined;
|
|
12
21
|
}
|
|
13
22
|
export declare const AUTONOMY_OPTIONS: AutonomyOption[];
|
|
14
|
-
export declare function AutonomyPicker({ options, selected, hint, }: AutonomyPickerProps): React.ReactElement;
|
|
23
|
+
export declare function AutonomyPicker({ options, selected, hint, maxRows, }: AutonomyPickerProps): React.ReactElement;
|
|
15
24
|
//# sourceMappingURL=autonomy-picker.d.ts.map
|
|
@@ -4,6 +4,15 @@ interface DesignPickerProps {
|
|
|
4
4
|
kits: DesignKitEntry[];
|
|
5
5
|
selected: number;
|
|
6
6
|
stack: string;
|
|
7
|
+
/**
|
|
8
|
+
* Measured vertical budget for the whole picker box (terminal rows minus
|
|
9
|
+
* status bar, input, and margins — see `pickerMaxRows` in app-view.tsx).
|
|
10
|
+
* When provided the window math uses it instead of the
|
|
11
|
+
* `rows - shellReservedRows` guess, which under-reserves whenever the
|
|
12
|
+
* status bar or input bar grows — the same overflow class the resume
|
|
13
|
+
* panel had before it consumed this budget.
|
|
14
|
+
*/
|
|
15
|
+
maxRows?: number | undefined;
|
|
7
16
|
}
|
|
8
17
|
/**
|
|
9
18
|
* Design Studio kit picker overlay (opened by `/design`). Presentational only —
|
|
@@ -13,6 +22,6 @@ interface DesignPickerProps {
|
|
|
13
22
|
* Windowed via {@link useWindowedPicker} so 50+ installed kits stay
|
|
14
23
|
* navigable on small terminals.
|
|
15
24
|
*/
|
|
16
|
-
export declare function DesignPicker({ kits, selected, stack }: DesignPickerProps): React.ReactElement;
|
|
25
|
+
export declare function DesignPicker({ kits, selected, stack, maxRows, }: DesignPickerProps): React.ReactElement;
|
|
17
26
|
export {};
|
|
18
27
|
//# sourceMappingURL=design-picker.d.ts.map
|
|
@@ -6,6 +6,15 @@ interface ResumePickerProps {
|
|
|
6
6
|
busy: boolean;
|
|
7
7
|
error?: string | undefined;
|
|
8
8
|
hint?: string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Measured vertical budget for the whole picker box (terminal rows minus
|
|
11
|
+
* status bar, input, and margins — see `pickerMaxRows` in app-view.tsx).
|
|
12
|
+
* When provided the window math uses it instead of the legacy
|
|
13
|
+
* `rows - shellReservedRows` guess, which reserved less than the real
|
|
14
|
+
* status bar + input bar chrome — the resume panel could then grow past
|
|
15
|
+
* the terminal on ordinary session counts.
|
|
16
|
+
*/
|
|
17
|
+
maxRows?: number | undefined;
|
|
9
18
|
}
|
|
10
19
|
/**
|
|
11
20
|
* Interactive session-resume picker. Renders a scrollable list of recent
|
|
@@ -16,6 +25,6 @@ interface ResumePickerProps {
|
|
|
16
25
|
* three rows (title line, meta line, preview line). The window auto-recenters
|
|
17
26
|
* on the focused session so 30+ entries stay navigable on a 24-row terminal.
|
|
18
27
|
*/
|
|
19
|
-
export declare function ResumePicker({ sessions, selected, busy, error, hint, }: ResumePickerProps): React.ReactElement;
|
|
28
|
+
export declare function ResumePicker({ sessions, selected, busy, error, hint, maxRows, }: ResumePickerProps): React.ReactElement;
|
|
20
29
|
export {};
|
|
21
30
|
//# sourceMappingURL=resume-picker.d.ts.map
|
|
@@ -2,6 +2,7 @@ import type React from 'react';
|
|
|
2
2
|
/**
|
|
3
3
|
* Head-truncate a chip's free-text payload (branch, path, project name) with a
|
|
4
4
|
* trailing ellipsis so one long value can't blow out the line width.
|
|
5
|
+
* Caps on terminal columns, not UTF-16 length (CJK/emoji are 2 cells).
|
|
5
6
|
*/
|
|
6
7
|
export declare function truncateChip(text: string, max: number): string;
|
|
7
8
|
/**
|
|
@@ -17,7 +17,7 @@ export interface AppExecutionPipelineArgs {
|
|
|
17
17
|
}
|
|
18
18
|
export declare function useAppExecutionPipeline(args: AppExecutionPipelineArgs): {
|
|
19
19
|
handleKey: (input: string, key: import("../components/input.js").KeyEvent) => Promise<void>;
|
|
20
|
-
runBlocks: (blocks: import("@wrongstack/core/types").ContentBlock[]) => Promise<void>;
|
|
20
|
+
runBlocks: (blocks: import("@wrongstack/core/types").ContentBlock[], opts?: import("../tui-host-capabilities.js").RunBlocksOptions) => Promise<void>;
|
|
21
21
|
submit: (overrideRaw?: string) => Promise<void>;
|
|
22
22
|
stableOnKey: (input: string, key: import("../components/input.js").KeyEvent) => Promise<void>;
|
|
23
23
|
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { LeaderWakePort } from '@wrongstack/core/coordination';
|
|
2
|
+
import type { EventBus } from '@wrongstack/core/kernel';
|
|
3
|
+
import type { ContentBlock } from '@wrongstack/core/types';
|
|
4
|
+
import type { Action, State } from '../app-reducer.js';
|
|
5
|
+
import type { TuiLeaderAutoWake } from '../app-props.js';
|
|
6
|
+
import type { RunBlocksOptions } from '../tui-host-capabilities.js';
|
|
7
|
+
interface Cell<T> {
|
|
8
|
+
current: T;
|
|
9
|
+
}
|
|
10
|
+
export interface TuiLeaderWakePortDeps {
|
|
11
|
+
getSessionId(): string | undefined;
|
|
12
|
+
activeController: Cell<AbortController | null>;
|
|
13
|
+
eternalLoopRunning: Cell<boolean>;
|
|
14
|
+
parallelLoopRunning: Cell<boolean>;
|
|
15
|
+
enhanceAbort?: Cell<AbortController | null> | undefined;
|
|
16
|
+
state: Cell<Pick<State, 'queue' | 'confirmQueue'>>;
|
|
17
|
+
/** Open structured user-input forms (user.input_requested not yet resolved). */
|
|
18
|
+
pendingForms: Cell<number>;
|
|
19
|
+
runBlocks: Cell<(blocks: ContentBlock[], opts?: RunBlocksOptions) => Promise<void>>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The foreground TUI's wake port. The TUI shows exactly one session — the
|
|
23
|
+
* agent's current one — so open and displayed both mean "is that session".
|
|
24
|
+
* Idle is the race-free busy signal `runBlocks` itself uses (the controller
|
|
25
|
+
* cell), plus the autonomy loop drivers that call the engine directly.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createTuiLeaderWakePort(deps: TuiLeaderWakePortDeps): LeaderWakePort;
|
|
28
|
+
export interface UseLeaderAutoWakeArgs extends Omit<TuiLeaderWakePortDeps, 'pendingForms'> {
|
|
29
|
+
leaderAutoWake?: TuiLeaderAutoWake | undefined;
|
|
30
|
+
events: EventBus;
|
|
31
|
+
dispatch(action: Action): void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Bind the auto-wake controller to the foreground TUI session and surface its
|
|
35
|
+
* activity in history: a notice line when a wake fires (the turn's own
|
|
36
|
+
* turn-summary line carries its token/cost), and a warning when the chain cap
|
|
37
|
+
* holds results until the user sends a message.
|
|
38
|
+
*/
|
|
39
|
+
export declare function useLeaderAutoWake(args: UseLeaderAutoWakeArgs): void;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=use-leader-auto-wake.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -342,7 +342,7 @@ var init_connections_health = __esm({
|
|
|
342
342
|
});
|
|
343
343
|
|
|
344
344
|
// src/app.tsx
|
|
345
|
-
import { useCallback as useCallback31, useEffect as
|
|
345
|
+
import { useCallback as useCallback31, useEffect as useEffect80 } from "react";
|
|
346
346
|
|
|
347
347
|
// src/hooks/use-terminal-size.ts
|
|
348
348
|
import { onResize } from "@wrongstack/core/utils";
|
|
@@ -3339,6 +3339,93 @@ function EmptyPanelState({
|
|
|
3339
3339
|
// src/components/status-bar-format.tsx
|
|
3340
3340
|
import { isValidElement } from "react";
|
|
3341
3341
|
|
|
3342
|
+
// src/terminal-width.ts
|
|
3343
|
+
import stringWidth from "string-width";
|
|
3344
|
+
var ANSI_RE = /\x1b\[[0-?]*[ -/]*[@-~]/g;
|
|
3345
|
+
var MAX_STRING_BODY = 4096;
|
|
3346
|
+
var ANSI_OSC_RE = new RegExp(
|
|
3347
|
+
`\\x1b\\][^\\x07\\x1b]{0,${MAX_STRING_BODY}}(?:\\x07|\\x1b\\\\)`,
|
|
3348
|
+
"g"
|
|
3349
|
+
);
|
|
3350
|
+
var ANSI_CONTROL_STRING_RE = new RegExp(
|
|
3351
|
+
`\\x1b[P^_X][^\\x1b]{0,${MAX_STRING_BODY}}\\x1b\\\\`,
|
|
3352
|
+
"g"
|
|
3353
|
+
);
|
|
3354
|
+
var ANSI_ESCAPE_RE = /\x1b[ -/]*[@-~]/g;
|
|
3355
|
+
var GRAPHEME_SEGMENTER = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
3356
|
+
var LIGATURE_PAIRS = /* @__PURE__ */ new Set(["->", "<-", "=>", "<=", ">=", "!=", "==", "~>", "<~"]);
|
|
3357
|
+
var LIGATURE_SEPARATOR = " ";
|
|
3358
|
+
function breakTerminalLigatures(value) {
|
|
3359
|
+
if (!/[-<=>!~]/.test(value)) return value;
|
|
3360
|
+
let safe = "";
|
|
3361
|
+
let previous = "";
|
|
3362
|
+
for (const char of value) {
|
|
3363
|
+
if (previous && LIGATURE_PAIRS.has(`${previous}${char}`)) safe += LIGATURE_SEPARATOR;
|
|
3364
|
+
safe += char;
|
|
3365
|
+
previous = char;
|
|
3366
|
+
}
|
|
3367
|
+
return safe;
|
|
3368
|
+
}
|
|
3369
|
+
function sanitizeTerminalText(value, tabWidth = 2) {
|
|
3370
|
+
const tab = " ".repeat(Math.max(1, Math.min(8, Math.floor(tabWidth))));
|
|
3371
|
+
const withoutEscapes = value.replace(ANSI_OSC_RE, "").replace(ANSI_CONTROL_STRING_RE, "").replace(ANSI_RE, "").replace(ANSI_ESCAPE_RE, "").replace(/\t/g, tab).replace(/\r/g, "");
|
|
3372
|
+
let safe = "";
|
|
3373
|
+
for (const char of withoutEscapes) {
|
|
3374
|
+
const code = char.codePointAt(0) ?? 0;
|
|
3375
|
+
if (char === "\n" || code >= 32 && code !== 127 && !(code >= 128 && code <= 159)) {
|
|
3376
|
+
safe += char;
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
return safe;
|
|
3380
|
+
}
|
|
3381
|
+
function displayWidth(value) {
|
|
3382
|
+
return stringWidth(value);
|
|
3383
|
+
}
|
|
3384
|
+
function truncateDisplay(value, maxWidth, ellipsis = "\u2026") {
|
|
3385
|
+
if (maxWidth <= 0) return "";
|
|
3386
|
+
if (displayWidth(value) <= maxWidth) return value;
|
|
3387
|
+
const ellipsisWidth = displayWidth(ellipsis);
|
|
3388
|
+
if (ellipsisWidth >= maxWidth) return ellipsis;
|
|
3389
|
+
let out = "";
|
|
3390
|
+
let used = 0;
|
|
3391
|
+
for (const { segment } of GRAPHEME_SEGMENTER.segment(value)) {
|
|
3392
|
+
const segmentWidth = displayWidth(segment);
|
|
3393
|
+
if (used + segmentWidth + ellipsisWidth > maxWidth) break;
|
|
3394
|
+
out += segment;
|
|
3395
|
+
used += segmentWidth;
|
|
3396
|
+
}
|
|
3397
|
+
return `${out}${ellipsis}`;
|
|
3398
|
+
}
|
|
3399
|
+
function truncateDisplayStart(value, maxWidth, ellipsis = "\u2026") {
|
|
3400
|
+
if (maxWidth <= 0) return "";
|
|
3401
|
+
if (displayWidth(value) <= maxWidth) return value;
|
|
3402
|
+
const ellipsisWidth = displayWidth(ellipsis);
|
|
3403
|
+
if (ellipsisWidth >= maxWidth) return ellipsis;
|
|
3404
|
+
const segments = [...GRAPHEME_SEGMENTER.segment(value)].map(({ segment }) => segment);
|
|
3405
|
+
let out = "";
|
|
3406
|
+
let used = 0;
|
|
3407
|
+
for (let index = segments.length - 1; index >= 0; index--) {
|
|
3408
|
+
const segment = segments[index] ?? "";
|
|
3409
|
+
const segmentWidth = displayWidth(segment);
|
|
3410
|
+
if (used + segmentWidth + ellipsisWidth > maxWidth) break;
|
|
3411
|
+
out = `${segment}${out}`;
|
|
3412
|
+
used += segmentWidth;
|
|
3413
|
+
}
|
|
3414
|
+
return `${ellipsis}${out}`;
|
|
3415
|
+
}
|
|
3416
|
+
function splitDisplay(value, maxWidth) {
|
|
3417
|
+
if (maxWidth <= 0) return ["", value];
|
|
3418
|
+
let end = 0;
|
|
3419
|
+
let used = 0;
|
|
3420
|
+
for (const { segment, index } of GRAPHEME_SEGMENTER.segment(value)) {
|
|
3421
|
+
const segmentWidth = displayWidth(segment);
|
|
3422
|
+
if (used + segmentWidth > maxWidth) break;
|
|
3423
|
+
used += segmentWidth;
|
|
3424
|
+
end = index + segment.length;
|
|
3425
|
+
}
|
|
3426
|
+
return [value.slice(0, end), value.slice(end)];
|
|
3427
|
+
}
|
|
3428
|
+
|
|
3342
3429
|
// src/thinking-word.ts
|
|
3343
3430
|
var DEFAULT_TUI_THINKING_WORD = "thinking";
|
|
3344
3431
|
var MAX_TUI_THINKING_WORD_LENGTH = 16;
|
|
@@ -3392,7 +3479,7 @@ function pickRandomTuiThinkingWord(previous) {
|
|
|
3392
3479
|
|
|
3393
3480
|
// src/components/status-bar-format.tsx
|
|
3394
3481
|
function truncateChip(text, max) {
|
|
3395
|
-
return text
|
|
3482
|
+
return truncateDisplay(text, max);
|
|
3396
3483
|
}
|
|
3397
3484
|
function shortenPath(path12, segments) {
|
|
3398
3485
|
const parts = path12.split(/[\\/]+/).filter(Boolean);
|
|
@@ -3481,7 +3568,8 @@ function fmtMemory(bytes) {
|
|
|
3481
3568
|
return `${Math.round(bytes)}B`;
|
|
3482
3569
|
}
|
|
3483
3570
|
function fmtElapsed(ms) {
|
|
3484
|
-
const
|
|
3571
|
+
const safeMs = Number.isFinite(ms) && ms > 0 ? ms : 0;
|
|
3572
|
+
const totalSec = Math.floor(safeMs / 1e3);
|
|
3485
3573
|
const h = Math.floor(totalSec / 3600);
|
|
3486
3574
|
const m = Math.floor(totalSec % 3600 / 60);
|
|
3487
3575
|
const s2 = totalSec % 60;
|
|
@@ -8482,95 +8570,6 @@ function useTokenCounterRefresh(tokenCounter, events, sessionId) {
|
|
|
8482
8570
|
|
|
8483
8571
|
// src/components/powerline-rail.tsx
|
|
8484
8572
|
import { isValidElement as isValidElement2 } from "react";
|
|
8485
|
-
|
|
8486
|
-
// src/terminal-width.ts
|
|
8487
|
-
import stringWidth from "string-width";
|
|
8488
|
-
var ANSI_RE = /\x1b\[[0-?]*[ -/]*[@-~]/g;
|
|
8489
|
-
var MAX_STRING_BODY = 4096;
|
|
8490
|
-
var ANSI_OSC_RE = new RegExp(
|
|
8491
|
-
`\\x1b\\][^\\x07\\x1b]{0,${MAX_STRING_BODY}}(?:\\x07|\\x1b\\\\)`,
|
|
8492
|
-
"g"
|
|
8493
|
-
);
|
|
8494
|
-
var ANSI_CONTROL_STRING_RE = new RegExp(
|
|
8495
|
-
`\\x1b[P^_X][^\\x1b]{0,${MAX_STRING_BODY}}\\x1b\\\\`,
|
|
8496
|
-
"g"
|
|
8497
|
-
);
|
|
8498
|
-
var ANSI_ESCAPE_RE = /\x1b[ -/]*[@-~]/g;
|
|
8499
|
-
var GRAPHEME_SEGMENTER = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
8500
|
-
var LIGATURE_PAIRS = /* @__PURE__ */ new Set(["->", "<-", "=>", "<=", ">=", "!=", "==", "~>", "<~"]);
|
|
8501
|
-
var LIGATURE_SEPARATOR = " ";
|
|
8502
|
-
function breakTerminalLigatures(value) {
|
|
8503
|
-
if (!/[-<=>!~]/.test(value)) return value;
|
|
8504
|
-
let safe = "";
|
|
8505
|
-
let previous = "";
|
|
8506
|
-
for (const char of value) {
|
|
8507
|
-
if (previous && LIGATURE_PAIRS.has(`${previous}${char}`)) safe += LIGATURE_SEPARATOR;
|
|
8508
|
-
safe += char;
|
|
8509
|
-
previous = char;
|
|
8510
|
-
}
|
|
8511
|
-
return safe;
|
|
8512
|
-
}
|
|
8513
|
-
function sanitizeTerminalText(value, tabWidth = 2) {
|
|
8514
|
-
const tab = " ".repeat(Math.max(1, Math.min(8, Math.floor(tabWidth))));
|
|
8515
|
-
const withoutEscapes = value.replace(ANSI_OSC_RE, "").replace(ANSI_CONTROL_STRING_RE, "").replace(ANSI_RE, "").replace(ANSI_ESCAPE_RE, "").replace(/\t/g, tab).replace(/\r/g, "");
|
|
8516
|
-
let safe = "";
|
|
8517
|
-
for (const char of withoutEscapes) {
|
|
8518
|
-
const code = char.codePointAt(0) ?? 0;
|
|
8519
|
-
if (char === "\n" || code >= 32 && code !== 127 && !(code >= 128 && code <= 159)) {
|
|
8520
|
-
safe += char;
|
|
8521
|
-
}
|
|
8522
|
-
}
|
|
8523
|
-
return safe;
|
|
8524
|
-
}
|
|
8525
|
-
function displayWidth(value) {
|
|
8526
|
-
return stringWidth(value);
|
|
8527
|
-
}
|
|
8528
|
-
function truncateDisplay(value, maxWidth, ellipsis = "\u2026") {
|
|
8529
|
-
if (maxWidth <= 0) return "";
|
|
8530
|
-
if (displayWidth(value) <= maxWidth) return value;
|
|
8531
|
-
const ellipsisWidth = displayWidth(ellipsis);
|
|
8532
|
-
if (ellipsisWidth >= maxWidth) return ellipsis;
|
|
8533
|
-
let out = "";
|
|
8534
|
-
let used = 0;
|
|
8535
|
-
for (const { segment } of GRAPHEME_SEGMENTER.segment(value)) {
|
|
8536
|
-
const segmentWidth = displayWidth(segment);
|
|
8537
|
-
if (used + segmentWidth + ellipsisWidth > maxWidth) break;
|
|
8538
|
-
out += segment;
|
|
8539
|
-
used += segmentWidth;
|
|
8540
|
-
}
|
|
8541
|
-
return `${out}${ellipsis}`;
|
|
8542
|
-
}
|
|
8543
|
-
function truncateDisplayStart(value, maxWidth, ellipsis = "\u2026") {
|
|
8544
|
-
if (maxWidth <= 0) return "";
|
|
8545
|
-
if (displayWidth(value) <= maxWidth) return value;
|
|
8546
|
-
const ellipsisWidth = displayWidth(ellipsis);
|
|
8547
|
-
if (ellipsisWidth >= maxWidth) return ellipsis;
|
|
8548
|
-
const segments = [...GRAPHEME_SEGMENTER.segment(value)].map(({ segment }) => segment);
|
|
8549
|
-
let out = "";
|
|
8550
|
-
let used = 0;
|
|
8551
|
-
for (let index = segments.length - 1; index >= 0; index--) {
|
|
8552
|
-
const segment = segments[index] ?? "";
|
|
8553
|
-
const segmentWidth = displayWidth(segment);
|
|
8554
|
-
if (used + segmentWidth + ellipsisWidth > maxWidth) break;
|
|
8555
|
-
out = `${segment}${out}`;
|
|
8556
|
-
used += segmentWidth;
|
|
8557
|
-
}
|
|
8558
|
-
return `${ellipsis}${out}`;
|
|
8559
|
-
}
|
|
8560
|
-
function splitDisplay(value, maxWidth) {
|
|
8561
|
-
if (maxWidth <= 0) return ["", value];
|
|
8562
|
-
let end = 0;
|
|
8563
|
-
let used = 0;
|
|
8564
|
-
for (const { segment, index } of GRAPHEME_SEGMENTER.segment(value)) {
|
|
8565
|
-
const segmentWidth = displayWidth(segment);
|
|
8566
|
-
if (used + segmentWidth > maxWidth) break;
|
|
8567
|
-
used += segmentWidth;
|
|
8568
|
-
end = index + segment.length;
|
|
8569
|
-
}
|
|
8570
|
-
return [value.slice(0, end), value.slice(end)];
|
|
8571
|
-
}
|
|
8572
|
-
|
|
8573
|
-
// src/components/powerline-rail.tsx
|
|
8574
8573
|
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
8575
8574
|
function visibleNodeText(node) {
|
|
8576
8575
|
if (node == null || typeof node === "boolean") return "";
|
|
@@ -12983,12 +12982,17 @@ var AUTONOMY_OPTIONS = [
|
|
|
12983
12982
|
function AutonomyPicker({
|
|
12984
12983
|
options,
|
|
12985
12984
|
selected,
|
|
12986
|
-
hint
|
|
12985
|
+
hint,
|
|
12986
|
+
maxRows
|
|
12987
12987
|
}) {
|
|
12988
12988
|
const { start, end, hasAbove, hasBelow } = useWindowedPicker({
|
|
12989
12989
|
total: options.length,
|
|
12990
12990
|
selected,
|
|
12991
|
-
chromeRows: 4
|
|
12991
|
+
chromeRows: 4,
|
|
12992
|
+
// 2 marker slots + 1 conditional hint slot — worst case, so the window
|
|
12993
|
+
// never overflows whether or not those rows actually render.
|
|
12994
|
+
markerRows: 3,
|
|
12995
|
+
maxRows
|
|
12992
12996
|
});
|
|
12993
12997
|
const visibleOptions = options.slice(start, end);
|
|
12994
12998
|
return /* @__PURE__ */ jsxs34(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [
|
|
@@ -14384,8 +14388,8 @@ function brainRiskColor(risk) {
|
|
|
14384
14388
|
|
|
14385
14389
|
// src/components/history/basic-format.ts
|
|
14386
14390
|
function shortenPath2(p, max) {
|
|
14387
|
-
if (p
|
|
14388
|
-
return
|
|
14391
|
+
if (displayWidth(p) <= max) return p;
|
|
14392
|
+
return truncateDisplayStart(p, max);
|
|
14389
14393
|
}
|
|
14390
14394
|
function fmtTok3(n) {
|
|
14391
14395
|
if (!Number.isFinite(n) || n <= 0) return "0";
|
|
@@ -14394,9 +14398,10 @@ function fmtTok3(n) {
|
|
|
14394
14398
|
return String(n);
|
|
14395
14399
|
}
|
|
14396
14400
|
function fmtDuration2(ms) {
|
|
14397
|
-
|
|
14398
|
-
if (
|
|
14399
|
-
|
|
14401
|
+
const safeMs = Number.isFinite(ms) && ms > 0 ? ms : 0;
|
|
14402
|
+
if (safeMs < 1e3) return `${safeMs}ms`;
|
|
14403
|
+
if (safeMs < 6e4) return `${(safeMs / 1e3).toFixed(1)}s`;
|
|
14404
|
+
const totalSec = Math.floor(safeMs / 1e3);
|
|
14400
14405
|
return `${Math.floor(totalSec / 60)}m${totalSec % 60}s`;
|
|
14401
14406
|
}
|
|
14402
14407
|
function fmtBytes(n) {
|
|
@@ -14405,8 +14410,7 @@ function fmtBytes(n) {
|
|
|
14405
14410
|
return `${(n / (1024 * 1024)).toFixed(1)}MB`;
|
|
14406
14411
|
}
|
|
14407
14412
|
function truncMid(s2, max) {
|
|
14408
|
-
|
|
14409
|
-
return `${s2.slice(0, max - 1)}\u2026`;
|
|
14413
|
+
return truncateDisplay(s2, max);
|
|
14410
14414
|
}
|
|
14411
14415
|
function stringOf(v) {
|
|
14412
14416
|
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
@@ -16058,6 +16062,7 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
|
|
|
16058
16062
|
const duration = numOf(o["duration_ms"]);
|
|
16059
16063
|
const head = [];
|
|
16060
16064
|
if (runner && runner !== "none") head.push(runner);
|
|
16065
|
+
if (o["status"] === "no_tests") return [[...head, "no tests"].join(" \xB7 ")];
|
|
16061
16066
|
head.push(`${passed}/${total} passed`);
|
|
16062
16067
|
if (failed > 0) head.push(`${failed} failed`);
|
|
16063
16068
|
if (duration !== void 0) head.push(fmtDuration2(duration));
|
|
@@ -16433,7 +16438,7 @@ function visualVerifier(toolName, text, ok) {
|
|
|
16433
16438
|
errors > 0 ? `${errors} error${errors === 1 ? "" : "s"}` : void 0,
|
|
16434
16439
|
warnings > 0 ? `${warnings} warning${warnings === 1 ? "" : "s"}` : void 0,
|
|
16435
16440
|
changed2 > 0 ? `${changed2} changed` : void 0,
|
|
16436
|
-
toolName === "test" ? `${numOf(obj["passed"]) ?? 0}/${numOf(obj["tests_run"]) ?? 0} passed` : void 0
|
|
16441
|
+
toolName === "test" ? obj["status"] === "no_tests" ? "no tests" : `${numOf(obj["passed"]) ?? 0}/${numOf(obj["tests_run"]) ?? 0} passed` : void 0
|
|
16437
16442
|
].filter(Boolean);
|
|
16438
16443
|
return [
|
|
16439
16444
|
{
|
|
@@ -17572,7 +17577,12 @@ function CoordinatorPanel({
|
|
|
17572
17577
|
|
|
17573
17578
|
// src/components/design-picker.tsx
|
|
17574
17579
|
import { Fragment as Fragment17, jsx as jsx50, jsxs as jsxs48 } from "react/jsx-runtime";
|
|
17575
|
-
function DesignPicker({
|
|
17580
|
+
function DesignPicker({
|
|
17581
|
+
kits,
|
|
17582
|
+
selected,
|
|
17583
|
+
stack,
|
|
17584
|
+
maxRows
|
|
17585
|
+
}) {
|
|
17576
17586
|
const { start, end, hasAbove, hasBelow } = useWindowedPicker({
|
|
17577
17587
|
total: kits.length,
|
|
17578
17588
|
selected,
|
|
@@ -17580,7 +17590,8 @@ function DesignPicker({ kits, selected, stack }) {
|
|
|
17580
17590
|
// 2 marker slots — the `… more above/below` rows render below the header
|
|
17581
17591
|
// and hint but were never counted in the chrome, so a windowed list
|
|
17582
17592
|
// overflowed its own budget by 2 rows on short terminals.
|
|
17583
|
-
markerRows: 2
|
|
17593
|
+
markerRows: 2,
|
|
17594
|
+
maxRows
|
|
17584
17595
|
});
|
|
17585
17596
|
const visibleKits = kits.slice(start, end);
|
|
17586
17597
|
return /* @__PURE__ */ jsxs48(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [
|
|
@@ -19844,13 +19855,16 @@ function ResumePicker({
|
|
|
19844
19855
|
selected,
|
|
19845
19856
|
busy,
|
|
19846
19857
|
error,
|
|
19847
|
-
hint
|
|
19858
|
+
hint,
|
|
19859
|
+
maxRows
|
|
19848
19860
|
}) {
|
|
19849
19861
|
const { start, end, hasAbove, hasBelow } = useWindowedPicker({
|
|
19850
19862
|
total: sessions.length,
|
|
19851
19863
|
selected,
|
|
19852
19864
|
rowSpan: 3,
|
|
19853
|
-
chromeRows: 4
|
|
19865
|
+
chromeRows: 4,
|
|
19866
|
+
markerRows: 3,
|
|
19867
|
+
maxRows
|
|
19854
19868
|
});
|
|
19855
19869
|
const visibleSessions = sessions.slice(start, end);
|
|
19856
19870
|
return /* @__PURE__ */ jsxs65(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
|
|
@@ -22214,7 +22228,8 @@ function AppViewPickers({
|
|
|
22214
22228
|
{
|
|
22215
22229
|
options: state.autonomyPicker.options,
|
|
22216
22230
|
selected: state.autonomyPicker.selected,
|
|
22217
|
-
hint: state.autonomyPicker.hint
|
|
22231
|
+
hint: state.autonomyPicker.hint,
|
|
22232
|
+
maxRows: pickerMaxRows
|
|
22218
22233
|
}
|
|
22219
22234
|
) : null,
|
|
22220
22235
|
state.modePicker.open ? /* @__PURE__ */ jsx81(
|
|
@@ -22264,7 +22279,8 @@ function AppViewPickers({
|
|
|
22264
22279
|
{
|
|
22265
22280
|
kits: state.designPicker.kits,
|
|
22266
22281
|
selected: state.designPicker.selected,
|
|
22267
|
-
stack: state.designPicker.stack
|
|
22282
|
+
stack: state.designPicker.stack,
|
|
22283
|
+
maxRows: pickerMaxRows
|
|
22268
22284
|
}
|
|
22269
22285
|
) : null,
|
|
22270
22286
|
state.promptPicker.open ? /* @__PURE__ */ jsx81(
|
|
@@ -22290,7 +22306,8 @@ function AppViewPickers({
|
|
|
22290
22306
|
selected: state.resumePicker.selected,
|
|
22291
22307
|
busy: state.resumePicker.busy,
|
|
22292
22308
|
error: state.resumePicker.error,
|
|
22293
|
-
hint: state.resumePicker.hint
|
|
22309
|
+
hint: state.resumePicker.hint,
|
|
22310
|
+
maxRows: pickerMaxRows
|
|
22294
22311
|
}
|
|
22295
22312
|
) : null,
|
|
22296
22313
|
state.settingsPicker.open ? /* @__PURE__ */ jsx81(
|
|
@@ -26662,6 +26679,8 @@ function statusBarLineRow(opts) {
|
|
|
26662
26679
|
}
|
|
26663
26680
|
|
|
26664
26681
|
// src/layout-engine.ts
|
|
26682
|
+
import { stripVTControlCharacters } from "node:util";
|
|
26683
|
+
var CONTROL_STRING = /(?:(?:\x1b\]|\x9d)[\s\S]*?(?:\x07|\x1b\\|\x9c|$)|(?:\x1b[PX^_]|[\x90\x98\x9e\x9f])[\s\S]*?(?:\x1b\\|\x9c|$))/g;
|
|
26665
26684
|
var LAYOUT_VERSION = 3;
|
|
26666
26685
|
var FALLBACK_ROWS = 3;
|
|
26667
26686
|
var MAX_ESTIMATE_ROWS = 2e3;
|
|
@@ -26682,8 +26701,7 @@ function wrappedRows(text, width, maxRows = MAX_ESTIMATE_ROWS) {
|
|
|
26682
26701
|
return Math.max(1, rows);
|
|
26683
26702
|
}
|
|
26684
26703
|
function visibleChars(text) {
|
|
26685
|
-
|
|
26686
|
-
return cleaned.length;
|
|
26704
|
+
return [...stripVTControlCharacters(text.replace(CONTROL_STRING, ""))].length;
|
|
26687
26705
|
}
|
|
26688
26706
|
var MAX_MEMORY_PROOF_ENTRIES = 4;
|
|
26689
26707
|
function computeEntryRows(kind, text, termWidth, meta) {
|
|
@@ -34245,8 +34263,10 @@ import { PROMPT_JOURNAL_RAW_MARKER } from "@wrongstack/core/prompts";
|
|
|
34245
34263
|
import { toErrorMessage as toErrorMessage4 } from "@wrongstack/core/utils";
|
|
34246
34264
|
import { routeImagesForModel } from "@wrongstack/runtime/vision";
|
|
34247
34265
|
function createRunBlocksController(host) {
|
|
34248
|
-
const runBlocks = async (blocks) => {
|
|
34266
|
+
const runBlocks = async (blocks, opts) => {
|
|
34249
34267
|
const { capabilities, refs, dispatch } = host;
|
|
34268
|
+
const origin = opts?.origin ?? "user";
|
|
34269
|
+
if (origin === "auto_wake" && refs.activeController.current) return;
|
|
34250
34270
|
const { agent } = capabilities;
|
|
34251
34271
|
const capturedRaw = typeof agent.ctx.meta[PROMPT_JOURNAL_RAW_MARKER] === "string" ? agent.ctx.meta[PROMPT_JOURNAL_RAW_MARKER] : void 0;
|
|
34252
34272
|
if (capturedRaw !== void 0) {
|
|
@@ -34273,7 +34293,14 @@ function createRunBlocksController(host) {
|
|
|
34273
34293
|
});
|
|
34274
34294
|
const controller = new AbortController();
|
|
34275
34295
|
refs.activeController.current = controller;
|
|
34296
|
+
let finishedStatus;
|
|
34276
34297
|
refs.interrupts.current = 0;
|
|
34298
|
+
if (origin === "user") {
|
|
34299
|
+
try {
|
|
34300
|
+
capabilities.onUserRun?.();
|
|
34301
|
+
} catch {
|
|
34302
|
+
}
|
|
34303
|
+
}
|
|
34277
34304
|
dispatch({ type: "resetInterrupts" });
|
|
34278
34305
|
dispatch({ type: "status", status: "running" });
|
|
34279
34306
|
try {
|
|
@@ -34372,8 +34399,10 @@ function createRunBlocksController(host) {
|
|
|
34372
34399
|
} catch {
|
|
34373
34400
|
}
|
|
34374
34401
|
}
|
|
34402
|
+
finishedStatus = result.status;
|
|
34375
34403
|
capabilities.onRunFinished?.(result.status);
|
|
34376
34404
|
} catch (error) {
|
|
34405
|
+
finishedStatus = "failed";
|
|
34377
34406
|
if (runGeneration === refs.sessionGeneration.current) {
|
|
34378
34407
|
dispatch({ type: "addEntry", entry: { kind: "error", text: toErrorMessage4(error) } });
|
|
34379
34408
|
capabilities.onRunFinished?.("failed");
|
|
@@ -34398,6 +34427,12 @@ function createRunBlocksController(host) {
|
|
|
34398
34427
|
agent.ctx.meta[PROMPT_JOURNAL_RAW_MARKER] = head.journalRaw;
|
|
34399
34428
|
}
|
|
34400
34429
|
await runBlocks(head.blocks);
|
|
34430
|
+
return;
|
|
34431
|
+
}
|
|
34432
|
+
if (finishedStatus === "aborted") return;
|
|
34433
|
+
try {
|
|
34434
|
+
capabilities.onIdleAfterRun?.();
|
|
34435
|
+
} catch {
|
|
34401
34436
|
}
|
|
34402
34437
|
};
|
|
34403
34438
|
return runBlocks;
|
|
@@ -35610,19 +35645,25 @@ function rankSlashCommand(entry2, query) {
|
|
|
35610
35645
|
if (query === "" && cmd.hidden) return null;
|
|
35611
35646
|
const name = displayName2(entry2);
|
|
35612
35647
|
const lowerName = name.toLowerCase();
|
|
35648
|
+
const lowerBare = cmd.name.toLowerCase();
|
|
35613
35649
|
const aliases = aliasCandidates(entry2);
|
|
35614
35650
|
const category = cmd.category ?? "App";
|
|
35651
|
+
const namespaced = lowerBare !== lowerName;
|
|
35615
35652
|
let rank = null;
|
|
35616
35653
|
let matchedAlias;
|
|
35617
35654
|
if (query === "") {
|
|
35618
35655
|
rank = 100;
|
|
35619
35656
|
} else if (lowerName === query) {
|
|
35620
35657
|
rank = 0;
|
|
35658
|
+
} else if (namespaced && lowerBare === query) {
|
|
35659
|
+
rank = 2;
|
|
35621
35660
|
} else if (aliases.find((alias) => alias.toLowerCase() === query)) {
|
|
35622
35661
|
rank = 5;
|
|
35623
35662
|
matchedAlias = aliases.find((alias) => alias.toLowerCase() === query);
|
|
35624
35663
|
} else if (lowerName.startsWith(query)) {
|
|
35625
35664
|
rank = 10;
|
|
35665
|
+
} else if (namespaced && lowerBare.startsWith(query)) {
|
|
35666
|
+
rank = 12;
|
|
35626
35667
|
} else {
|
|
35627
35668
|
const prefixAlias = aliases.find((alias) => alias.toLowerCase().startsWith(query));
|
|
35628
35669
|
if (prefixAlias) {
|
|
@@ -35645,16 +35686,13 @@ function displayName2({ cmd, owner, fullName }) {
|
|
|
35645
35686
|
if (owner === "core") return cmd.name;
|
|
35646
35687
|
return fullName.includes(":") ? fullName : cmd.name;
|
|
35647
35688
|
}
|
|
35648
|
-
function aliasCandidates({ cmd, owner
|
|
35689
|
+
function aliasCandidates({ cmd, owner }) {
|
|
35649
35690
|
const aliases = cmd.aliases ?? [];
|
|
35650
35691
|
if (owner === "core") return aliases;
|
|
35651
35692
|
const out = [...aliases];
|
|
35652
35693
|
for (const alias of aliases) {
|
|
35653
35694
|
out.push(`${owner}:${alias}`);
|
|
35654
35695
|
}
|
|
35655
|
-
if (fullName.includes(":")) {
|
|
35656
|
-
return out.filter((alias) => alias.includes(":"));
|
|
35657
|
-
}
|
|
35658
35696
|
return out;
|
|
35659
35697
|
}
|
|
35660
35698
|
|
|
@@ -37453,7 +37491,13 @@ function buildAppPipelineArgs(params) {
|
|
|
37453
37491
|
onSuggestionsParsed: props.onSuggestionsParsed,
|
|
37454
37492
|
predictNext: props.predictNext,
|
|
37455
37493
|
shouldSuppressNextSteps: shouldSuppressBugHuntNextSteps,
|
|
37456
|
-
onRunFinished: onBugHuntRunFinished
|
|
37494
|
+
onRunFinished: onBugHuntRunFinished,
|
|
37495
|
+
...props.leaderAutoWake ? {
|
|
37496
|
+
onUserRun: () => props.leaderAutoWake?.noteUserInput(props.agent.ctx.session.id),
|
|
37497
|
+
onIdleAfterRun: () => {
|
|
37498
|
+
props.leaderAutoWake?.onRunFinished(props.agent.ctx.session.id);
|
|
37499
|
+
}
|
|
37500
|
+
} : {}
|
|
37457
37501
|
},
|
|
37458
37502
|
refs: {
|
|
37459
37503
|
sessionGeneration: sessionGenerationRef,
|
|
@@ -46231,7 +46275,7 @@ async function walk(root, rel, depth, out) {
|
|
|
46231
46275
|
}
|
|
46232
46276
|
function score(s2, query) {
|
|
46233
46277
|
if (!query) return s2.length;
|
|
46234
|
-
const ql = query.toLowerCase();
|
|
46278
|
+
const ql = query.toLowerCase().replaceAll("\\", "/");
|
|
46235
46279
|
const sl = s2.toLowerCase();
|
|
46236
46280
|
let si = 0;
|
|
46237
46281
|
let firstHit = -1;
|
|
@@ -51395,6 +51439,90 @@ function useTuiSlashCommands({
|
|
|
51395
51439
|
|
|
51396
51440
|
// src/app.tsx
|
|
51397
51441
|
import { buildGoalPreamble as buildGoalPreamble2 } from "@wrongstack/core/execution";
|
|
51442
|
+
|
|
51443
|
+
// src/hooks/use-leader-auto-wake.ts
|
|
51444
|
+
import { useEffect as useEffect79, useRef as useRef47 } from "react";
|
|
51445
|
+
function normalizeSessionId(sessionId) {
|
|
51446
|
+
return (sessionId ?? "").trim().replace(/\\/g, "/");
|
|
51447
|
+
}
|
|
51448
|
+
function createTuiLeaderWakePort(deps) {
|
|
51449
|
+
const isForeground = (sessionId) => {
|
|
51450
|
+
const current = normalizeSessionId(deps.getSessionId());
|
|
51451
|
+
return current.length > 0 && current === normalizeSessionId(sessionId);
|
|
51452
|
+
};
|
|
51453
|
+
return {
|
|
51454
|
+
isOpen: isForeground,
|
|
51455
|
+
isDisplayed: isForeground,
|
|
51456
|
+
isIdle: (sessionId) => isForeground(sessionId) && deps.activeController.current == null && !deps.eternalLoopRunning.current && !deps.parallelLoopRunning.current && deps.state.current.queue.length === 0,
|
|
51457
|
+
hasPendingUserInput: () => deps.state.current.queue.length > 0 || deps.enhanceAbort?.current != null,
|
|
51458
|
+
isConfirmPending: () => deps.state.current.confirmQueue.length > 0 || deps.pendingForms.current > 0,
|
|
51459
|
+
startWakeTurn: async (_sessionId, prompt) => {
|
|
51460
|
+
void deps.runBlocks.current([{ type: "text", text: prompt }], { origin: "auto_wake" });
|
|
51461
|
+
}
|
|
51462
|
+
};
|
|
51463
|
+
}
|
|
51464
|
+
function useLeaderAutoWake(args) {
|
|
51465
|
+
const argsRef = useRef47(args);
|
|
51466
|
+
argsRef.current = args;
|
|
51467
|
+
const pendingForms = useRef47(0);
|
|
51468
|
+
const { leaderAutoWake, events } = args;
|
|
51469
|
+
useEffect79(() => {
|
|
51470
|
+
if (!leaderAutoWake) return;
|
|
51471
|
+
const live = argsRef;
|
|
51472
|
+
const port = createTuiLeaderWakePort({
|
|
51473
|
+
getSessionId: () => live.current.getSessionId(),
|
|
51474
|
+
activeController: live.current.activeController,
|
|
51475
|
+
eternalLoopRunning: live.current.eternalLoopRunning,
|
|
51476
|
+
parallelLoopRunning: live.current.parallelLoopRunning,
|
|
51477
|
+
enhanceAbort: live.current.enhanceAbort,
|
|
51478
|
+
state: live.current.state,
|
|
51479
|
+
pendingForms,
|
|
51480
|
+
runBlocks: live.current.runBlocks
|
|
51481
|
+
});
|
|
51482
|
+
const detach = leaderAutoWake.attachPort(port);
|
|
51483
|
+
const isMine = (sessionId) => normalizeSessionId(sessionId) === normalizeSessionId(live.current.getSessionId());
|
|
51484
|
+
const offs = [
|
|
51485
|
+
events.on("user.input_requested", () => {
|
|
51486
|
+
pendingForms.current += 1;
|
|
51487
|
+
}),
|
|
51488
|
+
events.on("user.input_resolved", () => {
|
|
51489
|
+
pendingForms.current = Math.max(0, pendingForms.current - 1);
|
|
51490
|
+
}),
|
|
51491
|
+
events.on("leader.auto_wake_started", (e) => {
|
|
51492
|
+
if (!isMine(e.sessionId)) return;
|
|
51493
|
+
const n = e.delegationIds.length;
|
|
51494
|
+
live.current.dispatch({
|
|
51495
|
+
type: "addEntry",
|
|
51496
|
+
entry: {
|
|
51497
|
+
kind: "info",
|
|
51498
|
+
text: `\u27F3 auto-wake: ${n} background delegation result${n === 1 ? "" : "s"} arrived (${e.delegationIds.join(", ")}) \u2014 starting a new turn (woken turn ${e.chain} without your input; it spends tokens like any turn).`
|
|
51499
|
+
}
|
|
51500
|
+
});
|
|
51501
|
+
}),
|
|
51502
|
+
events.on("leader.auto_wake_suppressed", (e) => {
|
|
51503
|
+
if (!isMine(e.sessionId) || e.reason !== "chain_cap") return;
|
|
51504
|
+
live.current.dispatch({
|
|
51505
|
+
type: "addEntry",
|
|
51506
|
+
entry: {
|
|
51507
|
+
kind: "warn",
|
|
51508
|
+
text: `auto-wake paused: ${e.pending} background result${e.pending === 1 ? "" : "s"} held after too many consecutive woken turns \u2014 send any message to receive ${e.pending === 1 ? "it" : "them"}.`
|
|
51509
|
+
}
|
|
51510
|
+
});
|
|
51511
|
+
})
|
|
51512
|
+
];
|
|
51513
|
+
try {
|
|
51514
|
+
const sessionId = live.current.getSessionId();
|
|
51515
|
+
if (sessionId) leaderAutoWake.onSessionDisplayed(sessionId);
|
|
51516
|
+
} catch {
|
|
51517
|
+
}
|
|
51518
|
+
return () => {
|
|
51519
|
+
for (const off of offs) off();
|
|
51520
|
+
detach();
|
|
51521
|
+
};
|
|
51522
|
+
}, [leaderAutoWake, events]);
|
|
51523
|
+
}
|
|
51524
|
+
|
|
51525
|
+
// src/app.tsx
|
|
51398
51526
|
import { jsx as jsx106 } from "react/jsx-runtime";
|
|
51399
51527
|
function App(props) {
|
|
51400
51528
|
const {
|
|
@@ -51724,7 +51852,7 @@ function App(props) {
|
|
|
51724
51852
|
dispatch,
|
|
51725
51853
|
open: state.authPanel.open
|
|
51726
51854
|
});
|
|
51727
|
-
|
|
51855
|
+
useEffect80(() => {
|
|
51728
51856
|
if (!secretInputController) return;
|
|
51729
51857
|
const previousSecret = secretInputController.readSecret;
|
|
51730
51858
|
const previousText = secretInputController.readText;
|
|
@@ -52158,6 +52286,18 @@ function App(props) {
|
|
|
52158
52286
|
shouldSuppressBugHuntNextSteps: bugHuntLoop.shouldSuppressNextSteps
|
|
52159
52287
|
});
|
|
52160
52288
|
const { stableOnKey } = useAppExecutionPipeline(pipelineArgs);
|
|
52289
|
+
useLeaderAutoWake({
|
|
52290
|
+
leaderAutoWake: props.leaderAutoWake,
|
|
52291
|
+
events,
|
|
52292
|
+
dispatch,
|
|
52293
|
+
getSessionId: () => agent.ctx.session?.id,
|
|
52294
|
+
activeController: activeCtrlRef,
|
|
52295
|
+
eternalLoopRunning: eternalLoopRunningRef,
|
|
52296
|
+
parallelLoopRunning: parallelLoopRunningRef,
|
|
52297
|
+
enhanceAbort: enhanceAbortRef,
|
|
52298
|
+
state: stateRef,
|
|
52299
|
+
runBlocks: runBlocksRef
|
|
52300
|
+
});
|
|
52161
52301
|
useInitialPrompt({ initialGoal, initialAsk, builderRef, runBlocksRef, dispatch });
|
|
52162
52302
|
const viewState = deriveAppViewState({
|
|
52163
52303
|
state,
|
|
@@ -53028,6 +53168,7 @@ async function runTui(opts) {
|
|
|
53028
53168
|
banner: opts.banner ?? true,
|
|
53029
53169
|
queueStore: opts.queueStore,
|
|
53030
53170
|
onQueueChange: opts.onQueueChange,
|
|
53171
|
+
leaderAutoWake: opts.leaderAutoWake,
|
|
53031
53172
|
yolo: opts.yolo,
|
|
53032
53173
|
getYolo: opts.getYolo,
|
|
53033
53174
|
onYolo: opts.onYolo,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ContentBlock } from '@wrongstack/core/types';
|
|
2
2
|
import type { Action, State } from './app-reducer.js';
|
|
3
|
-
import type { RunBlocksCapabilities } from './tui-host-capabilities.js';
|
|
3
|
+
import type { RunBlocksCapabilities, RunBlocksOptions } from './tui-host-capabilities.js';
|
|
4
4
|
import type { MutableCell, StreamSegment } from './shared-types.js';
|
|
5
5
|
export interface RunBlocksRefs {
|
|
6
6
|
readonly sessionGeneration: MutableCell<number>;
|
|
@@ -26,6 +26,6 @@ interface RunBlocksHost {
|
|
|
26
26
|
* recovery, summaries, lifecycle cleanup, and FIFO queue draining; the App
|
|
27
27
|
* shell only supplies stable capabilities and mutable lifecycle cells.
|
|
28
28
|
*/
|
|
29
|
-
export declare function createRunBlocksController(host: RunBlocksHost): (blocks: ContentBlock[]) => Promise<void>;
|
|
29
|
+
export declare function createRunBlocksController(host: RunBlocksHost): (blocks: ContentBlock[], opts?: RunBlocksOptions) => Promise<void>;
|
|
30
30
|
export {};
|
|
31
31
|
//# sourceMappingURL=run-blocks-controller.d.ts.map
|
|
@@ -42,6 +42,12 @@ export interface RunTuiOptions {
|
|
|
42
42
|
* its next iteration boundary without the queue being delivered early.
|
|
43
43
|
*/
|
|
44
44
|
onQueueChange?: ((items: string[]) => void) | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Background-delegation auto-wake controller (core
|
|
47
|
+
* `LeaderAutoWakeController`). When present the TUI binds a wake port for
|
|
48
|
+
* the foreground session and reports user runs / idle-after-run to it.
|
|
49
|
+
*/
|
|
50
|
+
leaderAutoWake?: import('./app-props.js').TuiLeaderAutoWake | undefined;
|
|
45
51
|
/** Surfaces the "⚠ YOLO" chip in the status bar. */
|
|
46
52
|
yolo?: boolean | undefined;
|
|
47
53
|
/** Query live YOLO state from the permission policy. */
|
|
@@ -32,6 +32,22 @@ export interface RunBlocksCapabilities {
|
|
|
32
32
|
}) => Promise<string[]>) | undefined;
|
|
33
33
|
/** Observes a completed foreground run after its result has been rendered. */
|
|
34
34
|
onRunFinished?: ((status: 'done' | 'aborted' | 'failed' | 'max_iterations') => void) | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* A user-originated run is starting (typed input, queue drain, steer). The
|
|
37
|
+
* auto-wake chain cap resets on it. Not called for auto-wake turns.
|
|
38
|
+
*/
|
|
39
|
+
onUserRun?: (() => void) | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* The controller went idle with an empty queue after a run — the auto-wake
|
|
42
|
+
* post-run check (a background result that landed during the last
|
|
43
|
+
* iteration).
|
|
44
|
+
*/
|
|
45
|
+
onIdleAfterRun?: (() => void) | undefined;
|
|
46
|
+
}
|
|
47
|
+
/** Where a `runBlocks` invocation came from. Default `user`. */
|
|
48
|
+
export type RunBlocksOrigin = 'user' | 'auto_wake';
|
|
49
|
+
export interface RunBlocksOptions {
|
|
50
|
+
origin?: RunBlocksOrigin | undefined;
|
|
35
51
|
}
|
|
36
52
|
type ClearAction = Extract<Action, {
|
|
37
53
|
type: 'clearHistory';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/tui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack terminal UI: Ink-based interactive renderer with history, pickers, and slash menus.",
|
|
6
6
|
"repository": {
|
|
@@ -26,26 +26,26 @@
|
|
|
26
26
|
"!dist/**/*.map"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@wrongstack/core": "1.0.
|
|
30
|
-
"@wrongstack/kanban": "1.0.
|
|
31
|
-
"@wrongstack/runtime": "1.0.
|
|
32
|
-
"@wrongstack/sage": "1.0.
|
|
33
|
-
"@wrongstack/sdd": "1.0.
|
|
34
|
-
"@wrongstack/tools": "1.0.
|
|
29
|
+
"@wrongstack/core": "1.0.9",
|
|
30
|
+
"@wrongstack/kanban": "1.0.9",
|
|
31
|
+
"@wrongstack/runtime": "1.0.9",
|
|
32
|
+
"@wrongstack/sage": "1.0.9",
|
|
33
|
+
"@wrongstack/sdd": "1.0.9",
|
|
34
|
+
"@wrongstack/tools": "1.0.9",
|
|
35
35
|
"ink": "7.1.1",
|
|
36
|
-
"react": "^19.
|
|
36
|
+
"react": "^19.3.0",
|
|
37
37
|
"string-width": "8.2.2",
|
|
38
38
|
"wrap-ansi": "^10.0.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@testing-library/dom": "^10.4.1",
|
|
42
|
-
"@testing-library/react": "^16.3.
|
|
43
|
-
"@types/node": "^26.
|
|
44
|
-
"@types/react": "^19.
|
|
45
|
-
"@types/react-dom": "^19.
|
|
42
|
+
"@testing-library/react": "^16.3.3",
|
|
43
|
+
"@types/node": "^26.5.1",
|
|
44
|
+
"@types/react": "^19.3.0",
|
|
45
|
+
"@types/react-dom": "^19.3.0",
|
|
46
46
|
"ink-testing-library": "^4.0.0",
|
|
47
47
|
"jsdom": "^30.0.1",
|
|
48
|
-
"react-dom": "^19.
|
|
48
|
+
"react-dom": "^19.3.0",
|
|
49
49
|
"typescript": "^7.0.2"
|
|
50
50
|
},
|
|
51
51
|
"publishConfig": {
|