@wrongstack/tui 0.4.1 → 0.5.2
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/index.d.ts +20 -0
- package/dist/index.js +254 -16
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,10 @@ interface RunTuiOptions {
|
|
|
26
26
|
queueStore?: QueueStore;
|
|
27
27
|
/** Surfaces the "⚠ YOLO" chip in the status bar. */
|
|
28
28
|
yolo?: boolean;
|
|
29
|
+
/** Query live YOLO state from the permission policy. */
|
|
30
|
+
getYolo?: () => boolean;
|
|
31
|
+
/** Query the live autonomy mode. */
|
|
32
|
+
getAutonomy?: () => 'off' | 'suggest' | 'auto';
|
|
29
33
|
/** Renders in the startup banner. Read from the CLI's package.json. */
|
|
30
34
|
appVersion?: string;
|
|
31
35
|
/** Provider id for the startup banner ("openai", "anthropic", ...). */
|
|
@@ -110,6 +114,22 @@ interface RunTuiOptions {
|
|
|
110
114
|
* Ignored when `initialGoal` is also set.
|
|
111
115
|
*/
|
|
112
116
|
initialAsk?: string;
|
|
117
|
+
/**
|
|
118
|
+
* Directory containing session JSONL files. Required for rewind
|
|
119
|
+
* functionality. When provided the TUI can list checkpoints and
|
|
120
|
+
* trigger a rewind via `/rewind` or Ctrl+R.
|
|
121
|
+
*/
|
|
122
|
+
sessionsDir?: string;
|
|
123
|
+
/**
|
|
124
|
+
* SDD session context getter. When an SDD session is active, returns
|
|
125
|
+
* the AI prompt context to inject into user messages.
|
|
126
|
+
*/
|
|
127
|
+
getSDDContext?: () => string | null;
|
|
128
|
+
/**
|
|
129
|
+
* Process AI output for SDD auto-detection (spec, tasks, plan).
|
|
130
|
+
* Returns displayable status messages.
|
|
131
|
+
*/
|
|
132
|
+
onSDDOutput?: (output: string) => Promise<string[]>;
|
|
113
133
|
}
|
|
114
134
|
declare function runTui(opts: RunTuiOptions): Promise<number>;
|
|
115
135
|
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { render, useApp, Box, useStdout, Static, Text, useInput, useStdin } from 'ink';
|
|
2
|
-
import
|
|
2
|
+
import React2, { useState, useReducer, useRef, useEffect, useMemo } from 'react';
|
|
3
3
|
import * as fs2 from 'fs/promises';
|
|
4
4
|
import * as path2 from 'path';
|
|
5
|
-
import { InputBuilder, formatTodosList, buildChildEnv } from '@wrongstack/core';
|
|
5
|
+
import { InputBuilder, DefaultSessionRewinder, formatTodosList, buildChildEnv } from '@wrongstack/core';
|
|
6
6
|
import { routeImagesForModel } from '@wrongstack/runtime/vision';
|
|
7
7
|
import { readClipboardImage } from '@wrongstack/runtime/clipboard';
|
|
8
8
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
@@ -39,7 +39,7 @@ function ConfirmPrompt({
|
|
|
39
39
|
suggestedPattern,
|
|
40
40
|
onDecision
|
|
41
41
|
}) {
|
|
42
|
-
|
|
42
|
+
React2.useEffect(() => {
|
|
43
43
|
process.stdout.write("\x07");
|
|
44
44
|
}, []);
|
|
45
45
|
useInput((input2, key) => {
|
|
@@ -94,6 +94,49 @@ function ConfirmPrompt({
|
|
|
94
94
|
}
|
|
95
95
|
);
|
|
96
96
|
}
|
|
97
|
+
function CheckpointTimeline({
|
|
98
|
+
checkpoints,
|
|
99
|
+
selected,
|
|
100
|
+
onSelect,
|
|
101
|
+
onConfirm,
|
|
102
|
+
onClose
|
|
103
|
+
}) {
|
|
104
|
+
useInput((_, key) => {
|
|
105
|
+
if (key.escape) {
|
|
106
|
+
onClose();
|
|
107
|
+
} else if (key.upArrow) {
|
|
108
|
+
onSelect(Math.max(0, selected - 1));
|
|
109
|
+
} else if (key.downArrow) {
|
|
110
|
+
onSelect(Math.min(checkpoints.length - 1, selected + 1));
|
|
111
|
+
} else if (key.return) {
|
|
112
|
+
onConfirm(selected);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", padding: 1, children: [
|
|
116
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
117
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u27F2 Session Rewind" }),
|
|
118
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2014 \u2191/\u2193 navigate \xB7 Enter rewind \xB7 Esc cancel" })
|
|
119
|
+
] }),
|
|
120
|
+
checkpoints.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No checkpoints in this session." }) : checkpoints.map((cp, i) => {
|
|
121
|
+
const isSelected = i === selected;
|
|
122
|
+
const label = `[${cp.promptIndex}] ${cp.promptPreview}`;
|
|
123
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
124
|
+
/* @__PURE__ */ jsx(Text, { color: isSelected ? "cyan" : void 0, bold: isSelected, children: isSelected ? "\u25B8 " : " " }),
|
|
125
|
+
/* @__PURE__ */ jsx(Text, { color: isSelected ? "cyan" : void 0, bold: isSelected, children: label }),
|
|
126
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
127
|
+
" ",
|
|
128
|
+
new Date(cp.ts).toLocaleTimeString()
|
|
129
|
+
] }),
|
|
130
|
+
cp.fileCount > 0 && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
131
|
+
" \xB7 ",
|
|
132
|
+
cp.fileCount,
|
|
133
|
+
" file",
|
|
134
|
+
cp.fileCount !== 1 ? "s" : ""
|
|
135
|
+
] })
|
|
136
|
+
] }, cp.promptIndex);
|
|
137
|
+
})
|
|
138
|
+
] });
|
|
139
|
+
}
|
|
97
140
|
function FilePicker({ query, matches, selected }) {
|
|
98
141
|
if (matches.length === 0) {
|
|
99
142
|
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
@@ -244,6 +287,15 @@ function FleetPanel({ entries, totalCost, roster }) {
|
|
|
244
287
|
"msg: ",
|
|
245
288
|
message
|
|
246
289
|
] }) }, `${entry.id}-msg-${index}-${message}`)),
|
|
290
|
+
entry.budgetWarning ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
291
|
+
"\u26A1 hitting ",
|
|
292
|
+
entry.budgetWarning.kind,
|
|
293
|
+
" limit (",
|
|
294
|
+
entry.budgetWarning.used,
|
|
295
|
+
"/",
|
|
296
|
+
entry.budgetWarning.limit,
|
|
297
|
+
") \u2014 extending"
|
|
298
|
+
] }) }) : null,
|
|
247
299
|
entry.status === "running" && entry.streamingText ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
248
300
|
">",
|
|
249
301
|
" ",
|
|
@@ -1563,6 +1615,7 @@ function StatusBar({
|
|
|
1563
1615
|
hint,
|
|
1564
1616
|
queueCount = 0,
|
|
1565
1617
|
yolo = false,
|
|
1618
|
+
autonomy,
|
|
1566
1619
|
elapsedMs,
|
|
1567
1620
|
todos,
|
|
1568
1621
|
plan,
|
|
@@ -1578,7 +1631,7 @@ function StatusBar({
|
|
|
1578
1631
|
const cache2 = tokenCounter?.cacheStats();
|
|
1579
1632
|
const stateColor = state === "idle" ? "cyan" : state === "aborting" ? "yellow" : "green";
|
|
1580
1633
|
const stateLabel = state === "idle" ? "idle" : state === "aborting" ? "aborting\u2026" : "thinking\u2026";
|
|
1581
|
-
const hasSecondLine = yolo || elapsedMs !== void 0 || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0;
|
|
1634
|
+
const hasSecondLine = yolo || autonomy && autonomy !== "off" || elapsedMs !== void 0 || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0;
|
|
1582
1635
|
const fleetHasActivity = fleet && (fleet.running > 0 || fleet.idle > 0 || fleet.pending > 0 || fleet.completed > 0) || subagentCount > 0;
|
|
1583
1636
|
const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity;
|
|
1584
1637
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1643,6 +1696,13 @@ function StatusBar({
|
|
|
1643
1696
|
] }),
|
|
1644
1697
|
hasSecondLine ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
|
|
1645
1698
|
yolo ? /* @__PURE__ */ jsx(Text, { color: "red", bold: true, children: "\u26A0 YOLO" }) : null,
|
|
1699
|
+
autonomy && autonomy !== "off" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1700
|
+
yolo ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
|
|
1701
|
+
/* @__PURE__ */ jsxs(Text, { color: autonomy === "auto" ? "yellow" : "cyan", bold: true, children: [
|
|
1702
|
+
"\u221E ",
|
|
1703
|
+
autonomy.toUpperCase()
|
|
1704
|
+
] })
|
|
1705
|
+
] }) : null,
|
|
1646
1706
|
elapsedMs !== void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1647
1707
|
yolo ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
|
|
1648
1708
|
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
@@ -2341,6 +2401,8 @@ function reducer(state, action) {
|
|
|
2341
2401
|
...cur,
|
|
2342
2402
|
status: "running",
|
|
2343
2403
|
streamingText: "",
|
|
2404
|
+
budgetWarning: void 0,
|
|
2405
|
+
// clear on restart
|
|
2344
2406
|
startedAt: Date.now()
|
|
2345
2407
|
}
|
|
2346
2408
|
}
|
|
@@ -2423,6 +2485,23 @@ function reducer(state, action) {
|
|
|
2423
2485
|
toolCalls: action.toolCalls,
|
|
2424
2486
|
streamingText: "",
|
|
2425
2487
|
currentTool: void 0,
|
|
2488
|
+
budgetWarning: void 0,
|
|
2489
|
+
// clear on done/restart
|
|
2490
|
+
lastEventAt: Date.now()
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
case "fleetBudgetWarning": {
|
|
2496
|
+
const cur = state.fleet[action.id];
|
|
2497
|
+
if (!cur) return state;
|
|
2498
|
+
return {
|
|
2499
|
+
...state,
|
|
2500
|
+
fleet: {
|
|
2501
|
+
...state.fleet,
|
|
2502
|
+
[action.id]: {
|
|
2503
|
+
...cur,
|
|
2504
|
+
budgetWarning: { kind: action.kind, used: action.used, limit: action.limit, at: Date.now() },
|
|
2426
2505
|
lastEventAt: Date.now()
|
|
2427
2506
|
}
|
|
2428
2507
|
}
|
|
@@ -2434,6 +2513,34 @@ function reducer(state, action) {
|
|
|
2434
2513
|
case "setStreamFleet": {
|
|
2435
2514
|
return { ...state, streamFleet: action.enabled };
|
|
2436
2515
|
}
|
|
2516
|
+
case "checkpointReceived": {
|
|
2517
|
+
const existing = state.checkpoints.find((c) => c.promptIndex === action.cp.promptIndex);
|
|
2518
|
+
if (existing) return state;
|
|
2519
|
+
return { ...state, checkpoints: [...state.checkpoints, action.cp] };
|
|
2520
|
+
}
|
|
2521
|
+
case "rewindOverlayOpen": {
|
|
2522
|
+
return {
|
|
2523
|
+
...state,
|
|
2524
|
+
rewindOverlay: { checkpoints: state.checkpoints, selected: state.checkpoints.length - 1 }
|
|
2525
|
+
};
|
|
2526
|
+
}
|
|
2527
|
+
case "rewindOverlayClose": {
|
|
2528
|
+
return { ...state, rewindOverlay: null };
|
|
2529
|
+
}
|
|
2530
|
+
case "rewindOverlayMove": {
|
|
2531
|
+
if (!state.rewindOverlay) return state;
|
|
2532
|
+
const len = state.rewindOverlay.checkpoints.length;
|
|
2533
|
+
if (len === 0) return { ...state, rewindOverlay: null };
|
|
2534
|
+
const selected = Math.max(0, Math.min(len - 1, state.rewindOverlay.selected + action.delta));
|
|
2535
|
+
return { ...state, rewindOverlay: { ...state.rewindOverlay, selected } };
|
|
2536
|
+
}
|
|
2537
|
+
case "sessionRewound": {
|
|
2538
|
+
return {
|
|
2539
|
+
...state,
|
|
2540
|
+
checkpoints: state.checkpoints.filter((c) => c.promptIndex <= action.toPromptIndex),
|
|
2541
|
+
rewindOverlay: null
|
|
2542
|
+
};
|
|
2543
|
+
}
|
|
2437
2544
|
}
|
|
2438
2545
|
}
|
|
2439
2546
|
var PASTE_THRESHOLD_CHARS = 200;
|
|
@@ -2552,6 +2659,10 @@ function App({
|
|
|
2552
2659
|
banner = true,
|
|
2553
2660
|
queueStore,
|
|
2554
2661
|
yolo = false,
|
|
2662
|
+
getYolo,
|
|
2663
|
+
getAutonomy,
|
|
2664
|
+
getSDDContext,
|
|
2665
|
+
onSDDOutput,
|
|
2555
2666
|
appVersion,
|
|
2556
2667
|
provider,
|
|
2557
2668
|
family,
|
|
@@ -2565,11 +2676,14 @@ function App({
|
|
|
2565
2676
|
onClearHistory,
|
|
2566
2677
|
fleetStreamController,
|
|
2567
2678
|
initialGoal,
|
|
2568
|
-
initialAsk
|
|
2679
|
+
initialAsk,
|
|
2680
|
+
sessionsDir
|
|
2569
2681
|
}) {
|
|
2570
2682
|
const { exit } = useApp();
|
|
2571
2683
|
const [liveModel, setLiveModel] = useState(model);
|
|
2572
2684
|
const [liveProvider, setLiveProvider] = useState(provider ?? "agent");
|
|
2685
|
+
const [yoloLive, setYoloLive] = useState(yolo);
|
|
2686
|
+
const [autonomyLive, setAutonomyLive] = useState(getAutonomy?.() ?? "off");
|
|
2573
2687
|
const [state, dispatch] = useReducer(reducer, {
|
|
2574
2688
|
entries: banner ? [
|
|
2575
2689
|
{
|
|
@@ -2612,7 +2726,9 @@ function App({
|
|
|
2612
2726
|
contextChipVersion: 0,
|
|
2613
2727
|
fleet: {},
|
|
2614
2728
|
fleetCost: 0,
|
|
2615
|
-
streamFleet: true
|
|
2729
|
+
streamFleet: true,
|
|
2730
|
+
checkpoints: [],
|
|
2731
|
+
rewindOverlay: null
|
|
2616
2732
|
});
|
|
2617
2733
|
const builderRef = useRef(null);
|
|
2618
2734
|
if (builderRef.current === null) {
|
|
@@ -2622,7 +2738,7 @@ function App({
|
|
|
2622
2738
|
const inputGateRef = useRef(false);
|
|
2623
2739
|
const lastEnterAtRef = useRef(0);
|
|
2624
2740
|
const projectRoot = agent.ctx.projectRoot;
|
|
2625
|
-
const projectName =
|
|
2741
|
+
const projectName = React2.useMemo(() => {
|
|
2626
2742
|
const base = path2.basename(projectRoot);
|
|
2627
2743
|
return base && base !== path2.sep ? base : void 0;
|
|
2628
2744
|
}, [projectRoot]);
|
|
@@ -2633,6 +2749,13 @@ function App({
|
|
|
2633
2749
|
stateRef.current = state;
|
|
2634
2750
|
const draftRef = useRef({ buffer: state.buffer, cursor: state.cursor });
|
|
2635
2751
|
draftRef.current = { buffer: state.buffer, cursor: state.cursor };
|
|
2752
|
+
const handleRewindTo = React2.useCallback(async (checkpointIndex) => {
|
|
2753
|
+
const sessionId = agent.ctx.session.id;
|
|
2754
|
+
if (!sessionId) return;
|
|
2755
|
+
const rewinder = new DefaultSessionRewinder(sessionsDir ?? "");
|
|
2756
|
+
await rewinder.rewindToCheckpoint(sessionId, checkpointIndex);
|
|
2757
|
+
await agent.ctx.session.truncateToCheckpoint(checkpointIndex);
|
|
2758
|
+
}, [agent.ctx.session, sessionsDir]);
|
|
2636
2759
|
const setDraft = (buffer, cursor) => {
|
|
2637
2760
|
draftRef.current = { buffer, cursor };
|
|
2638
2761
|
dispatch({ type: "setBuffer", buffer, cursor });
|
|
@@ -2642,13 +2765,13 @@ function App({
|
|
|
2642
2765
|
dispatch({ type: "clearInput" });
|
|
2643
2766
|
};
|
|
2644
2767
|
const startedAtRef = useRef(Date.now());
|
|
2645
|
-
const [nowTick, setNowTick] =
|
|
2768
|
+
const [nowTick, setNowTick] = React2.useState(Date.now());
|
|
2646
2769
|
useEffect(() => {
|
|
2647
2770
|
const t = setInterval(() => setNowTick(Date.now()), 1e3);
|
|
2648
2771
|
return () => clearInterval(t);
|
|
2649
2772
|
}, []);
|
|
2650
2773
|
const elapsedMs = nowTick - startedAtRef.current;
|
|
2651
|
-
const [gitInfo, setGitInfo] =
|
|
2774
|
+
const [gitInfo, setGitInfo] = React2.useState(null);
|
|
2652
2775
|
useEffect(() => {
|
|
2653
2776
|
let cancelled = false;
|
|
2654
2777
|
const refresh = () => {
|
|
@@ -2663,12 +2786,13 @@ function App({
|
|
|
2663
2786
|
clearInterval(t);
|
|
2664
2787
|
};
|
|
2665
2788
|
}, [agent.ctx.cwd]);
|
|
2666
|
-
|
|
2789
|
+
(tokenCounter?.total().input ?? 0) + (tokenCounter?.total().cacheRead ?? 0) + (tokenCounter?.total().cacheWrite ?? 0);
|
|
2667
2790
|
const maxContext = effectiveMaxContext ?? agent.ctx.provider.capabilities.maxContext;
|
|
2791
|
+
const currentContextTokens = (tokenCounter?.currentRequestTokens()?.input ?? 0) + (tokenCounter?.currentRequestTokens()?.cacheRead ?? 0);
|
|
2668
2792
|
const contextWindow = useMemo(() => {
|
|
2669
2793
|
void state.contextChipVersion;
|
|
2670
|
-
return
|
|
2671
|
-
}, [
|
|
2794
|
+
return currentContextTokens > 0 && maxContext > 0 ? { used: currentContextTokens, max: maxContext } : void 0;
|
|
2795
|
+
}, [currentContextTokens, maxContext, state.contextChipVersion]);
|
|
2672
2796
|
const todos = useMemo(() => {
|
|
2673
2797
|
const counts = { pending: 0, inProgress: 0, completed: 0 };
|
|
2674
2798
|
for (const t of agent.ctx.todos) {
|
|
@@ -3028,6 +3152,39 @@ function App({
|
|
|
3028
3152
|
slashRegistry.unregister("steer");
|
|
3029
3153
|
};
|
|
3030
3154
|
}, [slashRegistry, director]);
|
|
3155
|
+
useEffect(() => {
|
|
3156
|
+
const cmd = {
|
|
3157
|
+
name: "rewind",
|
|
3158
|
+
description: "Open checkpoint timeline to rewind session: /rewind [checkpoint-index]",
|
|
3159
|
+
help: [
|
|
3160
|
+
"Usage: /rewind [checkpoint-index]",
|
|
3161
|
+
"",
|
|
3162
|
+
"Opens a checkpoint timeline. Use \u2191/\u2193 to navigate, Enter to rewind,",
|
|
3163
|
+
"Esc to cancel. The session is reverted to the selected checkpoint",
|
|
3164
|
+
"and conversation history is truncated \u2014 LLM continues fresh.",
|
|
3165
|
+
"",
|
|
3166
|
+
"If a checkpoint index is provided the timeline is skipped and",
|
|
3167
|
+
"rewind happens immediately."
|
|
3168
|
+
].join("\n"),
|
|
3169
|
+
async run(args) {
|
|
3170
|
+
const idx = Number.parseInt(args.trim(), 10);
|
|
3171
|
+
if (!Number.isNaN(idx) && idx >= 0) {
|
|
3172
|
+
handleRewindTo(idx);
|
|
3173
|
+
return {};
|
|
3174
|
+
}
|
|
3175
|
+
const s = stateRef.current;
|
|
3176
|
+
if (s.checkpoints.length === 0) {
|
|
3177
|
+
return { message: "No checkpoints in this session yet." };
|
|
3178
|
+
}
|
|
3179
|
+
dispatch({ type: "rewindOverlayOpen" });
|
|
3180
|
+
return {};
|
|
3181
|
+
}
|
|
3182
|
+
};
|
|
3183
|
+
slashRegistry.register(cmd);
|
|
3184
|
+
return () => {
|
|
3185
|
+
slashRegistry.unregister("rewind");
|
|
3186
|
+
};
|
|
3187
|
+
}, [slashRegistry, handleRewindTo]);
|
|
3031
3188
|
useEffect(() => {
|
|
3032
3189
|
const cmd = {
|
|
3033
3190
|
name: "goal",
|
|
@@ -3264,6 +3421,20 @@ function App({
|
|
|
3264
3421
|
}
|
|
3265
3422
|
});
|
|
3266
3423
|
});
|
|
3424
|
+
const offBudgetWarning = events.on("subagent.budget_warning", (e) => {
|
|
3425
|
+
const lbl = labelFor(e.subagentId);
|
|
3426
|
+
dispatch({ type: "fleetBudgetWarning", id: e.subagentId, kind: e.kind, used: e.used, limit: e.limit });
|
|
3427
|
+
dispatch({
|
|
3428
|
+
type: "addEntry",
|
|
3429
|
+
entry: {
|
|
3430
|
+
kind: "subagent",
|
|
3431
|
+
agentLabel: lbl.label,
|
|
3432
|
+
agentColor: lbl.color,
|
|
3433
|
+
icon: "\u26A1",
|
|
3434
|
+
text: `hitting ${e.kind} limit (${e.used}/${e.limit}) \u2014 extending`
|
|
3435
|
+
}
|
|
3436
|
+
});
|
|
3437
|
+
});
|
|
3267
3438
|
const offTool = events.on("subagent.tool_executed", (e) => {
|
|
3268
3439
|
if (director) return;
|
|
3269
3440
|
dispatch({
|
|
@@ -3280,9 +3451,34 @@ function App({
|
|
|
3280
3451
|
offSpawned();
|
|
3281
3452
|
offStarted();
|
|
3282
3453
|
offCompleted();
|
|
3454
|
+
offBudgetWarning();
|
|
3283
3455
|
offTool();
|
|
3284
3456
|
};
|
|
3285
3457
|
}, [events, director]);
|
|
3458
|
+
useEffect(() => {
|
|
3459
|
+
const offCheckpoint = events.on("checkpoint.written", (e) => {
|
|
3460
|
+
dispatch({
|
|
3461
|
+
type: "checkpointReceived",
|
|
3462
|
+
cp: {
|
|
3463
|
+
promptIndex: e.promptIndex,
|
|
3464
|
+
promptPreview: e.promptPreview,
|
|
3465
|
+
ts: e.ts,
|
|
3466
|
+
fileCount: e.fileCount
|
|
3467
|
+
}
|
|
3468
|
+
});
|
|
3469
|
+
});
|
|
3470
|
+
const offRewound = events.on("session.rewound", (_e) => {
|
|
3471
|
+
dispatch({ type: "sessionRewound", toPromptIndex: 0 });
|
|
3472
|
+
dispatch({ type: "clearHistory" });
|
|
3473
|
+
if (onClearHistory) {
|
|
3474
|
+
onClearHistory(dispatch);
|
|
3475
|
+
}
|
|
3476
|
+
});
|
|
3477
|
+
return () => {
|
|
3478
|
+
offCheckpoint();
|
|
3479
|
+
offRewound();
|
|
3480
|
+
};
|
|
3481
|
+
}, [events, onClearHistory]);
|
|
3286
3482
|
useEffect(() => {
|
|
3287
3483
|
if (!fleetStreamController) return;
|
|
3288
3484
|
fleetStreamController.enabled = state.streamFleet;
|
|
@@ -3513,7 +3709,7 @@ function App({
|
|
|
3513
3709
|
return () => {
|
|
3514
3710
|
process.off("SIGINT", onSigint);
|
|
3515
3711
|
};
|
|
3516
|
-
}, [
|
|
3712
|
+
}, [director]);
|
|
3517
3713
|
const handleKey = async (input, key) => {
|
|
3518
3714
|
if (state.status === "aborting" && state.interrupts === 0) return;
|
|
3519
3715
|
if (state.confirmQueue.length > 0) return;
|
|
@@ -3840,6 +4036,15 @@ function App({
|
|
|
3840
4036
|
entry: { kind: "warn", text: `Hit max iterations (${result.iterations}).` }
|
|
3841
4037
|
});
|
|
3842
4038
|
}
|
|
4039
|
+
if (result.status === "done" && result.finalText && onSDDOutput) {
|
|
4040
|
+
try {
|
|
4041
|
+
const sddMessages = await onSDDOutput(result.finalText);
|
|
4042
|
+
for (const msg of sddMessages) {
|
|
4043
|
+
dispatch({ type: "addEntry", entry: { kind: "info", text: msg } });
|
|
4044
|
+
}
|
|
4045
|
+
} catch {
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
3843
4048
|
if (tokenCounter && before) {
|
|
3844
4049
|
const after = tokenCounter.total();
|
|
3845
4050
|
const costAfter = tokenCounter.estimateCost().total;
|
|
@@ -3895,6 +4100,14 @@ function App({
|
|
|
3895
4100
|
if (ctxModel && ctxModel !== liveModel) setLiveModel(ctxModel);
|
|
3896
4101
|
const ctxProviderId = agent.ctx.provider?.id;
|
|
3897
4102
|
if (ctxProviderId && ctxProviderId !== liveProvider) setLiveProvider(ctxProviderId);
|
|
4103
|
+
if (getYolo) {
|
|
4104
|
+
const currentYolo = getYolo();
|
|
4105
|
+
if (currentYolo !== yoloLive) setYoloLive(currentYolo);
|
|
4106
|
+
}
|
|
4107
|
+
if (getAutonomy) {
|
|
4108
|
+
const currentAutonomy = getAutonomy();
|
|
4109
|
+
if (currentAutonomy !== autonomyLive) setAutonomyLive(currentAutonomy);
|
|
4110
|
+
}
|
|
3898
4111
|
if (res?.exit) {
|
|
3899
4112
|
exit();
|
|
3900
4113
|
onExit(0);
|
|
@@ -3926,6 +4139,15 @@ function App({
|
|
|
3926
4139
|
const builder = builderRef.current;
|
|
3927
4140
|
if (!builder) return;
|
|
3928
4141
|
const steering = state.steeringPending;
|
|
4142
|
+
const sddContext = getSDDContext?.();
|
|
4143
|
+
if (sddContext && trimmed) {
|
|
4144
|
+
builder.appendText(`[SDD SESSION ACTIVE]
|
|
4145
|
+
${sddContext}
|
|
4146
|
+
|
|
4147
|
+
---
|
|
4148
|
+
User message:
|
|
4149
|
+
`);
|
|
4150
|
+
}
|
|
3929
4151
|
if (trimmed) {
|
|
3930
4152
|
const toAppend = steering ? buildSteeringPreamble(state.steerSnapshot, trimmed) : trimmed;
|
|
3931
4153
|
builder.appendText(toAppend);
|
|
@@ -4030,6 +4252,16 @@ function App({
|
|
|
4030
4252
|
hint: state.modelPicker.hint
|
|
4031
4253
|
}
|
|
4032
4254
|
) : null,
|
|
4255
|
+
state.rewindOverlay ? /* @__PURE__ */ jsx(
|
|
4256
|
+
CheckpointTimeline,
|
|
4257
|
+
{
|
|
4258
|
+
checkpoints: state.rewindOverlay.checkpoints,
|
|
4259
|
+
selected: state.rewindOverlay.selected,
|
|
4260
|
+
onSelect: (i) => dispatch({ type: "rewindOverlayMove", delta: i - state.rewindOverlay.selected }),
|
|
4261
|
+
onConfirm: (i) => handleRewindTo(state.rewindOverlay.checkpoints[i].promptIndex),
|
|
4262
|
+
onClose: () => dispatch({ type: "rewindOverlayClose" })
|
|
4263
|
+
}
|
|
4264
|
+
) : null,
|
|
4033
4265
|
state.confirmQueue.length > 0 && (() => {
|
|
4034
4266
|
const head = state.confirmQueue[0];
|
|
4035
4267
|
let resolved = false;
|
|
@@ -4056,7 +4288,8 @@ function App({
|
|
|
4056
4288
|
tokenCounter,
|
|
4057
4289
|
hint: renderRunningTools(state.runningTools) || state.hint,
|
|
4058
4290
|
queueCount: state.queue.length,
|
|
4059
|
-
yolo,
|
|
4291
|
+
yolo: yoloLive,
|
|
4292
|
+
autonomy: autonomyLive,
|
|
4060
4293
|
elapsedMs,
|
|
4061
4294
|
todos,
|
|
4062
4295
|
plan: planCounts ?? void 0,
|
|
@@ -4179,7 +4412,7 @@ async function runTui(opts) {
|
|
|
4179
4412
|
let instance;
|
|
4180
4413
|
try {
|
|
4181
4414
|
instance = render(
|
|
4182
|
-
|
|
4415
|
+
React2.createElement(App, {
|
|
4183
4416
|
agent: opts.agent,
|
|
4184
4417
|
slashRegistry: opts.slashRegistry,
|
|
4185
4418
|
attachments: opts.attachments,
|
|
@@ -4191,6 +4424,8 @@ async function runTui(opts) {
|
|
|
4191
4424
|
banner: opts.banner ?? true,
|
|
4192
4425
|
queueStore: opts.queueStore,
|
|
4193
4426
|
yolo: opts.yolo,
|
|
4427
|
+
getYolo: opts.getYolo,
|
|
4428
|
+
getAutonomy: opts.getAutonomy,
|
|
4194
4429
|
appVersion: opts.appVersion,
|
|
4195
4430
|
provider: opts.provider,
|
|
4196
4431
|
family: opts.family,
|
|
@@ -4204,7 +4439,10 @@ async function runTui(opts) {
|
|
|
4204
4439
|
onClearHistory: opts.onClearHistory ? (dispatch) => opts.onClearHistory(dispatch) : void 0,
|
|
4205
4440
|
fleetStreamController: opts.fleetStreamController,
|
|
4206
4441
|
initialGoal: opts.initialGoal,
|
|
4207
|
-
initialAsk: opts.initialAsk
|
|
4442
|
+
initialAsk: opts.initialAsk,
|
|
4443
|
+
getSDDContext: opts.getSDDContext,
|
|
4444
|
+
onSDDOutput: opts.onSDDOutput,
|
|
4445
|
+
sessionsDir: opts.sessionsDir
|
|
4208
4446
|
}),
|
|
4209
4447
|
{ exitOnCtrlC: false }
|
|
4210
4448
|
);
|