@wrongstack/tui 0.68.0 → 0.77.0

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,12 +1,11 @@
1
- import { Readable } from 'stream';
2
- 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';
3
2
  export { buildGoalPreamble } from '@wrongstack/core';
4
3
  import { Box, Text, render, useApp, useStdout, measureElement, Static, useInput, useStdin } from 'ink';
5
4
  import React6, { useState, useEffect, useReducer, useRef, useMemo, useCallback, useLayoutEffect } from 'react';
6
5
  import * as fs2 from 'fs/promises';
7
6
  import * as path2 from 'path';
8
7
  import { routeImagesForModel } from '@wrongstack/runtime/vision';
9
- import { getProcessRegistry } from '@wrongstack/tools';
8
+ import { getIndexState, onIndexStateChange, getProcessRegistry } from '@wrongstack/tools';
10
9
  import { readClipboardImage } from '@wrongstack/runtime/clipboard';
11
10
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
12
11
  import { spawn } from 'child_process';
@@ -35,8 +34,28 @@ var theme = Object.freeze({
35
34
  diffAddBg: "greenBright",
36
35
  diffDelBg: "redBright"
37
36
  });
37
+ var MODE_ICONS = {
38
+ teach: "\u{1F9D1}\u200D\u{1F3EB}",
39
+ brief: "\u26A1",
40
+ "code-reviewer": "\u{1F50D}",
41
+ "bug-hunter": "\u{1F41B}",
42
+ "security-scanner": "\u{1F6E1}\uFE0F",
43
+ "refactor-planner": "\u{1F527}",
44
+ architect: "\u{1F3D7}\uFE0F",
45
+ debugger: "\u{1FAB2}",
46
+ test: "\u{1F9EA}",
47
+ document: "\u{1F4DD}",
48
+ "skill-creator": "\u{1F6E0}\uFE0F"
49
+ };
50
+ function modeIcon(label) {
51
+ if (!label) return "";
52
+ const icon = MODE_ICONS[label] ?? "\u25AA";
53
+ return `${icon} ${label}`;
54
+ }
38
55
  var COMPACT_THRESHOLD = 50;
39
56
  var COMFORTABLE_THRESHOLD = 90;
57
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
58
+ var SPINNER_INTERVAL_MS = 130;
40
59
  function StatusBar({
41
60
  model,
42
61
  version,
@@ -59,7 +78,9 @@ function StatusBar({
59
78
  processCount,
60
79
  hiddenItems,
61
80
  eternalStage,
62
- goalSummary
81
+ goalSummary,
82
+ indexState,
83
+ modeLabel
63
84
  }) {
64
85
  const { stdout } = useStdout();
65
86
  const [termWidth, setTermWidth] = useState(stdout?.columns ?? 90);
@@ -77,8 +98,19 @@ function StatusBar({
77
98
  const usage = tokenCounter?.total();
78
99
  const cost = tokenCounter?.estimateCost();
79
100
  const cache2 = tokenCounter?.cacheStats();
101
+ const [spinnerIdx, setSpinnerIdx] = useState(0);
102
+ useEffect(() => {
103
+ if (state === "idle" || state === "aborting") return;
104
+ const t = setInterval(
105
+ () => setSpinnerIdx((n) => (n + 1) % SPINNER_FRAMES.length),
106
+ SPINNER_INTERVAL_MS
107
+ );
108
+ return () => clearInterval(t);
109
+ }, [state]);
110
+ const spinner = SPINNER_FRAMES[spinnerIdx];
80
111
  const { label: stateLabel, color: stateColor } = stateChip(state, fleet?.running ?? 0);
81
- 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;
112
+ const statePrefix = state === "idle" || state === "aborting" ? "\u25CF" : spinner;
113
+ 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 || !!modeLabel;
82
114
  const fleetHasActivity = fleet && (fleet.running > 0 || fleet.idle > 0 || fleet.pending > 0 || fleet.completed > 0) || subagentCount > 0;
83
115
  const hasBrainActivity = !!brain && brain.state !== "idle";
84
116
  const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity || hasBrainActivity;
@@ -97,7 +129,7 @@ function StatusBar({
97
129
  // Ultra-compact: state · model
98
130
  /* @__PURE__ */ jsxs(Fragment, { children: [
99
131
  /* @__PURE__ */ jsxs(Text, { color: stateColor, children: [
100
- "\u25CF",
132
+ statePrefix,
101
133
  stateLabel
102
134
  ] }),
103
135
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
@@ -117,7 +149,8 @@ function StatusBar({
117
149
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" })
118
150
  ] }) : null,
119
151
  /* @__PURE__ */ jsxs(Text, { color: stateColor, children: [
120
- "\u25CF ",
152
+ statePrefix,
153
+ " ",
121
154
  stateLabel
122
155
  ] }),
123
156
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
@@ -171,6 +204,15 @@ function StatusBar({
171
204
  hint ? /* @__PURE__ */ jsxs(Fragment, { children: [
172
205
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
173
206
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint })
207
+ ] }) : null,
208
+ indexState && indexState.indexing ? /* @__PURE__ */ jsxs(Fragment, { children: [
209
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
210
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
211
+ "\u2699 indexing ",
212
+ indexState.currentFile,
213
+ "/",
214
+ indexState.totalFiles
215
+ ] })
174
216
  ] }) : null
175
217
  ] })
176
218
  ) }),
@@ -228,6 +270,10 @@ function StatusBar({
228
270
  }
229
271
  )
230
272
  ] }) : null,
273
+ modeLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
274
+ yolo || autonomy && autonomy !== "off" || eternalStage || elapsedMs !== void 0 || projectName || goalSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
275
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: modeIcon(modeLabel) })
276
+ ] }) : null,
231
277
  git ? /* @__PURE__ */ jsxs(Fragment, { children: [
232
278
  yolo || elapsedMs !== void 0 || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
233
279
  /* @__PURE__ */ jsxs(Text, { children: [
@@ -249,7 +295,7 @@ function StatusBar({
249
295
  ] }) : null
250
296
  ] })
251
297
  ] }) : null
252
- ] }) : null,
298
+ ] }) : /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }),
253
299
  hasThirdLine ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
254
300
  todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) ? /* @__PURE__ */ jsxs(Text, { children: [
255
301
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "todos " }),
@@ -323,7 +369,7 @@ function StatusBar({
323
369
  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,
324
370
  /* @__PURE__ */ jsx(BrainChip, { brain })
325
371
  ] }) : null
326
- ] }) : null,
372
+ ] }) : /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }),
327
373
  fleetAgents && fleetAgents.length > 0 ? /* @__PURE__ */ jsx(Box, { flexDirection: "row", gap: 2, children: fleetAgents.map((a, i) => (
328
374
  // biome-ignore lint/suspicious/noArrayIndexKey: agent list is stable per render
329
375
  /* @__PURE__ */ jsxs(Text, { children: [
@@ -349,7 +395,7 @@ function StatusBar({
349
395
  ] })
350
396
  ] }) : null
351
397
  ] }, i)
352
- )) }) : null
398
+ )) }) : /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) })
353
399
  ]
354
400
  }
355
401
  );
@@ -450,28 +496,6 @@ function ContextChip({ ctx }) {
450
496
  ] })
451
497
  ] });
452
498
  }
453
- var SB_GAP = 2;
454
- var SB_PADX = 1;
455
- function statusBarModelSpan(opts) {
456
- let col = SB_PADX;
457
- if (opts.version) {
458
- col += `WS v${opts.version}`.length + SB_GAP;
459
- col += 1 + SB_GAP;
460
- }
461
- const { label } = stateChip(opts.state, opts.fleetRunning ?? 0);
462
- col += 2 + label.length + SB_GAP;
463
- col += 1 + SB_GAP;
464
- return { start: col, len: opts.model.length };
465
- }
466
- function statusBarAutonomySpan(opts) {
467
- if (!opts.autonomy || opts.autonomy === "off") return null;
468
- let col = SB_PADX;
469
- if (opts.yolo) {
470
- col += "\u26A0 YOLO".length + SB_GAP;
471
- col += 1 + SB_GAP;
472
- }
473
- return { start: col, len: 2 + opts.autonomy.toUpperCase().length };
474
- }
475
499
  function stateChip(state, fleetRunning) {
476
500
  if (state === "idle" && fleetRunning > 0) {
477
501
  return { label: `agents \u25B6${fleetRunning}`, color: "magenta" };
@@ -802,10 +826,12 @@ function ContextBar({
802
826
  function AgentsMonitor({
803
827
  entries,
804
828
  totalCost,
829
+ leaderCost = 0,
805
830
  totalTokens,
806
831
  nowTick
807
832
  }) {
808
833
  const all = Object.values(entries);
834
+ const grandCost = leaderCost + totalCost;
809
835
  const live = selectLiveAgents(all, nowTick);
810
836
  const running = live.filter((e) => e.status === "running").length;
811
837
  const totalDone = all.filter((e) => e.status === "success").length;
@@ -847,9 +873,17 @@ function AgentsMonitor({
847
873
  fmtTokens2(totalTokens.output),
848
874
  "\u2193"
849
875
  ] }) : null,
850
- /* @__PURE__ */ jsxs(Text, { color: "green", children: [
876
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "total" }),
877
+ /* @__PURE__ */ jsxs(Text, { color: "green", bold: true, children: [
851
878
  "$",
852
- totalCost.toFixed(3)
879
+ grandCost.toFixed(3)
880
+ ] }),
881
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
882
+ "(leader $",
883
+ leaderCost.toFixed(3),
884
+ " \xB7 fleet $",
885
+ totalCost.toFixed(3),
886
+ ")"
853
887
  ] }),
854
888
  hiddenIdle > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
855
889
  "\xB7 ",
@@ -892,6 +926,10 @@ function AgentsMonitor({
892
926
  e.extensions && e.extensions > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
893
927
  "\u26A1\xD7",
894
928
  e.extensions
929
+ ] }) : null,
930
+ e.cost > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
931
+ "$",
932
+ e.cost.toFixed(3)
895
933
  ] }) : null
896
934
  ] }),
897
935
  e.status === "running" && e.currentTool ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, paddingLeft: 2, children: [
@@ -977,7 +1015,7 @@ function AutonomyPicker({
977
1015
  selected,
978
1016
  hint
979
1017
  }) {
980
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
1018
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [
981
1019
  /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Autonomy Mode \u2501\u2501" }),
982
1020
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel \xB7 Ctrl+C exit" }),
983
1021
  options.map((opt, i) => /* @__PURE__ */ jsxs(
@@ -1100,7 +1138,7 @@ function CheckpointTimeline({
1100
1138
  onConfirm(selected);
1101
1139
  }
1102
1140
  });
1103
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", padding: 1, children: [
1141
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, children: [
1104
1142
  /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
1105
1143
  /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u27F2 Session Rewind" }),
1106
1144
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2014 \u2191/\u2193 navigate \xB7 Enter rewind \xB7 Esc cancel" })
@@ -1140,16 +1178,6 @@ function buttonLabels(suggestedPattern) {
1140
1178
  { decision: "deny", bracket: "[d]", rest: "eny" }
1141
1179
  ];
1142
1180
  }
1143
- function confirmButtonSegments(suggestedPattern) {
1144
- const out = [];
1145
- let col = 0;
1146
- for (const l of buttonLabels(suggestedPattern)) {
1147
- const len = l.bracket.length + l.rest.length;
1148
- out.push({ decision: l.decision, start: col, len });
1149
- col += len;
1150
- }
1151
- return out;
1152
- }
1153
1181
  function stringifyInput(input) {
1154
1182
  if (!input || typeof input !== "object") return "";
1155
1183
  const obj = input;
@@ -1215,15 +1243,76 @@ function ConfirmPrompt({
1215
1243
  ] }, l.decision)) }) })
1216
1244
  ] });
1217
1245
  }
1246
+ function EnhancePanel({
1247
+ original,
1248
+ refined,
1249
+ delayMs,
1250
+ onDecision
1251
+ }) {
1252
+ const totalSecs = Math.max(1, Math.ceil(delayMs / 1e3));
1253
+ const [remaining, setRemaining] = React6.useState(totalSecs);
1254
+ const decideRef = React6.useRef(onDecision);
1255
+ decideRef.current = onDecision;
1256
+ React6.useEffect(() => {
1257
+ const id = setInterval(() => {
1258
+ setRemaining((r) => {
1259
+ if (r <= 1) {
1260
+ clearInterval(id);
1261
+ decideRef.current("refined");
1262
+ return 0;
1263
+ }
1264
+ return r - 1;
1265
+ });
1266
+ }, 1e3);
1267
+ return () => clearInterval(id);
1268
+ }, []);
1269
+ useInput((input, key) => {
1270
+ if (key.return) {
1271
+ onDecision("refined");
1272
+ } else if (key.escape) {
1273
+ onDecision("original");
1274
+ } else if (input?.toLowerCase() === "e") {
1275
+ onDecision("edit");
1276
+ }
1277
+ });
1278
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
1279
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
1280
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u2728 Refined request" }),
1281
+ /* @__PURE__ */ jsx(Text, { children: " " }),
1282
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1283
+ "\u2014 sending in ",
1284
+ remaining,
1285
+ "s"
1286
+ ] })
1287
+ ] }),
1288
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
1289
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "original: " }),
1290
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: original })
1291
+ ] }),
1292
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
1293
+ /* @__PURE__ */ jsx(Text, { color: "green", children: "refined: " }),
1294
+ /* @__PURE__ */ jsx(Text, { color: "white", children: refined })
1295
+ ] }),
1296
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
1297
+ /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsxs(Text, { children: [
1298
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "green", children: "[Enter]" }),
1299
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " send \xB7 " }),
1300
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: "[Esc]" }),
1301
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " use original \xB7 " }),
1302
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "[e]" }),
1303
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "dit" })
1304
+ ] }) })
1305
+ ] });
1306
+ }
1218
1307
  function FilePicker({ query, matches, selected }) {
1219
1308
  if (matches.length === 0) {
1220
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1309
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1221
1310
  "@",
1222
1311
  query,
1223
1312
  " \u2014 no matches"
1224
1313
  ] }) });
1225
1314
  }
1226
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
1315
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
1227
1316
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1228
1317
  "@",
1229
1318
  query || "\u2026",
@@ -1306,13 +1395,6 @@ function FleetPanel({
1306
1395
  function helpSections(opts) {
1307
1396
  const nav = [];
1308
1397
  if (opts.managed) nav.push({ keys: "PgUp/PgDn", desc: "scroll chat history" });
1309
- if (opts.mouse)
1310
- nav.push(
1311
- { keys: "wheel", desc: "scroll chat history" },
1312
- { keys: "drag scrollbar", desc: "scrub to any position" },
1313
- { keys: "click input", desc: "move the caret" },
1314
- { keys: "click", desc: "select / confirm" }
1315
- );
1316
1398
  nav.push(
1317
1399
  { keys: "\u2191/\u2193", desc: "previous / next input (empty prompt)" },
1318
1400
  { keys: "?", desc: "open this help (empty prompt)" }
@@ -1325,6 +1407,9 @@ function helpSections(opts) {
1325
1407
  { keys: "Ctrl+F / F2", desc: "fleet orchestration monitor" },
1326
1408
  { keys: "Ctrl+G / F3", desc: "agents live monitor" },
1327
1409
  { keys: "Ctrl+T / F4", desc: "worktree monitor" },
1410
+ { keys: "F5", desc: "autonomy settings (also Ctrl+S)" },
1411
+ { keys: "F6", desc: "todos monitor overlay" },
1412
+ { keys: "F7", desc: "queue panel" },
1328
1413
  { keys: "Esc", desc: "close the open monitor / overlay" }
1329
1414
  ]
1330
1415
  },
@@ -1353,10 +1438,9 @@ function helpSections(opts) {
1353
1438
  ];
1354
1439
  }
1355
1440
  function HelpOverlay({
1356
- managed,
1357
- mouse
1441
+ managed
1358
1442
  }) {
1359
- const sections = helpSections({ managed, mouse });
1443
+ const sections = helpSections({ managed });
1360
1444
  const keyWidth = Math.max(...sections.flatMap((s2) => s2.entries.map((e) => e.keys.length)), 0);
1361
1445
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1362
1446
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
@@ -1901,8 +1985,9 @@ function computeWidths(allRows, cols, maxWidth, sepWidths) {
1901
1985
  for (const row of allRows) {
1902
1986
  for (let c = 0; c < cols; c++) {
1903
1987
  const cell = row[c] ?? "";
1904
- const w = longestWord(cell);
1905
- const total = strWidth(cell);
1988
+ const stripped = stripInlineMarkers(cell);
1989
+ const w = longestWord(stripped);
1990
+ const total = strWidth(stripped);
1906
1991
  natural[c] = Math.max(natural[c], w, total);
1907
1992
  }
1908
1993
  }
@@ -1935,11 +2020,77 @@ function computeWidths(allRows, cols, maxWidth, sepWidths) {
1935
2020
  return widths;
1936
2021
  }
1937
2022
  var MIN_COL_WIDTH = 4;
2023
+ var LIGATURE_PAIRS = [
2024
+ ["-", ">"],
2025
+ // → arrow
2026
+ ["<", "-"],
2027
+ // ←
2028
+ ["=", ">"],
2029
+ // ⇒
2030
+ ["<", "="],
2031
+ // ≤
2032
+ [">", "="],
2033
+ // ≥
2034
+ ["!", "="],
2035
+ // ≠
2036
+ ["=", "="],
2037
+ // equality (some fonts)
2038
+ ["~", ">"],
2039
+ // ⇝
2040
+ ["<", "~"]
2041
+ // ⇜
2042
+ ];
2043
+ var ZWSP = "\u200B";
2044
+ function breakLigatures(text) {
2045
+ if (!/[-<=>!~]/.test(text)) return text;
2046
+ let result = "";
2047
+ for (let i = 0; i < text.length; i++) {
2048
+ result += text[i];
2049
+ if (i + 1 >= text.length) break;
2050
+ for (const [a, b] of LIGATURE_PAIRS) {
2051
+ if (text[i] === a && text[i + 1] === b) {
2052
+ result += ZWSP;
2053
+ break;
2054
+ }
2055
+ }
2056
+ }
2057
+ return result;
2058
+ }
1938
2059
  function strWidth(s2) {
1939
2060
  let width = 0;
1940
- for (const cp of s2) {
1941
- const code = cp.codePointAt(0);
2061
+ const len = s2.length;
2062
+ let i = 0;
2063
+ while (i < len) {
2064
+ if (s2[i] === "\x1B" && i + 1 < len && s2[i + 1] === "[") {
2065
+ i += 2;
2066
+ while (i < len && s2[i] !== "m") i++;
2067
+ if (i < len) i++;
2068
+ continue;
2069
+ }
2070
+ const code = s2.codePointAt(i);
2071
+ const cpLen = code > 65535 ? 2 : 1;
2072
+ if (code === 8205 || // ZWJ — Zero Width Joiner (emoji sequences)
2073
+ code === 8203 || // ZWSP — Zero Width Space
2074
+ code === 8204 || // ZWNJ — Zero Width Non-Joiner
2075
+ code === 8206 || // LRM — Left-to-Right Mark
2076
+ code === 8207 || // RLM — Right-to-Left Mark
2077
+ code === 8288 || // WJ — Word Joiner
2078
+ code === 65279 || // BOM / ZWNBSP
2079
+ code >= 65024 && code <= 65039 || // Variation Selectors 1–16
2080
+ code >= 917760 && code <= 917999) {
2081
+ i += cpLen;
2082
+ continue;
2083
+ }
1942
2084
  if (code < 32 || code >= 127 && code < 160) {
2085
+ i += cpLen;
2086
+ continue;
2087
+ }
2088
+ if (code >= 768 && code <= 879 || // Combining Diacritical Marks
2089
+ code >= 6832 && code <= 6911 || // Combining Diacritical Marks Extended
2090
+ code >= 7616 && code <= 7679 || // Combining Diacritical Marks Supplement
2091
+ code >= 8400 && code <= 8447 || // Combining Diacritical Marks for Symbols
2092
+ code >= 65056 && code <= 65071) {
2093
+ i += cpLen;
1943
2094
  continue;
1944
2095
  }
1945
2096
  if (code >= 126976 || // Supplementary Pictographs (U+1F000-U+1FFFF)
@@ -1952,11 +2103,11 @@ function strWidth(s2) {
1952
2103
  code >= 9664 && code <= 9726 || // More Geometric Shapes (includes ▶)
1953
2104
  code >= 9984 && code <= 10175) {
1954
2105
  width += 2;
2106
+ i += cpLen;
1955
2107
  continue;
1956
2108
  }
1957
2109
  if (code >= 4352 && code <= 4447 || // Hangul Jamo
1958
- code === 9001 || // LEFT-POINTING ANGLE BRACKET
1959
- code === 9002 || // RIGHT-POINTING ANGLE BRACKET
2110
+ code === 9001 || code === 9002 || // Angle brackets
1960
2111
  code >= 11904 && code <= 12350 || // CJK Radicals Supplement
1961
2112
  code >= 12352 && code <= 42191 || // Hiragana, Katakana, CJK
1962
2113
  code >= 44032 && code <= 55203 || // Hangul Syllables
@@ -1968,9 +2119,11 @@ function strWidth(s2) {
1968
2119
  code >= 131072 && code <= 196605 || // CJK Extension B+
1969
2120
  code >= 196608 && code <= 262141) {
1970
2121
  width += 2;
2122
+ i += cpLen;
1971
2123
  continue;
1972
2124
  }
1973
2125
  width += 1;
2126
+ i += cpLen;
1974
2127
  }
1975
2128
  return width;
1976
2129
  }
@@ -1985,8 +2138,22 @@ function longestWord(s2) {
1985
2138
  function border(left, mid, right, widths) {
1986
2139
  return left + widths.map((w) => "\u2500".repeat(w + 2)).join(mid) + right;
1987
2140
  }
2141
+ function stripInlineMarkers(text) {
2142
+ return text.replace(/\*\*(.+?)\*\*/g, "$1").replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "$1").replace(/`(.+?)`/g, "$1").replace(/~~(.+?)~~/g, "$1");
2143
+ }
2144
+ var ANSI_BOLD = "\x1B[1m";
2145
+ var ANSI_RESET = "\x1B[22m";
2146
+ var ANSI_DIM = "\x1B[2m";
2147
+ var ANSI_CYAN = "\x1B[36m";
2148
+ var ANSI_STRIKE = "\x1B[9m";
2149
+ var ANSI_RESET_ALL = "\x1B[0m";
2150
+ function applyInlineAnsi(text) {
2151
+ 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}`);
2152
+ }
1988
2153
  function renderRow(cells, widths, aligns) {
1989
- const wrapped = cells.map((c, i) => wrapCell(c, widths[i] ?? MIN_COL_WIDTH));
2154
+ const styled = cells.map((c) => applyInlineAnsi(c));
2155
+ const safe = styled.map((c) => breakLigatures(c));
2156
+ const wrapped = safe.map((c, i) => wrapCell(c, widths[i] ?? MIN_COL_WIDTH));
1990
2157
  const height = Math.max(1, ...wrapped.map((w) => w.length));
1991
2158
  const out = [];
1992
2159
  for (let line = 0; line < height; line++) {
@@ -3435,52 +3602,6 @@ function layoutInputRows(prompt, value, cursor, width) {
3435
3602
  if (row.length > 0 || rows.length === 0) rows.push(row);
3436
3603
  return rows;
3437
3604
  }
3438
- function inputIndexAtRowCol(prompt, value, width, row, col) {
3439
- const w = Math.max(1, Math.floor(width));
3440
- const flat = [];
3441
- for (let i = 0; i < prompt.length; i++) flat.push({ ch: prompt[i], buf: -1 });
3442
- for (let i = 0; i < value.length; i++) flat.push({ ch: value[i], buf: i });
3443
- const rows = [];
3444
- const starts = [];
3445
- let cur = [];
3446
- let curStart = 0;
3447
- for (const cell of flat) {
3448
- if (cell.ch === "\n") {
3449
- rows.push(cur);
3450
- starts.push(curStart);
3451
- cur = [];
3452
- curStart = cell.buf + 1;
3453
- continue;
3454
- }
3455
- if (cur.length === 0 && cell.buf >= 0) curStart = cell.buf;
3456
- cur.push({ buf: cell.buf });
3457
- if (cur.length >= w) {
3458
- rows.push(cur);
3459
- starts.push(curStart);
3460
- cur = [];
3461
- curStart = -1;
3462
- }
3463
- }
3464
- if (cur.length > 0 || rows.length === 0) {
3465
- rows.push(cur);
3466
- starts.push(curStart);
3467
- }
3468
- const clamp = (n) => Math.max(0, Math.min(value.length, n));
3469
- if (row < 0) return 0;
3470
- if (row >= rows.length) return value.length;
3471
- const r = rows[row];
3472
- const c = Math.max(0, col);
3473
- if (c < r.length) {
3474
- const b = r[c].buf;
3475
- return b < 0 ? 0 : clamp(b);
3476
- }
3477
- for (let k = r.length - 1; k >= 0; k--) {
3478
- const b = r[k].buf;
3479
- if (b >= 0) return clamp(b + 1);
3480
- }
3481
- const start = starts[row];
3482
- return clamp(start !== void 0 && start >= 0 ? start : value.length);
3483
- }
3484
3605
  function renderRow2(cells, rowKey, promptColor) {
3485
3606
  const out = [];
3486
3607
  let run = "";
@@ -3529,6 +3650,11 @@ function isHomeEnd(data) {
3529
3650
  return "end";
3530
3651
  return null;
3531
3652
  }
3653
+ function isBackspaceOrDelete(data) {
3654
+ if (data === "\x7F" || data === "\b") return "backspace";
3655
+ if (data === "\x1B[3~") return "delete";
3656
+ return null;
3657
+ }
3532
3658
  var EMPTY_KEY = {
3533
3659
  upArrow: false,
3534
3660
  downArrow: false,
@@ -3564,15 +3690,24 @@ function Input({
3564
3690
  if (!stdin || disabled) return;
3565
3691
  const handleData = (data) => {
3566
3692
  const s2 = data.toString();
3567
- const kind = isHomeEnd(s2);
3568
- if (kind === "home") {
3693
+ const homeEnd = isHomeEnd(s2);
3694
+ if (homeEnd === "home") {
3569
3695
  onKey("", { ...EMPTY_KEY, home: true });
3570
3696
  return;
3571
3697
  }
3572
- if (kind === "end") {
3698
+ if (homeEnd === "end") {
3573
3699
  onKey("", { ...EMPTY_KEY, end: true });
3574
3700
  return;
3575
3701
  }
3702
+ const bsdel = isBackspaceOrDelete(s2);
3703
+ if (bsdel === "backspace") {
3704
+ onKey("", { ...EMPTY_KEY, backspace: true });
3705
+ return;
3706
+ }
3707
+ if (bsdel === "delete") {
3708
+ onKey("", { ...EMPTY_KEY, delete: true });
3709
+ return;
3710
+ }
3576
3711
  const fn = fnKey(s2);
3577
3712
  if (fn !== null) onKey("", { ...EMPTY_KEY, fn });
3578
3713
  };
@@ -3620,8 +3755,7 @@ function hintsFor(ctx) {
3620
3755
  return [
3621
3756
  { key: "\u2191\u2193", label: "move" },
3622
3757
  { key: "\u21B5", label: "select" },
3623
- { key: "Esc", label: "cancel" },
3624
- ...ctx.mouse ? [{ key: "click", label: "pick" }] : []
3758
+ { key: "Esc", label: "cancel" }
3625
3759
  ];
3626
3760
  }
3627
3761
  if (ctx.monitor) {
@@ -3629,12 +3763,12 @@ function hintsFor(ctx) {
3629
3763
  { key: "Esc", label: "close" },
3630
3764
  { key: "^F", label: "fleet" },
3631
3765
  { key: "^G", label: "agents" },
3632
- { key: "^T", label: "worktrees" }
3766
+ { key: "^T", label: "worktrees" },
3767
+ { key: "F6", label: "todos" }
3633
3768
  ];
3634
3769
  }
3635
3770
  const base = [{ key: "?", label: "help" }];
3636
- if (ctx.managed) base.push({ key: "PgUp/PgDn", label: "scroll" });
3637
- if (ctx.mouse) base.push({ key: "wheel", label: "scroll" }, { key: "click", label: "select" });
3771
+ if (ctx.managed) base.push({ key: "PgUp/PgDn", label: "scroll" }, { key: "F5", label: "Settings" });
3638
3772
  base.push({ key: "^G", label: "agents" }, { key: "^C", label: "stop" });
3639
3773
  return base;
3640
3774
  }
@@ -3680,7 +3814,10 @@ var LiveActivityStrip = React6.memo(function LiveActivityStrip2({
3680
3814
  maxRows = 4
3681
3815
  }) {
3682
3816
  const running = Object.values(entries).filter((e) => e.status === "running").sort((a, b) => a.startedAt - b.startedAt).slice(0, maxRows);
3683
- if (running.length === 0) return null;
3817
+ if (running.length === 0) {
3818
+ if (Object.keys(entries).length === 0) return null;
3819
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingX: 1, children: Array.from({ length: maxRows }, (_, i) => /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }, `empty-${i}`)) });
3820
+ }
3684
3821
  const now = Date.now();
3685
3822
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
3686
3823
  running.map((e) => {
@@ -3750,7 +3887,7 @@ function ModelPicker({
3750
3887
  hint
3751
3888
  }) {
3752
3889
  if (step === "provider") {
3753
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
3890
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3754
3891
  /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Switch model \u2014 Step 1/2: Pick provider \u2501\u2501" }),
3755
3892
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel \xB7 Ctrl+C exit" }),
3756
3893
  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: [
@@ -3775,7 +3912,7 @@ function ModelPicker({
3775
3912
  const { start, end } = getVisibleWindow(selected, total);
3776
3913
  const visibleItems = filteredOptions.slice(start, end);
3777
3914
  const searchHint = searchQuery ? ` | filter:"${searchQuery}" \u2192 ${total} match${total === 1 ? "" : "es"}` : total > MAX_VISIBLE ? ` (${total} models \u2014 type to filter)` : "";
3778
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
3915
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3779
3916
  /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
3780
3917
  "\u2501\u2501 Switch model \u2014 Step 2/2: Pick model ",
3781
3918
  "(",
@@ -3990,6 +4127,52 @@ function PhasePanel({ phases, nowTick }) {
3990
4127
  }
3991
4128
  );
3992
4129
  }
4130
+ function QueuePanel({ items }) {
4131
+ const { stdout } = useStdout();
4132
+ const w = stdout?.columns ?? 80;
4133
+ const h = stdout?.rows ?? 24;
4134
+ const avail = Math.max(10, Math.floor(w * 0.3) - 4);
4135
+ const labelMax = Math.max(4, avail - 7);
4136
+ const trunc = (s2) => {
4137
+ if (s2.length <= labelMax) return s2;
4138
+ return s2.slice(0, labelMax - 1) + "\u2026";
4139
+ };
4140
+ const OVERHEAD = 7;
4141
+ const maxVisible = Math.max(4, h - OVERHEAD);
4142
+ const visible = items.slice(0, maxVisible);
4143
+ const overflow = items.length - visible.length;
4144
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
4145
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
4146
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "QUEUE" }),
4147
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: items.length }),
4148
+ overflow > 0 ? /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
4149
+ "+",
4150
+ overflow
4151
+ ] }) : null,
4152
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 F7 / Esc to close" })
4153
+ ] }) }),
4154
+ items.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No queued messages" }) : /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
4155
+ visible.map((item, i) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", flexShrink: 0, children: [
4156
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4157
+ String(i + 1).padStart(2),
4158
+ "."
4159
+ ] }),
4160
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4161
+ " ",
4162
+ trunc(item.displayText)
4163
+ ] })
4164
+ ] }, item.id)),
4165
+ overflow > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", flexShrink: 0, marginTop: 0, children: [
4166
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2026" }),
4167
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4168
+ " +",
4169
+ overflow,
4170
+ " more"
4171
+ ] })
4172
+ ] }) : null
4173
+ ] })
4174
+ ] });
4175
+ }
3993
4176
  var MAX_MOUNTED = 500;
3994
4177
  function scrollbarThumb(rows, offset, total) {
3995
4178
  const scrollable = total > rows;
@@ -4000,13 +4183,6 @@ function scrollbarThumb(rows, offset, total) {
4000
4183
  const top = Math.max(0, Math.min(rawTop, rows - size));
4001
4184
  return { top, size, scrollable: true };
4002
4185
  }
4003
- function scrollOffsetForTrackRow(rows, total, cell) {
4004
- if (total <= rows) return 0;
4005
- const maxOffset = total - rows;
4006
- const clampedCell = Math.max(0, Math.min(rows - 1, cell));
4007
- const windowTop = Math.round(clampedCell / Math.max(1, rows - 1) * maxOffset);
4008
- return Math.max(0, Math.min(maxOffset, maxOffset - windowTop));
4009
- }
4010
4186
  function Scrollbar({
4011
4187
  rows,
4012
4188
  offset,
@@ -4034,17 +4210,12 @@ function ScrollableHistory({
4034
4210
  scrollOffset,
4035
4211
  viewportRows,
4036
4212
  totalLines,
4037
- onMeasure
4213
+ onMeasure,
4214
+ maxWidth
4038
4215
  }) {
4039
4216
  const { stdout } = useStdout();
4040
- const [termWidth, setTermWidth] = useState(stdout?.columns ?? 80);
4041
- useEffect(() => {
4042
- const onResize = () => setTermWidth(stdout?.columns ?? 80);
4043
- process.stdout.on("resize", onResize);
4044
- return () => {
4045
- process.stdout.off("resize", onResize);
4046
- };
4047
- }, [stdout]);
4217
+ const rawWidth = stdout?.columns ?? 80;
4218
+ const termWidth = maxWidth ? Math.min(rawWidth, maxWidth) : rawWidth;
4048
4219
  const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
4049
4220
  const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
4050
4221
  const hiddenCount = Math.max(0, entries.length - MAX_MOUNTED);
@@ -4100,46 +4271,221 @@ function ScrollableHistory({
4100
4271
  }
4101
4272
  var DELAY_PRESETS_MS = [0, 15e3, 3e4, 45e3, 6e4, 12e4];
4102
4273
  var SETTINGS_MODES = ["off", "suggest", "auto"];
4274
+ var LOG_LEVELS = ["error", "warn", "info", "debug", "trace"];
4275
+ var AUDIT_LEVELS = ["minimal", "standard", "full"];
4276
+ var COMPACTOR_STRATEGIES = ["hybrid", "intelligent", "selective"];
4277
+ var MAX_ITERATIONS_PRESETS = [100, 200, 500, 1e3, 0];
4103
4278
  function formatSettingsDelay(ms) {
4104
4279
  if (ms === 0) return "disabled";
4105
4280
  if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
4106
4281
  return `${Math.round(ms / 1e3)}s`;
4107
4282
  }
4283
+ function formatMaxIterations(n) {
4284
+ if (n === 0) return "unlimited";
4285
+ return String(n);
4286
+ }
4108
4287
  var MODE_DESC = {
4109
4288
  off: "Agent stops after each turn (normal)",
4110
4289
  suggest: "Shows next-step suggestions after each turn",
4111
4290
  auto: "Self-driving \u2014 agent continues automatically"
4112
4291
  };
4292
+ var SETTINGS_FIELD_COUNT = 19;
4113
4293
  function SettingsPicker({
4114
4294
  field,
4115
4295
  mode,
4116
4296
  delayMs,
4297
+ titleAnimation,
4298
+ yolo,
4299
+ streamFleet,
4300
+ chime,
4301
+ confirmExit,
4302
+ nextPrediction,
4303
+ featureMcp,
4304
+ featurePlugins,
4305
+ featureMemory,
4306
+ featureSkills,
4307
+ featureModelsRegistry,
4308
+ contextAutoCompact,
4309
+ contextStrategy,
4310
+ logLevel,
4311
+ auditLevel,
4312
+ indexOnStart,
4313
+ maxIterations,
4117
4314
  hint
4118
4315
  }) {
4316
+ const boolVal = (v) => v ? "on" : "off";
4119
4317
  const rows = [
4318
+ // ── Autonomy ──
4319
+ { section: "Autonomy" },
4120
4320
  { label: "Default autonomy mode", value: mode, detail: MODE_DESC[mode] },
4121
4321
  {
4122
4322
  label: "Auto-proceed delay",
4123
4323
  value: formatSettingsDelay(delayMs),
4124
4324
  detail: "Wait before auto-continuing in auto mode"
4325
+ },
4326
+ // ── UX ──
4327
+ { section: "UX" },
4328
+ {
4329
+ label: "Terminal title animation",
4330
+ value: boolVal(titleAnimation),
4331
+ detail: "Animated window/tab title with status"
4332
+ },
4333
+ {
4334
+ label: "YOLO mode",
4335
+ value: boolVal(yolo),
4336
+ detail: "Skip all confirmation prompts"
4337
+ },
4338
+ {
4339
+ label: "Stream fleet to chat",
4340
+ value: boolVal(streamFleet),
4341
+ detail: "Show subagent messages in main chat"
4342
+ },
4343
+ {
4344
+ label: "Completion chime",
4345
+ value: boolVal(chime),
4346
+ detail: "Play a sound when agent finishes"
4347
+ },
4348
+ {
4349
+ label: "Confirm before exit",
4350
+ value: boolVal(confirmExit),
4351
+ detail: "Ask for confirmation on Ctrl+C exit"
4352
+ },
4353
+ {
4354
+ label: "Next-step prediction",
4355
+ value: boolVal(nextPrediction),
4356
+ detail: "Show LLM-predicted next steps (/next)"
4357
+ },
4358
+ // ── Features ──
4359
+ { section: "Features" },
4360
+ {
4361
+ label: "MCP servers",
4362
+ value: boolVal(featureMcp),
4363
+ detail: "Load MCP servers from config"
4364
+ },
4365
+ {
4366
+ label: "Plugins",
4367
+ value: boolVal(featurePlugins),
4368
+ detail: "Load npm plugins from config"
4369
+ },
4370
+ {
4371
+ label: "Memory",
4372
+ value: boolVal(featureMemory),
4373
+ detail: "Enable remember/forget tools"
4374
+ },
4375
+ {
4376
+ label: "Skills",
4377
+ value: boolVal(featureSkills),
4378
+ detail: "Discover and load skills from disk"
4379
+ },
4380
+ {
4381
+ label: "Models registry",
4382
+ value: boolVal(featureModelsRegistry),
4383
+ detail: "Fetch models.dev catalog at startup"
4384
+ },
4385
+ // ── Context ──
4386
+ { section: "Context" },
4387
+ {
4388
+ label: "Auto-compact",
4389
+ value: boolVal(contextAutoCompact),
4390
+ detail: "Auto-compact context when thresholds crossed"
4391
+ },
4392
+ {
4393
+ label: "Compactor strategy",
4394
+ value: contextStrategy,
4395
+ detail: "hybrid (fast) | intelligent (LLM) | selective"
4396
+ },
4397
+ // ── Logging ──
4398
+ { section: "Logging" },
4399
+ {
4400
+ label: "Log level",
4401
+ value: logLevel,
4402
+ detail: "Console log verbosity"
4403
+ },
4404
+ // ── Session ──
4405
+ { section: "Session" },
4406
+ {
4407
+ label: "Audit level",
4408
+ value: auditLevel,
4409
+ detail: "minimal | standard | full (large)"
4410
+ },
4411
+ // ── Indexing ──
4412
+ { section: "Indexing" },
4413
+ {
4414
+ label: "Index on session start",
4415
+ value: boolVal(indexOnStart),
4416
+ detail: "Run incremental index at session start"
4417
+ },
4418
+ // ── Tools ──
4419
+ { section: "Tools" },
4420
+ {
4421
+ label: "Max iterations",
4422
+ value: formatMaxIterations(maxIterations),
4423
+ detail: "100\u20131000 or unlimited (0)"
4125
4424
  }
4126
4425
  ];
4127
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
4426
+ const fieldRowIndex = [];
4427
+ for (let i = 0; i < rows.length; i++) {
4428
+ if (!rows[i].section) fieldRowIndex.push(i);
4429
+ }
4430
+ const VISIBLE_FIELDS = 8;
4431
+ const totalFields = fieldRowIndex.length;
4432
+ const windowStart = totalFields <= VISIBLE_FIELDS ? 0 : Math.max(0, Math.min(field - Math.floor(VISIBLE_FIELDS / 2), totalFields - VISIBLE_FIELDS));
4433
+ const windowEnd = Math.min(windowStart + VISIBLE_FIELDS, totalFields);
4434
+ const hasAbove = windowStart > 0;
4435
+ const hasBelow = windowEnd < totalFields;
4436
+ const sectionFields = [];
4437
+ let curHeader = -1;
4438
+ for (let i = 0; i < rows.length; i++) {
4439
+ if (rows[i].section) curHeader = i;
4440
+ else if (curHeader >= 0) {
4441
+ const fieldIdx = fieldRowIndex.indexOf(i);
4442
+ if (fieldIdx === -1) continue;
4443
+ const entry = sectionFields.find((s2) => s2.headerIdx === curHeader);
4444
+ if (entry) {
4445
+ entry.fieldEnd = fieldIdx + 1;
4446
+ } else {
4447
+ sectionFields.push({ headerIdx: curHeader, fieldStart: fieldIdx, fieldEnd: fieldIdx + 1 });
4448
+ }
4449
+ }
4450
+ }
4451
+ const shouldShowSection = (headerIdx) => {
4452
+ const sec = sectionFields.find((s2) => s2.headerIdx === headerIdx);
4453
+ if (!sec) return false;
4454
+ return sec.fieldStart < windowEnd && sec.fieldEnd > windowStart;
4455
+ };
4456
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
4128
4457
  /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Settings \u2501\u2501" }),
4129
4458
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 field \xB7 \u2190/\u2192 change \xB7 Enter save \xB7 Esc cancel" }),
4130
- rows.map((row, i) => /* @__PURE__ */ jsxs(Text, { color: i === field ? "yellow" : void 0, inverse: i === field, children: [
4131
- i === field ? "\u203A " : " ",
4132
- /* @__PURE__ */ jsx(Text, { bold: true, children: row.label.padEnd(22) }),
4133
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: String(row.value).padEnd(10) }),
4134
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: row.detail })
4135
- ] }, row.label)),
4459
+ hasAbove ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2191 ${windowStart} field${windowStart === 1 ? "" : "s"} above` }) : null,
4460
+ rows.map((row, i) => {
4461
+ const fieldAtRow = fieldRowIndex.indexOf(i);
4462
+ if (fieldAtRow === -1) {
4463
+ if (shouldShowSection(i)) {
4464
+ return /* @__PURE__ */ jsxs(Text, { bold: true, color: "green", children: [
4465
+ "\u2500\u2500 ",
4466
+ row.section,
4467
+ " \u2500\u2500"
4468
+ ] }, `section-${i}`);
4469
+ }
4470
+ return null;
4471
+ }
4472
+ if (fieldAtRow < windowStart || fieldAtRow >= windowEnd) return null;
4473
+ const selected = fieldAtRow === field;
4474
+ return /* @__PURE__ */ jsxs(Text, { color: selected ? "yellow" : void 0, inverse: selected, children: [
4475
+ selected ? "\u203A " : " ",
4476
+ /* @__PURE__ */ jsx(Text, { bold: true, children: (row.label ?? "").padEnd(26) }),
4477
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: String(row.value ?? "").padEnd(12) }),
4478
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: row.detail ?? "" })
4479
+ ] }, `row-${i}`);
4480
+ }),
4481
+ hasBelow ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2193 ${totalFields - windowEnd} field${totalFields - windowEnd === 1 ? "" : "s"} below` }) : null,
4136
4482
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Persisted to ~/.wrongstack/config.json" }),
4137
4483
  hint ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) : null
4138
4484
  ] });
4139
4485
  }
4140
4486
  function SlashMenu({ query, matches, selected }) {
4141
4487
  const placeholder = query ? `/${query}` : "/";
4142
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
4488
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
4143
4489
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4144
4490
  placeholder || "/",
4145
4491
  " \u2014 \u2191/\u2193 select, Enter dispatch, Tab autocomplete, Esc close"
@@ -4159,6 +4505,110 @@ function SlashMenu({ query, matches, selected }) {
4159
4505
  matches.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No matching commands" })
4160
4506
  ] });
4161
4507
  }
4508
+ function TodosMonitor({ todos }) {
4509
+ const { stdout } = useStdout();
4510
+ useInput((_input, key) => {
4511
+ if (key.escape) ;
4512
+ });
4513
+ const done = todos.filter((t) => t.status === "completed").length;
4514
+ const inProgress = todos.filter((t) => t.status === "in_progress").length;
4515
+ const pending = todos.filter((t) => t.status === "pending").length;
4516
+ const w = stdout?.columns ?? 80;
4517
+ const twoCols = w >= 100;
4518
+ const mid = Math.ceil(todos.length / 2);
4519
+ const colWidth = twoCols ? Math.floor((w - 8) / 2) : w - 6;
4520
+ const trunc = (text, maxLen) => {
4521
+ if (text.length <= maxLen) return text;
4522
+ return text.slice(0, maxLen - 1) + "\u2026";
4523
+ };
4524
+ const renderRow3 = (t, idx) => {
4525
+ const num = String(idx + 1).padStart(2);
4526
+ const label = t.status === "in_progress" && t.activeForm ? t.activeForm : t.content;
4527
+ const display = trunc(label, colWidth - 8);
4528
+ if (t.status === "completed") {
4529
+ return /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4530
+ " ",
4531
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4532
+ num,
4533
+ "."
4534
+ ] }),
4535
+ " ",
4536
+ /* @__PURE__ */ jsx(Text, { color: "green", children: "[x]" }),
4537
+ " ",
4538
+ display
4539
+ ] }, t.id);
4540
+ }
4541
+ if (t.status === "in_progress") {
4542
+ return /* @__PURE__ */ jsxs(Text, { children: [
4543
+ " ",
4544
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4545
+ num,
4546
+ "."
4547
+ ] }),
4548
+ " ",
4549
+ /* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: "[~]" }),
4550
+ " ",
4551
+ /* @__PURE__ */ jsx(Text, { color: "yellow", children: display })
4552
+ ] }, t.id);
4553
+ }
4554
+ return /* @__PURE__ */ jsxs(Text, { children: [
4555
+ " ",
4556
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4557
+ num,
4558
+ "."
4559
+ ] }),
4560
+ " ",
4561
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "[ ]" }),
4562
+ " ",
4563
+ display
4564
+ ] }, t.id);
4565
+ };
4566
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
4567
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, marginBottom: 1, children: [
4568
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: "TODOS" }),
4569
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
4570
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4571
+ done,
4572
+ "/",
4573
+ todos.length,
4574
+ " done"
4575
+ ] }),
4576
+ inProgress > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
4577
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
4578
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
4579
+ "\u231B",
4580
+ inProgress
4581
+ ] })
4582
+ ] }) : null,
4583
+ pending > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
4584
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
4585
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4586
+ "\u2610",
4587
+ pending
4588
+ ] })
4589
+ ] }) : null,
4590
+ done > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
4591
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
4592
+ /* @__PURE__ */ jsxs(Text, { color: "green", children: [
4593
+ "\u2713",
4594
+ done
4595
+ ] })
4596
+ ] }) : null,
4597
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 F6 / Esc to close" })
4598
+ ] }),
4599
+ 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 ? (
4600
+ /* Two-column layout: split the list in half, render side-by-side.
4601
+ Pass the absolute position so numbering is continuous across columns. */
4602
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
4603
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", width: colWidth, children: todos.slice(0, mid).map((t, i) => renderRow3(t, i)) }),
4604
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", width: colWidth, children: todos.slice(mid).map((t, i) => renderRow3(t, mid + i)) })
4605
+ ] })
4606
+ ) : (
4607
+ /* Single column layout */
4608
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: todos.map(renderRow3) })
4609
+ )
4610
+ ] });
4611
+ }
4162
4612
  var fmtElapsed5 = (ms) => {
4163
4613
  const s2 = Math.floor(ms / 1e3);
4164
4614
  const m = Math.floor(s2 / 60);
@@ -4491,6 +4941,34 @@ function runGit(cwd, args) {
4491
4941
  }
4492
4942
  });
4493
4943
  }
4944
+ function useBrainEvents(events, dispatch) {
4945
+ useEffect(() => {
4946
+ const requestSummary = (request) => `${request.source}: ${request.question}`.slice(0, 80);
4947
+ const addBrainEntry = (status, payload) => {
4948
+ const p = payload;
4949
+ const decision = p.decision.optionId ?? p.decision.text ?? p.decision.reason ?? p.decision.prompt ?? p.decision.type;
4950
+ dispatch({ type: "brainStatus", state: status, source: p.request.source, risk: p.request.risk, summary: decision });
4951
+ if (status === "ask_human") {
4952
+ 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 } });
4953
+ } else {
4954
+ dispatch({ type: "brainPromptClear" });
4955
+ }
4956
+ dispatch({ type: "addEntry", entry: { kind: "brain", status, source: p.request.source, risk: p.request.risk, question: p.request.question, decision, rationale: p.decision.rationale } });
4957
+ };
4958
+ const offRequested = events.on("brain.decision_requested", ({ request }) => {
4959
+ dispatch({ type: "brainStatus", state: "deciding", source: request.source, risk: request.risk, summary: requestSummary(request) });
4960
+ });
4961
+ const offAnswered = events.on("brain.decision_answered", (payload) => addBrainEntry("answered", payload));
4962
+ const offAskHuman = events.on("brain.decision_ask_human", (payload) => addBrainEntry("ask_human", payload));
4963
+ const offDenied = events.on("brain.decision_denied", (payload) => addBrainEntry("denied", payload));
4964
+ return () => {
4965
+ offRequested();
4966
+ offAnswered();
4967
+ offAskHuman();
4968
+ offDenied();
4969
+ };
4970
+ }, [events, dispatch]);
4971
+ }
4494
4972
  var STREAM_COLORS = ["cyan", "magenta", "yellow", "green", "blue"];
4495
4973
  function labelFor(labelsRef, id, name) {
4496
4974
  const m = labelsRef.current;
@@ -4506,7 +4984,11 @@ function labelFor(labelsRef, id, name) {
4506
4984
  }
4507
4985
  function useSubagentEvents(events, dispatch, setActiveMaxContext) {
4508
4986
  const labelsRef = useRef(/* @__PURE__ */ new Map());
4509
- const lbl = (id, name) => labelFor(labelsRef, id, name);
4987
+ const lbl = useCallback(
4988
+ (id, name) => labelFor(labelsRef, id, name),
4989
+ []
4990
+ // labelsRef is a stable ref
4991
+ );
4510
4992
  useEffect(() => {
4511
4993
  const offSpawned = events.on("subagent.spawned", (e) => {
4512
4994
  const l = lbl(e.subagentId, e.name);
@@ -4553,6 +5035,12 @@ function useSubagentEvents(events, dispatch, setActiveMaxContext) {
4553
5035
  const offCtxPct = events.on("subagent.ctx_pct", (e) => {
4554
5036
  dispatch({ type: "fleetCtxPct", id: e.subagentId, load: e.load, tokens: e.tokens, maxContext: e.maxContext });
4555
5037
  });
5038
+ const offConcurrencyChanged = events.on("concurrency.changed", (e) => {
5039
+ const { n } = e;
5040
+ if (typeof n === "number" && n > 0) {
5041
+ dispatch({ type: "fleetConcurrency", n });
5042
+ }
5043
+ });
4556
5044
  const offLeaderCtxPct = events.on("ctx.pct", (e) => {
4557
5045
  setActiveMaxContext(e.maxContext);
4558
5046
  dispatch({ type: "leaderCtxPct", load: e.load, tokens: e.tokens, maxContext: e.maxContext });
@@ -4572,51 +5060,24 @@ function useSubagentEvents(events, dispatch, setActiveMaxContext) {
4572
5060
  offBudgetExtended();
4573
5061
  offIterationSummary();
4574
5062
  offCtxPct();
5063
+ offConcurrencyChanged();
4575
5064
  offLeaderCtxPct();
4576
5065
  offLeaderMaxContext();
4577
5066
  offTool();
4578
5067
  };
4579
- }, [events, dispatch, setActiveMaxContext]);
5068
+ }, [events, dispatch, setActiveMaxContext, lbl]);
4580
5069
  }
4581
- function useBrainEvents(events, dispatch) {
4582
- useEffect(() => {
4583
- const requestSummary = (request) => `${request.source}: ${request.question}`.slice(0, 80);
4584
- const addBrainEntry = (status, payload) => {
4585
- const p = payload;
4586
- const decision = p.decision.optionId ?? p.decision.text ?? p.decision.reason ?? p.decision.prompt ?? p.decision.type;
4587
- dispatch({ type: "brainStatus", state: status, source: p.request.source, risk: p.request.risk, summary: decision });
4588
- if (status === "ask_human") {
4589
- 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 } });
4590
- } else {
4591
- dispatch({ type: "brainPromptClear" });
4592
- }
4593
- dispatch({ type: "addEntry", entry: { kind: "brain", status, source: p.request.source, risk: p.request.risk, question: p.request.question, decision, rationale: p.decision.rationale } });
4594
- };
4595
- const offRequested = events.on("brain.decision_requested", ({ request }) => {
4596
- dispatch({ type: "brainStatus", state: "deciding", source: request.source, risk: request.risk, summary: requestSummary(request) });
4597
- });
4598
- const offAnswered = events.on("brain.decision_answered", (payload) => addBrainEntry("answered", payload));
4599
- const offAskHuman = events.on("brain.decision_ask_human", (payload) => addBrainEntry("ask_human", payload));
4600
- const offDenied = events.on("brain.decision_denied", (payload) => addBrainEntry("denied", payload));
4601
- return () => {
4602
- offRequested();
4603
- offAnswered();
4604
- offAskHuman();
4605
- offDenied();
4606
- };
4607
- }, [events, dispatch]);
4608
- }
4609
- 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";
4610
- function createKillSlashCommand() {
4611
- return {
4612
- name: "kill",
4613
- description: "List or kill active bash/exec processes managed by the process registry.",
4614
- async run(args) {
4615
- const trimmed = args.trim();
4616
- const parts = trimmed.split(/\s+/);
4617
- const sub = parts[0]?.toLowerCase() ?? "";
4618
- if (sub === "" || sub === "list") {
4619
- return { message: renderList() };
5070
+ 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";
5071
+ function createKillSlashCommand() {
5072
+ return {
5073
+ name: "kill",
5074
+ description: "List or kill active bash/exec processes managed by the process registry.",
5075
+ async run(args) {
5076
+ const trimmed = args.trim();
5077
+ const parts = trimmed.split(/\s+/);
5078
+ const sub = parts[0]?.toLowerCase() ?? "";
5079
+ if (sub === "" || sub === "list") {
5080
+ return { message: renderList() };
4620
5081
  }
4621
5082
  if (sub === "all") {
4622
5083
  const pids = getProcessRegistry().killAll();
@@ -4826,6 +5287,42 @@ function oneLine(s2, max) {
4826
5287
  return collapsed.length <= max ? collapsed : `${collapsed.slice(0, max - 1)}\u2026`;
4827
5288
  }
4828
5289
 
5290
+ // src/steering-preamble.ts
5291
+ function buildSteeringPreamble(snapshot, newDirection) {
5292
+ const lines = ["[STEERING \u2014 I pressed Esc to interrupt you mid-task on purpose.", ""];
5293
+ const ctx = [];
5294
+ if (snapshot?.runningTools && snapshot.runningTools.length > 0) {
5295
+ ctx.push(`- in-flight tools (now cancelled): ${snapshot.runningTools.join(", ")}`);
5296
+ }
5297
+ if (snapshot?.subagentsTerminated && snapshot.subagentsTerminated > 0) {
5298
+ const subDetails = snapshot.subagents.map((s2) => `${s2.label}${s2.tool ? ` (was running: ${s2.tool})` : ""}`).join(", ");
5299
+ ctx.push(
5300
+ `- subagents (${snapshot.subagentsTerminated} terminated by me, do NOT await them): ${subDetails}`
5301
+ );
5302
+ }
5303
+ if (snapshot?.partialAssistantText && snapshot.partialAssistantText.trim().length > 0) {
5304
+ const tail = snapshot.partialAssistantText.trim().slice(-300);
5305
+ ctx.push(`- your last partial output (truncated, for context only): "${tail}"`);
5306
+ }
5307
+ if (ctx.length > 0) {
5308
+ lines.push("What was happening when I cut you off:");
5309
+ lines.push(...ctx);
5310
+ lines.push("");
5311
+ }
5312
+ lines.push("You have authority to:");
5313
+ lines.push("- Abandon the prior plan entirely if the new direction makes it stale.");
5314
+ lines.push("- Re-spawn fresh subagents (with different roles or tasks) if needed.");
5315
+ lines.push('- Skip a polite "should I continue?" \u2014 just pivot.');
5316
+ lines.push("- Ask me to clarify if the new direction is genuinely ambiguous.");
5317
+ lines.push("");
5318
+ lines.push("New direction:");
5319
+ lines.push("---");
5320
+ lines.push(newDirection);
5321
+ lines.push("---");
5322
+ lines.push("]");
5323
+ return lines.join("\n");
5324
+ }
5325
+
4829
5326
  // src/app-reducer.ts
4830
5327
  function reducer(state, action) {
4831
5328
  switch (action.type) {
@@ -4852,7 +5349,22 @@ function reducer(state, action) {
4852
5349
  queue: [],
4853
5350
  nextQueueId: 1,
4854
5351
  scrollOffset: 0,
4855
- pendingNewLines: 0
5352
+ pendingNewLines: 0,
5353
+ // Reset fleet state on /clear so old subagent entries don't
5354
+ // cause the LiveActivityStrip to render stale spacers, and
5355
+ // the fleet cost/tokens chips show zero.
5356
+ fleet: {},
5357
+ fleetCost: 0,
5358
+ fleetTokens: { input: 0, output: 0 },
5359
+ leader: {
5360
+ iterations: 0,
5361
+ toolCalls: 0,
5362
+ recentTools: [],
5363
+ currentTool: void 0,
5364
+ startedAt: Date.now(),
5365
+ lastEventAt: Date.now(),
5366
+ iterating: false
5367
+ }
4856
5368
  };
4857
5369
  }
4858
5370
  case "streamDelta":
@@ -5133,6 +5645,23 @@ function reducer(state, action) {
5133
5645
  field: 0,
5134
5646
  mode: action.mode,
5135
5647
  delayMs: action.delayMs,
5648
+ titleAnimation: action.titleAnimation,
5649
+ yolo: action.yolo,
5650
+ streamFleet: action.streamFleet,
5651
+ chime: action.chime,
5652
+ confirmExit: action.confirmExit,
5653
+ nextPrediction: action.nextPrediction,
5654
+ featureMcp: action.featureMcp,
5655
+ featurePlugins: action.featurePlugins,
5656
+ featureMemory: action.featureMemory,
5657
+ featureSkills: action.featureSkills,
5658
+ featureModelsRegistry: action.featureModelsRegistry,
5659
+ contextAutoCompact: action.contextAutoCompact,
5660
+ contextStrategy: action.contextStrategy,
5661
+ logLevel: action.logLevel,
5662
+ auditLevel: action.auditLevel,
5663
+ indexOnStart: action.indexOnStart,
5664
+ maxIterations: action.maxIterations,
5136
5665
  hint: void 0
5137
5666
  }
5138
5667
  };
@@ -5142,37 +5671,68 @@ function reducer(state, action) {
5142
5671
  settingsPicker: { ...state.settingsPicker, open: false, hint: void 0 }
5143
5672
  };
5144
5673
  case "settingsFieldMove": {
5145
- const next = (state.settingsPicker.field + action.delta + 2) % 2;
5674
+ const next = (state.settingsPicker.field + action.delta + SETTINGS_FIELD_COUNT) % SETTINGS_FIELD_COUNT;
5146
5675
  return {
5147
5676
  ...state,
5148
5677
  settingsPicker: { ...state.settingsPicker, field: next, hint: void 0 }
5149
5678
  };
5150
5679
  }
5151
5680
  case "settingsFieldSet": {
5152
- const field = action.field === 1 ? 1 : 0;
5681
+ const field = action.field >= 0 && action.field < SETTINGS_FIELD_COUNT ? action.field : 0;
5153
5682
  return { ...state, settingsPicker: { ...state.settingsPicker, field, hint: void 0 } };
5154
5683
  }
5155
5684
  case "settingsValueChange": {
5156
- if (state.settingsPicker.field === 0) {
5157
- const i = SETTINGS_MODES.indexOf(state.settingsPicker.mode);
5158
- const base2 = i < 0 ? 0 : i;
5159
- const next2 = (base2 + action.delta + SETTINGS_MODES.length) % SETTINGS_MODES.length;
5160
- return {
5161
- ...state,
5162
- settingsPicker: { ...state.settingsPicker, mode: SETTINGS_MODES[next2], hint: void 0 }
5163
- };
5685
+ const sp = state.settingsPicker;
5686
+ const f = sp.field;
5687
+ if (f === 0) {
5688
+ const i = SETTINGS_MODES.indexOf(sp.mode);
5689
+ const base = i < 0 ? 0 : i;
5690
+ const next = (base + action.delta + SETTINGS_MODES.length) % SETTINGS_MODES.length;
5691
+ return { ...state, settingsPicker: { ...sp, mode: SETTINGS_MODES[next], hint: void 0 } };
5692
+ }
5693
+ if (f === 1) {
5694
+ const j = DELAY_PRESETS_MS.indexOf(sp.delayMs);
5695
+ const base = j < 0 ? 0 : j;
5696
+ const next = (base + action.delta + DELAY_PRESETS_MS.length) % DELAY_PRESETS_MS.length;
5697
+ return { ...state, settingsPicker: { ...sp, delayMs: DELAY_PRESETS_MS[next], hint: void 0 } };
5698
+ }
5699
+ if (f === 2) return { ...state, settingsPicker: { ...sp, titleAnimation: !sp.titleAnimation, hint: void 0 } };
5700
+ if (f === 3) return { ...state, settingsPicker: { ...sp, yolo: !sp.yolo, hint: void 0 } };
5701
+ if (f === 4) return { ...state, settingsPicker: { ...sp, streamFleet: !sp.streamFleet, hint: void 0 } };
5702
+ if (f === 5) return { ...state, settingsPicker: { ...sp, chime: !sp.chime, hint: void 0 } };
5703
+ if (f === 6) return { ...state, settingsPicker: { ...sp, confirmExit: !sp.confirmExit, hint: void 0 } };
5704
+ if (f === 7) return { ...state, settingsPicker: { ...sp, nextPrediction: !sp.nextPrediction, hint: void 0 } };
5705
+ if (f === 8) return { ...state, settingsPicker: { ...sp, featureMcp: !sp.featureMcp, hint: void 0 } };
5706
+ if (f === 9) return { ...state, settingsPicker: { ...sp, featurePlugins: !sp.featurePlugins, hint: void 0 } };
5707
+ if (f === 10) return { ...state, settingsPicker: { ...sp, featureMemory: !sp.featureMemory, hint: void 0 } };
5708
+ if (f === 11) return { ...state, settingsPicker: { ...sp, featureSkills: !sp.featureSkills, hint: void 0 } };
5709
+ if (f === 12) return { ...state, settingsPicker: { ...sp, featureModelsRegistry: !sp.featureModelsRegistry, hint: void 0 } };
5710
+ if (f === 13) return { ...state, settingsPicker: { ...sp, contextAutoCompact: !sp.contextAutoCompact, hint: void 0 } };
5711
+ if (f === 14) {
5712
+ const i = COMPACTOR_STRATEGIES.indexOf(sp.contextStrategy);
5713
+ const base = i < 0 ? 0 : i;
5714
+ const next = (base + action.delta + COMPACTOR_STRATEGIES.length) % COMPACTOR_STRATEGIES.length;
5715
+ return { ...state, settingsPicker: { ...sp, contextStrategy: COMPACTOR_STRATEGIES[next], hint: void 0 } };
5716
+ }
5717
+ if (f === 15) {
5718
+ const i = LOG_LEVELS.indexOf(sp.logLevel);
5719
+ const base = i < 0 ? 0 : i;
5720
+ const next = (base + action.delta + LOG_LEVELS.length) % LOG_LEVELS.length;
5721
+ return { ...state, settingsPicker: { ...sp, logLevel: LOG_LEVELS[next], hint: void 0 } };
5722
+ }
5723
+ if (f === 16) {
5724
+ const i = AUDIT_LEVELS.indexOf(sp.auditLevel);
5725
+ const base = i < 0 ? 0 : i;
5726
+ const next = (base + action.delta + AUDIT_LEVELS.length) % AUDIT_LEVELS.length;
5727
+ return { ...state, settingsPicker: { ...sp, auditLevel: AUDIT_LEVELS[next], hint: void 0 } };
5728
+ }
5729
+ if (f === 17) return { ...state, settingsPicker: { ...sp, indexOnStart: !sp.indexOnStart, hint: void 0 } };
5730
+ {
5731
+ const j = MAX_ITERATIONS_PRESETS.indexOf(sp.maxIterations);
5732
+ const base = j < 0 ? 0 : j;
5733
+ const next = (base + action.delta + MAX_ITERATIONS_PRESETS.length) % MAX_ITERATIONS_PRESETS.length;
5734
+ return { ...state, settingsPicker: { ...sp, maxIterations: MAX_ITERATIONS_PRESETS[next], hint: void 0 } };
5164
5735
  }
5165
- const j = DELAY_PRESETS_MS.indexOf(state.settingsPicker.delayMs);
5166
- const base = j < 0 ? 0 : j;
5167
- const next = (base + action.delta + DELAY_PRESETS_MS.length) % DELAY_PRESETS_MS.length;
5168
- return {
5169
- ...state,
5170
- settingsPicker: {
5171
- ...state.settingsPicker,
5172
- delayMs: DELAY_PRESETS_MS[next],
5173
- hint: void 0
5174
- }
5175
- };
5176
5736
  }
5177
5737
  case "settingsHint":
5178
5738
  return { ...state, settingsPicker: { ...state.settingsPicker, hint: action.text } };
@@ -5180,6 +5740,14 @@ function reducer(state, action) {
5180
5740
  return { ...state, confirmQueue: [...state.confirmQueue, action.info] };
5181
5741
  case "confirmClose":
5182
5742
  return { ...state, confirmQueue: state.confirmQueue.slice(1) };
5743
+ case "enhanceOpen":
5744
+ return { ...state, enhance: action.info };
5745
+ case "enhanceClose":
5746
+ return { ...state, enhance: null };
5747
+ case "enhanceSet":
5748
+ return { ...state, enhanceEnabled: action.enabled };
5749
+ case "enhanceBusy":
5750
+ return { ...state, enhanceBusy: action.on };
5183
5751
  case "resetContextChip":
5184
5752
  return { ...state, contextChipVersion: state.contextChipVersion + 1 };
5185
5753
  // --- Fleet ---
@@ -5411,8 +5979,24 @@ function reducer(state, action) {
5411
5979
  };
5412
5980
  }
5413
5981
  case "fleetCost": {
5982
+ let fleet = state.fleet;
5983
+ if (action.perAgent) {
5984
+ let changed = false;
5985
+ const next = {};
5986
+ for (const [id, entry] of Object.entries(state.fleet)) {
5987
+ const cost = action.perAgent[id]?.cost;
5988
+ if (cost !== void 0 && cost !== entry.cost) {
5989
+ next[id] = { ...entry, cost };
5990
+ changed = true;
5991
+ } else {
5992
+ next[id] = entry;
5993
+ }
5994
+ }
5995
+ if (changed) fleet = next;
5996
+ }
5414
5997
  return {
5415
5998
  ...state,
5999
+ fleet,
5416
6000
  fleetCost: action.cost,
5417
6001
  fleetTokens: action.input !== void 0 || action.output !== void 0 ? {
5418
6002
  input: action.input ?? state.fleetTokens.input,
@@ -5420,6 +6004,9 @@ function reducer(state, action) {
5420
6004
  } : state.fleetTokens
5421
6005
  };
5422
6006
  }
6007
+ case "fleetConcurrency": {
6008
+ return { ...state, fleetConcurrency: action.n };
6009
+ }
5423
6010
  case "leaderIterStart": {
5424
6011
  return {
5425
6012
  ...state,
@@ -5488,6 +6075,12 @@ function reducer(state, action) {
5488
6075
  case "toggleHelp": {
5489
6076
  return { ...state, helpOpen: !state.helpOpen };
5490
6077
  }
6078
+ case "toggleTodosMonitor": {
6079
+ return { ...state, todosMonitorOpen: !state.todosMonitorOpen };
6080
+ }
6081
+ case "toggleQueuePanel": {
6082
+ return { ...state, queuePanelOpen: !state.queuePanelOpen };
6083
+ }
5491
6084
  case "checkpointReceived": {
5492
6085
  const existing = state.checkpoints.find((c) => c.promptIndex === action.cp.promptIndex);
5493
6086
  if (existing) return state;
@@ -5608,7 +6201,7 @@ function reducer(state, action) {
5608
6201
  case "worktreeMonitorToggle": {
5609
6202
  return { ...state, worktreeMonitorOpen: !state.worktreeMonitorOpen };
5610
6203
  }
5611
- // --- In-app chat scroll (mouse mode) ---
6204
+ // --- In-app chat scroll ---
5612
6205
  case "scrollBy": {
5613
6206
  const maxOffset = Math.max(0, state.totalLines - state.viewportRows);
5614
6207
  const next = Math.max(0, Math.min(maxOffset, state.scrollOffset + action.delta));
@@ -5786,7 +6379,6 @@ function reducer(state, action) {
5786
6379
  }
5787
6380
  }
5788
6381
  }
5789
- var WHEEL_STEP = 3;
5790
6382
  var MIN_VIEWPORT = 3;
5791
6383
  var INPUT_PROMPT = "\u203A ";
5792
6384
  function selectedSlashCommandLine(picker) {
@@ -5794,41 +6386,22 @@ function selectedSlashCommandLine(picker) {
5794
6386
  const picked = picker.matches[picker.selected];
5795
6387
  return picked ? `/${picked.name}` : null;
5796
6388
  }
5797
- var PASTE_THRESHOLD_CHARS = 200;
5798
- function buildSteeringPreamble(snapshot, newDirection) {
5799
- const lines = ["[STEERING \u2014 I pressed Esc to interrupt you mid-task on purpose.", ""];
5800
- const ctx = [];
5801
- if (snapshot?.runningTools && snapshot.runningTools.length > 0) {
5802
- ctx.push(`- in-flight tools (now cancelled): ${snapshot.runningTools.join(", ")}`);
5803
- }
5804
- if (snapshot?.subagentsTerminated && snapshot.subagentsTerminated > 0) {
5805
- const subDetails = snapshot.subagents.map((s2) => `${s2.label}${s2.tool ? ` (was running: ${s2.tool})` : ""}`).join(", ");
5806
- ctx.push(
5807
- `- subagents (${snapshot.subagentsTerminated} terminated by me, do NOT await them): ${subDetails}`
5808
- );
5809
- }
5810
- if (snapshot?.partialAssistantText && snapshot.partialAssistantText.trim().length > 0) {
5811
- const tail = snapshot.partialAssistantText.trim().slice(-300);
5812
- ctx.push(`- your last partial output (truncated, for context only): "${tail}"`);
5813
- }
5814
- if (ctx.length > 0) {
5815
- lines.push("What was happening when I cut you off:");
5816
- lines.push(...ctx);
5817
- lines.push("");
6389
+ function rehydrateHistory(messages, startId) {
6390
+ const entries = [];
6391
+ for (const msg of messages) {
6392
+ if (msg.role === "system") continue;
6393
+ const text = typeof msg.content === "string" ? msg.content : msg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
6394
+ const trimmed = text.trim();
6395
+ if (!trimmed) continue;
6396
+ if (msg.role === "user") {
6397
+ entries.push({ id: startId++, kind: "user", text: trimmed });
6398
+ } else if (msg.role === "assistant") {
6399
+ entries.push({ id: startId++, kind: "assistant", text: trimmed });
6400
+ }
5818
6401
  }
5819
- lines.push("You have authority to:");
5820
- lines.push("- Abandon the prior plan entirely if the new direction makes it stale.");
5821
- lines.push("- Re-spawn fresh subagents (with different roles or tasks) if needed.");
5822
- lines.push('- Skip a polite "should I continue?" \u2014 just pivot.');
5823
- lines.push("- Ask me to clarify if the new direction is genuinely ambiguous.");
5824
- lines.push("");
5825
- lines.push("New direction:");
5826
- lines.push("---");
5827
- lines.push(newDirection);
5828
- lines.push("---");
5829
- lines.push("]");
5830
- return lines.join("\n");
6402
+ return entries;
5831
6403
  }
6404
+ var PASTE_THRESHOLD_CHARS = 200;
5832
6405
  function App({
5833
6406
  agent,
5834
6407
  slashRegistry,
@@ -5841,6 +6414,11 @@ function App({
5841
6414
  banner = true,
5842
6415
  queueStore,
5843
6416
  yolo = false,
6417
+ chime = false,
6418
+ confirmExit = true,
6419
+ enhanceEnabled = true,
6420
+ enhanceController,
6421
+ enhanceDelayMs = 4e3,
5844
6422
  getYolo,
5845
6423
  getAutonomy,
5846
6424
  getEternalEngine,
@@ -5872,9 +6450,9 @@ function App({
5872
6450
  initialGoal,
5873
6451
  initialAsk,
5874
6452
  sessionsDir,
5875
- mouse = false,
5876
- subscribeMouse,
5877
- managed = false
6453
+ managed = false,
6454
+ modeLabel,
6455
+ getModeLabel
5878
6456
  }) {
5879
6457
  const { exit } = useApp();
5880
6458
  const [liveModel, setLiveModel] = useState(model);
@@ -5882,21 +6460,24 @@ function App({
5882
6460
  const [activeMaxContext, setActiveMaxContext] = useState(effectiveMaxContext);
5883
6461
  const [yoloLive, setYoloLive] = useState(yolo);
5884
6462
  const [autonomyLive, setAutonomyLive] = useState(getAutonomy?.() ?? "off");
6463
+ const [liveModeLabel, setLiveModeLabel] = useState(modeLabel ?? "");
5885
6464
  const [hiddenItems, setHiddenItems] = useState(statuslineHiddenItems);
6465
+ const [indexState, setIndexState] = useState(() => getIndexState());
6466
+ useEffect(() => {
6467
+ setIndexState(getIndexState());
6468
+ return onIndexStateChange((next) => setIndexState(next));
6469
+ }, []);
5886
6470
  const { stdout } = useStdout();
5887
6471
  const [termRows, setTermRows] = useState(stdout?.rows ?? 24);
5888
- const [termCols, setTermCols] = useState(stdout?.columns ?? 80);
5889
6472
  useEffect(() => {
5890
6473
  const onResize = () => {
5891
6474
  setTermRows(process.stdout.rows ?? 24);
5892
- setTermCols(process.stdout.columns ?? 80);
5893
6475
  };
5894
6476
  process.stdout.on("resize", onResize);
5895
6477
  return () => {
5896
6478
  process.stdout.off("resize", onResize);
5897
6479
  };
5898
6480
  }, []);
5899
- const [mouseLive, setMouseLive] = useState(mouse);
5900
6481
  const [managedLive, setManagedLive] = useState(managed);
5901
6482
  useEffect(() => {
5902
6483
  setHiddenItems(statuslineHiddenItems);
@@ -5926,19 +6507,34 @@ function App({
5926
6507
  }).catch(() => {
5927
6508
  });
5928
6509
  }, [projectRoot]);
6510
+ const restoredEntries = (() => {
6511
+ const msgs = agent.ctx.messages;
6512
+ if (!msgs || msgs.length === 0) return [];
6513
+ const visible = msgs.filter((m) => m.role !== "system");
6514
+ if (visible.length === 0) return [];
6515
+ return rehydrateHistory(
6516
+ visible,
6517
+ /* startId */
6518
+ 1
6519
+ );
6520
+ })();
6521
+ const initialNextId = 1 + restoredEntries.length;
5929
6522
  const [state, dispatch] = useReducer(reducer, {
5930
- entries: banner ? [
5931
- {
5932
- id: 0,
5933
- kind: "banner",
5934
- version: appVersion ?? "dev",
5935
- provider: provider ?? "agent",
5936
- model,
5937
- cwd: agent.ctx.cwd,
5938
- family,
5939
- keyTail
5940
- }
5941
- ] : [],
6523
+ entries: [
6524
+ ...banner ? [
6525
+ {
6526
+ id: 0,
6527
+ kind: "banner",
6528
+ version: appVersion ?? "dev",
6529
+ provider: provider ?? "agent",
6530
+ model,
6531
+ cwd: agent.ctx.cwd,
6532
+ family,
6533
+ keyTail
6534
+ }
6535
+ ] : [],
6536
+ ...restoredEntries
6537
+ ],
5942
6538
  buffer: "",
5943
6539
  cursor: 0,
5944
6540
  streamingText: "",
@@ -5950,7 +6546,7 @@ function App({
5950
6546
  hint: "",
5951
6547
  brain: { state: "idle" },
5952
6548
  brainPrompt: null,
5953
- nextId: 1,
6549
+ nextId: initialNextId,
5954
6550
  picker: { open: false, query: "", matches: [], selected: 0 },
5955
6551
  slashPicker: { open: false, query: "", matches: [], selected: 0 },
5956
6552
  runningTools: /* @__PURE__ */ new Map(),
@@ -5968,8 +6564,11 @@ function App({
5968
6564
  searchQuery: ""
5969
6565
  },
5970
6566
  autonomyPicker: { open: false, options: [], selected: 0 },
5971
- settingsPicker: { open: false, field: 0, mode: "off", delayMs: 0 },
6567
+ 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 },
5972
6568
  confirmQueue: [],
6569
+ enhance: null,
6570
+ enhanceEnabled,
6571
+ enhanceBusy: false,
5973
6572
  contextChipVersion: 0,
5974
6573
  fleet: {},
5975
6574
  leader: {
@@ -5983,10 +6582,13 @@ function App({
5983
6582
  },
5984
6583
  fleetCost: 0,
5985
6584
  fleetTokens: { input: 0, output: 0 },
6585
+ fleetConcurrency: 4,
5986
6586
  streamFleet: true,
5987
6587
  monitorOpen: false,
5988
6588
  agentsMonitorOpen: false,
5989
6589
  helpOpen: false,
6590
+ todosMonitorOpen: false,
6591
+ queuePanelOpen: false,
5990
6592
  collabSession: null,
5991
6593
  checkpoints: [],
5992
6594
  rewindOverlay: null,
@@ -6015,6 +6617,10 @@ function App({
6015
6617
  const base = path2.basename(projectRoot);
6016
6618
  return base && base !== path2.sep ? base : void 0;
6017
6619
  }, [projectRoot]);
6620
+ const chimeRef = useRef(chime);
6621
+ chimeRef.current = chime;
6622
+ const confirmExitRef = useRef(confirmExit);
6623
+ confirmExitRef.current = confirmExit;
6018
6624
  const streamingTextRef = useRef("");
6019
6625
  const pendingDeltaRef = useRef("");
6020
6626
  const flushTimerRef = useRef(null);
@@ -6023,21 +6629,11 @@ function App({
6023
6629
  const draftRef = useRef({ buffer: state.buffer, cursor: state.cursor });
6024
6630
  draftRef.current = { buffer: state.buffer, cursor: state.cursor };
6025
6631
  const bottomRef = useRef(null);
6026
- const prePickerRef = useRef(null);
6027
- const preRowsRef = useRef(0);
6028
- const liveStripRef = useRef(null);
6029
- const liveStripRowsRef = useRef(0);
6030
6632
  React6.useLayoutEffect(() => {
6031
6633
  if (!managedLive) return;
6032
6634
  const node = bottomRef.current;
6033
6635
  if (!node) return;
6034
6636
  const { height } = measureElement(node);
6035
- if (prePickerRef.current) {
6036
- preRowsRef.current = measureElement(prePickerRef.current).height;
6037
- }
6038
- if (liveStripRef.current) {
6039
- liveStripRowsRef.current = measureElement(liveStripRef.current).height;
6040
- }
6041
6637
  const s2 = stateRef.current;
6042
6638
  const affordance = s2.scrollOffset > 0 && s2.pendingNewLines > 0 ? 1 : 0;
6043
6639
  const vp = Math.max(MIN_VIEWPORT, termRows - height - affordance - 1);
@@ -6046,213 +6642,6 @@ function App({
6046
6642
  }
6047
6643
  }, [managedLive, termRows]);
6048
6644
  const handleKeyRef = useRef(null);
6049
- const pendingClickConfirmRef = useRef(null);
6050
- const openModelPickerRef = useRef(null);
6051
- const openAutonomyPickerRef = useRef(null);
6052
- const handleRewindToRef = useRef(null);
6053
- const confirmRef = useRef(null);
6054
- const confirmDecisionRef = useRef(null);
6055
- const scrollbarDragRef = useRef(false);
6056
- const lastLeftClickRef = useRef(null);
6057
- const DOUBLE_CLICK_MS = 500;
6058
- const DOUBLE_CLICK_DIST = 5;
6059
- const statusChipRef = useRef({ version: appVersion, model, fleetRunning: 0, yolo, autonomy: "off" });
6060
- const handleMouse = React6.useCallback(
6061
- (ev) => {
6062
- const s2 = stateRef.current;
6063
- if (ev.type === "wheel") {
6064
- const up = ev.button === "wheelUp";
6065
- const step = up ? -1 : 1;
6066
- if (s2.slashPicker.open) return dispatch({ type: "slashPickerMove", delta: step });
6067
- if (s2.modelPicker.open) return dispatch({ type: "modelPickerMove", delta: step });
6068
- if (s2.autonomyPicker.open) return dispatch({ type: "autonomyPickerMove", delta: step });
6069
- if (s2.settingsPicker.open) return dispatch({ type: "settingsFieldMove", delta: step });
6070
- if (s2.picker.open) return dispatch({ type: "pickerMove", delta: step });
6071
- if (s2.rewindOverlay) return dispatch({ type: "rewindOverlayMove", delta: step });
6072
- return dispatch({ type: "scrollBy", delta: up ? WHEEL_STEP : -WHEEL_STEP });
6073
- }
6074
- if (ev.type === "release") {
6075
- scrollbarDragRef.current = false;
6076
- return;
6077
- }
6078
- if (ev.button !== "left") return;
6079
- const now = Date.now();
6080
- const lastClick = lastLeftClickRef.current;
6081
- const isMultiClick = lastClick !== null && now - lastClick.ts < DOUBLE_CLICK_MS && Math.abs(ev.x - lastClick.x) <= DOUBLE_CLICK_DIST && Math.abs(ev.y - lastClick.y) <= DOUBLE_CLICK_DIST;
6082
- const clickCount = isMultiClick ? lastClick.count + 1 : 1;
6083
- lastLeftClickRef.current = { x: ev.x, y: ev.y, ts: now, count: clickCount };
6084
- {
6085
- const rows = s2.viewportRows;
6086
- if (ev.drag) {
6087
- if (scrollbarDragRef.current && s2.totalLines > rows) {
6088
- dispatch({
6089
- type: "scrollTo",
6090
- offset: scrollOffsetForTrackRow(rows, s2.totalLines, ev.y - 1)
6091
- });
6092
- }
6093
- return;
6094
- }
6095
- const cols = termCols || 80;
6096
- const onScrollbar = cols > 0 && ev.x >= cols - 2 && ev.y >= 1 && ev.y <= rows;
6097
- if (onScrollbar && s2.totalLines > rows) {
6098
- scrollbarDragRef.current = true;
6099
- dispatch({
6100
- type: "scrollTo",
6101
- offset: scrollOffsetForTrackRow(rows, s2.totalLines, ev.y - 1)
6102
- });
6103
- return;
6104
- }
6105
- }
6106
- if (ev.y > s2.viewportRows) {
6107
- if (s2.helpOpen) return dispatch({ type: "toggleHelp" });
6108
- if (s2.agentsMonitorOpen) return dispatch({ type: "toggleAgentsMonitor" });
6109
- if (s2.monitorOpen) return dispatch({ type: "toggleMonitor" });
6110
- if (s2.worktreeMonitorOpen) return dispatch({ type: "worktreeMonitorToggle" });
6111
- if (s2.autoPhase?.monitorOpen) return dispatch({ type: "autoPhaseMonitorToggle" });
6112
- }
6113
- const affordance = s2.scrollOffset > 0 && s2.pendingNewLines > 0 ? 1 : 0;
6114
- if (affordance && ev.y === s2.viewportRows + 1) {
6115
- return dispatch({ type: "scrollToBottom" });
6116
- }
6117
- if (s2.confirmQueue.length > 0) {
6118
- const node = confirmRef.current;
6119
- const head = s2.confirmQueue[0];
6120
- if (node && head) {
6121
- const { height } = measureElement(node);
6122
- const top = s2.viewportRows + affordance + preRowsRef.current + 1;
6123
- const buttonsRow = top + height - 2;
6124
- if (ev.y === buttonsRow) {
6125
- const contentX = ev.x - 1 - 2;
6126
- for (const seg of confirmButtonSegments(head.suggestedPattern)) {
6127
- if (contentX >= seg.start && contentX < seg.start + seg.len) {
6128
- confirmDecisionRef.current?.(seg.decision);
6129
- return;
6130
- }
6131
- }
6132
- }
6133
- }
6134
- return;
6135
- }
6136
- if (s2.rewindOverlay) {
6137
- const cps = s2.rewindOverlay.checkpoints;
6138
- const firstItemRow2 = s2.viewportRows + affordance + preRowsRef.current + 3 + 1;
6139
- const index2 = ev.y - firstItemRow2;
6140
- if (index2 < 0 || index2 >= cps.length) return;
6141
- if (index2 === s2.rewindOverlay.selected) {
6142
- handleRewindToRef.current?.(cps[index2].promptIndex);
6143
- } else {
6144
- dispatch({ type: "rewindOverlayMove", delta: index2 - s2.rewindOverlay.selected });
6145
- }
6146
- return;
6147
- }
6148
- if (s2.settingsPicker.open) {
6149
- const firstRow = s2.viewportRows + affordance + preRowsRef.current + 2 + 1;
6150
- const field = ev.y - firstRow;
6151
- if (field < 0 || field > 1) return;
6152
- if (field === s2.settingsPicker.field) {
6153
- dispatch({ type: "settingsValueChange", delta: 1 });
6154
- } else {
6155
- dispatch({ type: "settingsFieldSet", field });
6156
- }
6157
- return;
6158
- }
6159
- const picker = s2.modelPicker.open ? {
6160
- header: 2,
6161
- count: s2.modelPicker.step === "provider" ? s2.modelPicker.providerOptions.length : s2.modelPicker.modelOptions.length,
6162
- selected: s2.modelPicker.selected,
6163
- move: (delta) => dispatch({ type: "modelPickerMove", delta })
6164
- } : s2.autonomyPicker.open ? {
6165
- header: 2,
6166
- count: s2.autonomyPicker.options.length,
6167
- selected: s2.autonomyPicker.selected,
6168
- move: (delta) => dispatch({ type: "autonomyPickerMove", delta })
6169
- } : s2.slashPicker.open ? {
6170
- header: 1,
6171
- count: s2.slashPicker.matches.length,
6172
- selected: s2.slashPicker.selected,
6173
- move: (delta) => dispatch({ type: "slashPickerMove", delta })
6174
- } : s2.picker.open ? {
6175
- header: 1,
6176
- count: s2.picker.matches.length,
6177
- selected: s2.picker.selected,
6178
- move: (delta) => dispatch({ type: "pickerMove", delta })
6179
- } : null;
6180
- if (!picker || picker.count === 0) {
6181
- const inputDisabled = s2.status === "aborting" && !s2.steeringPending;
6182
- if (!inputDisabled) {
6183
- const cols = termCols || 80;
6184
- const inputTop = s2.viewportRows + affordance + liveStripRowsRef.current + 1;
6185
- const inputRows = layoutInputRows(INPUT_PROMPT, s2.buffer, s2.cursor, cols).length;
6186
- const rowIdx = ev.y - inputTop;
6187
- if (rowIdx >= 0 && rowIdx < inputRows) {
6188
- const next = inputIndexAtRowCol(INPUT_PROMPT, s2.buffer, cols, rowIdx, ev.x - 1);
6189
- return dispatch({ type: "setBuffer", buffer: s2.buffer, cursor: next });
6190
- }
6191
- }
6192
- if (!s2.helpOpen && !s2.agentsMonitorOpen && !s2.monitorOpen && !s2.worktreeMonitorOpen && !s2.autoPhase?.monitorOpen) {
6193
- const chip = statusChipRef.current;
6194
- const statusTop = s2.viewportRows + affordance + preRowsRef.current + 1;
6195
- const contentX = ev.x - 1;
6196
- if (ev.y === statusTop + 1) {
6197
- const span = statusBarModelSpan({
6198
- version: chip.version,
6199
- state: s2.status,
6200
- fleetRunning: chip.fleetRunning,
6201
- model: chip.model
6202
- });
6203
- if (contentX >= span.start && contentX < span.start + span.len) {
6204
- openModelPickerRef.current?.();
6205
- }
6206
- } else if (ev.y === statusTop + 2) {
6207
- const span = statusBarAutonomySpan({ yolo: chip.yolo, autonomy: chip.autonomy });
6208
- if (span && contentX >= span.start && contentX < span.start + span.len) {
6209
- openAutonomyPickerRef.current?.();
6210
- }
6211
- }
6212
- }
6213
- return;
6214
- }
6215
- const firstItemRow = s2.viewportRows + affordance + preRowsRef.current + picker.header + 1;
6216
- const index = ev.y - firstItemRow;
6217
- if (index < 0 || index >= picker.count) return;
6218
- if (index === picker.selected) {
6219
- handleKeyRef.current?.("", { ...EMPTY_KEY, return: true });
6220
- } else {
6221
- pendingClickConfirmRef.current = index;
6222
- picker.move(index - picker.selected);
6223
- }
6224
- },
6225
- // dispatch is stable (useReducer); refs are mutable — no reactive deps.
6226
- // termCols is stable (useState + resize effect).
6227
- [termCols]
6228
- );
6229
- useEffect(() => {
6230
- if (!subscribeMouse) return;
6231
- return subscribeMouse(handleMouse);
6232
- }, [subscribeMouse, handleMouse]);
6233
- useEffect(() => {
6234
- const target = pendingClickConfirmRef.current;
6235
- if (target === null) return;
6236
- const open = state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.picker.open;
6237
- if (!open) {
6238
- pendingClickConfirmRef.current = null;
6239
- return;
6240
- }
6241
- const sel = state.slashPicker.open ? state.slashPicker.selected : state.modelPicker.open ? state.modelPicker.selected : state.autonomyPicker.open ? state.autonomyPicker.selected : state.picker.selected;
6242
- if (sel === target) {
6243
- pendingClickConfirmRef.current = null;
6244
- handleKeyRef.current?.("", { ...EMPTY_KEY, return: true });
6245
- }
6246
- }, [
6247
- state.slashPicker.open,
6248
- state.slashPicker.selected,
6249
- state.modelPicker.open,
6250
- state.modelPicker.selected,
6251
- state.autonomyPicker.open,
6252
- state.autonomyPicker.selected,
6253
- state.picker.open,
6254
- state.picker.selected
6255
- ]);
6256
6645
  const handleRewindTo = React6.useCallback(
6257
6646
  async (checkpointIndex) => {
6258
6647
  const sessionId = agent.ctx.session.id;
@@ -6333,7 +6722,10 @@ function App({
6333
6722
  toolCalls: state.leader.toolCalls,
6334
6723
  recentTools: state.leader.recentTools,
6335
6724
  recentMessages: [],
6336
- cost: 0,
6725
+ // Leader (main session) cost — the same number the statusline shows.
6726
+ // Kept distinct from fleet (subagent) cost so the monitor can show a
6727
+ // trustworthy grand total = leader + fleet.
6728
+ cost: tokenCounter?.estimateCost().total ?? 0,
6337
6729
  startedAt: state.leader.startedAt,
6338
6730
  lastEventAt: state.leader.lastEventAt,
6339
6731
  currentTool: state.leader.currentTool,
@@ -6342,7 +6734,7 @@ function App({
6342
6734
  ctxMaxTokens: state.leader.ctxMaxTokens ?? effectiveMaxContext
6343
6735
  };
6344
6736
  return { leader: leaderEntry, ...state.fleet };
6345
- }, [state.fleet, state.leader, state.status, provider, model, effectiveMaxContext]);
6737
+ }, [state.fleet, state.leader, state.status, provider, model, effectiveMaxContext, tokenCounter]);
6346
6738
  const STREAM_COLORS2 = ["cyan", "magenta", "yellow", "green", "blue"];
6347
6739
  const labelsRef = useRef(/* @__PURE__ */ new Map());
6348
6740
  const labelFor2 = (id, name) => {
@@ -6393,19 +6785,23 @@ function App({
6393
6785
  }, [agent.ctx.meta]);
6394
6786
  const prevAnyOverlayOpen = useRef(false);
6395
6787
  const prevEntriesCount = useRef(0);
6788
+ const prevToolStreamLen = useRef(0);
6396
6789
  const eraseLiveRegion = useCallback(() => {
6397
6790
  try {
6398
6791
  writeOut("\x1B[J");
6399
6792
  } catch {
6400
6793
  }
6401
6794
  }, []);
6402
- useEffect(() => {
6403
- const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || state.confirmQueue.length > 0;
6795
+ React6.useLayoutEffect(() => {
6796
+ 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;
6404
6797
  const overlayClosed = prevAnyOverlayOpen.current && !anyOpenNow;
6405
6798
  const newEntryCommitted = state.entries.length > prevEntriesCount.current;
6799
+ const curToolStreamLen = state.toolStream?.text.length ?? 0;
6800
+ const toolStreamGrew = curToolStreamLen > 0 && curToolStreamLen > prevToolStreamLen.current;
6406
6801
  prevAnyOverlayOpen.current = anyOpenNow;
6407
6802
  prevEntriesCount.current = state.entries.length;
6408
- if (overlayClosed || newEntryCommitted) {
6803
+ prevToolStreamLen.current = curToolStreamLen;
6804
+ if (overlayClosed || newEntryCommitted || toolStreamGrew) {
6409
6805
  eraseLiveRegion();
6410
6806
  }
6411
6807
  }, [
@@ -6414,8 +6810,11 @@ function App({
6414
6810
  state.modelPicker.open,
6415
6811
  state.autonomyPicker.open,
6416
6812
  state.settingsPicker.open,
6813
+ state.enhanceBusy,
6814
+ state.enhance,
6417
6815
  state.confirmQueue.length,
6418
6816
  state.entries.length,
6817
+ state.toolStream?.text,
6419
6818
  eraseLiveRegion
6420
6819
  ]);
6421
6820
  useEffect(() => {
@@ -6425,6 +6824,9 @@ function App({
6425
6824
  process.stdout.off("resize", handleResize);
6426
6825
  };
6427
6826
  }, [eraseLiveRegion]);
6827
+ React6.useLayoutEffect(() => {
6828
+ if (state.enhanceBusy || state.enhance != null) eraseLiveRegion();
6829
+ }, [nowTick, state.enhanceBusy, state.enhance, eraseLiveRegion]);
6428
6830
  useEffect(() => {
6429
6831
  const detected = detectAtToken(state.buffer, state.cursor);
6430
6832
  if (!detected) {
@@ -6635,60 +7037,6 @@ function App({
6635
7037
  slashRegistry.unregister("altscreen");
6636
7038
  };
6637
7039
  }, [slashRegistry]);
6638
- const mouseLiveRef = useRef(mouseLive);
6639
- mouseLiveRef.current = mouseLive;
6640
- useEffect(() => {
6641
- const MOUSE_ON_SEQ = "\x1B[?1000h\x1B[?1006h";
6642
- const MOUSE_OFF_SEQ = "\x1B[?1006l\x1B[?1000l";
6643
- const ALT_ON = "\x1B[?1049h";
6644
- const ALT_OFF = "\x1B[?1049l";
6645
- const cmd = {
6646
- name: "mouse",
6647
- description: "Toggle mouse mode (clickable menus + wheel-scroll chat). Needs launch with --mouse to enable.",
6648
- async run(args) {
6649
- const arg = args.trim().toLowerCase();
6650
- if (arg !== "on" && arg !== "off") {
6651
- return {
6652
- message: `Mouse mode is ${mouseLiveRef.current ? "ON" : "OFF"}. Usage: /mouse on|off`
6653
- };
6654
- }
6655
- if (arg === "on") {
6656
- if (!mouse) {
6657
- return {
6658
- message: "Mouse mode needs the --mouse launch flag (it rewires stdin so mouse bytes never reach the input). Restart with `wstack --tui --mouse`."
6659
- };
6660
- }
6661
- try {
6662
- writeOut(ALT_ON);
6663
- writeOut("\x1B[H");
6664
- writeOut(MOUSE_ON_SEQ);
6665
- } catch {
6666
- return { message: "Failed to enable mouse mode." };
6667
- }
6668
- setMouseLive(true);
6669
- setManagedLive(true);
6670
- return {
6671
- message: "Mouse mode ON. Click menu items, wheel-scroll the chat (PgUp/PgDn too). Native terminal copy/scroll are suspended until `/mouse off`."
6672
- };
6673
- }
6674
- try {
6675
- writeOut(MOUSE_OFF_SEQ);
6676
- writeOut(ALT_OFF);
6677
- } catch {
6678
- return { message: "Failed to disable mouse mode." };
6679
- }
6680
- setMouseLive(false);
6681
- setManagedLive(false);
6682
- return {
6683
- message: "Mouse mode OFF. Native terminal scroll/copy restored; chat history flows into native scrollback again."
6684
- };
6685
- }
6686
- };
6687
- slashRegistry.register(cmd);
6688
- return () => {
6689
- slashRegistry.unregister("mouse");
6690
- };
6691
- }, [slashRegistry, mouse]);
6692
7040
  useEffect(() => {
6693
7041
  const cmd = {
6694
7042
  name: "steer",
@@ -6800,14 +7148,31 @@ function App({
6800
7148
  const providers = await getPickableProviders();
6801
7149
  dispatch({ type: "modelPickerOpen", providers });
6802
7150
  }, [getPickableProviders]);
6803
- const openAutonomyPicker = React6.useCallback(() => {
6804
- if (!switchAutonomy) return;
6805
- dispatch({ type: "autonomyPickerOpen", options: AUTONOMY_OPTIONS });
6806
- }, [switchAutonomy]);
6807
7151
  const openSettings = React6.useCallback(() => {
6808
7152
  if (!getSettings) return;
6809
7153
  const s2 = getSettings();
6810
- dispatch({ type: "settingsOpen", mode: s2.mode, delayMs: s2.delayMs });
7154
+ dispatch({
7155
+ type: "settingsOpen",
7156
+ mode: s2.mode,
7157
+ delayMs: s2.delayMs,
7158
+ titleAnimation: s2.titleAnimation ?? true,
7159
+ yolo: s2.yolo ?? false,
7160
+ streamFleet: s2.streamFleet ?? true,
7161
+ chime: s2.chime ?? false,
7162
+ confirmExit: s2.confirmExit ?? true,
7163
+ nextPrediction: s2.nextPrediction ?? false,
7164
+ featureMcp: s2.featureMcp ?? true,
7165
+ featurePlugins: s2.featurePlugins ?? true,
7166
+ featureMemory: s2.featureMemory ?? true,
7167
+ featureSkills: s2.featureSkills ?? true,
7168
+ featureModelsRegistry: s2.featureModelsRegistry ?? true,
7169
+ contextAutoCompact: s2.contextAutoCompact ?? true,
7170
+ contextStrategy: s2.contextStrategy ?? "hybrid",
7171
+ logLevel: s2.logLevel ?? "info",
7172
+ auditLevel: s2.auditLevel ?? "standard",
7173
+ indexOnStart: s2.indexOnStart ?? true,
7174
+ maxIterations: s2.maxIterations ?? 500
7175
+ });
6811
7176
  }, [getSettings]);
6812
7177
  useEffect(() => {
6813
7178
  if (!getPickableProviders || !switchProviderAndModel) return;
@@ -6820,7 +7185,7 @@ function App({
6820
7185
  return { message: void 0 };
6821
7186
  }
6822
7187
  };
6823
- slashRegistry.register(cmd);
7188
+ slashRegistry.register(cmd, "tui", { official: true });
6824
7189
  return () => {
6825
7190
  slashRegistry.unregister("model");
6826
7191
  };
@@ -6830,13 +7195,13 @@ function App({
6830
7195
  const cmd = {
6831
7196
  name: "settings",
6832
7197
  aliases: ["config", "prefs"],
6833
- description: "Edit autonomy defaults (mode + auto-proceed delay).",
7198
+ description: "Open the interactive settings editor (19 config fields across 8 sections).",
6834
7199
  async run() {
6835
7200
  openSettings();
6836
7201
  return { message: void 0 };
6837
7202
  }
6838
7203
  };
6839
- slashRegistry.register(cmd);
7204
+ slashRegistry.register(cmd, "tui", { official: true });
6840
7205
  return () => {
6841
7206
  slashRegistry.unregister("settings");
6842
7207
  };
@@ -6852,7 +7217,7 @@ function App({
6852
7217
  return { message: void 0 };
6853
7218
  }
6854
7219
  };
6855
- slashRegistry.register(cmd);
7220
+ slashRegistry.register(cmd, "tui", { official: true });
6856
7221
  return () => {
6857
7222
  slashRegistry.unregister("autonomy");
6858
7223
  };
@@ -6893,23 +7258,25 @@ function App({
6893
7258
  });
6894
7259
  });
6895
7260
  const offTool = events.on("tool.executed", (e) => {
6896
- dispatch({
6897
- type: "addEntry",
6898
- entry: {
6899
- kind: "tool",
6900
- name: e.name,
6901
- durationMs: e.durationMs,
6902
- ok: e.ok,
6903
- input: e.input,
6904
- output: e.output,
6905
- // Real model-visible sizes — forwarded so the size chip beside
6906
- // the tool header can show what the model paid for instead of
6907
- // the misleading preview-byte count we used to surface.
6908
- outputBytes: e.outputBytes,
6909
- outputTokens: e.outputTokens,
6910
- outputLines: e.outputLines
6911
- }
6912
- });
7261
+ if (e.name !== "delegate") {
7262
+ dispatch({
7263
+ type: "addEntry",
7264
+ entry: {
7265
+ kind: "tool",
7266
+ name: e.name,
7267
+ durationMs: e.durationMs,
7268
+ ok: e.ok,
7269
+ input: e.input,
7270
+ output: e.output,
7271
+ // Real model-visible sizes forwarded so the size chip beside
7272
+ // the tool header can show what the model paid for instead of
7273
+ // the misleading preview-byte count we used to surface.
7274
+ outputBytes: e.outputBytes,
7275
+ outputTokens: e.outputTokens,
7276
+ outputLines: e.outputLines
7277
+ }
7278
+ });
7279
+ }
6913
7280
  dispatch({ type: "toolEnded", name: e.name });
6914
7281
  dispatch({ type: "toolStreamClear", name: e.name });
6915
7282
  dispatch({ type: "leaderToolEnd", name: e.name, ok: e.ok, durationMs: e.durationMs });
@@ -6969,6 +7336,34 @@ function App({
6969
7336
  }
6970
7337
  });
6971
7338
  });
7339
+ const offDelegateStart = events.on("delegate.started", (e) => {
7340
+ const task = e.task.length > 100 ? `${e.task.slice(0, 99)}\u2026` : e.task;
7341
+ dispatch({
7342
+ type: "addEntry",
7343
+ entry: {
7344
+ kind: "subagent",
7345
+ agentLabel: e.target,
7346
+ agentColor: "magenta",
7347
+ icon: "\u{1F91D}",
7348
+ text: "delegating",
7349
+ detail: task
7350
+ }
7351
+ });
7352
+ });
7353
+ const offDelegateDone = events.on("delegate.completed", (e) => {
7354
+ const cost = e.costUsd && e.costUsd > 0 ? `$${e.costUsd.toFixed(3)}` : void 0;
7355
+ dispatch({
7356
+ type: "addEntry",
7357
+ entry: {
7358
+ kind: "subagent",
7359
+ agentLabel: e.target,
7360
+ agentColor: e.ok ? "green" : "red",
7361
+ icon: e.ok ? "\u2713" : "\u2717",
7362
+ text: e.summary,
7363
+ detail: cost
7364
+ }
7365
+ });
7366
+ });
6972
7367
  return () => {
6973
7368
  offDelta();
6974
7369
  offToolStart();
@@ -6981,6 +7376,8 @@ function App({
6981
7376
  offProvResp();
6982
7377
  offConfirmNeeded();
6983
7378
  offTrustPersisted();
7379
+ offDelegateStart();
7380
+ offDelegateDone();
6984
7381
  if (flushTimerRef.current) clearTimeout(flushTimerRef.current);
6985
7382
  };
6986
7383
  }, [events, agent.ctx.todos]);
@@ -6988,6 +7385,11 @@ function App({
6988
7385
  useEffect(() => {
6989
7386
  streamFleetRef.current = state.streamFleet;
6990
7387
  }, [state.streamFleet]);
7388
+ const enhanceEnabledRef = useRef(state.enhanceEnabled);
7389
+ useEffect(() => {
7390
+ enhanceEnabledRef.current = state.enhanceEnabled;
7391
+ }, [state.enhanceEnabled]);
7392
+ const enhanceAbortRef = useRef(null);
6991
7393
  useSubagentEvents(events, dispatch, setActiveMaxContext);
6992
7394
  useEffect(() => {
6993
7395
  const offCheckpoint = events.on("checkpoint.written", (e) => {
@@ -7213,6 +7615,18 @@ function App({
7213
7615
  useEffect(() => {
7214
7616
  if (fleetStreamController) fleetStreamController.enabled = state.streamFleet;
7215
7617
  }, [state.streamFleet, fleetStreamController]);
7618
+ useEffect(() => {
7619
+ if (!enhanceController) return;
7620
+ enhanceController.enabled = state.enhanceEnabled;
7621
+ enhanceController.setEnabled = (enabled) => {
7622
+ dispatch({ type: "enhanceSet", enabled });
7623
+ };
7624
+ return () => {
7625
+ enhanceController.setEnabled = (enabled) => {
7626
+ enhanceController.enabled = enabled;
7627
+ };
7628
+ };
7629
+ }, [enhanceController, state.enhanceEnabled]);
7216
7630
  useEffect(() => {
7217
7631
  if (!agentsMonitorController) return;
7218
7632
  agentsMonitorController.visible = state.agentsMonitorOpen;
@@ -7292,7 +7706,8 @@ function App({
7292
7706
  type: "fleetCost",
7293
7707
  cost: d.snapshot().total.cost,
7294
7708
  input: d.snapshot().total.input,
7295
- output: d.snapshot().total.output
7709
+ output: d.snapshot().total.output,
7710
+ perAgent: d.snapshot().perSubagent
7296
7711
  });
7297
7712
  const seen = new Set(Object.keys(status.subagents));
7298
7713
  const pending = /* @__PURE__ */ new Map();
@@ -7421,7 +7836,8 @@ function App({
7421
7836
  type: "fleetCost",
7422
7837
  cost: d.snapshot().total.cost,
7423
7838
  input: d.snapshot().total.input,
7424
- output: d.snapshot().total.output
7839
+ output: d.snapshot().total.output,
7840
+ perAgent: d.snapshot().perSubagent
7425
7841
  });
7426
7842
  break;
7427
7843
  }
@@ -7549,7 +7965,8 @@ function App({
7549
7965
  type: "fleetCost",
7550
7966
  cost: d.snapshot().total.cost,
7551
7967
  input: d.snapshot().total.input,
7552
- output: d.snapshot().total.output
7968
+ output: d.snapshot().total.output,
7969
+ perAgent: d.snapshot().perSubagent
7553
7970
  });
7554
7971
  if (streamFlushTimer) {
7555
7972
  clearTimeout(streamFlushTimer);
@@ -7630,7 +8047,7 @@ function App({
7630
8047
  type: "addEntry",
7631
8048
  entry: {
7632
8049
  kind: "warn",
7633
- text: `Iteration cancelled${director ? " + fleet terminated" : ""}${procTag}. Dropped ${droppedCount} queued message${droppedCount === 1 ? "" : "s"}. Press Ctrl+C again to exit.`
8050
+ 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."}`
7634
8051
  }
7635
8052
  });
7636
8053
  } else {
@@ -7638,7 +8055,7 @@ function App({
7638
8055
  type: "addEntry",
7639
8056
  entry: {
7640
8057
  kind: "warn",
7641
- text: `Iteration cancelled${director ? " + fleet terminated" : ""}${procTag}. Press Ctrl+C again to exit.`
8058
+ text: `Iteration cancelled${director ? " + fleet terminated" : ""}${procTag}. ${confirmExitRef.current ? "Press Ctrl+C again to confirm exit." : "Press Ctrl+C again to exit."}`
7642
8059
  }
7643
8060
  });
7644
8061
  }
@@ -7669,7 +8086,7 @@ function App({
7669
8086
  type: "addEntry",
7670
8087
  entry: {
7671
8088
  kind: "warn",
7672
- text: `${bits.join(" + ") || "Background work stopped"}. Press Ctrl+C again to exit.`
8089
+ text: `${bits.join(" + ") || "Background work stopped"}. ${confirmExitRef.current ? "Press Ctrl+C again to confirm exit." : "Press Ctrl+C again to exit."}`
7673
8090
  }
7674
8091
  });
7675
8092
  return;
@@ -7712,10 +8129,16 @@ function App({
7712
8129
  const handleKey = async (input, key) => {
7713
8130
  if (state.status === "aborting" && !state.steeringPending && state.interrupts === 0) return;
7714
8131
  if (state.confirmQueue.length > 0) return;
8132
+ if (state.enhanceBusy) {
8133
+ if (key.escape) enhanceAbortRef.current?.abort();
8134
+ return;
8135
+ }
8136
+ if (state.enhance) return;
7715
8137
  if (state.helpOpen) {
7716
8138
  if (key.escape || input === "?" || input === "q") dispatch({ type: "toggleHelp" });
7717
8139
  return;
7718
8140
  }
8141
+ state.todosMonitorOpen || state.monitorOpen || state.agentsMonitorOpen || state.worktreeMonitorOpen || !!state.autoPhase?.monitorOpen;
7719
8142
  if (inputGateRef.current) return;
7720
8143
  if (managedLive) {
7721
8144
  if (key.pageUp) {
@@ -7887,8 +8310,8 @@ function App({
7887
8310
  const now = Date.now();
7888
8311
  if (now - lastEnterAtRef.current < 50) return;
7889
8312
  lastEnterAtRef.current = now;
7890
- const { mode, delayMs } = state.settingsPicker;
7891
- const err = await saveSettings?.({ mode, delayMs });
8313
+ 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;
8314
+ 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 });
7892
8315
  if (err) {
7893
8316
  dispatch({ type: "settingsHint", text: err });
7894
8317
  return;
@@ -8001,20 +8424,32 @@ function App({
8001
8424
  return;
8002
8425
  }
8003
8426
  const toggleFleetOverlay = () => {
8004
- if (state.agentsMonitorOpen) {
8005
- dispatch({ type: "toggleAgentsMonitor" });
8006
- dispatch({ type: "toggleMonitor" });
8007
- } else {
8427
+ if (state.monitorOpen) {
8008
8428
  dispatch({ type: "toggleMonitor" });
8429
+ return;
8009
8430
  }
8431
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
8432
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
8433
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
8434
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8435
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
8436
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
8437
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
8438
+ dispatch({ type: "toggleMonitor" });
8010
8439
  };
8011
8440
  const toggleAgentsOverlay = () => {
8012
- if (state.monitorOpen) {
8013
- dispatch({ type: "toggleMonitor" });
8014
- dispatch({ type: "toggleAgentsMonitor" });
8015
- } else {
8441
+ if (state.agentsMonitorOpen) {
8016
8442
  dispatch({ type: "toggleAgentsMonitor" });
8443
+ return;
8017
8444
  }
8445
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
8446
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
8447
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
8448
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8449
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
8450
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
8451
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
8452
+ dispatch({ type: "toggleAgentsMonitor" });
8018
8453
  };
8019
8454
  const toggleWorktreeOverlay = () => {
8020
8455
  if (state.worktreeMonitorOpen) {
@@ -8024,8 +8459,26 @@ function App({
8024
8459
  if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
8025
8460
  if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
8026
8461
  if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8462
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
8463
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
8464
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
8465
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
8027
8466
  dispatch({ type: "worktreeMonitorToggle" });
8028
8467
  };
8468
+ const toggleTodosOverlay = () => {
8469
+ if (state.todosMonitorOpen) {
8470
+ dispatch({ type: "toggleTodosMonitor" });
8471
+ return;
8472
+ }
8473
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
8474
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
8475
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
8476
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8477
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
8478
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
8479
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
8480
+ dispatch({ type: "toggleTodosMonitor" });
8481
+ };
8029
8482
  if (key.ctrl && input === "f" || key.fn === 2) {
8030
8483
  toggleFleetOverlay();
8031
8484
  return;
@@ -8038,12 +8491,96 @@ function App({
8038
8491
  toggleWorktreeOverlay();
8039
8492
  return;
8040
8493
  }
8494
+ if (key.fn === 5) {
8495
+ if (state.settingsPicker.open) {
8496
+ dispatch({ type: "settingsClose" });
8497
+ } else if (getSettings && saveSettings) {
8498
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
8499
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
8500
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
8501
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
8502
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8503
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
8504
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
8505
+ const cfg = getSettings();
8506
+ dispatch({
8507
+ type: "settingsOpen",
8508
+ mode: cfg.mode,
8509
+ delayMs: cfg.delayMs,
8510
+ titleAnimation: cfg.titleAnimation ?? true,
8511
+ yolo: cfg.yolo ?? false,
8512
+ streamFleet: cfg.streamFleet ?? true,
8513
+ chime: cfg.chime ?? false,
8514
+ confirmExit: cfg.confirmExit ?? true,
8515
+ nextPrediction: cfg.nextPrediction ?? false,
8516
+ featureMcp: cfg.featureMcp ?? true,
8517
+ featurePlugins: cfg.featurePlugins ?? true,
8518
+ featureMemory: cfg.featureMemory ?? true,
8519
+ featureSkills: cfg.featureSkills ?? true,
8520
+ featureModelsRegistry: cfg.featureModelsRegistry ?? true,
8521
+ contextAutoCompact: cfg.contextAutoCompact ?? true,
8522
+ contextStrategy: cfg.contextStrategy ?? "hybrid",
8523
+ logLevel: cfg.logLevel ?? "info",
8524
+ auditLevel: cfg.auditLevel ?? "standard",
8525
+ indexOnStart: cfg.indexOnStart ?? true,
8526
+ maxIterations: cfg.maxIterations ?? 500
8527
+ });
8528
+ }
8529
+ return;
8530
+ }
8531
+ if (key.fn === 6) {
8532
+ toggleTodosOverlay();
8533
+ return;
8534
+ }
8535
+ if (key.fn === 7) {
8536
+ if (state.queuePanelOpen) {
8537
+ dispatch({ type: "toggleQueuePanel" });
8538
+ } else {
8539
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
8540
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
8541
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
8542
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
8543
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8544
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
8545
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
8546
+ dispatch({ type: "toggleQueuePanel" });
8547
+ }
8548
+ return;
8549
+ }
8041
8550
  if (key.ctrl && input === "s") {
8042
8551
  if (state.settingsPicker.open) {
8043
8552
  dispatch({ type: "settingsClose" });
8044
8553
  } else if (getSettings && saveSettings) {
8554
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
8555
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
8556
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
8557
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
8558
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
8559
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
8560
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
8045
8561
  const cfg = getSettings();
8046
- dispatch({ type: "settingsOpen", mode: cfg.mode, delayMs: cfg.delayMs });
8562
+ dispatch({
8563
+ type: "settingsOpen",
8564
+ mode: cfg.mode,
8565
+ delayMs: cfg.delayMs,
8566
+ titleAnimation: cfg.titleAnimation ?? true,
8567
+ yolo: cfg.yolo ?? false,
8568
+ streamFleet: cfg.streamFleet ?? true,
8569
+ chime: cfg.chime ?? false,
8570
+ confirmExit: cfg.confirmExit ?? true,
8571
+ nextPrediction: cfg.nextPrediction ?? false,
8572
+ featureMcp: cfg.featureMcp ?? true,
8573
+ featurePlugins: cfg.featurePlugins ?? true,
8574
+ featureMemory: cfg.featureMemory ?? true,
8575
+ featureSkills: cfg.featureSkills ?? true,
8576
+ featureModelsRegistry: cfg.featureModelsRegistry ?? true,
8577
+ contextAutoCompact: cfg.contextAutoCompact ?? true,
8578
+ contextStrategy: cfg.contextStrategy ?? "hybrid",
8579
+ logLevel: cfg.logLevel ?? "info",
8580
+ auditLevel: cfg.auditLevel ?? "standard",
8581
+ indexOnStart: cfg.indexOnStart ?? true,
8582
+ maxIterations: cfg.maxIterations ?? 500
8583
+ });
8047
8584
  }
8048
8585
  return;
8049
8586
  }
@@ -8060,12 +8597,34 @@ function App({
8060
8597
  dispatch({ type: "worktreeMonitorToggle" });
8061
8598
  return;
8062
8599
  }
8600
+ if (state.todosMonitorOpen) {
8601
+ dispatch({ type: "toggleTodosMonitor" });
8602
+ return;
8603
+ }
8604
+ if (state.autoPhase?.monitorOpen) {
8605
+ dispatch({ type: "autoPhaseMonitorToggle" });
8606
+ return;
8607
+ }
8608
+ if (state.settingsPicker.open) {
8609
+ dispatch({ type: "settingsClose" });
8610
+ return;
8611
+ }
8612
+ if (state.queuePanelOpen) {
8613
+ dispatch({ type: "toggleQueuePanel" });
8614
+ return;
8615
+ }
8063
8616
  }
8064
- 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) {
8617
+ 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) {
8065
8618
  dispatch({ type: "toggleHelp" });
8066
8619
  return;
8067
8620
  }
8068
8621
  if (isEnter) {
8622
+ if (key.shift) {
8623
+ const { buffer: buffer2, cursor: cursor2 } = draftRef.current;
8624
+ const next2 = buffer2.slice(0, cursor2) + "\n" + buffer2.slice(cursor2);
8625
+ setDraft(next2, cursor2 + 1);
8626
+ return;
8627
+ }
8069
8628
  const now = Date.now();
8070
8629
  if (now - lastEnterAtRef.current < 50) return;
8071
8630
  lastEnterAtRef.current = now;
@@ -8073,36 +8632,41 @@ function App({
8073
8632
  return;
8074
8633
  }
8075
8634
  const { buffer, cursor } = draftRef.current;
8076
- if (key.backspace || key.delete) {
8635
+ if (key.backspace) {
8077
8636
  if (key.ctrl) {
8078
- if (key.backspace) {
8079
- if (cursor === 0) return;
8080
- const beforeCursor = buffer.slice(0, cursor);
8081
- const lastWordStart = beforeCursor.lastIndexOf(" ") + 1;
8082
- const next3 = beforeCursor.slice(0, lastWordStart) + buffer.slice(cursor);
8083
- setDraft(next3, lastWordStart);
8084
- } else {
8085
- if (cursor >= buffer.length) return;
8086
- const afterCursor = buffer.slice(cursor);
8087
- const nextWordStart = afterCursor.indexOf(" ");
8088
- const end = nextWordStart === -1 ? buffer.length : cursor + nextWordStart + 1;
8089
- const next3 = buffer.slice(0, cursor) + buffer.slice(end);
8090
- setDraft(next3, cursor);
8091
- }
8637
+ if (cursor === 0) return;
8638
+ const beforeCursor = buffer.slice(0, cursor);
8639
+ const lastWordStart = beforeCursor.lastIndexOf(" ") + 1;
8640
+ const next3 = beforeCursor.slice(0, lastWordStart) + buffer.slice(cursor);
8641
+ setDraft(next3, lastWordStart);
8092
8642
  return;
8093
8643
  }
8094
- if (key.backspace) {
8095
- const tokenDel = deleteTokenBackward(buffer, cursor);
8096
- if (tokenDel) {
8097
- setDraft(tokenDel.buffer, tokenDel.cursor);
8098
- return;
8099
- }
8644
+ const tokenDel = deleteTokenBackward(buffer, cursor);
8645
+ if (tokenDel) {
8646
+ setDraft(tokenDel.buffer, tokenDel.cursor);
8647
+ return;
8100
8648
  }
8101
8649
  if (cursor === 0) return;
8102
8650
  const next2 = buffer.slice(0, cursor - 1) + buffer.slice(cursor);
8103
8651
  setDraft(next2, cursor - 1);
8104
8652
  return;
8105
8653
  }
8654
+ if (key.delete) {
8655
+ if (key.ctrl) {
8656
+ if (cursor >= buffer.length) return;
8657
+ const afterCursor = buffer.slice(cursor);
8658
+ const nextWordStart = afterCursor.indexOf(" ");
8659
+ const end = nextWordStart === -1 ? buffer.length : cursor + nextWordStart + 1;
8660
+ const next3 = buffer.slice(0, cursor) + buffer.slice(end);
8661
+ setDraft(next3, cursor);
8662
+ return;
8663
+ }
8664
+ if (cursor >= buffer.length) return;
8665
+ const span = tokenLengthForward(buffer, cursor) || 1;
8666
+ const next2 = buffer.slice(0, cursor) + buffer.slice(cursor + span);
8667
+ setDraft(next2, cursor);
8668
+ return;
8669
+ }
8106
8670
  if (key.leftArrow) {
8107
8671
  if (key.ctrl) {
8108
8672
  if (cursor === 0) return;
@@ -8173,7 +8737,7 @@ function App({
8173
8737
  setDraft("", 0);
8174
8738
  return;
8175
8739
  }
8176
- if (key.delete || key.ctrl && input === "d") {
8740
+ if (key.ctrl && input === "d") {
8177
8741
  if (cursor >= buffer.length) return;
8178
8742
  const span = tokenLengthForward(buffer, cursor) || 1;
8179
8743
  const next2 = buffer.slice(0, cursor) + buffer.slice(cursor + span);
@@ -8300,10 +8864,20 @@ function App({
8300
8864
  } finally {
8301
8865
  activeCtrlRef.current = null;
8302
8866
  dispatch({ type: "status", status: "idle" });
8867
+ if (chimeRef.current) {
8868
+ try {
8869
+ process.stdout.write("\x07");
8870
+ } catch {
8871
+ }
8872
+ }
8303
8873
  }
8304
8874
  const head = stateRef.current.queue[0];
8305
8875
  if (head) {
8306
8876
  dispatch({ type: "dequeueFirst" });
8877
+ dispatch({
8878
+ type: "addEntry",
8879
+ entry: { kind: "user", text: head.displayText }
8880
+ });
8307
8881
  await runBlocks(head.blocks);
8308
8882
  }
8309
8883
  };
@@ -8448,6 +9022,10 @@ function App({
8448
9022
  void runParallelLoopRef.current();
8449
9023
  }
8450
9024
  }
9025
+ if (getModeLabel) {
9026
+ const currentMode = getModeLabel();
9027
+ if (currentMode !== liveModeLabel) setLiveModeLabel(currentMode);
9028
+ }
8451
9029
  if (res?.exit) {
8452
9030
  exit();
8453
9031
  onExit(0);
@@ -8467,6 +9045,7 @@ function App({
8467
9045
  const cmd = trimmed.slice(1).split(/\s+/, 1)[0];
8468
9046
  if (cmd === "clear") {
8469
9047
  onClearHistory?.(dispatch);
9048
+ tokenCounter?.reset();
8470
9049
  }
8471
9050
  } catch (err) {
8472
9051
  dispatch({
@@ -8479,6 +9058,52 @@ function App({
8479
9058
  const builder = builderRef.current;
8480
9059
  if (!builder) return;
8481
9060
  const steering = state.steeringPending;
9061
+ let effectiveText = trimmed;
9062
+ const hasChips = trimmed ? new RegExp(INLINE_TOKEN_SRC, "g").test(trimmed) : false;
9063
+ if (enhanceEnabledRef.current && state.status === "idle" && !steering && !hasChips && shouldEnhance(trimmed)) {
9064
+ dispatch({ type: "enhanceBusy", on: true });
9065
+ const ac = new AbortController();
9066
+ enhanceAbortRef.current = ac;
9067
+ let refined = null;
9068
+ let enhanceErr = null;
9069
+ try {
9070
+ refined = await enhanceUserPrompt({
9071
+ provider: agent.ctx.provider,
9072
+ model: agent.ctx.model,
9073
+ text: trimmed,
9074
+ signal: ac.signal,
9075
+ onError: (reason) => {
9076
+ enhanceErr = reason;
9077
+ },
9078
+ // Feed recent conversation so follow-ups ("do the same", "that file")
9079
+ // resolve against context instead of being refined blind.
9080
+ history: recentTextTurns(agent.ctx.messages)
9081
+ });
9082
+ } finally {
9083
+ enhanceAbortRef.current = null;
9084
+ dispatch({ type: "enhanceBusy", on: false });
9085
+ }
9086
+ if (refined === null && !ac.signal.aborted) {
9087
+ dispatch({
9088
+ type: "addEntry",
9089
+ entry: {
9090
+ kind: "info",
9091
+ text: enhanceErr ? `\u2728 refinement unavailable (${enhanceErr}) \u2014 sent your message as-is` : "\u2728 refinement unavailable \u2014 sent your message as-is"
9092
+ }
9093
+ });
9094
+ }
9095
+ if (refined && !normalizedEqual(refined, trimmed)) {
9096
+ const decision = await new Promise((resolve) => {
9097
+ dispatch({ type: "enhanceOpen", info: { original: trimmed, refined, resolve } });
9098
+ });
9099
+ dispatch({ type: "enhanceClose" });
9100
+ if (decision === "edit") {
9101
+ setDraft(refined, refined.length);
9102
+ return;
9103
+ }
9104
+ effectiveText = decision === "refined" ? refined : trimmed;
9105
+ }
9106
+ }
8482
9107
  const sddContext = getSDDContext?.();
8483
9108
  if (sddContext && trimmed) {
8484
9109
  builder.appendText(`[SDD SESSION ACTIVE]
@@ -8489,11 +9114,11 @@ User message:
8489
9114
  `);
8490
9115
  }
8491
9116
  if (trimmed) {
8492
- const toAppend = steering ? buildSteeringPreamble(state.steerSnapshot, trimmed) : trimmed;
9117
+ const toAppend = steering ? buildSteeringPreamble(state.steerSnapshot, effectiveText) : effectiveText;
8493
9118
  builder.appendText(toAppend);
8494
9119
  }
8495
9120
  if (steering) dispatch({ type: "steerConsume" });
8496
- const displayText = trimmed ? steering ? `\u21AF ${trimmed}` : trimmed : "(attachments only)";
9121
+ const displayText = trimmed ? steering ? `\u21AF ${effectiveText}` : effectiveText : "(attachments only)";
8497
9122
  const pasteParts = [];
8498
9123
  for (const m of trimmed.matchAll(new RegExp(INLINE_TOKEN_SRC, "g"))) {
8499
9124
  const token = m[0];
@@ -8547,20 +9172,6 @@ User message:
8547
9172
  })();
8548
9173
  }, [initialAsk, initialGoal]);
8549
9174
  handleKeyRef.current = handleKey;
8550
- openModelPickerRef.current = () => {
8551
- void openModelPicker();
8552
- };
8553
- openAutonomyPickerRef.current = openAutonomyPicker;
8554
- handleRewindToRef.current = (promptIndex) => {
8555
- void handleRewindTo(promptIndex);
8556
- };
8557
- statusChipRef.current = {
8558
- version: appVersion,
8559
- model: `${liveProvider}/${liveModel}`,
8560
- fleetRunning: fleetCounts?.running ?? 0,
8561
- yolo: yoloLive,
8562
- autonomy: autonomyLive
8563
- };
8564
9175
  const inputHint = useMemo(() => {
8565
9176
  if (state.status !== "idle") return "";
8566
9177
  if (state.buffer.startsWith("/")) return "slash command \u2014 Enter to dispatch";
@@ -8568,7 +9179,8 @@ User message:
8568
9179
  return "";
8569
9180
  }, [state.buffer, state.status, state.picker.open]);
8570
9181
  const affordanceShown = managedLive && state.scrollOffset > 0 && state.pendingNewLines > 0;
8571
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", height: managedLive ? termRows : void 0, children: [
9182
+ const enhanceActive = state.enhanceBusy || state.enhance != null;
9183
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", height: managedLive ? termRows : void 0, children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 0, children: [
8572
9184
  managedLive ? /* @__PURE__ */ jsx(
8573
9185
  ScrollableHistory,
8574
9186
  {
@@ -8590,20 +9202,18 @@ User message:
8590
9202
  ),
8591
9203
  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,
8592
9204
  /* @__PURE__ */ jsxs(Box, { ref: managedLive ? bottomRef : void 0, flexDirection: "column", flexShrink: 0, children: [
8593
- /* @__PURE__ */ jsxs(Box, { ref: managedLive ? prePickerRef : void 0, flexDirection: "column", flexShrink: 0, children: [
8594
- /* @__PURE__ */ jsx(Box, { ref: managedLive ? liveStripRef : void 0, flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx(LiveActivityStrip, { entries: state.fleet, nowTick }) }),
8595
- /* @__PURE__ */ jsx(
8596
- Input,
8597
- {
8598
- prompt: INPUT_PROMPT,
8599
- value: state.buffer,
8600
- cursor: state.cursor,
8601
- disabled: state.status === "aborting" && !state.steeringPending || state.confirmQueue.length > 0,
8602
- hint: inputHint,
8603
- onKey: handleKey
8604
- }
8605
- )
8606
- ] }),
9205
+ /* @__PURE__ */ jsx(LiveActivityStrip, { entries: state.fleet, nowTick }),
9206
+ /* @__PURE__ */ jsx(
9207
+ Input,
9208
+ {
9209
+ prompt: INPUT_PROMPT,
9210
+ value: enhanceActive ? "" : state.buffer,
9211
+ cursor: enhanceActive ? 0 : state.cursor,
9212
+ disabled: state.status === "aborting" && !state.steeringPending || state.confirmQueue.length > 0,
9213
+ hint: enhanceActive ? "" : inputHint,
9214
+ onKey: handleKey
9215
+ }
9216
+ ),
8607
9217
  state.picker.open ? /* @__PURE__ */ jsx(
8608
9218
  FilePicker,
8609
9219
  {
@@ -8647,6 +9257,23 @@ User message:
8647
9257
  field: state.settingsPicker.field,
8648
9258
  mode: state.settingsPicker.mode,
8649
9259
  delayMs: state.settingsPicker.delayMs,
9260
+ titleAnimation: state.settingsPicker.titleAnimation,
9261
+ yolo: state.settingsPicker.yolo,
9262
+ streamFleet: state.settingsPicker.streamFleet,
9263
+ chime: state.settingsPicker.chime,
9264
+ confirmExit: state.settingsPicker.confirmExit,
9265
+ nextPrediction: state.settingsPicker.nextPrediction,
9266
+ featureMcp: state.settingsPicker.featureMcp,
9267
+ featurePlugins: state.settingsPicker.featurePlugins,
9268
+ featureMemory: state.settingsPicker.featureMemory,
9269
+ featureSkills: state.settingsPicker.featureSkills,
9270
+ featureModelsRegistry: state.settingsPicker.featureModelsRegistry,
9271
+ contextAutoCompact: state.settingsPicker.contextAutoCompact,
9272
+ contextStrategy: state.settingsPicker.contextStrategy,
9273
+ logLevel: state.settingsPicker.logLevel,
9274
+ auditLevel: state.settingsPicker.auditLevel,
9275
+ indexOnStart: state.settingsPicker.indexOnStart,
9276
+ maxIterations: state.settingsPicker.maxIterations,
8650
9277
  hint: state.settingsPicker.hint
8651
9278
  }
8652
9279
  ) : null,
@@ -8679,8 +9306,7 @@ User message:
8679
9306
  head.resolve(decision);
8680
9307
  dispatch({ type: "confirmClose" });
8681
9308
  };
8682
- confirmDecisionRef.current = onDecision;
8683
- return /* @__PURE__ */ jsx(Box, { ref: confirmRef, flexDirection: "column", marginY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx(
9309
+ return /* @__PURE__ */ jsx(
8684
9310
  ConfirmPrompt,
8685
9311
  {
8686
9312
  toolName: head.toolName,
@@ -8688,8 +9314,27 @@ User message:
8688
9314
  suggestedPattern: head.suggestedPattern,
8689
9315
  onDecision
8690
9316
  }
8691
- ) });
9317
+ );
8692
9318
  })(),
9319
+ state.enhanceBusy && !state.enhance ? /* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u2728 refining your request\u2026" }) }) : null,
9320
+ state.enhance ? (() => {
9321
+ const info = state.enhance;
9322
+ let resolved = false;
9323
+ const onDecision = (decision) => {
9324
+ if (resolved) return;
9325
+ resolved = true;
9326
+ info.resolve(decision);
9327
+ };
9328
+ return /* @__PURE__ */ jsx(
9329
+ EnhancePanel,
9330
+ {
9331
+ original: info.original,
9332
+ refined: info.refined,
9333
+ delayMs: enhanceDelayMs,
9334
+ onDecision
9335
+ }
9336
+ );
9337
+ })() : null,
8693
9338
  /* @__PURE__ */ jsx(
8694
9339
  StatusBar,
8695
9340
  {
@@ -8713,7 +9358,9 @@ User message:
8713
9358
  processCount: getProcessRegistry().activeCount,
8714
9359
  hiddenItems,
8715
9360
  eternalStage: state.eternalStage,
8716
- goalSummary: state.goalSummary
9361
+ goalSummary: state.goalSummary,
9362
+ indexState,
9363
+ modeLabel: liveModeLabel || void 0
8717
9364
  }
8718
9365
  ),
8719
9366
  managedLive ? /* @__PURE__ */ jsx(
@@ -8722,18 +9369,18 @@ User message:
8722
9369
  context: {
8723
9370
  confirm: state.confirmQueue.length > 0,
8724
9371
  picker: state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || !!state.rewindOverlay,
8725
- monitor: state.agentsMonitorOpen || state.monitorOpen || state.worktreeMonitorOpen || !!state.autoPhase?.monitorOpen,
8726
- managed: managedLive,
8727
- mouse: mouseLive
9372
+ monitor: state.agentsMonitorOpen || state.monitorOpen || state.worktreeMonitorOpen || state.todosMonitorOpen || !!state.autoPhase?.monitorOpen,
9373
+ managed: managedLive
8728
9374
  }
8729
9375
  }
8730
9376
  ) : null,
8731
- state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, { managed: managedLive, mouse: mouseLive }) : null,
9377
+ state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, { managed: managedLive }) : null,
8732
9378
  state.agentsMonitorOpen ? /* @__PURE__ */ jsx(
8733
9379
  AgentsMonitor,
8734
9380
  {
8735
9381
  entries: entriesWithLeader,
8736
9382
  totalCost: state.fleetCost,
9383
+ leaderCost: tokenCounter?.estimateCost().total ?? 0,
8737
9384
  totalTokens: state.fleetTokens,
8738
9385
  nowTick
8739
9386
  }
@@ -8754,12 +9401,13 @@ User message:
8754
9401
  nowTick,
8755
9402
  onClose: () => dispatch({ type: "worktreeMonitorToggle" })
8756
9403
  }
8757
- ) : state.monitorOpen ? /* @__PURE__ */ jsx(
9404
+ ) : state.todosMonitorOpen && !managedLive ? /* @__PURE__ */ jsx(TodosMonitor, { todos: agent.ctx.todos }) : state.monitorOpen ? /* @__PURE__ */ jsx(
8758
9405
  FleetMonitor,
8759
9406
  {
8760
9407
  entries: state.fleet,
8761
9408
  totalCost: state.fleetCost,
8762
9409
  totalTokens: state.fleetTokens,
9410
+ maxConcurrent: state.fleetConcurrency,
8763
9411
  nowTick,
8764
9412
  collabSession: state.collabSession
8765
9413
  }
@@ -8780,9 +9428,10 @@ User message:
8780
9428
  nowTick
8781
9429
  }
8782
9430
  ) : null,
8783
- Object.keys(state.worktrees).length > 0 && !state.worktreeMonitorOpen && !state.monitorOpen ? /* @__PURE__ */ jsx(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null
9431
+ Object.keys(state.worktrees).length > 0 && !state.worktreeMonitorOpen && !state.monitorOpen ? /* @__PURE__ */ jsx(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null,
9432
+ state.queuePanelOpen ? /* @__PURE__ */ jsx(QueuePanel, { items: state.queue }) : null
8784
9433
  ] })
8785
- ] });
9434
+ ] }) });
8786
9435
  }
8787
9436
  function renderRunningTools(running) {
8788
9437
  if (running.size === 0) return "";
@@ -8816,48 +9465,6 @@ function fmtTok3(n) {
8816
9465
  return `${(n / 1e6).toFixed(1)}M`;
8817
9466
  }
8818
9467
 
8819
- // src/mouse.ts
8820
- var SGR_MOUSE_RE = /\x1b?\[<(\d+);(\d+);(\d+)([Mm])/g;
8821
- function stripSgrMouse(s2) {
8822
- return s2.replace(SGR_MOUSE_RE, "");
8823
- }
8824
- function parseSgrMouse(s2) {
8825
- const events = [];
8826
- for (const m of s2.matchAll(SGR_MOUSE_RE)) {
8827
- const cb = Number.parseInt(m[1] ?? "", 10);
8828
- const x = Number.parseInt(m[2] ?? "", 10);
8829
- const y = Number.parseInt(m[3] ?? "", 10);
8830
- const final = m[4];
8831
- if (!Number.isFinite(cb) || !Number.isFinite(x) || !Number.isFinite(y)) continue;
8832
- const isWheel = (cb & 64) !== 0;
8833
- const drag = (cb & 32) !== 0;
8834
- const low = cb & 3;
8835
- let button;
8836
- let type;
8837
- if (isWheel) {
8838
- type = "wheel";
8839
- button = low === 0 ? "wheelUp" : low === 1 ? "wheelDown" : "other";
8840
- } else if (final === "m") {
8841
- type = "release";
8842
- button = low === 0 ? "left" : low === 1 ? "middle" : low === 2 ? "right" : "other";
8843
- } else {
8844
- type = "press";
8845
- button = low === 0 ? "left" : low === 1 ? "middle" : low === 2 ? "right" : "other";
8846
- }
8847
- events.push({
8848
- type,
8849
- button,
8850
- x,
8851
- y,
8852
- shift: (cb & 4) !== 0,
8853
- alt: (cb & 8) !== 0,
8854
- ctrl: (cb & 16) !== 0,
8855
- drag
8856
- });
8857
- }
8858
- return events;
8859
- }
8860
-
8861
9468
  // src/terminal-title.ts
8862
9469
  var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
8863
9470
  var setTitle = (s2) => `\x1B]0;${s2}\x07`;
@@ -8927,8 +9534,6 @@ var BRACKETED_PASTE_OFF = "\x1B[?2004l";
8927
9534
  var ALT_SCREEN_ON = "\x1B[?1049h";
8928
9535
  var ALT_SCREEN_OFF = "\x1B[?1049l";
8929
9536
  var CURSOR_HOME = "\x1B[H";
8930
- var MOUSE_ON = "\x1B[?1000h\x1B[?1002h\x1B[?1006h";
8931
- var MOUSE_OFF = "\x1B[?1006l\x1B[?1002l\x1B[?1000l";
8932
9537
  async function runTui(opts) {
8933
9538
  const stdout = process.stdout;
8934
9539
  const stdin = process.stdin;
@@ -8938,108 +9543,15 @@ async function runTui(opts) {
8938
9543
  );
8939
9544
  return 2;
8940
9545
  }
8941
- const useMouse = opts.mouse === true;
8942
- const useAltScreen = opts.altScreen === true || useMouse;
9546
+ const useAltScreen = opts.altScreen === true;
8943
9547
  if (useAltScreen) {
8944
9548
  stdout.write(ALT_SCREEN_ON);
8945
9549
  stdout.write(CURSOR_HOME);
8946
9550
  }
8947
9551
  stdout.write(BRACKETED_PASTE_ON);
8948
- if (useMouse) {
8949
- stdout.write(MOUSE_ON);
8950
- }
8951
- const mouseListeners = /* @__PURE__ */ new Set();
8952
- let inkStdin = stdin;
8953
- let detachMouse = null;
8954
- if (useMouse) {
8955
- class KeyboardReadable extends Readable {
8956
- pendingChunks = [];
8957
- // eslint-disable-next-line no-useless-constructor
8958
- constructor() {
8959
- super({ encoding: "utf8", highWaterMark: 64 * 1024 });
8960
- }
8961
- _read(_size) {
8962
- this.flushPending();
8963
- }
8964
- flushPending() {
8965
- while (this.pendingChunks.length > 0) {
8966
- const chunk = this.pendingChunks[0];
8967
- const ok = this.push(chunk);
8968
- this.pendingChunks.shift();
8969
- if (!ok) {
8970
- break;
8971
- }
8972
- }
8973
- }
8974
- /** Called by the stdin data handler when keyboard bytes are available. */
8975
- doPush(chunk) {
8976
- if (chunk.length === 0) return;
8977
- const ok = this.push(chunk);
8978
- if (ok) {
8979
- if (this.pendingChunks.length > 0) {
8980
- this.flushPending();
8981
- }
8982
- } else {
8983
- if (this.pendingChunks.length >= 100) {
8984
- this.pendingChunks.shift();
8985
- }
8986
- this.pendingChunks.push(chunk);
8987
- }
8988
- }
8989
- /** Called on shutdown so the stream closes cleanly. */
8990
- doEnd() {
8991
- this.pendingChunks = [];
8992
- this.push(null);
8993
- }
8994
- }
8995
- const keyboardStream = new KeyboardReadable();
8996
- const p = keyboardStream;
8997
- p.isTTY = true;
8998
- p.setRawMode = (mode) => {
8999
- try {
9000
- stdin.setRawMode?.(mode);
9001
- } catch {
9002
- }
9003
- return p;
9004
- };
9005
- const realRef = stdin.ref?.bind(stdin);
9006
- const realUnref = stdin.unref?.bind(stdin);
9007
- p.ref = () => {
9008
- realRef?.();
9009
- return p;
9010
- };
9011
- p.unref = () => {
9012
- realUnref?.();
9013
- return p;
9014
- };
9015
- stdin.setEncoding("utf8");
9016
- const onData = (chunk) => {
9017
- const evs = parseSgrMouse(chunk);
9018
- for (const ev of evs) {
9019
- for (const fn of mouseListeners) {
9020
- try {
9021
- fn(ev);
9022
- } catch {
9023
- }
9024
- }
9025
- }
9026
- const rest = stripSgrMouse(chunk);
9027
- keyboardStream.doPush(rest);
9028
- };
9029
- stdin.on("data", onData);
9030
- detachMouse = () => {
9031
- stdin.off("data", onData);
9032
- keyboardStream.doEnd();
9033
- };
9034
- inkStdin = p;
9035
- }
9036
- const subscribeMouse = useMouse ? (fn) => {
9037
- mouseListeners.add(fn);
9038
- return () => {
9039
- mouseListeners.delete(fn);
9040
- };
9041
- } : void 0;
9042
- const stopTitle = startTerminalTitle({ stdout, events: opts.events, model: opts.model });
9552
+ const inkStdin = stdin;
9553
+ const stopTitle = opts.titleAnimation !== false ? startTerminalTitle({ stdout, events: opts.events, model: opts.model }) : (() => {
9554
+ });
9043
9555
  const swallowSignals = ["SIGTSTP", "SIGQUIT", "SIGTTIN", "SIGTTOU"];
9044
9556
  const swallow = () => {
9045
9557
  };
@@ -9057,15 +9569,8 @@ async function runTui(opts) {
9057
9569
  stopTitle();
9058
9570
  } catch {
9059
9571
  }
9060
- try {
9061
- detachMouse?.();
9062
- } catch {
9063
- }
9064
9572
  try {
9065
9573
  stdout.write(BRACKETED_PASTE_OFF);
9066
- if (useMouse) {
9067
- stdout.write(MOUSE_OFF);
9068
- }
9069
9574
  if (useAltScreen) {
9070
9575
  stdout.write(ALT_SCREEN_OFF);
9071
9576
  }
@@ -9138,6 +9643,8 @@ async function runTui(opts) {
9138
9643
  fleetRoster: opts.fleetRoster,
9139
9644
  onClearHistory: opts.onClearHistory ? (dispatch) => opts.onClearHistory(dispatch) : void 0,
9140
9645
  fleetStreamController: opts.fleetStreamController,
9646
+ enhanceController: opts.enhanceController,
9647
+ enhanceEnabled: opts.enhanceController?.enabled ?? true,
9141
9648
  statuslineHiddenItems: opts.statuslineHiddenItems,
9142
9649
  setStatuslineHiddenItems: opts.setStatuslineHiddenItems,
9143
9650
  agentsMonitorController: opts.agentsMonitorController,
@@ -9150,12 +9657,13 @@ async function runTui(opts) {
9150
9657
  getSettings: opts.getSettings,
9151
9658
  saveSettings: opts.saveSettings,
9152
9659
  predictNext: opts.predictNext,
9153
- mouse: useMouse,
9154
- subscribeMouse,
9155
9660
  // Managed viewport (in-app scroll + collapsibility) follows
9156
- // alt-screen: it owns the screen, so there's no native-scrollback
9157
- // leak. Decoupled from mouse so --alt-screen alone gets it.
9158
- managed: useAltScreen
9661
+ // alt-screen: it owns the screen, so there's no native-scrollback leak.
9662
+ managed: useAltScreen,
9663
+ chime: opts.chime,
9664
+ confirmExit: opts.confirmExit,
9665
+ modeLabel: opts.modeLabel,
9666
+ getModeLabel: opts.getModeLabel
9159
9667
  }),
9160
9668
  { exitOnCtrlC: false, stdin: inkStdin }
9161
9669
  );