@wrongstack/tui 1.0.7 → 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 +309 -143
- package/dist/input-tokens.d.ts +0 -10
- package/dist/layout-engine.d.ts +1 -1
- package/dist/run-blocks-controller.d.ts +2 -2
- package/dist/run-tui-options.d.ts +8 -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(
|
|
@@ -25299,6 +25316,10 @@ var INLINE_TOKEN_SRC = `${ATTACHMENT_TOKEN_SRC}|${PROTOCOL_TOKEN_SRC}`;
|
|
|
25299
25316
|
var AT_END = new RegExp(`(?:${INLINE_TOKEN_SRC})$`, "i");
|
|
25300
25317
|
var AT_START = new RegExp(`^(?:${INLINE_TOKEN_SRC})`, "i");
|
|
25301
25318
|
var GLOBAL = new RegExp(INLINE_TOKEN_SRC, "gi");
|
|
25319
|
+
function rowWouldOverflow(rowChars, next, width) {
|
|
25320
|
+
if (!rowChars) return false;
|
|
25321
|
+
return displayWidth(rowChars + next) > width;
|
|
25322
|
+
}
|
|
25302
25323
|
function deleteTokenBackward(buffer, cursor) {
|
|
25303
25324
|
const m = buffer.slice(0, cursor).match(AT_END);
|
|
25304
25325
|
if (!m) return null;
|
|
@@ -25354,24 +25375,30 @@ function layoutInputRows(prompt, value, cursor, width) {
|
|
|
25354
25375
|
}
|
|
25355
25376
|
const rows = [];
|
|
25356
25377
|
let row = [];
|
|
25378
|
+
let rowChars = "";
|
|
25357
25379
|
for (const cell of cells) {
|
|
25358
25380
|
if (cell.ch === "\n") {
|
|
25359
25381
|
if (cell.cursor) {
|
|
25360
|
-
if (
|
|
25382
|
+
if (rowWouldOverflow(rowChars, " ", w)) {
|
|
25361
25383
|
rows.push(row);
|
|
25362
25384
|
row = [];
|
|
25385
|
+
rowChars = "";
|
|
25363
25386
|
}
|
|
25364
25387
|
row.push({ ch: " ", chip: false, prompt: false, cursor: true });
|
|
25388
|
+
rowChars += " ";
|
|
25365
25389
|
}
|
|
25366
25390
|
rows.push(row);
|
|
25367
25391
|
row = [];
|
|
25392
|
+
rowChars = "";
|
|
25368
25393
|
continue;
|
|
25369
25394
|
}
|
|
25370
|
-
|
|
25371
|
-
if (row.length >= w) {
|
|
25395
|
+
if (rowWouldOverflow(rowChars, cell.ch, w)) {
|
|
25372
25396
|
rows.push(row);
|
|
25373
25397
|
row = [];
|
|
25398
|
+
rowChars = "";
|
|
25374
25399
|
}
|
|
25400
|
+
row.push(cell);
|
|
25401
|
+
rowChars += cell.ch;
|
|
25375
25402
|
}
|
|
25376
25403
|
if (row.length > 0 || rows.length === 0) rows.push(row);
|
|
25377
25404
|
return rows;
|
|
@@ -25384,23 +25411,27 @@ function inputIndexAtRowCol(prompt, value, width, row, col) {
|
|
|
25384
25411
|
const rows = [];
|
|
25385
25412
|
const starts = [];
|
|
25386
25413
|
let cur = [];
|
|
25414
|
+
let curChars = "";
|
|
25387
25415
|
let curStart = 0;
|
|
25388
25416
|
for (const cell of flat) {
|
|
25389
25417
|
if (cell.ch === "\n") {
|
|
25390
25418
|
rows.push(cur);
|
|
25391
25419
|
starts.push(curStart);
|
|
25392
25420
|
cur = [];
|
|
25421
|
+
curChars = "";
|
|
25393
25422
|
curStart = cell.buf + 1;
|
|
25394
25423
|
continue;
|
|
25395
25424
|
}
|
|
25396
|
-
if (
|
|
25397
|
-
cur.push({ buf: cell.buf });
|
|
25398
|
-
if (cur.length >= w) {
|
|
25425
|
+
if (rowWouldOverflow(curChars, cell.ch, w)) {
|
|
25399
25426
|
rows.push(cur);
|
|
25400
25427
|
starts.push(curStart);
|
|
25401
25428
|
cur = [];
|
|
25429
|
+
curChars = "";
|
|
25402
25430
|
curStart = -1;
|
|
25403
25431
|
}
|
|
25432
|
+
if (cur.length === 0 && cell.buf >= 0) curStart = cell.buf;
|
|
25433
|
+
cur.push({ buf: cell.buf, ch: cell.ch });
|
|
25434
|
+
curChars += cell.ch;
|
|
25404
25435
|
}
|
|
25405
25436
|
if (cur.length > 0 || rows.length === 0) {
|
|
25406
25437
|
rows.push(cur);
|
|
@@ -25411,9 +25442,14 @@ function inputIndexAtRowCol(prompt, value, width, row, col) {
|
|
|
25411
25442
|
if (row >= rows.length) return value.length;
|
|
25412
25443
|
const r = rows[row];
|
|
25413
25444
|
const c = Math.max(0, col);
|
|
25414
|
-
|
|
25415
|
-
|
|
25416
|
-
|
|
25445
|
+
let visual = 0;
|
|
25446
|
+
for (const cell of r) {
|
|
25447
|
+
const span = displayWidth(cell.ch);
|
|
25448
|
+
const hit = span <= 0 ? visual === c : c >= visual && c < visual + span;
|
|
25449
|
+
if (hit) {
|
|
25450
|
+
return cell.buf < 0 ? 0 : clamp2(cell.buf);
|
|
25451
|
+
}
|
|
25452
|
+
visual += span;
|
|
25417
25453
|
}
|
|
25418
25454
|
for (let k = r.length - 1; k >= 0; k--) {
|
|
25419
25455
|
const b = r[k].buf;
|
|
@@ -26643,7 +26679,9 @@ function statusBarLineRow(opts) {
|
|
|
26643
26679
|
}
|
|
26644
26680
|
|
|
26645
26681
|
// src/layout-engine.ts
|
|
26646
|
-
|
|
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;
|
|
26684
|
+
var LAYOUT_VERSION = 3;
|
|
26647
26685
|
var FALLBACK_ROWS = 3;
|
|
26648
26686
|
var MAX_ESTIMATE_ROWS = 2e3;
|
|
26649
26687
|
var MIN_CONTENT_WIDTH = 16;
|
|
@@ -26654,16 +26692,16 @@ function wrappedRows(text, width, maxRows = MAX_ESTIMATE_ROWS) {
|
|
|
26654
26692
|
let lineStart = 0;
|
|
26655
26693
|
for (let index = 0; index <= text.length; index++) {
|
|
26656
26694
|
if (index < text.length && text.charCodeAt(index) !== 10) continue;
|
|
26657
|
-
const
|
|
26658
|
-
|
|
26695
|
+
const line = text.slice(lineStart, index);
|
|
26696
|
+
const cols = displayWidth(line);
|
|
26697
|
+
rows += line.length === 0 ? 1 : Math.max(1, Math.ceil(cols / w));
|
|
26659
26698
|
if (rows >= maxRows) return maxRows;
|
|
26660
26699
|
lineStart = index + 1;
|
|
26661
26700
|
}
|
|
26662
26701
|
return Math.max(1, rows);
|
|
26663
26702
|
}
|
|
26664
26703
|
function visibleChars(text) {
|
|
26665
|
-
|
|
26666
|
-
return cleaned.length;
|
|
26704
|
+
return [...stripVTControlCharacters(text.replace(CONTROL_STRING, ""))].length;
|
|
26667
26705
|
}
|
|
26668
26706
|
var MAX_MEMORY_PROOF_ENTRIES = 4;
|
|
26669
26707
|
function computeEntryRows(kind, text, termWidth, meta) {
|
|
@@ -26676,12 +26714,8 @@ function computeEntryRows(kind, text, termWidth, meta) {
|
|
|
26676
26714
|
case "user": {
|
|
26677
26715
|
const contentWidth = w - 2;
|
|
26678
26716
|
const baseText = text;
|
|
26679
|
-
const
|
|
26680
|
-
|
|
26681
|
-
for (const line of lines) {
|
|
26682
|
-
rows += Math.max(1, Math.ceil(line.length / contentWidth));
|
|
26683
|
-
}
|
|
26684
|
-
return Math.max(1, rows + 1);
|
|
26717
|
+
const bodyRows = wrappedRows(baseText, contentWidth);
|
|
26718
|
+
return Math.max(1, bodyRows + 1);
|
|
26685
26719
|
}
|
|
26686
26720
|
// ── Assistant entry ──
|
|
26687
26721
|
// Header (1) + body (wrapped at contentWidth) + chrome (2: border + padding)
|
|
@@ -33805,9 +33839,9 @@ function validatePasteContent(content) {
|
|
|
33805
33839
|
var BEGIN = "[200~";
|
|
33806
33840
|
var END = "[201~";
|
|
33807
33841
|
var OVERFLOW_STATE = Object.freeze({ overflow: true });
|
|
33808
|
-
var BEGIN_RE = /\x1b?\[200
|
|
33809
|
-
var END_RE = /\x1b?\[201
|
|
33810
|
-
var PARTIAL_ANSI_RE = /^\[[\x30-\x3f]*[\x20-\x2f]*[
|
|
33842
|
+
var BEGIN_RE = /\x1b?\[200~/;
|
|
33843
|
+
var END_RE = /\x1b?\[201~/;
|
|
33844
|
+
var PARTIAL_ANSI_RE = /^\[[\x30-\x3f]*[\x20-\x2f]*[mHJKfA-Dhlnsu]$/;
|
|
33811
33845
|
var ANSI_RE2 = new RegExp(
|
|
33812
33846
|
[
|
|
33813
33847
|
// CSI: ESC [ params* intermediates* final
|
|
@@ -33847,15 +33881,23 @@ function feedPaste(accum, input) {
|
|
|
33847
33881
|
}
|
|
33848
33882
|
}
|
|
33849
33883
|
const combined = `${accum ?? ""}${input}`;
|
|
33850
|
-
const
|
|
33884
|
+
const stripOpeningBegin = accum === null || typeof accum === "string" && isMarkerPrefix(accum);
|
|
33885
|
+
let working = stripOpeningBegin ? combined.replace(BEGIN_RE, "") : combined;
|
|
33886
|
+
const endMatches = [...working.matchAll(new RegExp(END_RE, "g"))];
|
|
33887
|
+
const closed = endMatches.length > 0;
|
|
33888
|
+
if (closed) {
|
|
33889
|
+
const last = endMatches[endMatches.length - 1];
|
|
33890
|
+
if (last && last.index !== void 0) working = working.slice(0, last.index);
|
|
33891
|
+
}
|
|
33892
|
+
const stripped = working.replace(ANSI_RE2, "");
|
|
33851
33893
|
if (stripped.length > MAX_PASTE_CHARS) {
|
|
33852
33894
|
return {
|
|
33853
|
-
accum:
|
|
33895
|
+
accum: closed ? null : OVERFLOW_STATE,
|
|
33854
33896
|
complete: null,
|
|
33855
33897
|
error: `Paste rejected: exceeds ${MAX_PASTE_CHARS.toLocaleString()} characters.`
|
|
33856
33898
|
};
|
|
33857
33899
|
}
|
|
33858
|
-
if (
|
|
33900
|
+
if (closed) return { accum: null, complete: stripped };
|
|
33859
33901
|
return { accum: stripped, complete: null };
|
|
33860
33902
|
}
|
|
33861
33903
|
|
|
@@ -34221,8 +34263,10 @@ import { PROMPT_JOURNAL_RAW_MARKER } from "@wrongstack/core/prompts";
|
|
|
34221
34263
|
import { toErrorMessage as toErrorMessage4 } from "@wrongstack/core/utils";
|
|
34222
34264
|
import { routeImagesForModel } from "@wrongstack/runtime/vision";
|
|
34223
34265
|
function createRunBlocksController(host) {
|
|
34224
|
-
const runBlocks = async (blocks) => {
|
|
34266
|
+
const runBlocks = async (blocks, opts) => {
|
|
34225
34267
|
const { capabilities, refs, dispatch } = host;
|
|
34268
|
+
const origin = opts?.origin ?? "user";
|
|
34269
|
+
if (origin === "auto_wake" && refs.activeController.current) return;
|
|
34226
34270
|
const { agent } = capabilities;
|
|
34227
34271
|
const capturedRaw = typeof agent.ctx.meta[PROMPT_JOURNAL_RAW_MARKER] === "string" ? agent.ctx.meta[PROMPT_JOURNAL_RAW_MARKER] : void 0;
|
|
34228
34272
|
if (capturedRaw !== void 0) {
|
|
@@ -34249,7 +34293,14 @@ function createRunBlocksController(host) {
|
|
|
34249
34293
|
});
|
|
34250
34294
|
const controller = new AbortController();
|
|
34251
34295
|
refs.activeController.current = controller;
|
|
34296
|
+
let finishedStatus;
|
|
34252
34297
|
refs.interrupts.current = 0;
|
|
34298
|
+
if (origin === "user") {
|
|
34299
|
+
try {
|
|
34300
|
+
capabilities.onUserRun?.();
|
|
34301
|
+
} catch {
|
|
34302
|
+
}
|
|
34303
|
+
}
|
|
34253
34304
|
dispatch({ type: "resetInterrupts" });
|
|
34254
34305
|
dispatch({ type: "status", status: "running" });
|
|
34255
34306
|
try {
|
|
@@ -34348,8 +34399,10 @@ function createRunBlocksController(host) {
|
|
|
34348
34399
|
} catch {
|
|
34349
34400
|
}
|
|
34350
34401
|
}
|
|
34402
|
+
finishedStatus = result.status;
|
|
34351
34403
|
capabilities.onRunFinished?.(result.status);
|
|
34352
34404
|
} catch (error) {
|
|
34405
|
+
finishedStatus = "failed";
|
|
34353
34406
|
if (runGeneration === refs.sessionGeneration.current) {
|
|
34354
34407
|
dispatch({ type: "addEntry", entry: { kind: "error", text: toErrorMessage4(error) } });
|
|
34355
34408
|
capabilities.onRunFinished?.("failed");
|
|
@@ -34374,6 +34427,12 @@ function createRunBlocksController(host) {
|
|
|
34374
34427
|
agent.ctx.meta[PROMPT_JOURNAL_RAW_MARKER] = head.journalRaw;
|
|
34375
34428
|
}
|
|
34376
34429
|
await runBlocks(head.blocks);
|
|
34430
|
+
return;
|
|
34431
|
+
}
|
|
34432
|
+
if (finishedStatus === "aborted") return;
|
|
34433
|
+
try {
|
|
34434
|
+
capabilities.onIdleAfterRun?.();
|
|
34435
|
+
} catch {
|
|
34377
34436
|
}
|
|
34378
34437
|
};
|
|
34379
34438
|
return runBlocks;
|
|
@@ -35586,19 +35645,25 @@ function rankSlashCommand(entry2, query) {
|
|
|
35586
35645
|
if (query === "" && cmd.hidden) return null;
|
|
35587
35646
|
const name = displayName2(entry2);
|
|
35588
35647
|
const lowerName = name.toLowerCase();
|
|
35648
|
+
const lowerBare = cmd.name.toLowerCase();
|
|
35589
35649
|
const aliases = aliasCandidates(entry2);
|
|
35590
35650
|
const category = cmd.category ?? "App";
|
|
35651
|
+
const namespaced = lowerBare !== lowerName;
|
|
35591
35652
|
let rank = null;
|
|
35592
35653
|
let matchedAlias;
|
|
35593
35654
|
if (query === "") {
|
|
35594
35655
|
rank = 100;
|
|
35595
35656
|
} else if (lowerName === query) {
|
|
35596
35657
|
rank = 0;
|
|
35658
|
+
} else if (namespaced && lowerBare === query) {
|
|
35659
|
+
rank = 2;
|
|
35597
35660
|
} else if (aliases.find((alias) => alias.toLowerCase() === query)) {
|
|
35598
35661
|
rank = 5;
|
|
35599
35662
|
matchedAlias = aliases.find((alias) => alias.toLowerCase() === query);
|
|
35600
35663
|
} else if (lowerName.startsWith(query)) {
|
|
35601
35664
|
rank = 10;
|
|
35665
|
+
} else if (namespaced && lowerBare.startsWith(query)) {
|
|
35666
|
+
rank = 12;
|
|
35602
35667
|
} else {
|
|
35603
35668
|
const prefixAlias = aliases.find((alias) => alias.toLowerCase().startsWith(query));
|
|
35604
35669
|
if (prefixAlias) {
|
|
@@ -35621,16 +35686,13 @@ function displayName2({ cmd, owner, fullName }) {
|
|
|
35621
35686
|
if (owner === "core") return cmd.name;
|
|
35622
35687
|
return fullName.includes(":") ? fullName : cmd.name;
|
|
35623
35688
|
}
|
|
35624
|
-
function aliasCandidates({ cmd, owner
|
|
35689
|
+
function aliasCandidates({ cmd, owner }) {
|
|
35625
35690
|
const aliases = cmd.aliases ?? [];
|
|
35626
35691
|
if (owner === "core") return aliases;
|
|
35627
35692
|
const out = [...aliases];
|
|
35628
35693
|
for (const alias of aliases) {
|
|
35629
35694
|
out.push(`${owner}:${alias}`);
|
|
35630
35695
|
}
|
|
35631
|
-
if (fullName.includes(":")) {
|
|
35632
|
-
return out.filter((alias) => alias.includes(":"));
|
|
35633
|
-
}
|
|
35634
35696
|
return out;
|
|
35635
35697
|
}
|
|
35636
35698
|
|
|
@@ -37429,7 +37491,13 @@ function buildAppPipelineArgs(params) {
|
|
|
37429
37491
|
onSuggestionsParsed: props.onSuggestionsParsed,
|
|
37430
37492
|
predictNext: props.predictNext,
|
|
37431
37493
|
shouldSuppressNextSteps: shouldSuppressBugHuntNextSteps,
|
|
37432
|
-
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
|
+
} : {}
|
|
37433
37501
|
},
|
|
37434
37502
|
refs: {
|
|
37435
37503
|
sessionGeneration: sessionGenerationRef,
|
|
@@ -46207,7 +46275,7 @@ async function walk(root, rel, depth, out) {
|
|
|
46207
46275
|
}
|
|
46208
46276
|
function score(s2, query) {
|
|
46209
46277
|
if (!query) return s2.length;
|
|
46210
|
-
const ql = query.toLowerCase();
|
|
46278
|
+
const ql = query.toLowerCase().replaceAll("\\", "/");
|
|
46211
46279
|
const sl = s2.toLowerCase();
|
|
46212
46280
|
let si = 0;
|
|
46213
46281
|
let firstHit = -1;
|
|
@@ -51371,6 +51439,90 @@ function useTuiSlashCommands({
|
|
|
51371
51439
|
|
|
51372
51440
|
// src/app.tsx
|
|
51373
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
|
|
51374
51526
|
import { jsx as jsx106 } from "react/jsx-runtime";
|
|
51375
51527
|
function App(props) {
|
|
51376
51528
|
const {
|
|
@@ -51700,7 +51852,7 @@ function App(props) {
|
|
|
51700
51852
|
dispatch,
|
|
51701
51853
|
open: state.authPanel.open
|
|
51702
51854
|
});
|
|
51703
|
-
|
|
51855
|
+
useEffect80(() => {
|
|
51704
51856
|
if (!secretInputController) return;
|
|
51705
51857
|
const previousSecret = secretInputController.readSecret;
|
|
51706
51858
|
const previousText = secretInputController.readText;
|
|
@@ -52134,6 +52286,18 @@ function App(props) {
|
|
|
52134
52286
|
shouldSuppressBugHuntNextSteps: bugHuntLoop.shouldSuppressNextSteps
|
|
52135
52287
|
});
|
|
52136
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
|
+
});
|
|
52137
52301
|
useInitialPrompt({ initialGoal, initialAsk, builderRef, runBlocksRef, dispatch });
|
|
52138
52302
|
const viewState = deriveAppViewState({
|
|
52139
52303
|
state,
|
|
@@ -53004,6 +53168,7 @@ async function runTui(opts) {
|
|
|
53004
53168
|
banner: opts.banner ?? true,
|
|
53005
53169
|
queueStore: opts.queueStore,
|
|
53006
53170
|
onQueueChange: opts.onQueueChange,
|
|
53171
|
+
leaderAutoWake: opts.leaderAutoWake,
|
|
53007
53172
|
yolo: opts.yolo,
|
|
53008
53173
|
getYolo: opts.getYolo,
|
|
53009
53174
|
onYolo: opts.onYolo,
|
|
@@ -53069,6 +53234,7 @@ async function runTui(opts) {
|
|
|
53069
53234
|
getBrainData: opts.getBrainData,
|
|
53070
53235
|
onBrainRiskLevel: opts.onBrainRiskLevel,
|
|
53071
53236
|
brainPanelHost: opts.brainPanelHost,
|
|
53237
|
+
subagentModelsHost: opts.subagentModelsHost,
|
|
53072
53238
|
getShadowData: opts.getShadowData,
|
|
53073
53239
|
onShadowStart: opts.onShadowStart,
|
|
53074
53240
|
onShadowStop: opts.onShadowStop,
|
package/dist/input-tokens.d.ts
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Inline chip token grammar, shared by the editable input for chip rendering
|
|
3
|
-
* and whole-token cursor deletion. Attachment shapes stay in sync with the
|
|
4
|
-
* AttachmentStore placeholder regex in @wrongstack/core:
|
|
5
|
-
* - seq-keyed `[pasted|image|file #N …]` (a cosmetic suffix after the seq,
|
|
6
|
-
* e.g. `, 123 lines`, is tolerated; legacy `[file #N]` is included)
|
|
7
|
-
* - path-keyed `[file:<path>]`
|
|
8
|
-
* `[VIBE]` is a protocol chip: it renders and deletes as a token, but it is
|
|
9
|
-
* not an attachment and must stay in the prompt sent to the refiner/agent.
|
|
10
|
-
*/
|
|
11
1
|
/** Attachment placeholders only. Protocol tags must not be stripped as chips. */
|
|
12
2
|
export declare const ATTACHMENT_TOKEN_SRC = "\\[(?:pasted|image|file) #\\d+[^\\]]*\\]|\\[file:[^\\]]+\\]";
|
|
13
3
|
/** Protocol trigger rendered as a chip but kept in the prompt text. */
|
package/dist/layout-engine.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export interface LayoutSnapshot {
|
|
|
50
50
|
entries: Record<number, EntryLayout>;
|
|
51
51
|
}
|
|
52
52
|
/** Current layout computation version. Bump when the algorithm changes. */
|
|
53
|
-
export declare const LAYOUT_VERSION =
|
|
53
|
+
export declare const LAYOUT_VERSION = 3;
|
|
54
54
|
/** Default fallback row count for entries without computed layout. */
|
|
55
55
|
export declare const FALLBACK_ROWS = 3;
|
|
56
56
|
/**
|
|
@@ -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. */
|
|
@@ -425,6 +431,8 @@ export interface RunTuiOptions {
|
|
|
425
431
|
onBrainRiskLevel?: ((level: 'off' | 'low' | 'medium' | 'high' | 'all') => string | undefined) | undefined;
|
|
426
432
|
/** Full Brain settings editor bridge (live apply + persist to global config). */
|
|
427
433
|
brainPanelHost?: import('./brain-panel-model.js').BrainPanelHost | undefined;
|
|
434
|
+
/** Session-scoped model-lane bridge for the interactive `/subagent-models` panel. */
|
|
435
|
+
subagentModelsHost?: import('./subagent-models-panel-model.js').SubagentModelsPanelHost | undefined;
|
|
428
436
|
/** Get current Shadow Agent state. */
|
|
429
437
|
getShadowData?: (() => {
|
|
430
438
|
activeId: string | null;
|
|
@@ -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": {
|