@wrongstack/tui 0.73.1 → 0.82.6

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.js CHANGED
@@ -1,11 +1,11 @@
1
- import { writeErr, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, buildChildEnv } from '@wrongstack/core';
1
+ import { writeErr, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, buildChildEnv } from '@wrongstack/core';
2
2
  export { buildGoalPreamble } from '@wrongstack/core';
3
- import { Box, Text, render, useApp, useStdout, measureElement, Static, useInput, useStdin } from 'ink';
4
- import React5, { useState, useEffect, useReducer, useRef, useMemo, useCallback, useLayoutEffect } from 'react';
3
+ import { Box, Text, render, useApp, useStdout, Static, useInput, useStdin } from 'ink';
4
+ import React6, { useState, useEffect, useReducer, useRef, useMemo, useCallback } from 'react';
5
5
  import * as fs2 from 'fs/promises';
6
6
  import * as path2 from 'path';
7
7
  import { routeImagesForModel } from '@wrongstack/runtime/vision';
8
- import { getProcessRegistry } from '@wrongstack/tools';
8
+ import { getIndexState, onIndexStateChange, getProcessRegistry } from '@wrongstack/tools';
9
9
  import { readClipboardImage } from '@wrongstack/runtime/clipboard';
10
10
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
11
11
  import { spawn } from 'child_process';
@@ -34,8 +34,34 @@ var theme = Object.freeze({
34
34
  diffAddBg: "greenBright",
35
35
  diffDelBg: "redBright"
36
36
  });
37
+ function expectDefined(value) {
38
+ if (value === null || value === void 0) {
39
+ throw new Error("Expected value to be defined");
40
+ }
41
+ return value;
42
+ }
43
+ var MODE_ICONS = {
44
+ teach: "\u{1F9D1}\u200D\u{1F3EB}",
45
+ brief: "\u26A1",
46
+ "code-reviewer": "\u{1F50D}",
47
+ "bug-hunter": "\u{1F41B}",
48
+ "security-scanner": "\u{1F6E1}\uFE0F",
49
+ "refactor-planner": "\u{1F527}",
50
+ architect: "\u{1F3D7}\uFE0F",
51
+ debugger: "\u{1FAB2}",
52
+ test: "\u{1F9EA}",
53
+ document: "\u{1F4DD}",
54
+ "skill-creator": "\u{1F6E0}\uFE0F"
55
+ };
56
+ function modeIcon(label) {
57
+ if (!label) return "";
58
+ const icon = MODE_ICONS[label] ?? "\u25AA";
59
+ return `${icon} ${label}`;
60
+ }
37
61
  var COMPACT_THRESHOLD = 50;
38
62
  var COMFORTABLE_THRESHOLD = 90;
63
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
64
+ var SPINNER_INTERVAL_MS = 250;
39
65
  function StatusBar({
40
66
  model,
41
67
  version,
@@ -45,7 +71,7 @@ function StatusBar({
45
71
  queueCount = 0,
46
72
  yolo = false,
47
73
  autonomy,
48
- elapsedMs,
74
+ startedAt,
49
75
  todos,
50
76
  plan,
51
77
  fleet,
@@ -58,7 +84,9 @@ function StatusBar({
58
84
  processCount,
59
85
  hiddenItems,
60
86
  eternalStage,
61
- goalSummary
87
+ goalSummary,
88
+ indexState,
89
+ modeLabel
62
90
  }) {
63
91
  const { stdout } = useStdout();
64
92
  const [termWidth, setTermWidth] = useState(stdout?.columns ?? 90);
@@ -76,8 +104,26 @@ function StatusBar({
76
104
  const usage = tokenCounter?.total();
77
105
  const cost = tokenCounter?.estimateCost();
78
106
  const cache2 = tokenCounter?.cacheStats();
107
+ const [elapsedMs, setElapsedMs] = useState(startedAt ? Date.now() - startedAt : 0);
108
+ useEffect(() => {
109
+ if (startedAt == null) return;
110
+ const t = setInterval(() => setElapsedMs(Date.now() - startedAt), 1e3);
111
+ return () => clearInterval(t);
112
+ }, [startedAt]);
113
+ const [spinnerIdx, setSpinnerIdx] = useState(0);
114
+ useEffect(() => {
115
+ if (state === "idle" || state === "aborting") return;
116
+ const t = setInterval(
117
+ () => setSpinnerIdx((n) => (n + 1) % SPINNER_FRAMES.length),
118
+ SPINNER_INTERVAL_MS
119
+ );
120
+ return () => clearInterval(t);
121
+ }, [state]);
122
+ const spinner = expectDefined(SPINNER_FRAMES[spinnerIdx]);
79
123
  const { label: stateLabel, color: stateColor } = stateChip(state, fleet?.running ?? 0);
80
- const hasSecondLine = yolo || autonomy && autonomy !== "off" || elapsedMs !== void 0 || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0 || goalSummary !== null && goalSummary !== void 0;
124
+ const statePrefix = state === "idle" || state === "aborting" ? "\u25CF" : spinner;
125
+ const thinking = state === "running" || state === "streaming";
126
+ const hasSecondLine = yolo || autonomy && autonomy !== "off" || startedAt != null || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0 || goalSummary !== null && goalSummary !== void 0 || !!modeLabel;
81
127
  const fleetHasActivity = fleet && (fleet.running > 0 || fleet.idle > 0 || fleet.pending > 0 || fleet.completed > 0) || subagentCount > 0;
82
128
  const hasBrainActivity = !!brain && brain.state !== "idle";
83
129
  const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity || hasBrainActivity;
@@ -95,8 +141,8 @@ function StatusBar({
95
141
  /* @__PURE__ */ jsx(Box, { flexDirection: "row", gap: 2, children: isCompact ? (
96
142
  // Ultra-compact: state · model
97
143
  /* @__PURE__ */ jsxs(Fragment, { children: [
98
- /* @__PURE__ */ jsxs(Text, { color: stateColor, children: [
99
- "\u25CF",
144
+ thinking ? /* @__PURE__ */ jsx(WaveText, { text: `${statePrefix}${stateLabel}`, phase: spinnerIdx }) : /* @__PURE__ */ jsxs(Text, { color: stateColor, children: [
145
+ statePrefix,
100
146
  stateLabel
101
147
  ] }),
102
148
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
@@ -115,8 +161,9 @@ function StatusBar({
115
161
  ] }),
116
162
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" })
117
163
  ] }) : null,
118
- /* @__PURE__ */ jsxs(Text, { color: stateColor, children: [
119
- "\u25CF ",
164
+ thinking ? /* @__PURE__ */ jsx(WaveText, { text: `${statePrefix} ${stateLabel}`, phase: spinnerIdx }) : /* @__PURE__ */ jsxs(Text, { color: stateColor, children: [
165
+ statePrefix,
166
+ " ",
120
167
  stateLabel
121
168
  ] }),
122
169
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
@@ -170,6 +217,15 @@ function StatusBar({
170
217
  hint ? /* @__PURE__ */ jsxs(Fragment, { children: [
171
218
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
172
219
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint })
220
+ ] }) : null,
221
+ indexState?.indexing ? /* @__PURE__ */ jsxs(Fragment, { children: [
222
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
223
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
224
+ "\u2699 indexing ",
225
+ indexState.currentFile,
226
+ "/",
227
+ indexState.totalFiles
228
+ ] })
173
229
  ] }) : null
174
230
  ] })
175
231
  ) }),
@@ -201,14 +257,14 @@ function StatusBar({
201
257
  ] })
202
258
  ] }) : null,
203
259
  projectName ? /* @__PURE__ */ jsxs(Fragment, { children: [
204
- yolo || elapsedMs !== void 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
260
+ yolo || startedAt != null ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
205
261
  /* @__PURE__ */ jsxs(Text, { color: "blue", children: [
206
262
  "\u{1F4C1} ",
207
263
  projectName
208
264
  ] })
209
265
  ] }) : null,
210
266
  goalSummary ? /* @__PURE__ */ jsxs(Fragment, { children: [
211
- yolo || elapsedMs !== void 0 || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
267
+ yolo || startedAt != null || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
212
268
  /* @__PURE__ */ jsxs(
213
269
  Text,
214
270
  {
@@ -227,8 +283,12 @@ function StatusBar({
227
283
  }
228
284
  )
229
285
  ] }) : null,
286
+ modeLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
287
+ yolo || autonomy && autonomy !== "off" || eternalStage || startedAt != null || projectName || goalSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
288
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: modeIcon(modeLabel) })
289
+ ] }) : null,
230
290
  git ? /* @__PURE__ */ jsxs(Fragment, { children: [
231
- yolo || elapsedMs !== void 0 || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
291
+ yolo || startedAt != null || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
232
292
  /* @__PURE__ */ jsxs(Text, { children: [
233
293
  /* @__PURE__ */ jsxs(Text, { color: "magenta", children: [
234
294
  "\u2387 ",
@@ -248,7 +308,7 @@ function StatusBar({
248
308
  ] }) : null
249
309
  ] })
250
310
  ] }) : null
251
- ] }) : null,
311
+ ] }) : /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }),
252
312
  hasThirdLine ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
253
313
  todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) ? /* @__PURE__ */ jsxs(Text, { children: [
254
314
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "todos " }),
@@ -322,13 +382,13 @@ function StatusBar({
322
382
  todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
323
383
  /* @__PURE__ */ jsx(BrainChip, { brain })
324
384
  ] }) : null
325
- ] }) : null,
385
+ ] }) : /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }),
326
386
  fleetAgents && fleetAgents.length > 0 ? /* @__PURE__ */ jsx(Box, { flexDirection: "row", gap: 2, children: fleetAgents.map((a, i) => (
327
387
  // biome-ignore lint/suspicious/noArrayIndexKey: agent list is stable per render
328
388
  /* @__PURE__ */ jsxs(Text, { children: [
329
389
  /* @__PURE__ */ jsx(Text, { color: a.color, bold: true, children: a.label }),
330
390
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
331
- /* @__PURE__ */ jsx(Text, { color: a.running ? "yellow" : void 0, dimColor: !a.running, children: a.running ? "\u25B6" : "\xB7" }),
391
+ /* @__PURE__ */ jsx(Text, { dimColor: !a.running, ...a.running ? { color: "yellow" } : {}, children: a.running ? "\u25B6" : "\xB7" }),
332
392
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
333
393
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtElapsed(a.elapsedMs) }),
334
394
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
@@ -348,7 +408,7 @@ function StatusBar({
348
408
  ] })
349
409
  ] }) : null
350
410
  ] }, i)
351
- )) }) : null
411
+ )) }) : /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) })
352
412
  ]
353
413
  }
354
414
  );
@@ -457,6 +517,26 @@ function stateChip(state, fleetRunning) {
457
517
  if (state === "aborting") return { label: "aborting\u2026", color: "yellow" };
458
518
  return { label: "thinking\u2026", color: "green" };
459
519
  }
520
+ var WAVE_COLORS = [
521
+ "#ff5f5f",
522
+ "#ff8f3f",
523
+ "#ffd23f",
524
+ "#bce84a",
525
+ "#6bcb77",
526
+ "#3dd9c0",
527
+ "#3fb6ff",
528
+ "#5f8bff",
529
+ "#845ef7",
530
+ "#b15bff",
531
+ "#f06595",
532
+ "#ff5fa2"
533
+ ];
534
+ function WaveText({ text, phase }) {
535
+ return /* @__PURE__ */ jsx(Text, { bold: true, children: Array.from(text).map((ch, i) => (
536
+ // biome-ignore lint/suspicious/noArrayIndexKey: glyph order is positional and re-rendered each tick
537
+ /* @__PURE__ */ jsx(Text, { color: WAVE_COLORS[(i + phase) % WAVE_COLORS.length], children: ch }, i)
538
+ )) });
539
+ }
460
540
  var FILLED = "\u2588";
461
541
  var EMPTY = "\u2591";
462
542
  function renderProgress(ratio, width) {
@@ -518,7 +598,7 @@ function bucketActivity(recentTools, now, bins = 12, binMs = 2e3) {
518
598
  let idx = Math.floor((t.at - windowStart) / binMs);
519
599
  if (idx < 0) idx = 0;
520
600
  if (idx >= bins) idx = bins - 1;
521
- out[idx]++;
601
+ out[idx] = (out[idx] ?? 0) + 1;
522
602
  }
523
603
  return out;
524
604
  }
@@ -647,7 +727,7 @@ function FleetMonitor({
647
727
  // biome-ignore lint/suspicious/noArrayIndexKey: timeline is rebuilt per render
648
728
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
649
729
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${fmtElapsed(Math.max(0, nowTick - ev.at))} ago`.padEnd(10) }),
650
- /* @__PURE__ */ jsx(Text, { color: ev.color, children: ev.icon }),
730
+ /* @__PURE__ */ jsx(Text, { ...ev.color ? { color: ev.color } : {}, children: ev.icon }),
651
731
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: ev.text })
652
732
  ] }, i)
653
733
  )) }) : null
@@ -779,10 +859,12 @@ function ContextBar({
779
859
  function AgentsMonitor({
780
860
  entries,
781
861
  totalCost,
862
+ leaderCost = 0,
782
863
  totalTokens,
783
864
  nowTick
784
865
  }) {
785
866
  const all = Object.values(entries);
867
+ const grandCost = leaderCost + totalCost;
786
868
  const live = selectLiveAgents(all, nowTick);
787
869
  const running = live.filter((e) => e.status === "running").length;
788
870
  const totalDone = all.filter((e) => e.status === "success").length;
@@ -824,9 +906,17 @@ function AgentsMonitor({
824
906
  fmtTokens2(totalTokens.output),
825
907
  "\u2193"
826
908
  ] }) : null,
827
- /* @__PURE__ */ jsxs(Text, { color: "green", children: [
909
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "total" }),
910
+ /* @__PURE__ */ jsxs(Text, { color: "green", bold: true, children: [
828
911
  "$",
829
- totalCost.toFixed(3)
912
+ grandCost.toFixed(3)
913
+ ] }),
914
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
915
+ "(leader $",
916
+ leaderCost.toFixed(3),
917
+ " \xB7 fleet $",
918
+ totalCost.toFixed(3),
919
+ ")"
830
920
  ] }),
831
921
  hiddenIdle > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
832
922
  "\xB7 ",
@@ -869,6 +959,10 @@ function AgentsMonitor({
869
959
  e.extensions && e.extensions > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
870
960
  "\u26A1\xD7",
871
961
  e.extensions
962
+ ] }) : null,
963
+ e.cost > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
964
+ "$",
965
+ e.cost.toFixed(3)
872
966
  ] }) : null
873
967
  ] }),
874
968
  e.status === "running" && e.currentTool ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, paddingLeft: 2, children: [
@@ -954,14 +1048,14 @@ function AutonomyPicker({
954
1048
  selected,
955
1049
  hint
956
1050
  }) {
957
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
1051
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [
958
1052
  /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Autonomy Mode \u2501\u2501" }),
959
1053
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel \xB7 Ctrl+C exit" }),
960
1054
  options.map((opt, i) => /* @__PURE__ */ jsxs(
961
1055
  Text,
962
1056
  {
963
- color: i === selected ? opt.color : void 0,
964
1057
  inverse: i === selected,
1058
+ ...i === selected ? { color: opt.color } : {},
965
1059
  children: [
966
1060
  i === selected ? "\u203A " : " ",
967
1061
  /* @__PURE__ */ jsx(Text, { bold: true, children: opt.label.padEnd(12) }),
@@ -1077,7 +1171,7 @@ function CheckpointTimeline({
1077
1171
  onConfirm(selected);
1078
1172
  }
1079
1173
  });
1080
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", padding: 1, children: [
1174
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, children: [
1081
1175
  /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
1082
1176
  /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u27F2 Session Rewind" }),
1083
1177
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2014 \u2191/\u2193 navigate \xB7 Enter rewind \xB7 Esc cancel" })
@@ -1086,8 +1180,8 @@ function CheckpointTimeline({
1086
1180
  const isSelected = i === selected;
1087
1181
  const label = `[${cp.promptIndex}] ${cp.promptPreview}`;
1088
1182
  return /* @__PURE__ */ jsxs(Box, { children: [
1089
- /* @__PURE__ */ jsx(Text, { color: isSelected ? "cyan" : void 0, bold: isSelected, children: isSelected ? "\u25B8 " : " " }),
1090
- /* @__PURE__ */ jsx(Text, { color: isSelected ? "cyan" : void 0, bold: isSelected, children: label }),
1183
+ /* @__PURE__ */ jsx(Text, { bold: isSelected, ...isSelected ? { color: "cyan" } : {}, children: isSelected ? "\u25B8 " : " " }),
1184
+ /* @__PURE__ */ jsx(Text, { bold: isSelected, ...isSelected ? { color: "cyan" } : {}, children: label }),
1091
1185
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1092
1186
  " ",
1093
1187
  new Date(cp.ts).toLocaleTimeString()
@@ -1132,7 +1226,7 @@ function hasDiff(input) {
1132
1226
  }
1133
1227
  function renderDiffLine(line) {
1134
1228
  const prefix = line.startsWith("+") ? "green" : line.startsWith("-") ? "red" : line.startsWith("@@") ? "cyan" : void 0;
1135
- return /* @__PURE__ */ jsxs(Text, { color: prefix, children: [
1229
+ return /* @__PURE__ */ jsxs(Text, { ...prefix ? { color: prefix } : {}, children: [
1136
1230
  line,
1137
1231
  "\n"
1138
1232
  ] }, line);
@@ -1147,7 +1241,7 @@ function ConfirmPrompt({
1147
1241
  suggestedPattern,
1148
1242
  onDecision
1149
1243
  }) {
1150
- React5.useEffect(() => {
1244
+ React6.useEffect(() => {
1151
1245
  writeOut("\x07");
1152
1246
  }, []);
1153
1247
  useInput((input2, _key) => {
@@ -1176,27 +1270,88 @@ function ConfirmPrompt({
1176
1270
  inputSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: inputSummary }) : null,
1177
1271
  showDiff && diff ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginY: 1, children: renderDiff(diff) }) : null,
1178
1272
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
1179
- /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { children: buttonLabels(suggestedPattern).map((l) => /* @__PURE__ */ jsxs(React5.Fragment, { children: [
1273
+ /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { children: buttonLabels(suggestedPattern).map((l) => /* @__PURE__ */ jsxs(React6.Fragment, { children: [
1180
1274
  /* @__PURE__ */ jsx(Text, { bold: true, color: BUTTON_COLOR[l.decision], children: l.bracket }),
1181
1275
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: l.rest })
1182
1276
  ] }, l.decision)) }) })
1183
1277
  ] });
1184
1278
  }
1279
+ function EnhancePanel({
1280
+ original,
1281
+ refined,
1282
+ delayMs,
1283
+ onDecision
1284
+ }) {
1285
+ const totalSecs = Math.max(1, Math.ceil(delayMs / 1e3));
1286
+ const [remaining, setRemaining] = React6.useState(totalSecs);
1287
+ const decideRef = React6.useRef(onDecision);
1288
+ decideRef.current = onDecision;
1289
+ React6.useEffect(() => {
1290
+ const id = setInterval(() => {
1291
+ setRemaining((r) => {
1292
+ if (r <= 1) {
1293
+ clearInterval(id);
1294
+ decideRef.current("refined");
1295
+ return 0;
1296
+ }
1297
+ return r - 1;
1298
+ });
1299
+ }, 1e3);
1300
+ return () => clearInterval(id);
1301
+ }, []);
1302
+ useInput((input, key) => {
1303
+ if (key.return) {
1304
+ onDecision("refined");
1305
+ } else if (key.escape) {
1306
+ onDecision("original");
1307
+ } else if (input?.toLowerCase() === "e") {
1308
+ onDecision("edit");
1309
+ }
1310
+ });
1311
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
1312
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
1313
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u2728 Refined request" }),
1314
+ /* @__PURE__ */ jsx(Text, { children: " " }),
1315
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1316
+ "\u2014 sending in ",
1317
+ remaining,
1318
+ "s"
1319
+ ] })
1320
+ ] }),
1321
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
1322
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "original: " }),
1323
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: original })
1324
+ ] }),
1325
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
1326
+ /* @__PURE__ */ jsx(Text, { color: "green", children: "refined: " }),
1327
+ /* @__PURE__ */ jsx(Text, { color: "white", children: refined })
1328
+ ] }),
1329
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
1330
+ /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsxs(Text, { children: [
1331
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "green", children: "[Enter]" }),
1332
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " send \xB7 " }),
1333
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: "[Esc]" }),
1334
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " use original \xB7 " }),
1335
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "[e]" }),
1336
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "dit" })
1337
+ ] }) })
1338
+ ] });
1339
+ }
1185
1340
  function FilePicker({ query, matches, selected }) {
1186
1341
  if (matches.length === 0) {
1187
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1342
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1188
1343
  "@",
1189
1344
  query,
1190
1345
  " \u2014 no matches"
1191
1346
  ] }) });
1192
1347
  }
1193
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
1348
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
1194
1349
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1195
1350
  "@",
1196
1351
  query || "\u2026",
1197
1352
  " \u2014 \u2191/\u2193 select, Enter attach, Esc cancel"
1198
1353
  ] }),
1199
- matches.map((m, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? "cyan" : void 0, inverse: i === selected, children: [
1354
+ matches.map((m, i) => /* @__PURE__ */ jsxs(Text, { inverse: i === selected, ...i === selected ? { color: "cyan" } : {}, children: [
1200
1355
  i === selected ? "\u203A " : " ",
1201
1356
  highlight(m)
1202
1357
  ] }, m))
@@ -1270,9 +1425,8 @@ function FleetPanel({
1270
1425
  ] }) : null
1271
1426
  ] });
1272
1427
  }
1273
- function helpSections(opts) {
1428
+ function helpSections() {
1274
1429
  const nav = [];
1275
- if (opts.managed) nav.push({ keys: "PgUp/PgDn", desc: "scroll chat history" });
1276
1430
  nav.push(
1277
1431
  { keys: "\u2191/\u2193", desc: "previous / next input (empty prompt)" },
1278
1432
  { keys: "?", desc: "open this help (empty prompt)" }
@@ -1285,6 +1439,9 @@ function helpSections(opts) {
1285
1439
  { keys: "Ctrl+F / F2", desc: "fleet orchestration monitor" },
1286
1440
  { keys: "Ctrl+G / F3", desc: "agents live monitor" },
1287
1441
  { keys: "Ctrl+T / F4", desc: "worktree monitor" },
1442
+ { keys: "F5", desc: "autonomy settings (also Ctrl+S)" },
1443
+ { keys: "F6", desc: "todos monitor overlay" },
1444
+ { keys: "F7", desc: "queue panel" },
1288
1445
  { keys: "Esc", desc: "close the open monitor / overlay" }
1289
1446
  ]
1290
1447
  },
@@ -1312,10 +1469,8 @@ function helpSections(opts) {
1312
1469
  }
1313
1470
  ];
1314
1471
  }
1315
- function HelpOverlay({
1316
- managed
1317
- }) {
1318
- const sections = helpSections({ managed });
1472
+ function HelpOverlay() {
1473
+ const sections = helpSections();
1319
1474
  const keyWidth = Math.max(...sections.flatMap((s2) => s2.entries.map((e) => e.keys.length)), 0);
1320
1475
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1321
1476
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
@@ -1780,6 +1935,12 @@ function detectLang(fenceInfo) {
1780
1935
  }
1781
1936
 
1782
1937
  // src/markdown-table.ts
1938
+ function expectDefined2(value) {
1939
+ if (value === null || value === void 0) {
1940
+ throw new Error("Expected value to be defined");
1941
+ }
1942
+ return value;
1943
+ }
1783
1944
  var ROW_RE = /^\s*\|.*\|\s*$/;
1784
1945
  var SEP_RE = /^\s*\|[\s\-:|]+\|\s*$/;
1785
1946
  function detectTable(lines, start) {
@@ -1860,16 +2021,17 @@ function computeWidths(allRows, cols, maxWidth, sepWidths) {
1860
2021
  for (const row of allRows) {
1861
2022
  for (let c = 0; c < cols; c++) {
1862
2023
  const cell = row[c] ?? "";
1863
- const w = longestWord(cell);
1864
- const total = strWidth(cell);
1865
- natural[c] = Math.max(natural[c], w, total);
2024
+ const stripped = stripInlineMarkers(cell);
2025
+ const w = longestWord(stripped);
2026
+ const total = strWidth(stripped);
2027
+ natural[c] = Math.max(expectDefined2(natural[c]), w, total);
1866
2028
  }
1867
2029
  }
1868
2030
  if (sepWidths) {
1869
2031
  for (let c = 0; c < cols && c < sepWidths.length; c++) {
1870
2032
  const sepW = sepWidths[c];
1871
2033
  if (sepW != null) {
1872
- natural[c] = Math.max(natural[c], sepW);
2034
+ natural[c] = Math.max(expectDefined2(natural[c]), sepW);
1873
2035
  }
1874
2036
  }
1875
2037
  }
@@ -1881,24 +2043,90 @@ function computeWidths(allRows, cols, maxWidth, sepWidths) {
1881
2043
  let maxIdx = -1;
1882
2044
  let maxVal = MIN_COL_WIDTH;
1883
2045
  for (let i = 0; i < cols; i++) {
1884
- const w = widths[i];
2046
+ const w = expectDefined2(widths[i]);
1885
2047
  if (w > maxVal) {
1886
2048
  maxVal = w;
1887
2049
  maxIdx = i;
1888
2050
  }
1889
2051
  }
1890
2052
  if (maxIdx < 0) break;
1891
- widths[maxIdx]--;
2053
+ widths[maxIdx] = (widths[maxIdx] ?? 0) - 1;
1892
2054
  sum--;
1893
2055
  }
1894
2056
  return widths;
1895
2057
  }
1896
2058
  var MIN_COL_WIDTH = 4;
2059
+ var LIGATURE_PAIRS = [
2060
+ ["-", ">"],
2061
+ // → arrow
2062
+ ["<", "-"],
2063
+ // ←
2064
+ ["=", ">"],
2065
+ // ⇒
2066
+ ["<", "="],
2067
+ // ≤
2068
+ [">", "="],
2069
+ // ≥
2070
+ ["!", "="],
2071
+ // ≠
2072
+ ["=", "="],
2073
+ // equality (some fonts)
2074
+ ["~", ">"],
2075
+ // ⇝
2076
+ ["<", "~"]
2077
+ // ⇜
2078
+ ];
2079
+ var ZWSP = "\u200B";
2080
+ function breakLigatures(text) {
2081
+ if (!/[-<=>!~]/.test(text)) return text;
2082
+ let result = "";
2083
+ for (let i = 0; i < text.length; i++) {
2084
+ result += text[i];
2085
+ if (i + 1 >= text.length) break;
2086
+ for (const [a, b] of LIGATURE_PAIRS) {
2087
+ if (text[i] === a && text[i + 1] === b) {
2088
+ result += ZWSP;
2089
+ break;
2090
+ }
2091
+ }
2092
+ }
2093
+ return result;
2094
+ }
1897
2095
  function strWidth(s2) {
1898
2096
  let width = 0;
1899
- for (const cp of s2) {
1900
- const code = cp.codePointAt(0);
2097
+ const len = s2.length;
2098
+ let i = 0;
2099
+ while (i < len) {
2100
+ if (s2[i] === "\x1B" && i + 1 < len && s2[i + 1] === "[") {
2101
+ i += 2;
2102
+ while (i < len && s2[i] !== "m") i++;
2103
+ if (i < len) i++;
2104
+ continue;
2105
+ }
2106
+ const code = expectDefined2(s2.codePointAt(i));
2107
+ const cpLen = code > 65535 ? 2 : 1;
2108
+ if (code === 8205 || // ZWJ — Zero Width Joiner (emoji sequences)
2109
+ code === 8203 || // ZWSP — Zero Width Space
2110
+ code === 8204 || // ZWNJ — Zero Width Non-Joiner
2111
+ code === 8206 || // LRM — Left-to-Right Mark
2112
+ code === 8207 || // RLM — Right-to-Left Mark
2113
+ code === 8288 || // WJ — Word Joiner
2114
+ code === 65279 || // BOM / ZWNBSP
2115
+ code >= 65024 && code <= 65039 || // Variation Selectors 1–16
2116
+ code >= 917760 && code <= 917999) {
2117
+ i += cpLen;
2118
+ continue;
2119
+ }
1901
2120
  if (code < 32 || code >= 127 && code < 160) {
2121
+ i += cpLen;
2122
+ continue;
2123
+ }
2124
+ if (code >= 768 && code <= 879 || // Combining Diacritical Marks
2125
+ code >= 6832 && code <= 6911 || // Combining Diacritical Marks Extended
2126
+ code >= 7616 && code <= 7679 || // Combining Diacritical Marks Supplement
2127
+ code >= 8400 && code <= 8447 || // Combining Diacritical Marks for Symbols
2128
+ code >= 65056 && code <= 65071) {
2129
+ i += cpLen;
1902
2130
  continue;
1903
2131
  }
1904
2132
  if (code >= 126976 || // Supplementary Pictographs (U+1F000-U+1FFFF)
@@ -1911,11 +2139,11 @@ function strWidth(s2) {
1911
2139
  code >= 9664 && code <= 9726 || // More Geometric Shapes (includes ▶)
1912
2140
  code >= 9984 && code <= 10175) {
1913
2141
  width += 2;
2142
+ i += cpLen;
1914
2143
  continue;
1915
2144
  }
1916
2145
  if (code >= 4352 && code <= 4447 || // Hangul Jamo
1917
- code === 9001 || // LEFT-POINTING ANGLE BRACKET
1918
- code === 9002 || // RIGHT-POINTING ANGLE BRACKET
2146
+ code === 9001 || code === 9002 || // Angle brackets
1919
2147
  code >= 11904 && code <= 12350 || // CJK Radicals Supplement
1920
2148
  code >= 12352 && code <= 42191 || // Hiragana, Katakana, CJK
1921
2149
  code >= 44032 && code <= 55203 || // Hangul Syllables
@@ -1927,9 +2155,11 @@ function strWidth(s2) {
1927
2155
  code >= 131072 && code <= 196605 || // CJK Extension B+
1928
2156
  code >= 196608 && code <= 262141) {
1929
2157
  width += 2;
2158
+ i += cpLen;
1930
2159
  continue;
1931
2160
  }
1932
2161
  width += 1;
2162
+ i += cpLen;
1933
2163
  }
1934
2164
  return width;
1935
2165
  }
@@ -1944,8 +2174,22 @@ function longestWord(s2) {
1944
2174
  function border(left, mid, right, widths) {
1945
2175
  return left + widths.map((w) => "\u2500".repeat(w + 2)).join(mid) + right;
1946
2176
  }
2177
+ function stripInlineMarkers(text) {
2178
+ return text.replace(/\*\*(.+?)\*\*/g, "$1").replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "$1").replace(/`(.+?)`/g, "$1").replace(/~~(.+?)~~/g, "$1");
2179
+ }
2180
+ var ANSI_BOLD = "\x1B[1m";
2181
+ var ANSI_RESET = "\x1B[22m";
2182
+ var ANSI_DIM = "\x1B[2m";
2183
+ var ANSI_CYAN = "\x1B[36m";
2184
+ var ANSI_STRIKE = "\x1B[9m";
2185
+ var ANSI_RESET_ALL = "\x1B[0m";
2186
+ function applyInlineAnsi(text) {
2187
+ return text.replace(/\*\*(.+?)\*\*/g, `${ANSI_BOLD}$1${ANSI_RESET}`).replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, `${ANSI_DIM}$1${ANSI_RESET_ALL}`).replace(/`(.+?)`/g, `${ANSI_CYAN}$1${ANSI_RESET_ALL}`).replace(/~~(.+?)~~/g, `${ANSI_STRIKE}$1${ANSI_RESET_ALL}`);
2188
+ }
1947
2189
  function renderRow(cells, widths, aligns) {
1948
- const wrapped = cells.map((c, i) => wrapCell(c, widths[i] ?? MIN_COL_WIDTH));
2190
+ const styled = cells.map((c) => applyInlineAnsi(c));
2191
+ const safe = styled.map((c) => breakLigatures(c));
2192
+ const wrapped = safe.map((c, i) => wrapCell(c, widths[i] ?? MIN_COL_WIDTH));
1949
2193
  const height = Math.max(1, ...wrapped.map((w) => w.length));
1950
2194
  const out = [];
1951
2195
  for (let line = 0; line < height; line++) {
@@ -2102,10 +2346,10 @@ function InlineLine({ tokens, dim }) {
2102
2346
  Text,
2103
2347
  {
2104
2348
  color: t.code ? theme.accent : "white",
2105
- bold: t.bold,
2106
- italic: t.italic,
2107
- strikethrough: t.strike,
2108
- dimColor: dim,
2349
+ bold: Boolean(t.bold),
2350
+ italic: Boolean(t.italic),
2351
+ strikethrough: Boolean(t.strike),
2352
+ dimColor: Boolean(dim),
2109
2353
  children: t.text
2110
2354
  },
2111
2355
  j
@@ -2730,7 +2974,22 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
2730
2974
  }
2731
2975
  var MAX_STREAM_DISPLAY_CHARS = 480;
2732
2976
  var MAX_STREAM_LINES = 8;
2733
- var ToolStreamBox = React5.memo(function ToolStreamBox2({
2977
+ function streamBoxRows(text, maxLines, contentWidth) {
2978
+ const trunc = (line) => line.length > contentWidth ? `${line.slice(0, contentWidth - 1)}\u2026` : line;
2979
+ const lines = text.split("\n");
2980
+ const totalLines = lines.length;
2981
+ const hidden = Math.max(0, totalLines - maxLines);
2982
+ const rows = [];
2983
+ if (hidden > 0) {
2984
+ rows.push({ text: ` \u2026 ${hidden} more line${hidden === 1 ? "" : "s"} above`, italic: true });
2985
+ for (const line of lines.slice(totalLines - (maxLines - 1))) rows.push({ text: trunc(line) });
2986
+ } else {
2987
+ for (let i = 0; i < maxLines - totalLines; i++) rows.push({ text: "" });
2988
+ for (const line of lines) rows.push({ text: trunc(line) });
2989
+ }
2990
+ return rows;
2991
+ }
2992
+ React6.memo(function ToolStreamBox2({
2734
2993
  name,
2735
2994
  text,
2736
2995
  startedAt,
@@ -2742,11 +3001,10 @@ var ToolStreamBox = React5.memo(function ToolStreamBox2({
2742
3001
  return () => clearInterval(t);
2743
3002
  }, []);
2744
3003
  const elapsedMs = Date.now() - startedAt;
2745
- const lines = text.split("\n");
2746
- const totalLines = lines.length;
3004
+ const totalLines = text.split("\n").length;
2747
3005
  const hidden = Math.max(0, totalLines - MAX_STREAM_LINES);
2748
- const visible = hidden > 0 ? lines.slice(hidden) : lines;
2749
3006
  const contentWidth = Math.max(20, Math.min(termWidth - 4, 100));
3007
+ const rows = streamBoxRows(text, MAX_STREAM_LINES, contentWidth);
2750
3008
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 0, children: [
2751
3009
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
2752
3010
  /* @__PURE__ */ jsx(Text, { color: theme.warn, children: "\u25C6 " }),
@@ -2754,13 +3012,10 @@ var ToolStreamBox = React5.memo(function ToolStreamBox2({
2754
3012
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u23F1 ${fmtDuration(elapsedMs)}` }),
2755
3013
  hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` (${totalLines} lines, showing last ${MAX_STREAM_LINES})` }) : null
2756
3014
  ] }),
2757
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
2758
- hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: ` \u2026 ${hidden} more line${hidden === 1 ? "" : "s"} above` }) : null,
2759
- visible.map((line, i) => {
2760
- const trimmed = line.length > contentWidth ? `${line.slice(0, contentWidth - 1)}\u2026` : line;
2761
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: trimmed || " " }, i);
2762
- })
2763
- ] })
3015
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginLeft: 2, children: rows.map((r, i) => (
3016
+ // biome-ignore lint/suspicious/noArrayIndexKey: fixed-height block, index is the row
3017
+ /* @__PURE__ */ jsx(Text, { dimColor: true, italic: Boolean(r.italic), children: r.text || " " }, i)
3018
+ )) })
2764
3019
  ] });
2765
3020
  });
2766
3021
  function tailForDisplay(text, maxChars) {
@@ -2806,9 +3061,15 @@ function CodeBlock({
2806
3061
  // biome-ignore lint/suspicious/noArrayIndexKey: code lines are positional
2807
3062
  /* @__PURE__ */ jsxs(Text, { children: [
2808
3063
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${String(i + 1).padStart(gutterW, " ")} ` }),
2809
- tokens.length === 0 ? " " : tokens.map((t, j) => (
2810
- // biome-ignore lint/suspicious/noArrayIndexKey: token order is stable per line
2811
- /* @__PURE__ */ jsx(Text, { color: t.color, dimColor: t.dim, bold: t.bold, children: t.text }, j)
3064
+ tokens.length === 0 ? " " : tokens.map((t, j) => /* @__PURE__ */ jsx(
3065
+ Text,
3066
+ {
3067
+ dimColor: Boolean(t.dim),
3068
+ bold: Boolean(t.bold),
3069
+ ...t.color ? { color: t.color } : {},
3070
+ children: t.text
3071
+ },
3072
+ j
2812
3073
  ))
2813
3074
  ] }, i)
2814
3075
  )),
@@ -2962,7 +3223,22 @@ function AssistantBody({
2962
3223
  )
2963
3224
  ) });
2964
3225
  }
2965
- function AssistantTail({ text }) {
3226
+ var ASSISTANT_TAIL_LINES = 8;
3227
+ function assistantTailRows(text, tailLines, contentWidth) {
3228
+ const tail = text.split("\n").slice(-tailLines);
3229
+ const rows = [];
3230
+ for (let i = 0; i < tailLines - tail.length; i++) rows.push("");
3231
+ for (const line of tail) {
3232
+ rows.push(line.length > contentWidth ? `${line.slice(0, contentWidth - 1)}\u2026` : line);
3233
+ }
3234
+ return rows;
3235
+ }
3236
+ function AssistantTail({
3237
+ text,
3238
+ termWidth
3239
+ }) {
3240
+ const contentWidth = Math.max(20, termWidth - 3);
3241
+ const rows = assistantTailRows(text, ASSISTANT_TAIL_LINES, contentWidth);
2966
3242
  return /* @__PURE__ */ jsxs(
2967
3243
  Box,
2968
3244
  {
@@ -2979,7 +3255,10 @@ function AssistantTail({ text }) {
2979
3255
  /* @__PURE__ */ jsx(Text, { bold: true, color: theme.assistant, children: "ASSISTANT" }),
2980
3256
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " (streaming\u2026)" })
2981
3257
  ] }),
2982
- /* @__PURE__ */ jsx(Text, { color: "white", children: text })
3258
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: rows.map((r, i) => (
3259
+ // biome-ignore lint/suspicious/noArrayIndexKey: fixed-height block, index is the row
3260
+ /* @__PURE__ */ jsx(Text, { color: "white", children: r || " " }, i)
3261
+ )) })
2983
3262
  ]
2984
3263
  }
2985
3264
  );
@@ -3050,7 +3329,7 @@ function brainRiskColor(risk) {
3050
3329
  function assistantContentWidth(termWidth) {
3051
3330
  return Math.max(20, termWidth - MESSAGE_PANEL_CHROME_WIDTH);
3052
3331
  }
3053
- var Entry = React5.memo(function Entry2({
3332
+ var Entry = React6.memo(function Entry2({
3054
3333
  entry,
3055
3334
  termWidth
3056
3335
  }) {
@@ -3142,8 +3421,8 @@ var Entry = React5.memo(function Entry2({
3142
3421
  /* @__PURE__ */ jsx(
3143
3422
  Text,
3144
3423
  {
3145
- color: !entry.ok || line.startsWith("!") ? "red" : void 0,
3146
3424
  dimColor: entry.ok && !line.startsWith("!"),
3425
+ ...!entry.ok || line.startsWith("!") ? { color: "red" } : {},
3147
3426
  children: line
3148
3427
  }
3149
3428
  )
@@ -3276,19 +3555,9 @@ function History({ entries, streamingText, toolStream }) {
3276
3555
  }, [stdout]);
3277
3556
  const termWidth = termSize.columns;
3278
3557
  const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
3279
- const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
3280
3558
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3281
3559
  /* @__PURE__ */ jsx(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth }) }, entry.id) }),
3282
- tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail }) : null,
3283
- toolTail ? /* @__PURE__ */ jsx(
3284
- ToolStreamBox,
3285
- {
3286
- name: toolStream.name,
3287
- text: toolTail,
3288
- startedAt: toolStream.startedAt,
3289
- termWidth
3290
- }
3291
- ) : null
3560
+ tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail, termWidth }) : null
3292
3561
  ] });
3293
3562
  }
3294
3563
 
@@ -3394,6 +3663,12 @@ function layoutInputRows(prompt, value, cursor, width) {
3394
3663
  if (row.length > 0 || rows.length === 0) rows.push(row);
3395
3664
  return rows;
3396
3665
  }
3666
+ function expectDefined3(value) {
3667
+ if (value === null || value === void 0) {
3668
+ throw new Error("Expected value to be defined");
3669
+ }
3670
+ return value;
3671
+ }
3397
3672
  function renderRow2(cells, rowKey, promptColor) {
3398
3673
  const out = [];
3399
3674
  let run = "";
@@ -3442,6 +3717,19 @@ function isHomeEnd(data) {
3442
3717
  return "end";
3443
3718
  return null;
3444
3719
  }
3720
+ function isBackspaceOrDelete(data) {
3721
+ if (data === "\x7F" || data === "\b") return "backspace";
3722
+ if (data === "\x1B[3~") return "delete";
3723
+ return null;
3724
+ }
3725
+ function parseMouseWheel(data) {
3726
+ const m = data.match(new RegExp(`^${String.fromCharCode(27)}\\[<(\\d+);(\\d+);(\\d+)([Mm])$`, "u"));
3727
+ if (!m) return null;
3728
+ const cb = Number.parseInt(expectDefined3(m[1]), 10);
3729
+ if (cb === 64) return 1;
3730
+ if (cb === 65) return -1;
3731
+ return null;
3732
+ }
3445
3733
  var EMPTY_KEY = {
3446
3734
  upArrow: false,
3447
3735
  downArrow: false,
@@ -3477,15 +3765,29 @@ function Input({
3477
3765
  if (!stdin || disabled) return;
3478
3766
  const handleData = (data) => {
3479
3767
  const s2 = data.toString();
3480
- const kind = isHomeEnd(s2);
3481
- if (kind === "home") {
3768
+ const homeEnd = isHomeEnd(s2);
3769
+ if (homeEnd === "home") {
3482
3770
  onKey("", { ...EMPTY_KEY, home: true });
3483
3771
  return;
3484
3772
  }
3485
- if (kind === "end") {
3773
+ if (homeEnd === "end") {
3486
3774
  onKey("", { ...EMPTY_KEY, end: true });
3487
3775
  return;
3488
3776
  }
3777
+ const bsdel = isBackspaceOrDelete(s2);
3778
+ if (bsdel === "backspace") {
3779
+ onKey("", { ...EMPTY_KEY, backspace: true });
3780
+ return;
3781
+ }
3782
+ if (bsdel === "delete") {
3783
+ onKey("", { ...EMPTY_KEY, delete: true });
3784
+ return;
3785
+ }
3786
+ const wheelDelta = parseMouseWheel(s2);
3787
+ if (wheelDelta !== null) {
3788
+ onKey("", { ...EMPTY_KEY, wheelDeltaY: wheelDelta });
3789
+ return;
3790
+ }
3489
3791
  const fn = fnKey(s2);
3490
3792
  if (fn !== null) onKey("", { ...EMPTY_KEY, fn });
3491
3793
  };
@@ -3520,45 +3822,6 @@ function Input({
3520
3822
  hint ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint }) : null
3521
3823
  ] });
3522
3824
  }
3523
- function hintsFor(ctx) {
3524
- if (ctx.confirm) {
3525
- return [
3526
- { key: "y", label: "yes" },
3527
- { key: "n", label: "no" },
3528
- { key: "a", label: "always" },
3529
- { key: "d", label: "deny" }
3530
- ];
3531
- }
3532
- if (ctx.picker) {
3533
- return [
3534
- { key: "\u2191\u2193", label: "move" },
3535
- { key: "\u21B5", label: "select" },
3536
- { key: "Esc", label: "cancel" }
3537
- ];
3538
- }
3539
- if (ctx.monitor) {
3540
- return [
3541
- { key: "Esc", label: "close" },
3542
- { key: "^F", label: "fleet" },
3543
- { key: "^G", label: "agents" },
3544
- { key: "^T", label: "worktrees" }
3545
- ];
3546
- }
3547
- const base = [{ key: "?", label: "help" }];
3548
- if (ctx.managed) base.push({ key: "PgUp/PgDn", label: "scroll" });
3549
- base.push({ key: "^G", label: "agents" }, { key: "^C", label: "stop" });
3550
- return base;
3551
- }
3552
- function KeyHintBar({ context }) {
3553
- const hints = hintsFor(context);
3554
- return /* @__PURE__ */ jsx(Box, { flexDirection: "row", paddingX: 1, children: hints.map((h, i) => (
3555
- // biome-ignore lint/suspicious/noArrayIndexKey: hints are positional + stable
3556
- /* @__PURE__ */ jsxs(Box, { flexDirection: "row", marginRight: 2, children: [
3557
- /* @__PURE__ */ jsx(Text, { color: theme.accent, children: h.key }),
3558
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${h.label}` })
3559
- ] }, i)
3560
- )) });
3561
- }
3562
3825
  function fmtElapsed2(ms) {
3563
3826
  if (ms < 1e3) return `${ms}ms`;
3564
3827
  if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
@@ -3585,13 +3848,16 @@ function fmtRecentMessage(message) {
3585
3848
  const text = message.text.replace(/\s+/g, " ");
3586
3849
  return text.length > 48 ? `${text.slice(0, 47)}...` : text;
3587
3850
  }
3588
- var LiveActivityStrip = React5.memo(function LiveActivityStrip2({
3851
+ var LiveActivityStrip = React6.memo(function LiveActivityStrip2({
3589
3852
  entries,
3590
3853
  nowTick,
3591
3854
  maxRows = 4
3592
3855
  }) {
3593
3856
  const running = Object.values(entries).filter((e) => e.status === "running").sort((a, b) => a.startedAt - b.startedAt).slice(0, maxRows);
3594
- if (running.length === 0) return null;
3857
+ if (running.length === 0) {
3858
+ if (Object.keys(entries).length === 0) return null;
3859
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingX: 1, children: Array.from({ length: maxRows }, (_, slot) => slot).map((slot) => /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }, `empty-${slot}`)) });
3860
+ }
3595
3861
  const now = Date.now();
3596
3862
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
3597
3863
  running.map((e) => {
@@ -3661,10 +3927,10 @@ function ModelPicker({
3661
3927
  hint
3662
3928
  }) {
3663
3929
  if (step === "provider") {
3664
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
3930
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3665
3931
  /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Switch model \u2014 Step 1/2: Pick provider \u2501\u2501" }),
3666
3932
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel \xB7 Ctrl+C exit" }),
3667
- providerOptions.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(no providers with keys \u2014 add one via `wstack auth`)" }) : providerOptions.map((p, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? "cyan" : void 0, inverse: i === selected, children: [
3933
+ providerOptions.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(no providers with keys \u2014 add one via `wstack auth`)" }) : providerOptions.map((p, i) => /* @__PURE__ */ jsxs(Text, { inverse: i === selected, ...i === selected ? { color: "cyan" } : {}, children: [
3668
3934
  i === selected ? "\u203A " : " ",
3669
3935
  /* @__PURE__ */ jsx(Text, { bold: true, children: p.id.padEnd(28) }),
3670
3936
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
@@ -3686,7 +3952,7 @@ function ModelPicker({
3686
3952
  const { start, end } = getVisibleWindow(selected, total);
3687
3953
  const visibleItems = filteredOptions.slice(start, end);
3688
3954
  const searchHint = searchQuery ? ` | filter:"${searchQuery}" \u2192 ${total} match${total === 1 ? "" : "es"}` : total > MAX_VISIBLE ? ` (${total} models \u2014 type to filter)` : "";
3689
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
3955
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3690
3956
  /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
3691
3957
  "\u2501\u2501 Switch model \u2014 Step 2/2: Pick model ",
3692
3958
  "(",
@@ -3707,8 +3973,8 @@ function ModelPicker({
3707
3973
  return /* @__PURE__ */ jsxs(
3708
3974
  Text,
3709
3975
  {
3710
- color: absoluteIndex === selected ? "cyan" : void 0,
3711
3976
  inverse: absoluteIndex === selected,
3977
+ ...absoluteIndex === selected ? { color: "cyan" } : {},
3712
3978
  children: [
3713
3979
  absoluteIndex === selected ? "\u203A " : " ",
3714
3980
  id
@@ -3901,166 +4167,406 @@ function PhasePanel({ phases, nowTick }) {
3901
4167
  }
3902
4168
  );
3903
4169
  }
3904
- var MAX_MOUNTED = 500;
3905
- function scrollbarThumb(rows, offset, total) {
3906
- const scrollable = total > rows;
3907
- if (!scrollable) return { top: 0, size: rows, scrollable: false };
3908
- const windowTop = Math.max(0, total - rows - offset);
3909
- const size = Math.max(1, Math.round(rows / total * rows));
3910
- const rawTop = Math.round(windowTop / total * rows);
3911
- const top = Math.max(0, Math.min(rawTop, rows - size));
3912
- return { top, size, scrollable: true };
3913
- }
3914
- function Scrollbar({
3915
- rows,
3916
- offset,
3917
- total
3918
- }) {
3919
- const { top: thumbTop, size: thumbSize, scrollable } = scrollbarThumb(rows, offset, total);
3920
- const cells = [];
3921
- for (let i = 0; i < rows; i++) {
3922
- cells.push(i >= thumbTop && i < thumbTop + thumbSize ? "\u2588" : "\u2502");
3923
- }
3924
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginLeft: 1, flexShrink: 0, children: cells.map((c, i) => /* @__PURE__ */ jsx(
3925
- Text,
3926
- {
3927
- color: scrollable ? theme.accent : void 0,
3928
- dimColor: !scrollable || c === "\u2502",
3929
- children: c
3930
- },
3931
- i
3932
- )) });
3933
- }
3934
- function ScrollableHistory({
3935
- entries,
3936
- streamingText,
3937
- toolStream,
3938
- scrollOffset,
3939
- viewportRows,
3940
- totalLines,
3941
- onMeasure
3942
- }) {
4170
+ function QueuePanel({ items }) {
3943
4171
  const { stdout } = useStdout();
3944
- const [termWidth, setTermWidth] = useState(stdout?.columns ?? 80);
3945
- useEffect(() => {
3946
- const onResize = () => setTermWidth(stdout?.columns ?? 80);
3947
- process.stdout.on("resize", onResize);
3948
- return () => {
3949
- process.stdout.off("resize", onResize);
3950
- };
3951
- }, [stdout]);
3952
- const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
3953
- const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
3954
- const hiddenCount = Math.max(0, entries.length - MAX_MOUNTED);
3955
- const shown = hiddenCount > 0 ? entries.slice(-MAX_MOUNTED) : entries;
3956
- const contentRef = useRef(null);
3957
- const lastReported = useRef(-1);
3958
- useLayoutEffect(() => {
3959
- const node = contentRef.current;
3960
- if (!node) return;
3961
- const { height } = measureElement(node);
3962
- if (height !== lastReported.current) {
3963
- lastReported.current = height;
3964
- onMeasure(height);
3965
- }
3966
- }, [onMeasure]);
3967
- const vp = Math.max(1, viewportRows);
3968
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
3969
- /* @__PURE__ */ jsx(
3970
- Box,
3971
- {
3972
- flexDirection: "column",
3973
- flexGrow: 1,
3974
- height: vp,
3975
- overflowY: "hidden",
3976
- justifyContent: "flex-end",
3977
- children: /* @__PURE__ */ jsxs(
3978
- Box,
3979
- {
3980
- ref: contentRef,
3981
- flexDirection: "column",
3982
- marginBottom: Math.max(0, scrollOffset),
3983
- flexShrink: 0,
3984
- children: [
3985
- hiddenCount > 0 ? /* @__PURE__ */ jsx(Box, { flexShrink: 0, children: /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: ` \u2191 ${hiddenCount} earlier ${hiddenCount === 1 ? "entry" : "entries"} (scroll lives in this session; full log on disk)` }) }) : null,
3986
- shown.map((entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, flexShrink: 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth }) }, entry.id)),
3987
- tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail }) : null,
3988
- toolTail ? /* @__PURE__ */ jsx(
3989
- ToolStreamBox,
3990
- {
3991
- name: toolStream.name,
3992
- text: toolTail,
3993
- startedAt: toolStream.startedAt,
3994
- termWidth
3995
- }
3996
- ) : null
3997
- ]
3998
- }
3999
- )
4000
- }
4001
- ),
4002
- /* @__PURE__ */ jsx(Scrollbar, { rows: vp, offset: Math.max(0, scrollOffset), total: totalLines })
4172
+ const w = stdout?.columns ?? 80;
4173
+ const h = stdout?.rows ?? 24;
4174
+ const avail = Math.max(10, Math.floor(w * 0.3) - 4);
4175
+ const labelMax = Math.max(4, avail - 7);
4176
+ const trunc = (s2) => {
4177
+ if (s2.length <= labelMax) return s2;
4178
+ return s2.slice(0, labelMax - 1) + "\u2026";
4179
+ };
4180
+ const OVERHEAD = 7;
4181
+ const maxVisible = Math.max(4, h - OVERHEAD);
4182
+ const visible = items.slice(0, maxVisible);
4183
+ const overflow = items.length - visible.length;
4184
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
4185
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
4186
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "QUEUE" }),
4187
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: items.length }),
4188
+ overflow > 0 ? /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
4189
+ "+",
4190
+ overflow
4191
+ ] }) : null,
4192
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 F7 / Esc to close" })
4193
+ ] }) }),
4194
+ items.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No queued messages" }) : /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
4195
+ visible.map((item, i) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", flexShrink: 0, children: [
4196
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4197
+ String(i + 1).padStart(2),
4198
+ "."
4199
+ ] }),
4200
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4201
+ " ",
4202
+ trunc(item.displayText)
4203
+ ] })
4204
+ ] }, item.id)),
4205
+ overflow > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", flexShrink: 0, marginTop: 0, children: [
4206
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2026" }),
4207
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4208
+ " +",
4209
+ overflow,
4210
+ " more"
4211
+ ] })
4212
+ ] }) : null
4213
+ ] })
4003
4214
  ] });
4004
4215
  }
4005
4216
  var DELAY_PRESETS_MS = [0, 15e3, 3e4, 45e3, 6e4, 12e4];
4006
4217
  var SETTINGS_MODES = ["off", "suggest", "auto"];
4218
+ var LOG_LEVELS = ["error", "warn", "info", "debug", "trace"];
4219
+ var AUDIT_LEVELS = ["minimal", "standard", "full"];
4220
+ var COMPACTOR_STRATEGIES = ["hybrid", "intelligent", "selective"];
4221
+ var MAX_ITERATIONS_PRESETS = [100, 200, 500, 1e3, 0];
4007
4222
  function formatSettingsDelay(ms) {
4008
4223
  if (ms === 0) return "disabled";
4009
4224
  if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
4010
4225
  return `${Math.round(ms / 1e3)}s`;
4011
4226
  }
4227
+ function formatMaxIterations(n) {
4228
+ if (n === 0) return "unlimited";
4229
+ return String(n);
4230
+ }
4012
4231
  var MODE_DESC = {
4013
4232
  off: "Agent stops after each turn (normal)",
4014
4233
  suggest: "Shows next-step suggestions after each turn",
4015
4234
  auto: "Self-driving \u2014 agent continues automatically"
4016
4235
  };
4236
+ var SETTINGS_FIELD_COUNT = 19;
4017
4237
  function SettingsPicker({
4018
4238
  field,
4019
4239
  mode,
4020
4240
  delayMs,
4241
+ titleAnimation,
4242
+ yolo,
4243
+ streamFleet,
4244
+ chime,
4245
+ confirmExit,
4246
+ nextPrediction,
4247
+ featureMcp,
4248
+ featurePlugins,
4249
+ featureMemory,
4250
+ featureSkills,
4251
+ featureModelsRegistry,
4252
+ contextAutoCompact,
4253
+ contextStrategy,
4254
+ logLevel,
4255
+ auditLevel,
4256
+ indexOnStart,
4257
+ maxIterations,
4021
4258
  hint
4022
4259
  }) {
4260
+ const boolVal = (v) => v ? "on" : "off";
4023
4261
  const rows = [
4262
+ // ── Autonomy ──
4263
+ { section: "Autonomy" },
4024
4264
  { label: "Default autonomy mode", value: mode, detail: MODE_DESC[mode] },
4025
4265
  {
4026
4266
  label: "Auto-proceed delay",
4027
4267
  value: formatSettingsDelay(delayMs),
4028
4268
  detail: "Wait before auto-continuing in auto mode"
4269
+ },
4270
+ // ── UX ──
4271
+ { section: "UX" },
4272
+ {
4273
+ label: "Terminal title animation",
4274
+ value: boolVal(titleAnimation),
4275
+ detail: "Animated window/tab title with status"
4276
+ },
4277
+ {
4278
+ label: "YOLO mode",
4279
+ value: boolVal(yolo),
4280
+ detail: "Skip all confirmation prompts"
4281
+ },
4282
+ {
4283
+ label: "Stream fleet to chat",
4284
+ value: boolVal(streamFleet),
4285
+ detail: "Show subagent messages in main chat"
4286
+ },
4287
+ {
4288
+ label: "Completion chime",
4289
+ value: boolVal(chime),
4290
+ detail: "Play a sound when agent finishes"
4291
+ },
4292
+ {
4293
+ label: "Confirm before exit",
4294
+ value: boolVal(confirmExit),
4295
+ detail: "Ask for confirmation on Ctrl+C exit"
4296
+ },
4297
+ {
4298
+ label: "Next-step prediction",
4299
+ value: boolVal(nextPrediction),
4300
+ detail: "Show LLM-predicted next steps (/next)"
4301
+ },
4302
+ // ── Features ──
4303
+ { section: "Features" },
4304
+ {
4305
+ label: "MCP servers",
4306
+ value: boolVal(featureMcp),
4307
+ detail: "Load MCP servers from config"
4308
+ },
4309
+ {
4310
+ label: "Plugins",
4311
+ value: boolVal(featurePlugins),
4312
+ detail: "Load npm plugins from config"
4313
+ },
4314
+ {
4315
+ label: "Memory",
4316
+ value: boolVal(featureMemory),
4317
+ detail: "Enable remember/forget tools"
4318
+ },
4319
+ {
4320
+ label: "Skills",
4321
+ value: boolVal(featureSkills),
4322
+ detail: "Discover and load skills from disk"
4323
+ },
4324
+ {
4325
+ label: "Models registry",
4326
+ value: boolVal(featureModelsRegistry),
4327
+ detail: "Fetch models.dev catalog at startup"
4328
+ },
4329
+ // ── Context ──
4330
+ { section: "Context" },
4331
+ {
4332
+ label: "Auto-compact",
4333
+ value: boolVal(contextAutoCompact),
4334
+ detail: "Auto-compact context when thresholds crossed"
4335
+ },
4336
+ {
4337
+ label: "Compactor strategy",
4338
+ value: contextStrategy,
4339
+ detail: "hybrid (fast) | intelligent (LLM) | selective"
4340
+ },
4341
+ // ── Logging ──
4342
+ { section: "Logging" },
4343
+ {
4344
+ label: "Log level",
4345
+ value: logLevel,
4346
+ detail: "Console log verbosity"
4347
+ },
4348
+ // ── Session ──
4349
+ { section: "Session" },
4350
+ {
4351
+ label: "Audit level",
4352
+ value: auditLevel,
4353
+ detail: "minimal | standard | full (large)"
4354
+ },
4355
+ // ── Indexing ──
4356
+ { section: "Indexing" },
4357
+ {
4358
+ label: "Index on session start",
4359
+ value: boolVal(indexOnStart),
4360
+ detail: "Run incremental index at session start"
4361
+ },
4362
+ // ── Tools ──
4363
+ { section: "Tools" },
4364
+ {
4365
+ label: "Max iterations",
4366
+ value: formatMaxIterations(maxIterations),
4367
+ detail: "100\u20131000 or unlimited (0)"
4029
4368
  }
4030
4369
  ];
4031
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
4370
+ const fieldRowIndex = [];
4371
+ for (let i = 0; i < rows.length; i++) {
4372
+ if (!rows[i]?.section) fieldRowIndex.push(i);
4373
+ }
4374
+ const VISIBLE_FIELDS = 8;
4375
+ const totalFields = fieldRowIndex.length;
4376
+ const windowStart = totalFields <= VISIBLE_FIELDS ? 0 : Math.max(0, Math.min(field - Math.floor(VISIBLE_FIELDS / 2), totalFields - VISIBLE_FIELDS));
4377
+ const windowEnd = Math.min(windowStart + VISIBLE_FIELDS, totalFields);
4378
+ const hasAbove = windowStart > 0;
4379
+ const hasBelow = windowEnd < totalFields;
4380
+ const sectionFields = [];
4381
+ let curHeader = -1;
4382
+ for (let i = 0; i < rows.length; i++) {
4383
+ if (rows[i]?.section) curHeader = i;
4384
+ else if (curHeader >= 0) {
4385
+ const fieldIdx = fieldRowIndex.indexOf(i);
4386
+ if (fieldIdx === -1) continue;
4387
+ const entry = sectionFields.find((s2) => s2.headerIdx === curHeader);
4388
+ if (entry) {
4389
+ entry.fieldEnd = fieldIdx + 1;
4390
+ } else {
4391
+ sectionFields.push({ headerIdx: curHeader, fieldStart: fieldIdx, fieldEnd: fieldIdx + 1 });
4392
+ }
4393
+ }
4394
+ }
4395
+ const shouldShowSection = (headerIdx) => {
4396
+ const sec = sectionFields.find((s2) => s2.headerIdx === headerIdx);
4397
+ if (!sec) return false;
4398
+ return sec.fieldStart < windowEnd && sec.fieldEnd > windowStart;
4399
+ };
4400
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
4032
4401
  /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Settings \u2501\u2501" }),
4033
4402
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 field \xB7 \u2190/\u2192 change \xB7 Enter save \xB7 Esc cancel" }),
4034
- rows.map((row, i) => /* @__PURE__ */ jsxs(Text, { color: i === field ? "yellow" : void 0, inverse: i === field, children: [
4035
- i === field ? "\u203A " : " ",
4036
- /* @__PURE__ */ jsx(Text, { bold: true, children: row.label.padEnd(22) }),
4037
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: String(row.value).padEnd(10) }),
4038
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: row.detail })
4039
- ] }, row.label)),
4403
+ hasAbove ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2191 ${windowStart} field${windowStart === 1 ? "" : "s"} above` }) : null,
4404
+ rows.map((row, i) => {
4405
+ const fieldAtRow = fieldRowIndex.indexOf(i);
4406
+ if (fieldAtRow === -1) {
4407
+ if (shouldShowSection(i)) {
4408
+ return /* @__PURE__ */ jsxs(Text, { bold: true, color: "green", children: [
4409
+ "\u2500\u2500 ",
4410
+ row.section,
4411
+ " \u2500\u2500"
4412
+ ] }, `section-${row.section ?? i}`);
4413
+ }
4414
+ return null;
4415
+ }
4416
+ if (fieldAtRow < windowStart || fieldAtRow >= windowEnd) return null;
4417
+ const selected = fieldAtRow === field;
4418
+ return /* @__PURE__ */ jsxs(Text, { inverse: selected, ...selected ? { color: "yellow" } : {}, children: [
4419
+ selected ? "\u203A " : " ",
4420
+ /* @__PURE__ */ jsx(Text, { bold: true, children: (row.label ?? "").padEnd(26) }),
4421
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: String(row.value ?? "").padEnd(12) }),
4422
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: row.detail ?? "" })
4423
+ ] }, `row-${row.label ?? fieldAtRow}`);
4424
+ }),
4425
+ hasBelow ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2193 ${totalFields - windowEnd} field${totalFields - windowEnd === 1 ? "" : "s"} below` }) : null,
4040
4426
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Persisted to ~/.wrongstack/config.json" }),
4041
4427
  hint ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) : null
4042
4428
  ] });
4043
4429
  }
4044
4430
  function SlashMenu({ query, matches, selected }) {
4045
4431
  const placeholder = query ? `/${query}` : "/";
4046
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
4432
+ const rows = [];
4433
+ let lastCategory = "";
4434
+ for (let i = 0; i < matches.length; i++) {
4435
+ const m = matches[i];
4436
+ if (m.category !== lastCategory) {
4437
+ lastCategory = m.category;
4438
+ rows.push({ type: "header", category: m.category });
4439
+ }
4440
+ rows.push({ type: "item", match: m, index: i });
4441
+ }
4442
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
4047
4443
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4048
4444
  placeholder || "/",
4049
4445
  " \u2014 \u2191/\u2193 select, Enter dispatch, Tab autocomplete, Esc close"
4050
4446
  ] }),
4051
- matches.map((m, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? "cyan" : void 0, inverse: i === selected, children: [
4052
- i === selected ? "\u203A " : " ",
4053
- /* @__PURE__ */ jsx(Text, { bold: true, children: m.name }),
4054
- m.argsHint ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4447
+ rows.map((row) => {
4448
+ if (row.type === "header") {
4449
+ return /* @__PURE__ */ jsxs(Text, { bold: true, color: "yellow", dimColor: true, children: [
4450
+ " ",
4451
+ row.category
4452
+ ] }, `cat-${row.category}`);
4453
+ }
4454
+ const { match: m, index: i } = row;
4455
+ return /* @__PURE__ */ jsxs(Text, { inverse: i === selected, ...i === selected ? { color: "cyan" } : {}, children: [
4456
+ i === selected ? "\u203A " : " ",
4457
+ /* @__PURE__ */ jsx(Text, { bold: true, children: m.name }),
4458
+ m.argsHint ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4459
+ " ",
4460
+ m.argsHint
4461
+ ] }) : null,
4462
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4463
+ " \u2014 ",
4464
+ m.description
4465
+ ] })
4466
+ ] }, m.name);
4467
+ }),
4468
+ matches.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No matching commands" })
4469
+ ] });
4470
+ }
4471
+ function TodosMonitor({ todos }) {
4472
+ const { stdout } = useStdout();
4473
+ const done = todos.filter((t) => t.status === "completed").length;
4474
+ const inProgress = todos.filter((t) => t.status === "in_progress").length;
4475
+ const pending = todos.filter((t) => t.status === "pending").length;
4476
+ const w = stdout?.columns ?? 80;
4477
+ const twoCols = w >= 100;
4478
+ const mid = Math.ceil(todos.length / 2);
4479
+ const colWidth = twoCols ? Math.floor((w - 8) / 2) : w - 6;
4480
+ const trunc = (text, maxLen) => {
4481
+ if (text.length <= maxLen) return text;
4482
+ return text.slice(0, maxLen - 1) + "\u2026";
4483
+ };
4484
+ const renderRow3 = (t, idx) => {
4485
+ const num = String(idx + 1).padStart(2);
4486
+ const label = t.status === "in_progress" && t.activeForm ? t.activeForm : t.content;
4487
+ const display = trunc(label, colWidth - 8);
4488
+ if (t.status === "completed") {
4489
+ return /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4490
+ " ",
4491
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4492
+ num,
4493
+ "."
4494
+ ] }),
4055
4495
  " ",
4056
- m.argsHint
4057
- ] }) : null,
4496
+ /* @__PURE__ */ jsx(Text, { color: "green", children: "[x]" }),
4497
+ " ",
4498
+ display
4499
+ ] }, t.id);
4500
+ }
4501
+ if (t.status === "in_progress") {
4502
+ return /* @__PURE__ */ jsxs(Text, { children: [
4503
+ " ",
4504
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4505
+ num,
4506
+ "."
4507
+ ] }),
4508
+ " ",
4509
+ /* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: "[~]" }),
4510
+ " ",
4511
+ /* @__PURE__ */ jsx(Text, { color: "yellow", children: display })
4512
+ ] }, t.id);
4513
+ }
4514
+ return /* @__PURE__ */ jsxs(Text, { children: [
4515
+ " ",
4516
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4517
+ num,
4518
+ "."
4519
+ ] }),
4520
+ " ",
4521
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "[ ]" }),
4522
+ " ",
4523
+ display
4524
+ ] }, t.id);
4525
+ };
4526
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
4527
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, marginBottom: 1, children: [
4528
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: "TODOS" }),
4529
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
4058
4530
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4059
- " \u2014 ",
4060
- m.description
4531
+ done,
4532
+ "/",
4533
+ todos.length,
4534
+ " done"
4535
+ ] }),
4536
+ inProgress > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
4537
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
4538
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
4539
+ "\u231B",
4540
+ inProgress
4541
+ ] })
4542
+ ] }) : null,
4543
+ pending > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
4544
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
4545
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4546
+ "\u2610",
4547
+ pending
4548
+ ] })
4549
+ ] }) : null,
4550
+ done > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
4551
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
4552
+ /* @__PURE__ */ jsxs(Text, { color: "green", children: [
4553
+ "\u2713",
4554
+ done
4555
+ ] })
4556
+ ] }) : null,
4557
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 F6 / Esc to close" })
4558
+ ] }),
4559
+ todos.length === 0 ? /* @__PURE__ */ jsx(Box, { marginY: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No todos. The agent will create them as it plans work." }) }) : twoCols ? (
4560
+ /* Two-column layout: split the list in half, render side-by-side.
4561
+ Pass the absolute position so numbering is continuous across columns. */
4562
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
4563
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", width: colWidth, children: todos.slice(0, mid).map((t, i) => renderRow3(t, i)) }),
4564
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", width: colWidth, children: todos.slice(mid).map((t, i) => renderRow3(t, mid + i)) })
4061
4565
  ] })
4062
- ] }, m.name)),
4063
- matches.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No matching commands" })
4566
+ ) : (
4567
+ /* Single column layout */
4568
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: todos.map(renderRow3) })
4569
+ )
4064
4570
  ] });
4065
4571
  }
4066
4572
  var fmtElapsed5 = (ms) => {
@@ -4395,15 +4901,57 @@ function runGit(cwd, args) {
4395
4901
  }
4396
4902
  });
4397
4903
  }
4398
- var STREAM_COLORS = ["cyan", "magenta", "yellow", "green", "blue"];
4399
- function labelFor(labelsRef, id, name) {
4400
- const m = labelsRef.current;
4401
- const existing = m.get(id);
4904
+ function useBrainEvents(events, dispatch) {
4905
+ useEffect(() => {
4906
+ const requestSummary = (request) => `${request.source}: ${request.question}`.slice(0, 80);
4907
+ const addBrainEntry = (status, payload) => {
4908
+ const p = payload;
4909
+ const decision = p.decision.optionId ?? p.decision.text ?? p.decision.reason ?? p.decision.prompt ?? p.decision.type;
4910
+ dispatch({ type: "brainStatus", state: status, source: p.request.source, risk: p.request.risk, summary: decision });
4911
+ if (status === "ask_human") {
4912
+ const prompt = {
4913
+ requestId: p.request.id,
4914
+ source: p.request.source,
4915
+ risk: p.request.risk,
4916
+ question: p.request.question
4917
+ };
4918
+ if (p.request.context !== void 0) prompt.context = p.request.context;
4919
+ if (p.request.options !== void 0) prompt.options = p.request.options;
4920
+ dispatch({ type: "brainPromptSet", prompt });
4921
+ } else {
4922
+ dispatch({ type: "brainPromptClear" });
4923
+ }
4924
+ dispatch({ type: "addEntry", entry: { kind: "brain", status, source: p.request.source, risk: p.request.risk, question: p.request.question, decision, rationale: p.decision.rationale } });
4925
+ };
4926
+ const offRequested = events.on("brain.decision_requested", ({ request }) => {
4927
+ dispatch({ type: "brainStatus", state: "deciding", source: request.source, risk: request.risk, summary: requestSummary(request) });
4928
+ });
4929
+ const offAnswered = events.on("brain.decision_answered", (payload) => addBrainEntry("answered", payload));
4930
+ const offAskHuman = events.on("brain.decision_ask_human", (payload) => addBrainEntry("ask_human", payload));
4931
+ const offDenied = events.on("brain.decision_denied", (payload) => addBrainEntry("denied", payload));
4932
+ return () => {
4933
+ offRequested();
4934
+ offAnswered();
4935
+ offAskHuman();
4936
+ offDenied();
4937
+ };
4938
+ }, [events, dispatch]);
4939
+ }
4940
+ function expectDefined4(value) {
4941
+ if (value === null || value === void 0) {
4942
+ throw new Error("Expected value to be defined");
4943
+ }
4944
+ return value;
4945
+ }
4946
+ var STREAM_COLORS = ["cyan", "magenta", "yellow", "green", "blue"];
4947
+ function labelFor(labelsRef, id, name) {
4948
+ const m = labelsRef.current;
4949
+ const existing = m.get(id);
4402
4950
  if (existing) return existing;
4403
4951
  const n = m.size + 1;
4404
4952
  const v = {
4405
4953
  label: name && name !== id ? name : `AGENT#${n}`,
4406
- color: STREAM_COLORS[(n - 1) % STREAM_COLORS.length]
4954
+ color: expectDefined4(STREAM_COLORS[(n - 1) % STREAM_COLORS.length])
4407
4955
  };
4408
4956
  m.set(id, v);
4409
4957
  return v;
@@ -4493,34 +5041,6 @@ function useSubagentEvents(events, dispatch, setActiveMaxContext) {
4493
5041
  };
4494
5042
  }, [events, dispatch, setActiveMaxContext, lbl]);
4495
5043
  }
4496
- function useBrainEvents(events, dispatch) {
4497
- useEffect(() => {
4498
- const requestSummary = (request) => `${request.source}: ${request.question}`.slice(0, 80);
4499
- const addBrainEntry = (status, payload) => {
4500
- const p = payload;
4501
- const decision = p.decision.optionId ?? p.decision.text ?? p.decision.reason ?? p.decision.prompt ?? p.decision.type;
4502
- dispatch({ type: "brainStatus", state: status, source: p.request.source, risk: p.request.risk, summary: decision });
4503
- if (status === "ask_human") {
4504
- dispatch({ type: "brainPromptSet", prompt: { requestId: p.request.id, source: p.request.source, risk: p.request.risk, question: p.request.question, context: p.request.context, options: p.request.options } });
4505
- } else {
4506
- dispatch({ type: "brainPromptClear" });
4507
- }
4508
- dispatch({ type: "addEntry", entry: { kind: "brain", status, source: p.request.source, risk: p.request.risk, question: p.request.question, decision, rationale: p.decision.rationale } });
4509
- };
4510
- const offRequested = events.on("brain.decision_requested", ({ request }) => {
4511
- dispatch({ type: "brainStatus", state: "deciding", source: request.source, risk: request.risk, summary: requestSummary(request) });
4512
- });
4513
- const offAnswered = events.on("brain.decision_answered", (payload) => addBrainEntry("answered", payload));
4514
- const offAskHuman = events.on("brain.decision_ask_human", (payload) => addBrainEntry("ask_human", payload));
4515
- const offDenied = events.on("brain.decision_denied", (payload) => addBrainEntry("denied", payload));
4516
- return () => {
4517
- offRequested();
4518
- offAnswered();
4519
- offAskHuman();
4520
- offDenied();
4521
- };
4522
- }, [events, dispatch]);
4523
- }
4524
5044
  var USAGE = "Usage:\n /kill \u2014 list active processes + breaker state\n /kill list \u2014 same as /kill\n /kill all \u2014 kill all tracked processes (SIGTERM \u2192 SIGKILL)\n /kill force \u2014 kill all with SIGKILL immediately\n /kill reset \u2014 reset the circuit breaker to closed\n /kill <pid> \u2014 kill a specific process by PID";
4525
5045
  function createKillSlashCommand() {
4526
5046
  return {
@@ -4778,6 +5298,12 @@ function buildSteeringPreamble(snapshot, newDirection) {
4778
5298
  }
4779
5299
 
4780
5300
  // src/app-reducer.ts
5301
+ function expectDefined5(value) {
5302
+ if (value === null || value === void 0) {
5303
+ throw new Error("Expected value to be defined");
5304
+ }
5305
+ return value;
5306
+ }
4781
5307
  function reducer(state, action) {
4782
5308
  switch (action.type) {
4783
5309
  case "addEntry": {
@@ -4803,7 +5329,22 @@ function reducer(state, action) {
4803
5329
  queue: [],
4804
5330
  nextQueueId: 1,
4805
5331
  scrollOffset: 0,
4806
- pendingNewLines: 0
5332
+ pendingNewLines: 0,
5333
+ // Reset fleet state on /clear so old subagent entries don't
5334
+ // cause the LiveActivityStrip to render stale spacers, and
5335
+ // the fleet cost/tokens chips show zero.
5336
+ fleet: {},
5337
+ fleetCost: 0,
5338
+ fleetTokens: { input: 0, output: 0 },
5339
+ leader: {
5340
+ iterations: 0,
5341
+ toolCalls: 0,
5342
+ recentTools: [],
5343
+ currentTool: void 0,
5344
+ startedAt: Date.now(),
5345
+ lastEventAt: Date.now(),
5346
+ iterating: false
5347
+ }
4807
5348
  };
4808
5349
  }
4809
5350
  case "streamDelta":
@@ -5084,6 +5625,23 @@ function reducer(state, action) {
5084
5625
  field: 0,
5085
5626
  mode: action.mode,
5086
5627
  delayMs: action.delayMs,
5628
+ titleAnimation: action.titleAnimation,
5629
+ yolo: action.yolo,
5630
+ streamFleet: action.streamFleet,
5631
+ chime: action.chime,
5632
+ confirmExit: action.confirmExit,
5633
+ nextPrediction: action.nextPrediction,
5634
+ featureMcp: action.featureMcp,
5635
+ featurePlugins: action.featurePlugins,
5636
+ featureMemory: action.featureMemory,
5637
+ featureSkills: action.featureSkills,
5638
+ featureModelsRegistry: action.featureModelsRegistry,
5639
+ contextAutoCompact: action.contextAutoCompact,
5640
+ contextStrategy: action.contextStrategy,
5641
+ logLevel: action.logLevel,
5642
+ auditLevel: action.auditLevel,
5643
+ indexOnStart: action.indexOnStart,
5644
+ maxIterations: action.maxIterations,
5087
5645
  hint: void 0
5088
5646
  }
5089
5647
  };
@@ -5093,37 +5651,68 @@ function reducer(state, action) {
5093
5651
  settingsPicker: { ...state.settingsPicker, open: false, hint: void 0 }
5094
5652
  };
5095
5653
  case "settingsFieldMove": {
5096
- const next = (state.settingsPicker.field + action.delta + 2) % 2;
5654
+ const next = (state.settingsPicker.field + action.delta + SETTINGS_FIELD_COUNT) % SETTINGS_FIELD_COUNT;
5097
5655
  return {
5098
5656
  ...state,
5099
5657
  settingsPicker: { ...state.settingsPicker, field: next, hint: void 0 }
5100
5658
  };
5101
5659
  }
5102
5660
  case "settingsFieldSet": {
5103
- const field = action.field === 1 ? 1 : 0;
5661
+ const field = action.field >= 0 && action.field < SETTINGS_FIELD_COUNT ? action.field : 0;
5104
5662
  return { ...state, settingsPicker: { ...state.settingsPicker, field, hint: void 0 } };
5105
5663
  }
5106
5664
  case "settingsValueChange": {
5107
- if (state.settingsPicker.field === 0) {
5108
- const i = SETTINGS_MODES.indexOf(state.settingsPicker.mode);
5109
- const base2 = i < 0 ? 0 : i;
5110
- const next2 = (base2 + action.delta + SETTINGS_MODES.length) % SETTINGS_MODES.length;
5111
- return {
5112
- ...state,
5113
- settingsPicker: { ...state.settingsPicker, mode: SETTINGS_MODES[next2], hint: void 0 }
5114
- };
5665
+ const sp = state.settingsPicker;
5666
+ const f = sp.field;
5667
+ if (f === 0) {
5668
+ const i = SETTINGS_MODES.indexOf(sp.mode);
5669
+ const base = i < 0 ? 0 : i;
5670
+ const next = (base + action.delta + SETTINGS_MODES.length) % SETTINGS_MODES.length;
5671
+ return { ...state, settingsPicker: { ...sp, mode: expectDefined5(SETTINGS_MODES[next]), hint: void 0 } };
5672
+ }
5673
+ if (f === 1) {
5674
+ const j = DELAY_PRESETS_MS.indexOf(sp.delayMs);
5675
+ const base = j < 0 ? 0 : j;
5676
+ const next = (base + action.delta + DELAY_PRESETS_MS.length) % DELAY_PRESETS_MS.length;
5677
+ return { ...state, settingsPicker: { ...sp, delayMs: expectDefined5(DELAY_PRESETS_MS[next]), hint: void 0 } };
5678
+ }
5679
+ if (f === 2) return { ...state, settingsPicker: { ...sp, titleAnimation: !sp.titleAnimation, hint: void 0 } };
5680
+ if (f === 3) return { ...state, settingsPicker: { ...sp, yolo: !sp.yolo, hint: void 0 } };
5681
+ if (f === 4) return { ...state, settingsPicker: { ...sp, streamFleet: !sp.streamFleet, hint: void 0 } };
5682
+ if (f === 5) return { ...state, settingsPicker: { ...sp, chime: !sp.chime, hint: void 0 } };
5683
+ if (f === 6) return { ...state, settingsPicker: { ...sp, confirmExit: !sp.confirmExit, hint: void 0 } };
5684
+ if (f === 7) return { ...state, settingsPicker: { ...sp, nextPrediction: !sp.nextPrediction, hint: void 0 } };
5685
+ if (f === 8) return { ...state, settingsPicker: { ...sp, featureMcp: !sp.featureMcp, hint: void 0 } };
5686
+ if (f === 9) return { ...state, settingsPicker: { ...sp, featurePlugins: !sp.featurePlugins, hint: void 0 } };
5687
+ if (f === 10) return { ...state, settingsPicker: { ...sp, featureMemory: !sp.featureMemory, hint: void 0 } };
5688
+ if (f === 11) return { ...state, settingsPicker: { ...sp, featureSkills: !sp.featureSkills, hint: void 0 } };
5689
+ if (f === 12) return { ...state, settingsPicker: { ...sp, featureModelsRegistry: !sp.featureModelsRegistry, hint: void 0 } };
5690
+ if (f === 13) return { ...state, settingsPicker: { ...sp, contextAutoCompact: !sp.contextAutoCompact, hint: void 0 } };
5691
+ if (f === 14) {
5692
+ const i = COMPACTOR_STRATEGIES.indexOf(sp.contextStrategy);
5693
+ const base = i < 0 ? 0 : i;
5694
+ const next = (base + action.delta + COMPACTOR_STRATEGIES.length) % COMPACTOR_STRATEGIES.length;
5695
+ return { ...state, settingsPicker: { ...sp, contextStrategy: expectDefined5(COMPACTOR_STRATEGIES[next]), hint: void 0 } };
5696
+ }
5697
+ if (f === 15) {
5698
+ const i = LOG_LEVELS.indexOf(sp.logLevel);
5699
+ const base = i < 0 ? 0 : i;
5700
+ const next = (base + action.delta + LOG_LEVELS.length) % LOG_LEVELS.length;
5701
+ return { ...state, settingsPicker: { ...sp, logLevel: expectDefined5(LOG_LEVELS[next]), hint: void 0 } };
5702
+ }
5703
+ if (f === 16) {
5704
+ const i = AUDIT_LEVELS.indexOf(sp.auditLevel);
5705
+ const base = i < 0 ? 0 : i;
5706
+ const next = (base + action.delta + AUDIT_LEVELS.length) % AUDIT_LEVELS.length;
5707
+ return { ...state, settingsPicker: { ...sp, auditLevel: expectDefined5(AUDIT_LEVELS[next]), hint: void 0 } };
5708
+ }
5709
+ if (f === 17) return { ...state, settingsPicker: { ...sp, indexOnStart: !sp.indexOnStart, hint: void 0 } };
5710
+ {
5711
+ const j = MAX_ITERATIONS_PRESETS.indexOf(sp.maxIterations);
5712
+ const base = j < 0 ? 0 : j;
5713
+ const next = (base + action.delta + MAX_ITERATIONS_PRESETS.length) % MAX_ITERATIONS_PRESETS.length;
5714
+ return { ...state, settingsPicker: { ...sp, maxIterations: expectDefined5(MAX_ITERATIONS_PRESETS[next]), hint: void 0 } };
5115
5715
  }
5116
- const j = DELAY_PRESETS_MS.indexOf(state.settingsPicker.delayMs);
5117
- const base = j < 0 ? 0 : j;
5118
- const next = (base + action.delta + DELAY_PRESETS_MS.length) % DELAY_PRESETS_MS.length;
5119
- return {
5120
- ...state,
5121
- settingsPicker: {
5122
- ...state.settingsPicker,
5123
- delayMs: DELAY_PRESETS_MS[next],
5124
- hint: void 0
5125
- }
5126
- };
5127
5716
  }
5128
5717
  case "settingsHint":
5129
5718
  return { ...state, settingsPicker: { ...state.settingsPicker, hint: action.text } };
@@ -5131,6 +5720,14 @@ function reducer(state, action) {
5131
5720
  return { ...state, confirmQueue: [...state.confirmQueue, action.info] };
5132
5721
  case "confirmClose":
5133
5722
  return { ...state, confirmQueue: state.confirmQueue.slice(1) };
5723
+ case "enhanceOpen":
5724
+ return { ...state, enhance: action.info };
5725
+ case "enhanceClose":
5726
+ return { ...state, enhance: null };
5727
+ case "enhanceSet":
5728
+ return { ...state, enhanceEnabled: action.enabled };
5729
+ case "enhanceBusy":
5730
+ return { ...state, enhanceBusy: action.on };
5134
5731
  case "resetContextChip":
5135
5732
  return { ...state, contextChipVersion: state.contextChipVersion + 1 };
5136
5733
  // --- Fleet ---
@@ -5362,8 +5959,24 @@ function reducer(state, action) {
5362
5959
  };
5363
5960
  }
5364
5961
  case "fleetCost": {
5962
+ let fleet = state.fleet;
5963
+ if (action.perAgent) {
5964
+ let changed = false;
5965
+ const next = {};
5966
+ for (const [id, entry] of Object.entries(state.fleet)) {
5967
+ const cost = action.perAgent[id]?.cost;
5968
+ if (cost !== void 0 && cost !== entry.cost) {
5969
+ next[id] = { ...entry, cost };
5970
+ changed = true;
5971
+ } else {
5972
+ next[id] = entry;
5973
+ }
5974
+ }
5975
+ if (changed) fleet = next;
5976
+ }
5365
5977
  return {
5366
5978
  ...state,
5979
+ fleet,
5367
5980
  fleetCost: action.cost,
5368
5981
  fleetTokens: action.input !== void 0 || action.output !== void 0 ? {
5369
5982
  input: action.input ?? state.fleetTokens.input,
@@ -5442,6 +6055,12 @@ function reducer(state, action) {
5442
6055
  case "toggleHelp": {
5443
6056
  return { ...state, helpOpen: !state.helpOpen };
5444
6057
  }
6058
+ case "toggleTodosMonitor": {
6059
+ return { ...state, todosMonitorOpen: !state.todosMonitorOpen };
6060
+ }
6061
+ case "toggleQueuePanel": {
6062
+ return { ...state, queuePanelOpen: !state.queuePanelOpen };
6063
+ }
5445
6064
  case "checkpointReceived": {
5446
6065
  const existing = state.checkpoints.find((c) => c.promptIndex === action.cp.promptIndex);
5447
6066
  if (existing) return state;
@@ -5740,13 +6359,34 @@ function reducer(state, action) {
5740
6359
  }
5741
6360
  }
5742
6361
  }
5743
- var MIN_VIEWPORT = 3;
6362
+ function expectDefined6(value) {
6363
+ if (value === null || value === void 0) {
6364
+ throw new Error("Expected value to be defined");
6365
+ }
6366
+ return value;
6367
+ }
5744
6368
  var INPUT_PROMPT = "\u203A ";
5745
6369
  function selectedSlashCommandLine(picker) {
5746
6370
  if (!picker.open || picker.matches.length === 0) return null;
5747
6371
  const picked = picker.matches[picker.selected];
5748
6372
  return picked ? `/${picked.name}` : null;
5749
6373
  }
6374
+ function rehydrateHistory(messages, startId) {
6375
+ const entries = [];
6376
+ let nextId = startId;
6377
+ for (const msg of messages) {
6378
+ if (msg.role === "system") continue;
6379
+ const text = typeof msg.content === "string" ? msg.content : msg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
6380
+ const trimmed = text.trim();
6381
+ if (!trimmed) continue;
6382
+ if (msg.role === "user") {
6383
+ entries.push({ id: nextId++, kind: "user", text: trimmed });
6384
+ } else if (msg.role === "assistant") {
6385
+ entries.push({ id: nextId++, kind: "assistant", text: trimmed });
6386
+ }
6387
+ }
6388
+ return entries;
6389
+ }
5750
6390
  var PASTE_THRESHOLD_CHARS = 200;
5751
6391
  function App({
5752
6392
  agent,
@@ -5760,6 +6400,11 @@ function App({
5760
6400
  banner = true,
5761
6401
  queueStore,
5762
6402
  yolo = false,
6403
+ chime = false,
6404
+ confirmExit = true,
6405
+ enhanceEnabled = true,
6406
+ enhanceController,
6407
+ enhanceDelayMs = 4e3,
5763
6408
  getYolo,
5764
6409
  getAutonomy,
5765
6410
  getEternalEngine,
@@ -5791,7 +6436,8 @@ function App({
5791
6436
  initialGoal,
5792
6437
  initialAsk,
5793
6438
  sessionsDir,
5794
- managed = false
6439
+ modeLabel,
6440
+ getModeLabel
5795
6441
  }) {
5796
6442
  const { exit } = useApp();
5797
6443
  const [liveModel, setLiveModel] = useState(model);
@@ -5799,19 +6445,13 @@ function App({
5799
6445
  const [activeMaxContext, setActiveMaxContext] = useState(effectiveMaxContext);
5800
6446
  const [yoloLive, setYoloLive] = useState(yolo);
5801
6447
  const [autonomyLive, setAutonomyLive] = useState(getAutonomy?.() ?? "off");
6448
+ const [liveModeLabel, setLiveModeLabel] = useState(modeLabel ?? "");
5802
6449
  const [hiddenItems, setHiddenItems] = useState(statuslineHiddenItems);
5803
- const { stdout } = useStdout();
5804
- const [termRows, setTermRows] = useState(stdout?.rows ?? 24);
6450
+ const [indexState, setIndexState] = useState(() => getIndexState());
5805
6451
  useEffect(() => {
5806
- const onResize = () => {
5807
- setTermRows(process.stdout.rows ?? 24);
5808
- };
5809
- process.stdout.on("resize", onResize);
5810
- return () => {
5811
- process.stdout.off("resize", onResize);
5812
- };
6452
+ setIndexState(getIndexState());
6453
+ return onIndexStateChange((next) => setIndexState(next));
5813
6454
  }, []);
5814
- const [managedLive, setManagedLive] = useState(managed);
5815
6455
  useEffect(() => {
5816
6456
  setHiddenItems(statuslineHiddenItems);
5817
6457
  }, [statuslineHiddenItems]);
@@ -5840,19 +6480,34 @@ function App({
5840
6480
  }).catch(() => {
5841
6481
  });
5842
6482
  }, [projectRoot]);
6483
+ const restoredEntries = (() => {
6484
+ const msgs = agent.ctx.messages;
6485
+ if (!msgs || msgs.length === 0) return [];
6486
+ const visible = msgs.filter((m) => m.role !== "system");
6487
+ if (visible.length === 0) return [];
6488
+ return rehydrateHistory(
6489
+ visible,
6490
+ /* startId */
6491
+ 1
6492
+ );
6493
+ })();
6494
+ const initialNextId = 1 + restoredEntries.length;
5843
6495
  const [state, dispatch] = useReducer(reducer, {
5844
- entries: banner ? [
5845
- {
5846
- id: 0,
5847
- kind: "banner",
5848
- version: appVersion ?? "dev",
5849
- provider: provider ?? "agent",
5850
- model,
5851
- cwd: agent.ctx.cwd,
5852
- family,
5853
- keyTail
5854
- }
5855
- ] : [],
6496
+ entries: [
6497
+ ...banner ? [
6498
+ {
6499
+ id: 0,
6500
+ kind: "banner",
6501
+ version: appVersion ?? "dev",
6502
+ provider: provider ?? "agent",
6503
+ model,
6504
+ cwd: agent.ctx.cwd,
6505
+ family,
6506
+ keyTail
6507
+ }
6508
+ ] : [],
6509
+ ...restoredEntries
6510
+ ],
5856
6511
  buffer: "",
5857
6512
  cursor: 0,
5858
6513
  streamingText: "",
@@ -5864,7 +6519,7 @@ function App({
5864
6519
  hint: "",
5865
6520
  brain: { state: "idle" },
5866
6521
  brainPrompt: null,
5867
- nextId: 1,
6522
+ nextId: initialNextId,
5868
6523
  picker: { open: false, query: "", matches: [], selected: 0 },
5869
6524
  slashPicker: { open: false, query: "", matches: [], selected: 0 },
5870
6525
  runningTools: /* @__PURE__ */ new Map(),
@@ -5882,8 +6537,11 @@ function App({
5882
6537
  searchQuery: ""
5883
6538
  },
5884
6539
  autonomyPicker: { open: false, options: [], selected: 0 },
5885
- settingsPicker: { open: false, field: 0, mode: "off", delayMs: 0 },
6540
+ settingsPicker: { open: false, field: 0, mode: "off", delayMs: 0, titleAnimation: true, yolo: false, streamFleet: true, chime: false, confirmExit: true, nextPrediction: false, featureMcp: true, featurePlugins: true, featureMemory: true, featureSkills: true, featureModelsRegistry: true, contextAutoCompact: true, contextStrategy: "hybrid", logLevel: "info", auditLevel: "standard", indexOnStart: true, maxIterations: 500 },
5886
6541
  confirmQueue: [],
6542
+ enhance: null,
6543
+ enhanceEnabled,
6544
+ enhanceBusy: false,
5887
6545
  contextChipVersion: 0,
5888
6546
  fleet: {},
5889
6547
  leader: {
@@ -5902,6 +6560,8 @@ function App({
5902
6560
  monitorOpen: false,
5903
6561
  agentsMonitorOpen: false,
5904
6562
  helpOpen: false,
6563
+ todosMonitorOpen: false,
6564
+ queuePanelOpen: false,
5905
6565
  collabSession: null,
5906
6566
  checkpoints: [],
5907
6567
  rewindOverlay: null,
@@ -5926,10 +6586,14 @@ function App({
5926
6586
  const inputGateRef = useRef(false);
5927
6587
  const lastEnterAtRef = useRef(0);
5928
6588
  const tokenPreviewsRef = useRef(/* @__PURE__ */ new Map());
5929
- const projectName = React5.useMemo(() => {
6589
+ const projectName = React6.useMemo(() => {
5930
6590
  const base = path2.basename(projectRoot);
5931
6591
  return base && base !== path2.sep ? base : void 0;
5932
6592
  }, [projectRoot]);
6593
+ const chimeRef = useRef(chime);
6594
+ chimeRef.current = chime;
6595
+ const confirmExitRef = useRef(confirmExit);
6596
+ confirmExitRef.current = confirmExit;
5933
6597
  const streamingTextRef = useRef("");
5934
6598
  const pendingDeltaRef = useRef("");
5935
6599
  const flushTimerRef = useRef(null);
@@ -5937,21 +6601,8 @@ function App({
5937
6601
  stateRef.current = state;
5938
6602
  const draftRef = useRef({ buffer: state.buffer, cursor: state.cursor });
5939
6603
  draftRef.current = { buffer: state.buffer, cursor: state.cursor };
5940
- const bottomRef = useRef(null);
5941
- React5.useLayoutEffect(() => {
5942
- if (!managedLive) return;
5943
- const node = bottomRef.current;
5944
- if (!node) return;
5945
- const { height } = measureElement(node);
5946
- const s2 = stateRef.current;
5947
- const affordance = s2.scrollOffset > 0 && s2.pendingNewLines > 0 ? 1 : 0;
5948
- const vp = Math.max(MIN_VIEWPORT, termRows - height - affordance - 1);
5949
- if (vp !== s2.viewportRows) {
5950
- dispatch({ type: "setViewportRows", rows: vp });
5951
- }
5952
- }, [managedLive, termRows]);
5953
6604
  const handleKeyRef = useRef(null);
5954
- const handleRewindTo = React5.useCallback(
6605
+ const handleRewindTo = React6.useCallback(
5955
6606
  async (checkpointIndex) => {
5956
6607
  const sessionId = agent.ctx.session.id;
5957
6608
  if (!sessionId) return;
@@ -5970,13 +6621,24 @@ function App({
5970
6621
  dispatch({ type: "clearInput" });
5971
6622
  };
5972
6623
  const startedAtRef = useRef(Date.now());
5973
- const [nowTick, setNowTick] = React5.useState(Date.now());
6624
+ const [nowTick, setNowTick] = React6.useState(Date.now());
5974
6625
  useEffect(() => {
5975
- const t = setInterval(() => setNowTick(Date.now()), 1e3);
6626
+ const t = setInterval(() => setNowTick(Date.now()), 1e4);
5976
6627
  return () => clearInterval(t);
5977
6628
  }, []);
5978
- const elapsedMs = nowTick - startedAtRef.current;
5979
- const [gitInfo, setGitInfo] = React5.useState(null);
6629
+ const todosRef = useRef(JSON.stringify([]));
6630
+ useEffect(() => {
6631
+ const poll = () => {
6632
+ const snap = JSON.stringify(agent.ctx.todos.map((t2) => ({ s: t2.status })));
6633
+ if (snap !== todosRef.current) {
6634
+ todosRef.current = snap;
6635
+ setNowTick(Date.now());
6636
+ }
6637
+ };
6638
+ const t = setInterval(poll, 2e3);
6639
+ return () => clearInterval(t);
6640
+ }, [agent.ctx.todos]);
6641
+ const [gitInfo, setGitInfo] = React6.useState(null);
5980
6642
  useEffect(() => {
5981
6643
  let cancelled = false;
5982
6644
  const refresh = () => {
@@ -6031,7 +6693,10 @@ function App({
6031
6693
  toolCalls: state.leader.toolCalls,
6032
6694
  recentTools: state.leader.recentTools,
6033
6695
  recentMessages: [],
6034
- cost: 0,
6696
+ // Leader (main session) cost — the same number the statusline shows.
6697
+ // Kept distinct from fleet (subagent) cost so the monitor can show a
6698
+ // trustworthy grand total = leader + fleet.
6699
+ cost: tokenCounter?.estimateCost().total ?? 0,
6035
6700
  startedAt: state.leader.startedAt,
6036
6701
  lastEventAt: state.leader.lastEventAt,
6037
6702
  currentTool: state.leader.currentTool,
@@ -6040,7 +6705,7 @@ function App({
6040
6705
  ctxMaxTokens: state.leader.ctxMaxTokens ?? effectiveMaxContext
6041
6706
  };
6042
6707
  return { leader: leaderEntry, ...state.fleet };
6043
- }, [state.fleet, state.leader, state.status, provider, model, effectiveMaxContext]);
6708
+ }, [state.fleet, state.leader, state.status, provider, model, effectiveMaxContext, tokenCounter]);
6044
6709
  const STREAM_COLORS2 = ["cyan", "magenta", "yellow", "green", "blue"];
6045
6710
  const labelsRef = useRef(/* @__PURE__ */ new Map());
6046
6711
  const labelFor2 = (id, name) => {
@@ -6050,7 +6715,7 @@ function App({
6050
6715
  const n = m.size + 1;
6051
6716
  const v = {
6052
6717
  label: name && name !== id ? name : `AGENT#${n}`,
6053
- color: STREAM_COLORS2[(n - 1) % STREAM_COLORS2.length]
6718
+ color: STREAM_COLORS2[(n - 1) % STREAM_COLORS2.length] ?? "cyan"
6054
6719
  };
6055
6720
  m.set(id, v);
6056
6721
  return v;
@@ -6091,19 +6756,23 @@ function App({
6091
6756
  }, [agent.ctx.meta]);
6092
6757
  const prevAnyOverlayOpen = useRef(false);
6093
6758
  const prevEntriesCount = useRef(0);
6759
+ const prevToolStreamLen = useRef(0);
6094
6760
  const eraseLiveRegion = useCallback(() => {
6095
6761
  try {
6096
6762
  writeOut("\x1B[J");
6097
6763
  } catch {
6098
6764
  }
6099
6765
  }, []);
6100
- useEffect(() => {
6101
- const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || state.confirmQueue.length > 0;
6766
+ React6.useLayoutEffect(() => {
6767
+ const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || state.enhanceBusy || state.enhance != null || state.confirmQueue.length > 0;
6102
6768
  const overlayClosed = prevAnyOverlayOpen.current && !anyOpenNow;
6103
6769
  const newEntryCommitted = state.entries.length > prevEntriesCount.current;
6770
+ const curToolStreamLen = state.toolStream?.text.length ?? 0;
6771
+ const toolStreamGrew = curToolStreamLen > 0 && curToolStreamLen > prevToolStreamLen.current;
6104
6772
  prevAnyOverlayOpen.current = anyOpenNow;
6105
6773
  prevEntriesCount.current = state.entries.length;
6106
- if (overlayClosed || newEntryCommitted) {
6774
+ prevToolStreamLen.current = curToolStreamLen;
6775
+ if (overlayClosed || newEntryCommitted || toolStreamGrew) {
6107
6776
  eraseLiveRegion();
6108
6777
  }
6109
6778
  }, [
@@ -6112,8 +6781,11 @@ function App({
6112
6781
  state.modelPicker.open,
6113
6782
  state.autonomyPicker.open,
6114
6783
  state.settingsPicker.open,
6784
+ state.enhanceBusy,
6785
+ state.enhance,
6115
6786
  state.confirmQueue.length,
6116
6787
  state.entries.length,
6788
+ state.toolStream?.text,
6117
6789
  eraseLiveRegion
6118
6790
  ]);
6119
6791
  useEffect(() => {
@@ -6123,6 +6795,9 @@ function App({
6123
6795
  process.stdout.off("resize", handleResize);
6124
6796
  };
6125
6797
  }, [eraseLiveRegion]);
6798
+ React6.useLayoutEffect(() => {
6799
+ if (state.enhanceBusy || state.enhance != null) eraseLiveRegion();
6800
+ }, [state.enhanceBusy, state.enhance, eraseLiveRegion]);
6126
6801
  useEffect(() => {
6127
6802
  const detected = detectAtToken(state.buffer, state.cursor);
6128
6803
  if (!detected) {
@@ -6154,16 +6829,22 @@ function App({
6154
6829
  }
6155
6830
  const query = trimmed.slice(1).toLowerCase();
6156
6831
  const allCommands = slashRegistry.listWithOwner();
6832
+ const CATEGORY_ORDER = ["Run", "Session", "Inspect", "Agent", "Config", "App"];
6157
6833
  const matches = allCommands.filter(({ cmd }) => {
6158
6834
  const name = cmd.name.toLowerCase();
6159
6835
  const aliases = cmd.aliases ?? [];
6160
6836
  return name.includes(query) || aliases.some((a) => a.toLowerCase().includes(query));
6161
- }).slice(0, 12).map(({ cmd, owner }) => ({
6837
+ }).map(({ cmd, owner }) => ({
6162
6838
  name: cmd.name,
6163
6839
  description: cmd.description,
6164
6840
  argsHint: cmd.argsHint,
6165
- isBuiltin: owner === "core"
6166
- }));
6841
+ isBuiltin: owner === "core",
6842
+ category: cmd.category ?? "App"
6843
+ })).sort((a, b) => {
6844
+ const catDiff = CATEGORY_ORDER.indexOf(a.category) - CATEGORY_ORDER.indexOf(b.category);
6845
+ if (catDiff !== 0) return catDiff;
6846
+ return a.name.localeCompare(b.name);
6847
+ });
6167
6848
  if (!state.slashPicker.open) {
6168
6849
  dispatch({ type: "slashPickerOpen", query, matches });
6169
6850
  } else if (state.slashPicker.query !== query) {
@@ -6295,44 +6976,6 @@ function App({
6295
6976
  getProcessRegistry().killAll();
6296
6977
  };
6297
6978
  }, []);
6298
- useEffect(() => {
6299
- const ALT_OFF = "\x1B[?1049l";
6300
- const ALT_ON = "\x1B[?1049h";
6301
- const cmd = {
6302
- name: "altscreen",
6303
- description: "Toggle the alt-screen buffer. Default is OFF (native scroll); /altscreen on for full-screen mode.",
6304
- async run(args) {
6305
- const arg = args.trim().toLowerCase();
6306
- if (arg === "off") {
6307
- try {
6308
- writeOut(ALT_OFF);
6309
- } catch {
6310
- return { message: "Failed to exit alt-screen." };
6311
- }
6312
- setManagedLive(false);
6313
- return {
6314
- message: "Alt-screen disabled. New entries will land in normal scrollback (mouse wheel / Shift+PgUp work). On-screen history rendered before this command is no longer reachable via terminal scroll. Resize may now leak the live region \u2014 `/altscreen on` to re-enable."
6315
- };
6316
- }
6317
- if (arg === "on") {
6318
- try {
6319
- writeOut(ALT_ON);
6320
- } catch {
6321
- return { message: "Failed to re-enter alt-screen." };
6322
- }
6323
- setManagedLive(true);
6324
- return {
6325
- message: "Alt-screen re-enabled. Managed scroll (PgUp/PgDn) is now active; native scroll is off."
6326
- };
6327
- }
6328
- return { message: "Usage: /altscreen on|off" };
6329
- }
6330
- };
6331
- slashRegistry.register(cmd);
6332
- return () => {
6333
- slashRegistry.unregister("altscreen");
6334
- };
6335
- }, [slashRegistry]);
6336
6979
  useEffect(() => {
6337
6980
  const cmd = {
6338
6981
  name: "steer",
@@ -6439,15 +7082,36 @@ function App({
6439
7082
  slashRegistry.unregister("agents");
6440
7083
  };
6441
7084
  }, [slashRegistry]);
6442
- const openModelPicker = React5.useCallback(async () => {
7085
+ const openModelPicker = React6.useCallback(async () => {
6443
7086
  if (!getPickableProviders) return;
6444
7087
  const providers = await getPickableProviders();
6445
7088
  dispatch({ type: "modelPickerOpen", providers });
6446
7089
  }, [getPickableProviders]);
6447
- const openSettings = React5.useCallback(() => {
7090
+ const openSettings = React6.useCallback(() => {
6448
7091
  if (!getSettings) return;
6449
7092
  const s2 = getSettings();
6450
- dispatch({ type: "settingsOpen", mode: s2.mode, delayMs: s2.delayMs });
7093
+ dispatch({
7094
+ type: "settingsOpen",
7095
+ mode: s2.mode,
7096
+ delayMs: s2.delayMs,
7097
+ titleAnimation: s2.titleAnimation ?? true,
7098
+ yolo: s2.yolo ?? false,
7099
+ streamFleet: s2.streamFleet ?? true,
7100
+ chime: s2.chime ?? false,
7101
+ confirmExit: s2.confirmExit ?? true,
7102
+ nextPrediction: s2.nextPrediction ?? false,
7103
+ featureMcp: s2.featureMcp ?? true,
7104
+ featurePlugins: s2.featurePlugins ?? true,
7105
+ featureMemory: s2.featureMemory ?? true,
7106
+ featureSkills: s2.featureSkills ?? true,
7107
+ featureModelsRegistry: s2.featureModelsRegistry ?? true,
7108
+ contextAutoCompact: s2.contextAutoCompact ?? true,
7109
+ contextStrategy: s2.contextStrategy ?? "hybrid",
7110
+ logLevel: s2.logLevel ?? "info",
7111
+ auditLevel: s2.auditLevel ?? "standard",
7112
+ indexOnStart: s2.indexOnStart ?? true,
7113
+ maxIterations: s2.maxIterations ?? 500
7114
+ });
6451
7115
  }, [getSettings]);
6452
7116
  useEffect(() => {
6453
7117
  if (!getPickableProviders || !switchProviderAndModel) return;
@@ -6460,7 +7124,7 @@ function App({
6460
7124
  return { message: void 0 };
6461
7125
  }
6462
7126
  };
6463
- slashRegistry.register(cmd);
7127
+ slashRegistry.register(cmd, "tui", { official: true });
6464
7128
  return () => {
6465
7129
  slashRegistry.unregister("model");
6466
7130
  };
@@ -6470,13 +7134,13 @@ function App({
6470
7134
  const cmd = {
6471
7135
  name: "settings",
6472
7136
  aliases: ["config", "prefs"],
6473
- description: "Edit autonomy defaults (mode + auto-proceed delay).",
7137
+ description: "Open the interactive settings editor (19 config fields across 8 sections).",
6474
7138
  async run() {
6475
7139
  openSettings();
6476
7140
  return { message: void 0 };
6477
7141
  }
6478
7142
  };
6479
- slashRegistry.register(cmd);
7143
+ slashRegistry.register(cmd, "tui", { official: true });
6480
7144
  return () => {
6481
7145
  slashRegistry.unregister("settings");
6482
7146
  };
@@ -6492,7 +7156,7 @@ function App({
6492
7156
  return { message: void 0 };
6493
7157
  }
6494
7158
  };
6495
- slashRegistry.register(cmd);
7159
+ slashRegistry.register(cmd, "tui", { official: true });
6496
7160
  return () => {
6497
7161
  slashRegistry.unregister("autonomy");
6498
7162
  };
@@ -6533,23 +7197,25 @@ function App({
6533
7197
  });
6534
7198
  });
6535
7199
  const offTool = events.on("tool.executed", (e) => {
6536
- dispatch({
6537
- type: "addEntry",
6538
- entry: {
6539
- kind: "tool",
6540
- name: e.name,
6541
- durationMs: e.durationMs,
6542
- ok: e.ok,
6543
- input: e.input,
6544
- output: e.output,
6545
- // Real model-visible sizes — forwarded so the size chip beside
6546
- // the tool header can show what the model paid for instead of
6547
- // the misleading preview-byte count we used to surface.
6548
- outputBytes: e.outputBytes,
6549
- outputTokens: e.outputTokens,
6550
- outputLines: e.outputLines
6551
- }
6552
- });
7200
+ if (e.name !== "delegate") {
7201
+ dispatch({
7202
+ type: "addEntry",
7203
+ entry: {
7204
+ kind: "tool",
7205
+ name: e.name,
7206
+ durationMs: e.durationMs,
7207
+ ok: e.ok,
7208
+ input: e.input,
7209
+ output: e.output,
7210
+ // Real model-visible sizes forwarded so the size chip beside
7211
+ // the tool header can show what the model paid for instead of
7212
+ // the misleading preview-byte count we used to surface.
7213
+ outputBytes: e.outputBytes,
7214
+ outputTokens: e.outputTokens,
7215
+ outputLines: e.outputLines
7216
+ }
7217
+ });
7218
+ }
6553
7219
  dispatch({ type: "toolEnded", name: e.name });
6554
7220
  dispatch({ type: "toolStreamClear", name: e.name });
6555
7221
  dispatch({ type: "leaderToolEnd", name: e.name, ok: e.ok, durationMs: e.durationMs });
@@ -6609,6 +7275,34 @@ function App({
6609
7275
  }
6610
7276
  });
6611
7277
  });
7278
+ const offDelegateStart = events.on("delegate.started", (e) => {
7279
+ const task = e.task.length > 100 ? `${e.task.slice(0, 99)}\u2026` : e.task;
7280
+ dispatch({
7281
+ type: "addEntry",
7282
+ entry: {
7283
+ kind: "subagent",
7284
+ agentLabel: e.target,
7285
+ agentColor: "magenta",
7286
+ icon: "\u{1F91D}",
7287
+ text: "delegating",
7288
+ detail: task
7289
+ }
7290
+ });
7291
+ });
7292
+ const offDelegateDone = events.on("delegate.completed", (e) => {
7293
+ const cost = e.costUsd && e.costUsd > 0 ? `$${e.costUsd.toFixed(3)}` : void 0;
7294
+ dispatch({
7295
+ type: "addEntry",
7296
+ entry: {
7297
+ kind: "subagent",
7298
+ agentLabel: e.target,
7299
+ agentColor: e.ok ? "green" : "red",
7300
+ icon: e.ok ? "\u2713" : "\u2717",
7301
+ text: e.summary,
7302
+ detail: cost
7303
+ }
7304
+ });
7305
+ });
6612
7306
  return () => {
6613
7307
  offDelta();
6614
7308
  offToolStart();
@@ -6621,6 +7315,8 @@ function App({
6621
7315
  offProvResp();
6622
7316
  offConfirmNeeded();
6623
7317
  offTrustPersisted();
7318
+ offDelegateStart();
7319
+ offDelegateDone();
6624
7320
  if (flushTimerRef.current) clearTimeout(flushTimerRef.current);
6625
7321
  };
6626
7322
  }, [events, agent.ctx.todos]);
@@ -6628,6 +7324,11 @@ function App({
6628
7324
  useEffect(() => {
6629
7325
  streamFleetRef.current = state.streamFleet;
6630
7326
  }, [state.streamFleet]);
7327
+ const enhanceEnabledRef = useRef(state.enhanceEnabled);
7328
+ useEffect(() => {
7329
+ enhanceEnabledRef.current = state.enhanceEnabled;
7330
+ }, [state.enhanceEnabled]);
7331
+ const enhanceAbortRef = useRef(null);
6631
7332
  useSubagentEvents(events, dispatch, setActiveMaxContext);
6632
7333
  useEffect(() => {
6633
7334
  const offCheckpoint = events.on("checkpoint.written", (e) => {
@@ -6853,6 +7554,18 @@ function App({
6853
7554
  useEffect(() => {
6854
7555
  if (fleetStreamController) fleetStreamController.enabled = state.streamFleet;
6855
7556
  }, [state.streamFleet, fleetStreamController]);
7557
+ useEffect(() => {
7558
+ if (!enhanceController) return;
7559
+ enhanceController.enabled = state.enhanceEnabled;
7560
+ enhanceController.setEnabled = (enabled) => {
7561
+ dispatch({ type: "enhanceSet", enabled });
7562
+ };
7563
+ return () => {
7564
+ enhanceController.setEnabled = (enabled) => {
7565
+ enhanceController.enabled = enabled;
7566
+ };
7567
+ };
7568
+ }, [enhanceController, state.enhanceEnabled]);
6856
7569
  useEffect(() => {
6857
7570
  if (!agentsMonitorController) return;
6858
7571
  agentsMonitorController.visible = state.agentsMonitorOpen;
@@ -6932,7 +7645,8 @@ function App({
6932
7645
  type: "fleetCost",
6933
7646
  cost: d.snapshot().total.cost,
6934
7647
  input: d.snapshot().total.input,
6935
- output: d.snapshot().total.output
7648
+ output: d.snapshot().total.output,
7649
+ perAgent: d.snapshot().perSubagent
6936
7650
  });
6937
7651
  const seen = new Set(Object.keys(status.subagents));
6938
7652
  const pending = /* @__PURE__ */ new Map();
@@ -7061,7 +7775,8 @@ function App({
7061
7775
  type: "fleetCost",
7062
7776
  cost: d.snapshot().total.cost,
7063
7777
  input: d.snapshot().total.input,
7064
- output: d.snapshot().total.output
7778
+ output: d.snapshot().total.output,
7779
+ perAgent: d.snapshot().perSubagent
7065
7780
  });
7066
7781
  break;
7067
7782
  }
@@ -7189,7 +7904,8 @@ function App({
7189
7904
  type: "fleetCost",
7190
7905
  cost: d.snapshot().total.cost,
7191
7906
  input: d.snapshot().total.input,
7192
- output: d.snapshot().total.output
7907
+ output: d.snapshot().total.output,
7908
+ perAgent: d.snapshot().perSubagent
7193
7909
  });
7194
7910
  if (streamFlushTimer) {
7195
7911
  clearTimeout(streamFlushTimer);
@@ -7270,7 +7986,7 @@ function App({
7270
7986
  type: "addEntry",
7271
7987
  entry: {
7272
7988
  kind: "warn",
7273
- text: `Iteration cancelled${director ? " + fleet terminated" : ""}${procTag}. Dropped ${droppedCount} queued message${droppedCount === 1 ? "" : "s"}. Press Ctrl+C again to exit.`
7989
+ text: `Iteration cancelled${director ? " + fleet terminated" : ""}${procTag}. Dropped ${droppedCount} queued message${droppedCount === 1 ? "" : "s"}. ${confirmExitRef.current ? "Press Ctrl+C again to confirm exit." : "Press Ctrl+C again to exit."}`
7274
7990
  }
7275
7991
  });
7276
7992
  } else {
@@ -7278,7 +7994,7 @@ function App({
7278
7994
  type: "addEntry",
7279
7995
  entry: {
7280
7996
  kind: "warn",
7281
- text: `Iteration cancelled${director ? " + fleet terminated" : ""}${procTag}. Press Ctrl+C again to exit.`
7997
+ text: `Iteration cancelled${director ? " + fleet terminated" : ""}${procTag}. ${confirmExitRef.current ? "Press Ctrl+C again to confirm exit." : "Press Ctrl+C again to exit."}`
7282
7998
  }
7283
7999
  });
7284
8000
  }
@@ -7309,7 +8025,7 @@ function App({
7309
8025
  type: "addEntry",
7310
8026
  entry: {
7311
8027
  kind: "warn",
7312
- text: `${bits.join(" + ") || "Background work stopped"}. Press Ctrl+C again to exit.`
8028
+ text: `${bits.join(" + ") || "Background work stopped"}. ${confirmExitRef.current ? "Press Ctrl+C again to confirm exit." : "Press Ctrl+C again to exit."}`
7313
8029
  }
7314
8030
  });
7315
8031
  return;
@@ -7352,29 +8068,16 @@ function App({
7352
8068
  const handleKey = async (input, key) => {
7353
8069
  if (state.status === "aborting" && !state.steeringPending && state.interrupts === 0) return;
7354
8070
  if (state.confirmQueue.length > 0) return;
8071
+ if (state.enhanceBusy) {
8072
+ if (key.escape) enhanceAbortRef.current?.abort();
8073
+ return;
8074
+ }
8075
+ if (state.enhance) return;
7355
8076
  if (state.helpOpen) {
7356
8077
  if (key.escape || input === "?" || input === "q") dispatch({ type: "toggleHelp" });
7357
8078
  return;
7358
8079
  }
7359
8080
  if (inputGateRef.current) return;
7360
- if (managedLive) {
7361
- if (key.pageUp) {
7362
- dispatch({ type: "scrollPage", dir: "up" });
7363
- return;
7364
- }
7365
- if (key.pageDown) {
7366
- dispatch({ type: "scrollPage", dir: "down" });
7367
- return;
7368
- }
7369
- if (key.ctrl && key.home) {
7370
- dispatch({ type: "scrollToTop" });
7371
- return;
7372
- }
7373
- if (key.ctrl && key.end) {
7374
- dispatch({ type: "scrollToBottom" });
7375
- return;
7376
- }
7377
- }
7378
8081
  if (key.escape) {
7379
8082
  const now = Date.now();
7380
8083
  if (state.buffer.length > 0 && now - lastEscAtRef.current < ESC_DOUBLE_PRESS_MS) {
@@ -7527,8 +8230,8 @@ function App({
7527
8230
  const now = Date.now();
7528
8231
  if (now - lastEnterAtRef.current < 50) return;
7529
8232
  lastEnterAtRef.current = now;
7530
- const { mode, delayMs } = state.settingsPicker;
7531
- const err = await saveSettings?.({ mode, delayMs });
8233
+ const { mode, delayMs, titleAnimation, yolo: yolo2, streamFleet, chime: chime2, confirmExit: confirmExit2, nextPrediction, featureMcp, featurePlugins, featureMemory, featureSkills, featureModelsRegistry, contextAutoCompact, contextStrategy, logLevel, auditLevel, indexOnStart, maxIterations } = state.settingsPicker;
8234
+ const err = await saveSettings?.({ mode, delayMs, titleAnimation, yolo: yolo2, streamFleet, chime: chime2, confirmExit: confirmExit2, nextPrediction, featureMcp, featurePlugins, featureMemory, featureSkills, featureModelsRegistry, contextAutoCompact, contextStrategy, logLevel, auditLevel, indexOnStart, maxIterations });
7532
8235
  if (err) {
7533
8236
  dispatch({ type: "settingsHint", text: err });
7534
8237
  return;
@@ -7641,20 +8344,32 @@ function App({
7641
8344
  return;
7642
8345
  }
7643
8346
  const toggleFleetOverlay = () => {
7644
- if (state.agentsMonitorOpen) {
7645
- dispatch({ type: "toggleAgentsMonitor" });
7646
- dispatch({ type: "toggleMonitor" });
7647
- } else {
8347
+ if (state.monitorOpen) {
7648
8348
  dispatch({ type: "toggleMonitor" });
8349
+ return;
7649
8350
  }
8351
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
8352
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
8353
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
8354
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8355
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
8356
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
8357
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
8358
+ dispatch({ type: "toggleMonitor" });
7650
8359
  };
7651
8360
  const toggleAgentsOverlay = () => {
7652
- if (state.monitorOpen) {
7653
- dispatch({ type: "toggleMonitor" });
7654
- dispatch({ type: "toggleAgentsMonitor" });
7655
- } else {
8361
+ if (state.agentsMonitorOpen) {
7656
8362
  dispatch({ type: "toggleAgentsMonitor" });
8363
+ return;
7657
8364
  }
8365
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
8366
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
8367
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
8368
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8369
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
8370
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
8371
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
8372
+ dispatch({ type: "toggleAgentsMonitor" });
7658
8373
  };
7659
8374
  const toggleWorktreeOverlay = () => {
7660
8375
  if (state.worktreeMonitorOpen) {
@@ -7664,8 +8379,26 @@ function App({
7664
8379
  if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
7665
8380
  if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
7666
8381
  if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8382
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
8383
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
8384
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
8385
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
7667
8386
  dispatch({ type: "worktreeMonitorToggle" });
7668
8387
  };
8388
+ const toggleTodosOverlay = () => {
8389
+ if (state.todosMonitorOpen) {
8390
+ dispatch({ type: "toggleTodosMonitor" });
8391
+ return;
8392
+ }
8393
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
8394
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
8395
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
8396
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8397
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
8398
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
8399
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
8400
+ dispatch({ type: "toggleTodosMonitor" });
8401
+ };
7669
8402
  if (key.ctrl && input === "f" || key.fn === 2) {
7670
8403
  toggleFleetOverlay();
7671
8404
  return;
@@ -7678,12 +8411,96 @@ function App({
7678
8411
  toggleWorktreeOverlay();
7679
8412
  return;
7680
8413
  }
8414
+ if (key.fn === 5) {
8415
+ if (state.settingsPicker.open) {
8416
+ dispatch({ type: "settingsClose" });
8417
+ } else if (getSettings && saveSettings) {
8418
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
8419
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
8420
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
8421
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
8422
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8423
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
8424
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
8425
+ const cfg = getSettings();
8426
+ dispatch({
8427
+ type: "settingsOpen",
8428
+ mode: cfg.mode,
8429
+ delayMs: cfg.delayMs,
8430
+ titleAnimation: cfg.titleAnimation ?? true,
8431
+ yolo: cfg.yolo ?? false,
8432
+ streamFleet: cfg.streamFleet ?? true,
8433
+ chime: cfg.chime ?? false,
8434
+ confirmExit: cfg.confirmExit ?? true,
8435
+ nextPrediction: cfg.nextPrediction ?? false,
8436
+ featureMcp: cfg.featureMcp ?? true,
8437
+ featurePlugins: cfg.featurePlugins ?? true,
8438
+ featureMemory: cfg.featureMemory ?? true,
8439
+ featureSkills: cfg.featureSkills ?? true,
8440
+ featureModelsRegistry: cfg.featureModelsRegistry ?? true,
8441
+ contextAutoCompact: cfg.contextAutoCompact ?? true,
8442
+ contextStrategy: cfg.contextStrategy ?? "hybrid",
8443
+ logLevel: cfg.logLevel ?? "info",
8444
+ auditLevel: cfg.auditLevel ?? "standard",
8445
+ indexOnStart: cfg.indexOnStart ?? true,
8446
+ maxIterations: cfg.maxIterations ?? 500
8447
+ });
8448
+ }
8449
+ return;
8450
+ }
8451
+ if (key.fn === 6) {
8452
+ toggleTodosOverlay();
8453
+ return;
8454
+ }
8455
+ if (key.fn === 7) {
8456
+ if (state.queuePanelOpen) {
8457
+ dispatch({ type: "toggleQueuePanel" });
8458
+ } else {
8459
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
8460
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
8461
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
8462
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
8463
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8464
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
8465
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
8466
+ dispatch({ type: "toggleQueuePanel" });
8467
+ }
8468
+ return;
8469
+ }
7681
8470
  if (key.ctrl && input === "s") {
7682
8471
  if (state.settingsPicker.open) {
7683
8472
  dispatch({ type: "settingsClose" });
7684
8473
  } else if (getSettings && saveSettings) {
8474
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
8475
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
8476
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
8477
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
8478
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8479
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
8480
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
7685
8481
  const cfg = getSettings();
7686
- dispatch({ type: "settingsOpen", mode: cfg.mode, delayMs: cfg.delayMs });
8482
+ dispatch({
8483
+ type: "settingsOpen",
8484
+ mode: cfg.mode,
8485
+ delayMs: cfg.delayMs,
8486
+ titleAnimation: cfg.titleAnimation ?? true,
8487
+ yolo: cfg.yolo ?? false,
8488
+ streamFleet: cfg.streamFleet ?? true,
8489
+ chime: cfg.chime ?? false,
8490
+ confirmExit: cfg.confirmExit ?? true,
8491
+ nextPrediction: cfg.nextPrediction ?? false,
8492
+ featureMcp: cfg.featureMcp ?? true,
8493
+ featurePlugins: cfg.featurePlugins ?? true,
8494
+ featureMemory: cfg.featureMemory ?? true,
8495
+ featureSkills: cfg.featureSkills ?? true,
8496
+ featureModelsRegistry: cfg.featureModelsRegistry ?? true,
8497
+ contextAutoCompact: cfg.contextAutoCompact ?? true,
8498
+ contextStrategy: cfg.contextStrategy ?? "hybrid",
8499
+ logLevel: cfg.logLevel ?? "info",
8500
+ auditLevel: cfg.auditLevel ?? "standard",
8501
+ indexOnStart: cfg.indexOnStart ?? true,
8502
+ maxIterations: cfg.maxIterations ?? 500
8503
+ });
7687
8504
  }
7688
8505
  return;
7689
8506
  }
@@ -7700,12 +8517,34 @@ function App({
7700
8517
  dispatch({ type: "worktreeMonitorToggle" });
7701
8518
  return;
7702
8519
  }
8520
+ if (state.todosMonitorOpen) {
8521
+ dispatch({ type: "toggleTodosMonitor" });
8522
+ return;
8523
+ }
8524
+ if (state.autoPhase?.monitorOpen) {
8525
+ dispatch({ type: "autoPhaseMonitorToggle" });
8526
+ return;
8527
+ }
8528
+ if (state.settingsPicker.open) {
8529
+ dispatch({ type: "settingsClose" });
8530
+ return;
8531
+ }
8532
+ if (state.queuePanelOpen) {
8533
+ dispatch({ type: "toggleQueuePanel" });
8534
+ return;
8535
+ }
7703
8536
  }
7704
- if (input === "?" && !key.ctrl && !key.meta && draftRef.current.buffer === "" && !state.slashPicker.open && !state.picker.open && !state.modelPicker.open && !state.autonomyPicker.open && !state.settingsPicker.open && !state.rewindOverlay && !state.monitorOpen && !state.agentsMonitorOpen && !state.worktreeMonitorOpen && !state.autoPhase?.monitorOpen) {
8537
+ if (input === "?" && !key.ctrl && !key.meta && draftRef.current.buffer === "" && !state.slashPicker.open && !state.picker.open && !state.modelPicker.open && !state.autonomyPicker.open && !state.settingsPicker.open && !state.rewindOverlay && !state.monitorOpen && !state.agentsMonitorOpen && !state.worktreeMonitorOpen && !state.todosMonitorOpen && !state.autoPhase?.monitorOpen) {
7705
8538
  dispatch({ type: "toggleHelp" });
7706
8539
  return;
7707
8540
  }
7708
8541
  if (isEnter) {
8542
+ if (key.shift) {
8543
+ const { buffer: buffer2, cursor: cursor2 } = draftRef.current;
8544
+ const next2 = buffer2.slice(0, cursor2) + "\n" + buffer2.slice(cursor2);
8545
+ setDraft(next2, cursor2 + 1);
8546
+ return;
8547
+ }
7709
8548
  const now = Date.now();
7710
8549
  if (now - lastEnterAtRef.current < 50) return;
7711
8550
  lastEnterAtRef.current = now;
@@ -7713,36 +8552,41 @@ function App({
7713
8552
  return;
7714
8553
  }
7715
8554
  const { buffer, cursor } = draftRef.current;
7716
- if (key.backspace || key.delete) {
8555
+ if (key.backspace) {
7717
8556
  if (key.ctrl) {
7718
- if (key.backspace) {
7719
- if (cursor === 0) return;
7720
- const beforeCursor = buffer.slice(0, cursor);
7721
- const lastWordStart = beforeCursor.lastIndexOf(" ") + 1;
7722
- const next3 = beforeCursor.slice(0, lastWordStart) + buffer.slice(cursor);
7723
- setDraft(next3, lastWordStart);
7724
- } else {
7725
- if (cursor >= buffer.length) return;
7726
- const afterCursor = buffer.slice(cursor);
7727
- const nextWordStart = afterCursor.indexOf(" ");
7728
- const end = nextWordStart === -1 ? buffer.length : cursor + nextWordStart + 1;
7729
- const next3 = buffer.slice(0, cursor) + buffer.slice(end);
7730
- setDraft(next3, cursor);
7731
- }
8557
+ if (cursor === 0) return;
8558
+ const beforeCursor = buffer.slice(0, cursor);
8559
+ const lastWordStart = beforeCursor.lastIndexOf(" ") + 1;
8560
+ const next3 = beforeCursor.slice(0, lastWordStart) + buffer.slice(cursor);
8561
+ setDraft(next3, lastWordStart);
7732
8562
  return;
7733
8563
  }
7734
- if (key.backspace) {
7735
- const tokenDel = deleteTokenBackward(buffer, cursor);
7736
- if (tokenDel) {
7737
- setDraft(tokenDel.buffer, tokenDel.cursor);
7738
- return;
7739
- }
8564
+ const tokenDel = deleteTokenBackward(buffer, cursor);
8565
+ if (tokenDel) {
8566
+ setDraft(tokenDel.buffer, tokenDel.cursor);
8567
+ return;
7740
8568
  }
7741
8569
  if (cursor === 0) return;
7742
8570
  const next2 = buffer.slice(0, cursor - 1) + buffer.slice(cursor);
7743
8571
  setDraft(next2, cursor - 1);
7744
8572
  return;
7745
8573
  }
8574
+ if (key.delete) {
8575
+ if (key.ctrl) {
8576
+ if (cursor >= buffer.length) return;
8577
+ const afterCursor = buffer.slice(cursor);
8578
+ const nextWordStart = afterCursor.indexOf(" ");
8579
+ const end = nextWordStart === -1 ? buffer.length : cursor + nextWordStart + 1;
8580
+ const next3 = buffer.slice(0, cursor) + buffer.slice(end);
8581
+ setDraft(next3, cursor);
8582
+ return;
8583
+ }
8584
+ if (cursor >= buffer.length) return;
8585
+ const span = tokenLengthForward(buffer, cursor) || 1;
8586
+ const next2 = buffer.slice(0, cursor) + buffer.slice(cursor + span);
8587
+ setDraft(next2, cursor);
8588
+ return;
8589
+ }
7746
8590
  if (key.leftArrow) {
7747
8591
  if (key.ctrl) {
7748
8592
  if (cursor === 0) return;
@@ -7768,19 +8612,11 @@ function App({
7768
8612
  return;
7769
8613
  }
7770
8614
  if (key.home) {
7771
- if (managedLive && buffer.length === 0) {
7772
- dispatch({ type: "scrollToTop" });
7773
- } else {
7774
- setDraft(buffer, 0);
7775
- }
8615
+ setDraft(buffer, 0);
7776
8616
  return;
7777
8617
  }
7778
8618
  if (key.end) {
7779
- if (managedLive && buffer.length === 0) {
7780
- dispatch({ type: "scrollToBottom" });
7781
- } else {
7782
- setDraft(buffer, buffer.length);
7783
- }
8619
+ setDraft(buffer, buffer.length);
7784
8620
  return;
7785
8621
  }
7786
8622
  if (key.upArrow) {
@@ -7813,7 +8649,7 @@ function App({
7813
8649
  setDraft("", 0);
7814
8650
  return;
7815
8651
  }
7816
- if (key.delete || key.ctrl && input === "d") {
8652
+ if (key.ctrl && input === "d") {
7817
8653
  if (cursor >= buffer.length) return;
7818
8654
  const span = tokenLengthForward(buffer, cursor) || 1;
7819
8655
  const next2 = buffer.slice(0, cursor) + buffer.slice(cursor + span);
@@ -7940,10 +8776,20 @@ function App({
7940
8776
  } finally {
7941
8777
  activeCtrlRef.current = null;
7942
8778
  dispatch({ type: "status", status: "idle" });
8779
+ if (chimeRef.current) {
8780
+ try {
8781
+ process.stdout.write("\x07");
8782
+ } catch {
8783
+ }
8784
+ }
7943
8785
  }
7944
8786
  const head = stateRef.current.queue[0];
7945
8787
  if (head) {
7946
8788
  dispatch({ type: "dequeueFirst" });
8789
+ dispatch({
8790
+ type: "addEntry",
8791
+ entry: { kind: "user", text: head.displayText }
8792
+ });
7947
8793
  await runBlocks(head.blocks);
7948
8794
  }
7949
8795
  };
@@ -8088,6 +8934,10 @@ function App({
8088
8934
  void runParallelLoopRef.current();
8089
8935
  }
8090
8936
  }
8937
+ if (getModeLabel) {
8938
+ const currentMode = getModeLabel();
8939
+ if (currentMode !== liveModeLabel) setLiveModeLabel(currentMode);
8940
+ }
8091
8941
  if (res?.exit) {
8092
8942
  exit();
8093
8943
  onExit(0);
@@ -8107,6 +8957,7 @@ function App({
8107
8957
  const cmd = trimmed.slice(1).split(/\s+/, 1)[0];
8108
8958
  if (cmd === "clear") {
8109
8959
  onClearHistory?.(dispatch);
8960
+ tokenCounter?.reset();
8110
8961
  }
8111
8962
  } catch (err) {
8112
8963
  dispatch({
@@ -8119,6 +8970,52 @@ function App({
8119
8970
  const builder = builderRef.current;
8120
8971
  if (!builder) return;
8121
8972
  const steering = state.steeringPending;
8973
+ let effectiveText = trimmed;
8974
+ const hasChips = trimmed ? new RegExp(INLINE_TOKEN_SRC, "g").test(trimmed) : false;
8975
+ if (enhanceEnabledRef.current && state.status === "idle" && !steering && !hasChips && shouldEnhance(trimmed)) {
8976
+ dispatch({ type: "enhanceBusy", on: true });
8977
+ const ac = new AbortController();
8978
+ enhanceAbortRef.current = ac;
8979
+ let refined = null;
8980
+ let enhanceErr = null;
8981
+ try {
8982
+ refined = await enhanceUserPrompt({
8983
+ provider: agent.ctx.provider,
8984
+ model: agent.ctx.model,
8985
+ text: trimmed,
8986
+ signal: ac.signal,
8987
+ onError: (reason) => {
8988
+ enhanceErr = reason;
8989
+ },
8990
+ // Feed recent conversation so follow-ups ("do the same", "that file")
8991
+ // resolve against context instead of being refined blind.
8992
+ history: recentTextTurns(agent.ctx.messages)
8993
+ });
8994
+ } finally {
8995
+ enhanceAbortRef.current = null;
8996
+ dispatch({ type: "enhanceBusy", on: false });
8997
+ }
8998
+ if (refined === null && !ac.signal.aborted) {
8999
+ dispatch({
9000
+ type: "addEntry",
9001
+ entry: {
9002
+ kind: "info",
9003
+ text: enhanceErr ? `\u2728 refinement unavailable (${enhanceErr}) \u2014 sent your message as-is` : "\u2728 refinement unavailable \u2014 sent your message as-is"
9004
+ }
9005
+ });
9006
+ }
9007
+ if (refined && !normalizedEqual(refined, trimmed)) {
9008
+ const decision = await new Promise((resolve) => {
9009
+ dispatch({ type: "enhanceOpen", info: { original: trimmed, refined, resolve } });
9010
+ });
9011
+ dispatch({ type: "enhanceClose" });
9012
+ if (decision === "edit") {
9013
+ setDraft(refined, refined.length);
9014
+ return;
9015
+ }
9016
+ effectiveText = decision === "refined" ? refined : trimmed;
9017
+ }
9018
+ }
8122
9019
  const sddContext = getSDDContext?.();
8123
9020
  if (sddContext && trimmed) {
8124
9021
  builder.appendText(`[SDD SESSION ACTIVE]
@@ -8129,11 +9026,11 @@ User message:
8129
9026
  `);
8130
9027
  }
8131
9028
  if (trimmed) {
8132
- const toAppend = steering ? buildSteeringPreamble(state.steerSnapshot, trimmed) : trimmed;
9029
+ const toAppend = steering ? buildSteeringPreamble(state.steerSnapshot, effectiveText) : effectiveText;
8133
9030
  builder.appendText(toAppend);
8134
9031
  }
8135
9032
  if (steering) dispatch({ type: "steerConsume" });
8136
- const displayText = trimmed ? steering ? `\u21AF ${trimmed}` : trimmed : "(attachments only)";
9033
+ const displayText = trimmed ? steering ? `\u21AF ${effectiveText}` : effectiveText : "(attachments only)";
8137
9034
  const pasteParts = [];
8138
9035
  for (const m of trimmed.matchAll(new RegExp(INLINE_TOKEN_SRC, "g"))) {
8139
9036
  const token = m[0];
@@ -8193,20 +9090,9 @@ User message:
8193
9090
  if (state.picker.open) return "";
8194
9091
  return "";
8195
9092
  }, [state.buffer, state.status, state.picker.open]);
8196
- const affordanceShown = managedLive && state.scrollOffset > 0 && state.pendingNewLines > 0;
8197
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", height: managedLive ? termRows : void 0, children: [
8198
- managedLive ? /* @__PURE__ */ jsx(
8199
- ScrollableHistory,
8200
- {
8201
- entries: state.entries,
8202
- streamingText: state.streamingText,
8203
- toolStream: state.toolStream,
8204
- scrollOffset: state.scrollOffset,
8205
- viewportRows: state.viewportRows || Math.max(MIN_VIEWPORT, termRows - 8),
8206
- totalLines: state.totalLines,
8207
- onMeasure: (total) => dispatch({ type: "setMeasuredLines", totalLines: total })
8208
- }
8209
- ) : /* @__PURE__ */ jsx(
9093
+ const enhanceActive = state.enhanceBusy || state.enhance != null;
9094
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 0, children: [
9095
+ /* @__PURE__ */ jsx(
8210
9096
  History,
8211
9097
  {
8212
9098
  entries: state.entries,
@@ -8214,17 +9100,16 @@ User message:
8214
9100
  toolStream: state.toolStream
8215
9101
  }
8216
9102
  ),
8217
- affordanceShown ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2193 ${state.pendingNewLines} new line${state.pendingNewLines === 1 ? "" : "s"} \u2014 PgDn or click to jump to bottom` }) : null,
8218
- /* @__PURE__ */ jsxs(Box, { ref: managedLive ? bottomRef : void 0, flexDirection: "column", flexShrink: 0, children: [
9103
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [
8219
9104
  /* @__PURE__ */ jsx(LiveActivityStrip, { entries: state.fleet, nowTick }),
8220
9105
  /* @__PURE__ */ jsx(
8221
9106
  Input,
8222
9107
  {
8223
9108
  prompt: INPUT_PROMPT,
8224
- value: state.buffer,
8225
- cursor: state.cursor,
9109
+ value: enhanceActive ? "" : state.buffer,
9110
+ cursor: enhanceActive ? 0 : state.cursor,
8226
9111
  disabled: state.status === "aborting" && !state.steeringPending || state.confirmQueue.length > 0,
8227
- hint: inputHint,
9112
+ hint: enhanceActive ? "" : inputHint,
8228
9113
  onKey: handleKey
8229
9114
  }
8230
9115
  ),
@@ -8271,19 +9156,42 @@ User message:
8271
9156
  field: state.settingsPicker.field,
8272
9157
  mode: state.settingsPicker.mode,
8273
9158
  delayMs: state.settingsPicker.delayMs,
9159
+ titleAnimation: state.settingsPicker.titleAnimation,
9160
+ yolo: state.settingsPicker.yolo,
9161
+ streamFleet: state.settingsPicker.streamFleet,
9162
+ chime: state.settingsPicker.chime,
9163
+ confirmExit: state.settingsPicker.confirmExit,
9164
+ nextPrediction: state.settingsPicker.nextPrediction,
9165
+ featureMcp: state.settingsPicker.featureMcp,
9166
+ featurePlugins: state.settingsPicker.featurePlugins,
9167
+ featureMemory: state.settingsPicker.featureMemory,
9168
+ featureSkills: state.settingsPicker.featureSkills,
9169
+ featureModelsRegistry: state.settingsPicker.featureModelsRegistry,
9170
+ contextAutoCompact: state.settingsPicker.contextAutoCompact,
9171
+ contextStrategy: state.settingsPicker.contextStrategy,
9172
+ logLevel: state.settingsPicker.logLevel,
9173
+ auditLevel: state.settingsPicker.auditLevel,
9174
+ indexOnStart: state.settingsPicker.indexOnStart,
9175
+ maxIterations: state.settingsPicker.maxIterations,
8274
9176
  hint: state.settingsPicker.hint
8275
9177
  }
8276
9178
  ) : null,
8277
- state.rewindOverlay ? /* @__PURE__ */ jsx(
8278
- CheckpointTimeline,
8279
- {
8280
- checkpoints: state.rewindOverlay.checkpoints,
8281
- selected: state.rewindOverlay.selected,
8282
- onSelect: (i) => dispatch({ type: "rewindOverlayMove", delta: i - state.rewindOverlay.selected }),
8283
- onConfirm: (i) => handleRewindTo(state.rewindOverlay.checkpoints[i].promptIndex),
8284
- onClose: () => dispatch({ type: "rewindOverlayClose" })
8285
- }
8286
- ) : null,
9179
+ state.rewindOverlay ? (() => {
9180
+ const overlay = state.rewindOverlay;
9181
+ return /* @__PURE__ */ jsx(
9182
+ CheckpointTimeline,
9183
+ {
9184
+ checkpoints: overlay.checkpoints,
9185
+ selected: overlay.selected,
9186
+ onSelect: (i) => dispatch({ type: "rewindOverlayMove", delta: i - overlay.selected }),
9187
+ onConfirm: (i) => {
9188
+ const checkpoint = overlay.checkpoints[i];
9189
+ if (checkpoint) handleRewindTo(checkpoint.promptIndex);
9190
+ },
9191
+ onClose: () => dispatch({ type: "rewindOverlayClose" })
9192
+ }
9193
+ );
9194
+ })() : null,
8287
9195
  state.brainPrompt ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx(
8288
9196
  BrainDecisionPrompt,
8289
9197
  {
@@ -8295,7 +9203,7 @@ User message:
8295
9203
  }
8296
9204
  ) }) : null,
8297
9205
  state.confirmQueue.length > 0 && (() => {
8298
- const head = state.confirmQueue[0];
9206
+ const head = expectDefined6(state.confirmQueue[0]);
8299
9207
  let resolved = false;
8300
9208
  const onDecision = (decision) => {
8301
9209
  if (resolved) return;
@@ -8313,6 +9221,25 @@ User message:
8313
9221
  }
8314
9222
  );
8315
9223
  })(),
9224
+ state.enhanceBusy && !state.enhance ? /* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u2728 refining your request\u2026" }) }) : null,
9225
+ state.enhance ? (() => {
9226
+ const info = state.enhance;
9227
+ let resolved = false;
9228
+ const onDecision = (decision) => {
9229
+ if (resolved) return;
9230
+ resolved = true;
9231
+ info.resolve(decision);
9232
+ };
9233
+ return /* @__PURE__ */ jsx(
9234
+ EnhancePanel,
9235
+ {
9236
+ original: info.original,
9237
+ refined: info.refined,
9238
+ delayMs: enhanceDelayMs,
9239
+ onDecision
9240
+ }
9241
+ );
9242
+ })() : null,
8316
9243
  /* @__PURE__ */ jsx(
8317
9244
  StatusBar,
8318
9245
  {
@@ -8324,7 +9251,7 @@ User message:
8324
9251
  queueCount: state.queue.length,
8325
9252
  yolo: yoloLive,
8326
9253
  autonomy: autonomyLive,
8327
- elapsedMs,
9254
+ startedAt: startedAtRef.current,
8328
9255
  todos,
8329
9256
  plan: planCounts ?? void 0,
8330
9257
  fleet: fleetCounts,
@@ -8336,26 +9263,18 @@ User message:
8336
9263
  processCount: getProcessRegistry().activeCount,
8337
9264
  hiddenItems,
8338
9265
  eternalStage: state.eternalStage,
8339
- goalSummary: state.goalSummary
9266
+ goalSummary: state.goalSummary,
9267
+ indexState,
9268
+ modeLabel: liveModeLabel || void 0
8340
9269
  }
8341
9270
  ),
8342
- managedLive ? /* @__PURE__ */ jsx(
8343
- KeyHintBar,
8344
- {
8345
- context: {
8346
- confirm: state.confirmQueue.length > 0,
8347
- picker: state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || !!state.rewindOverlay,
8348
- monitor: state.agentsMonitorOpen || state.monitorOpen || state.worktreeMonitorOpen || !!state.autoPhase?.monitorOpen,
8349
- managed: managedLive
8350
- }
8351
- }
8352
- ) : null,
8353
- state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, { managed: managedLive }) : null,
9271
+ state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, {}) : null,
8354
9272
  state.agentsMonitorOpen ? /* @__PURE__ */ jsx(
8355
9273
  AgentsMonitor,
8356
9274
  {
8357
9275
  entries: entriesWithLeader,
8358
9276
  totalCost: state.fleetCost,
9277
+ leaderCost: tokenCounter?.estimateCost().total ?? 0,
8359
9278
  totalTokens: state.fleetTokens,
8360
9279
  nowTick
8361
9280
  }
@@ -8376,7 +9295,7 @@ User message:
8376
9295
  nowTick,
8377
9296
  onClose: () => dispatch({ type: "worktreeMonitorToggle" })
8378
9297
  }
8379
- ) : state.monitorOpen ? /* @__PURE__ */ jsx(
9298
+ ) : state.todosMonitorOpen ? /* @__PURE__ */ jsx(TodosMonitor, { todos: agent.ctx.todos }) : state.monitorOpen ? /* @__PURE__ */ jsx(
8380
9299
  FleetMonitor,
8381
9300
  {
8382
9301
  entries: state.fleet,
@@ -8403,9 +9322,10 @@ User message:
8403
9322
  nowTick
8404
9323
  }
8405
9324
  ) : null,
8406
- Object.keys(state.worktrees).length > 0 && !state.worktreeMonitorOpen && !state.monitorOpen ? /* @__PURE__ */ jsx(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null
9325
+ Object.keys(state.worktrees).length > 0 && !state.worktreeMonitorOpen && !state.monitorOpen ? /* @__PURE__ */ jsx(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null,
9326
+ state.queuePanelOpen ? /* @__PURE__ */ jsx(QueuePanel, { items: state.queue }) : null
8407
9327
  ] })
8408
- ] });
9328
+ ] }) });
8409
9329
  }
8410
9330
  function renderRunningTools(running) {
8411
9331
  if (running.size === 0) return "";
@@ -8505,9 +9425,6 @@ function startTerminalTitle(opts) {
8505
9425
  // src/run-tui.ts
8506
9426
  var BRACKETED_PASTE_ON = "\x1B[?2004h";
8507
9427
  var BRACKETED_PASTE_OFF = "\x1B[?2004l";
8508
- var ALT_SCREEN_ON = "\x1B[?1049h";
8509
- var ALT_SCREEN_OFF = "\x1B[?1049l";
8510
- var CURSOR_HOME = "\x1B[H";
8511
9428
  async function runTui(opts) {
8512
9429
  const stdout = process.stdout;
8513
9430
  const stdin = process.stdin;
@@ -8517,14 +9434,11 @@ async function runTui(opts) {
8517
9434
  );
8518
9435
  return 2;
8519
9436
  }
8520
- const useAltScreen = opts.altScreen === true;
8521
- if (useAltScreen) {
8522
- stdout.write(ALT_SCREEN_ON);
8523
- stdout.write(CURSOR_HOME);
8524
- }
8525
9437
  stdout.write(BRACKETED_PASTE_ON);
9438
+ stdout.write("\x1B[2J\x1B[H");
8526
9439
  const inkStdin = stdin;
8527
- const stopTitle = startTerminalTitle({ stdout, events: opts.events, model: opts.model });
9440
+ const stopTitle = opts.titleAnimation !== false ? startTerminalTitle({ stdout, events: opts.events, model: opts.model }) : (() => {
9441
+ });
8528
9442
  const swallowSignals = ["SIGTSTP", "SIGQUIT", "SIGTTIN", "SIGTTOU"];
8529
9443
  const swallow = () => {
8530
9444
  };
@@ -8544,9 +9458,6 @@ async function runTui(opts) {
8544
9458
  }
8545
9459
  try {
8546
9460
  stdout.write(BRACKETED_PASTE_OFF);
8547
- if (useAltScreen) {
8548
- stdout.write(ALT_SCREEN_OFF);
8549
- }
8550
9461
  } catch {
8551
9462
  }
8552
9463
  };
@@ -8573,18 +9484,12 @@ async function runTui(opts) {
8573
9484
  const settle = (code) => {
8574
9485
  cleanup();
8575
9486
  detachListeners();
8576
- if (useAltScreen && opts.onAfterExit) {
8577
- try {
8578
- opts.onAfterExit();
8579
- } catch {
8580
- }
8581
- }
8582
9487
  resolve(code);
8583
9488
  };
8584
9489
  let instance;
8585
9490
  try {
8586
9491
  instance = render(
8587
- React5.createElement(App, {
9492
+ React6.createElement(App, {
8588
9493
  agent: opts.agent,
8589
9494
  slashRegistry: opts.slashRegistry,
8590
9495
  attachments: opts.attachments,
@@ -8614,8 +9519,10 @@ async function runTui(opts) {
8614
9519
  onExit,
8615
9520
  director: opts.director ?? null,
8616
9521
  fleetRoster: opts.fleetRoster,
8617
- onClearHistory: opts.onClearHistory ? (dispatch) => opts.onClearHistory(dispatch) : void 0,
9522
+ onClearHistory: opts.onClearHistory ? (dispatch) => opts.onClearHistory?.(dispatch) : void 0,
8618
9523
  fleetStreamController: opts.fleetStreamController,
9524
+ enhanceController: opts.enhanceController,
9525
+ enhanceEnabled: opts.enhanceController?.enabled ?? true,
8619
9526
  statuslineHiddenItems: opts.statuslineHiddenItems,
8620
9527
  setStatuslineHiddenItems: opts.setStatuslineHiddenItems,
8621
9528
  agentsMonitorController: opts.agentsMonitorController,
@@ -8628,9 +9535,10 @@ async function runTui(opts) {
8628
9535
  getSettings: opts.getSettings,
8629
9536
  saveSettings: opts.saveSettings,
8630
9537
  predictNext: opts.predictNext,
8631
- // Managed viewport (in-app scroll + collapsibility) follows
8632
- // alt-screen: it owns the screen, so there's no native-scrollback leak.
8633
- managed: useAltScreen
9538
+ chime: opts.chime,
9539
+ confirmExit: opts.confirmExit,
9540
+ modeLabel: opts.modeLabel,
9541
+ getModeLabel: opts.getModeLabel
8634
9542
  }),
8635
9543
  { exitOnCtrlC: false, stdin: inkStdin }
8636
9544
  );
@@ -8643,16 +9551,14 @@ async function runTui(opts) {
8643
9551
  return;
8644
9552
  }
8645
9553
  let detachResize = null;
8646
- if (!useAltScreen) {
8647
- const onResize = () => {
8648
- try {
8649
- stdout.write("\x1B[J");
8650
- } catch {
8651
- }
8652
- };
8653
- stdout.on("resize", onResize);
8654
- detachResize = () => stdout.off("resize", onResize);
8655
- }
9554
+ const onResize = () => {
9555
+ try {
9556
+ stdout.write("\x1B[J");
9557
+ } catch {
9558
+ }
9559
+ };
9560
+ stdout.on("resize", onResize);
9561
+ detachResize = () => stdout.off("resize", onResize);
8656
9562
  instance.waitUntilExit().then(() => {
8657
9563
  detachResize?.();
8658
9564
  settle(exitCode);