@wrongstack/tui 0.109.1 → 0.141.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,7 +1,7 @@
1
1
  import { expectDefined, writeErr, resolveWstackPaths, loadGoal, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, buildChildEnv } from '@wrongstack/core';
2
2
  export { buildGoalPreamble } from '@wrongstack/core';
3
3
  import { Box, Text, useInput, useStdin, useStdout, render, useApp, Static } from 'ink';
4
- import React6, { useState, useEffect, memo, useCallback, useReducer, useRef, useMemo } from 'react';
4
+ import React5, { useState, useEffect, memo, useRef, useCallback, useReducer, useMemo } from 'react';
5
5
  import * as fs2 from 'fs/promises';
6
6
  import * as path2 from 'path';
7
7
  import { routeImagesForModel } from '@wrongstack/runtime/vision';
@@ -58,7 +58,8 @@ function StatusBar({
58
58
  eternalStage,
59
59
  goalSummary,
60
60
  indexState,
61
- modeLabel
61
+ modeLabel,
62
+ debugStreamStats
62
63
  }) {
63
64
  const { stdout } = useStdout();
64
65
  const [termWidth, setTermWidth] = useState(stdout?.columns ?? 90);
@@ -98,7 +99,8 @@ function StatusBar({
98
99
  const hasSecondLine = yolo || autonomy && autonomy !== "off" || startedAt != null || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0 || goalSummary !== null && goalSummary !== void 0 || !!modeLabel;
99
100
  const fleetHasActivity = fleet && (fleet.running > 0 || fleet.idle > 0 || fleet.pending > 0 || fleet.completed > 0) || subagentCount > 0;
100
101
  const hasBrainActivity = !!brain && brain.state !== "idle";
101
- const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity || hasBrainActivity;
102
+ const hasDebugStream = !!debugStreamStats;
103
+ const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity || hasBrainActivity || hasDebugStream;
102
104
  return /* @__PURE__ */ jsxs(
103
105
  Box,
104
106
  {
@@ -359,6 +361,30 @@ function StatusBar({
359
361
  hasBrainActivity && brain ? /* @__PURE__ */ jsxs(Fragment, { children: [
360
362
  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,
361
363
  /* @__PURE__ */ jsx(BrainChip, { brain })
364
+ ] }) : null,
365
+ hasDebugStream && debugStreamStats ? /* @__PURE__ */ jsxs(Fragment, { children: [
366
+ todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity || hasBrainActivity ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
367
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
368
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "\u{1F41B} stream" }),
369
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
370
+ " #",
371
+ debugStreamStats.chunkCount
372
+ ] }),
373
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
374
+ " \xB7 ",
375
+ debugStreamStats.lastChunkSize,
376
+ "B"
377
+ ] }),
378
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
379
+ " \xB7 +",
380
+ debugStreamStats.lastDeltaMs,
381
+ "ms"
382
+ ] }),
383
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
384
+ " \xB7 ",
385
+ fmtDebugBytes(debugStreamStats.totalBytes)
386
+ ] })
387
+ ] })
362
388
  ] }) : null
363
389
  ] }) : /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }),
364
390
  fleetAgents && fleetAgents.length > 0 ? /* @__PURE__ */ jsx(Box, { flexDirection: "row", gap: 2, children: fleetAgents.map((a, i) => (
@@ -492,7 +518,7 @@ var WAVE_COLORS = [
492
518
  function WaveText({ text, phase }) {
493
519
  return /* @__PURE__ */ jsx(Text, { bold: true, children: Array.from(text).map((ch, i) => (
494
520
  // biome-ignore lint/suspicious/noArrayIndexKey: glyph order is positional and re-rendered each tick
495
- /* @__PURE__ */ jsx(Text, { color: WAVE_COLORS[(i + phase) % WAVE_COLORS.length], children: ch }, i)
521
+ /* @__PURE__ */ jsx(Text, { color: WAVE_COLORS[(i + phase) % WAVE_COLORS.length] ?? "#ffffff", children: ch }, i)
496
522
  )) });
497
523
  }
498
524
  var FILLED = "\u2588";
@@ -536,6 +562,11 @@ function fmtElapsed(ms) {
536
562
  }
537
563
  return `${pad2(m)}:${pad2(s2)}`;
538
564
  }
565
+ function fmtDebugBytes(n) {
566
+ if (n < 1024) return `${n}B`;
567
+ if (n < 1048576) return `${(n / 1024).toFixed(1)}KB`;
568
+ return `${(n / 1048576).toFixed(1)}MB`;
569
+ }
539
570
  function pad2(n) {
540
571
  return n < 10 ? `0${n}` : String(n);
541
572
  }
@@ -684,7 +715,7 @@ function FleetMonitor({
684
715
  ] }),
685
716
  collabSession.overallVerdict ? /* @__PURE__ */ jsxs(Fragment, { children: [
686
717
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
687
- /* @__PURE__ */ jsx(Text, { bold: true, color: VERDICT_COLOR[collabSession.overallVerdict], children: collabSession.overallVerdict })
718
+ /* @__PURE__ */ jsx(Text, { bold: true, color: VERDICT_COLOR[collabSession.overallVerdict] ?? "white", children: collabSession.overallVerdict })
688
719
  ] }) : null
689
720
  ] }),
690
721
  collabSession.timeline.length > 0 ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 0, children: collabSession.timeline.slice(0, 6).map((ev, i) => (
@@ -826,10 +857,11 @@ function AgentRow({
826
857
  const s2 = STATUS2[entry.status];
827
858
  const elapsed = entry.status === "running" ? fmtElapsed(Math.max(0, now - entry.startedAt)) : entry.status;
828
859
  const modelLabel = fmtModelLabel(entry.provider, entry.model);
860
+ const ctxCostStr = entry.ctxCost !== void 0 && entry.ctxCost > 0 ? ` ctx ${entry.ctxCost.toFixed(4)}` : "";
829
861
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
830
862
  /* @__PURE__ */ jsx(Text, { color: selected ? "magenta" : "gray", children: selected ? "\u25B6" : " " }),
831
863
  /* @__PURE__ */ jsx(Text, { color: s2.color, bold: true, children: s2.icon }),
832
- /* @__PURE__ */ jsx(Text, { bold: selected, color: selected ? "magenta" : void 0, children: entry.name }),
864
+ /* @__PURE__ */ jsx(Text, { bold: selected, ...selected ? { color: "magenta" } : {}, children: entry.name }),
833
865
  modelLabel ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: modelLabel }) : null,
834
866
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
835
867
  "L",
@@ -839,6 +871,7 @@ function AgentRow({
839
871
  "t"
840
872
  ] }),
841
873
  entry.ctxPct !== void 0 ? /* @__PURE__ */ jsx(ContextBar, { pct: entry.ctxPct, tokens: entry.ctxTokens, maxTokens: entry.ctxMaxTokens }) : null,
874
+ ctxCostStr ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: ctxCostStr }) : null,
842
875
  entry.status === "running" && entry.currentTool ? /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
843
876
  "\u2192 ",
844
877
  entry.currentTool.name,
@@ -867,6 +900,7 @@ function AgentDetail({
867
900
  const lastTool = entry.recentTools[entry.recentTools.length - 1];
868
901
  const lastMessage = entry.recentMessages[entry.recentMessages.length - 1];
869
902
  const streamTail = entry.streamingText ? snippet(entry.streamingText.slice(-160)) : "";
903
+ const modelLabel = fmtModelLabel(entry.provider, entry.model);
870
904
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingLeft: 4, borderStyle: "single", borderColor: "magenta", borderLeft: true, children: [
871
905
  spark || lastTool ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
872
906
  /* @__PURE__ */ jsx(Text, { color: "green", children: spark || "" }),
@@ -877,6 +911,26 @@ function AgentDetail({
877
911
  lastTool.ok === false ? " \u2717" : ""
878
912
  ] }) : null
879
913
  ] }) : null,
914
+ !modelLabel && (entry.provider || entry.model) ? /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
915
+ "provider: ",
916
+ entry.provider || "(leader)",
917
+ " \xB7 model: ",
918
+ entry.model || "\u2014"
919
+ ] }) }) : null,
920
+ entry.cost > 0 || entry.ctxCost && entry.ctxCost > 0 ? /* @__PURE__ */ jsxs(Box, { children: [
921
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "cost: " }),
922
+ entry.cost > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
923
+ "$",
924
+ entry.cost.toFixed(4),
925
+ " total"
926
+ ] }) : null,
927
+ entry.cost > 0 && entry.ctxCost && entry.ctxCost > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }) : null,
928
+ entry.ctxCost && entry.ctxCost > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
929
+ "$",
930
+ entry.ctxCost.toFixed(4),
931
+ " ctx"
932
+ ] }) : null
933
+ ] }) : null,
880
934
  entry.status === "running" && streamTail ? /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
881
935
  ">",
882
936
  " ",
@@ -949,6 +1003,20 @@ function AgentsMonitor({
949
1003
  ] }) : null,
950
1004
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 \u2191\u2193 nav \xB7 Ctrl+G / F3 close" })
951
1005
  ] }),
1006
+ live.length > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1007
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "models" }),
1008
+ (() => {
1009
+ const seen = /* @__PURE__ */ new Map();
1010
+ for (const e of live) {
1011
+ if (e.model) seen.set(e.name ?? e.id, `${e.provider ?? "?"}/${e.model}`);
1012
+ }
1013
+ return [...seen.entries()].slice(0, 4).map(([name, mod]) => /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1014
+ name,
1015
+ ":",
1016
+ mod
1017
+ ] }, name));
1018
+ })()
1019
+ ] }) : null,
952
1020
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
953
1021
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "shown" }),
954
1022
  /* @__PURE__ */ jsx(Text, { color: "magenta", children: live.length }),
@@ -1219,7 +1287,7 @@ function ConfirmPrompt({
1219
1287
  suggestedPattern,
1220
1288
  onDecision
1221
1289
  }) {
1222
- React6.useEffect(() => {
1290
+ React5.useEffect(() => {
1223
1291
  writeOut("\x07");
1224
1292
  }, []);
1225
1293
  useInput((input2, _key) => {
@@ -1248,7 +1316,7 @@ function ConfirmPrompt({
1248
1316
  inputSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: inputSummary }) : null,
1249
1317
  showDiff && diff ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginY: 1, children: renderDiff(diff) }) : null,
1250
1318
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
1251
- /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { children: buttonLabels(suggestedPattern).map((l) => /* @__PURE__ */ jsxs(React6.Fragment, { children: [
1319
+ /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { children: buttonLabels(suggestedPattern).map((l) => /* @__PURE__ */ jsxs(React5.Fragment, { children: [
1252
1320
  /* @__PURE__ */ jsx(Text, { bold: true, color: BUTTON_COLOR[l.decision], children: l.bracket }),
1253
1321
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: l.rest })
1254
1322
  ] }, l.decision)) }) })
@@ -1262,10 +1330,10 @@ function EnhancePanel({
1262
1330
  onDecision
1263
1331
  }) {
1264
1332
  const totalSecs = Math.max(1, Math.ceil(delayMs / 1e3));
1265
- const [remaining, setRemaining] = React6.useState(totalSecs);
1266
- const decideRef = React6.useRef(onDecision);
1333
+ const [remaining, setRemaining] = React5.useState(totalSecs);
1334
+ const decideRef = React5.useRef(onDecision);
1267
1335
  decideRef.current = onDecision;
1268
- React6.useEffect(() => {
1336
+ React5.useEffect(() => {
1269
1337
  const id = setInterval(() => {
1270
1338
  setRemaining((r) => {
1271
1339
  if (r <= 1) {
@@ -2430,7 +2498,10 @@ function MarkdownView({
2430
2498
  rows.push(
2431
2499
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
2432
2500
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
2433
- /[\u2500-\u257F]/.test(qContent) ? /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: [...qContent].slice(0, (contentWidth ?? termWidth) - 2).map((ch, ci) => /* @__PURE__ */ jsx(Text, { dimColor: true, children: ch }, ci)) }) : /* @__PURE__ */ jsx(InlineLine, { tokens: parseInline(qContent), dim: true })
2501
+ /[\u2500-\u257F]/.test(qContent) ? /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: [...qContent].slice(0, (contentWidth ?? termWidth) - 2).map((ch, ci) => (
2502
+ /* biome-ignore lint/suspicious/noArrayIndexKey: characters are not reorderable */
2503
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ch }, ci)
2504
+ )) }) : /* @__PURE__ */ jsx(InlineLine, { tokens: parseInline(qContent), dim: true })
2434
2505
  ] }, `q${key++}`)
2435
2506
  );
2436
2507
  continue;
@@ -2459,7 +2530,10 @@ function MarkdownView({
2459
2530
  const maxW = contentWidth ?? termWidth;
2460
2531
  const chars = [...line].slice(0, maxW);
2461
2532
  rows.push(
2462
- /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: chars.map((ch, ci) => /* @__PURE__ */ jsx(Text, { children: ch }, ci)) }, `bx${key++}`)
2533
+ /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: chars.map((ch, ci) => (
2534
+ /* biome-ignore lint/suspicious/noArrayIndexKey: characters are not reorderable */
2535
+ /* @__PURE__ */ jsx(Text, { children: ch }, ci)
2536
+ )) }, `bx${key++}`)
2463
2537
  );
2464
2538
  continue;
2465
2539
  }
@@ -3035,7 +3109,7 @@ function streamBoxRows(text, maxLines, contentWidth) {
3035
3109
  }
3036
3110
  return rows;
3037
3111
  }
3038
- React6.memo(function ToolStreamBox2({
3112
+ React5.memo(function ToolStreamBox2({
3039
3113
  name,
3040
3114
  text,
3041
3115
  startedAt,
@@ -3379,7 +3453,7 @@ function brainRiskColor(risk) {
3379
3453
  return "red";
3380
3454
  }
3381
3455
  }
3382
- var Entry = React6.memo(function Entry2({
3456
+ var Entry = React5.memo(function Entry2({
3383
3457
  entry,
3384
3458
  termWidth
3385
3459
  }) {
@@ -3767,6 +3841,7 @@ function isHomeEnd(data) {
3767
3841
  return null;
3768
3842
  }
3769
3843
  function isBackspaceOrDelete(data) {
3844
+ if (data === "\x1B\x7F" || data === "\x1B\b") return "metaBackspace";
3770
3845
  if (data === "\x7F" || data === "\b") return "backspace";
3771
3846
  if (data === "\x1B[3~") return "delete";
3772
3847
  return null;
@@ -3807,15 +3882,49 @@ var Input = memo(function Input2({
3807
3882
  placeholderHeight,
3808
3883
  onKey
3809
3884
  }) {
3885
+ const suppressInkEscRef = useRef(false);
3886
+ const suppressInkBackspaceRef = useRef(false);
3887
+ const suppressInkDeleteRef = useRef(false);
3810
3888
  useInput((input, key) => {
3811
3889
  if (disabled) return;
3890
+ if (key.escape && suppressInkEscRef.current) {
3891
+ suppressInkEscRef.current = false;
3892
+ return;
3893
+ }
3894
+ if (key.backspace && suppressInkBackspaceRef.current) {
3895
+ suppressInkBackspaceRef.current = false;
3896
+ return;
3897
+ }
3898
+ if (key.delete && suppressInkDeleteRef.current) {
3899
+ suppressInkDeleteRef.current = false;
3900
+ return;
3901
+ }
3812
3902
  onKey(input, key);
3813
3903
  });
3814
3904
  const { stdin } = useStdin();
3815
3905
  useEffect(() => {
3816
3906
  if (!stdin || disabled) return;
3907
+ let escTimer = null;
3817
3908
  const handleData = (data) => {
3818
3909
  const s2 = data.toString();
3910
+ if (s2 === "\x1B") {
3911
+ escTimer = setTimeout(() => {
3912
+ escTimer = null;
3913
+ suppressInkEscRef.current = true;
3914
+ onKey("", { ...EMPTY_KEY, escape: true });
3915
+ }, 10);
3916
+ return;
3917
+ }
3918
+ if (escTimer !== null) {
3919
+ clearTimeout(escTimer);
3920
+ escTimer = null;
3921
+ if (s2 === "\x7F" || s2 === "\b") {
3922
+ suppressInkBackspaceRef.current = true;
3923
+ onKey("", { ...EMPTY_KEY, backspace: true, ctrl: true });
3924
+ return;
3925
+ }
3926
+ return;
3927
+ }
3819
3928
  const homeEnd = isHomeEnd(s2);
3820
3929
  if (homeEnd === "home") {
3821
3930
  onKey("", { ...EMPTY_KEY, home: true });
@@ -3827,13 +3936,20 @@ var Input = memo(function Input2({
3827
3936
  }
3828
3937
  const bsdel = isBackspaceOrDelete(s2);
3829
3938
  if (bsdel === "backspace") {
3939
+ suppressInkBackspaceRef.current = true;
3830
3940
  onKey("", { ...EMPTY_KEY, backspace: true });
3831
3941
  return;
3832
3942
  }
3833
3943
  if (bsdel === "delete") {
3944
+ suppressInkDeleteRef.current = true;
3834
3945
  onKey("", { ...EMPTY_KEY, delete: true });
3835
3946
  return;
3836
3947
  }
3948
+ if (bsdel === "metaBackspace") {
3949
+ suppressInkBackspaceRef.current = true;
3950
+ onKey("", { ...EMPTY_KEY, backspace: true, ctrl: true });
3951
+ return;
3952
+ }
3837
3953
  const wheelDelta = parseMouseWheel(s2);
3838
3954
  if (wheelDelta !== null) {
3839
3955
  onKey("", { ...EMPTY_KEY, wheelDeltaY: wheelDelta });
@@ -3844,6 +3960,7 @@ var Input = memo(function Input2({
3844
3960
  };
3845
3961
  stdin.on("data", handleData);
3846
3962
  return () => {
3963
+ if (escTimer !== null) clearTimeout(escTimer);
3847
3964
  stdin.off("data", handleData);
3848
3965
  };
3849
3966
  }, [stdin, disabled, onKey]);
@@ -3876,71 +3993,6 @@ var Input = memo(function Input2({
3876
3993
  hint ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint }) : null
3877
3994
  ] });
3878
3995
  });
3879
- function fmtElapsed2(ms) {
3880
- if (ms < 1e3) return `${ms}ms`;
3881
- if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
3882
- const m = Math.floor(ms / 6e4);
3883
- const s2 = Math.floor(ms % 6e4 / 1e3);
3884
- return `${m}m${s2.toString().padStart(2, "0")}s`;
3885
- }
3886
- function fmtBytes2(n) {
3887
- if (n < 1024) return `${n}B`;
3888
- return `${(n / 1024).toFixed(1)}KB`;
3889
- }
3890
- function fmtRecentTool(tool) {
3891
- const status = tool.ok === false ? "fail" : "ok";
3892
- const name = tool.name.length > 18 ? `${tool.name.slice(0, 17)}...` : tool.name;
3893
- const parts = [status, name];
3894
- if (typeof tool.durationMs === "number") parts.push(fmtElapsed2(tool.durationMs));
3895
- if (typeof tool.outputBytes === "number" && tool.outputBytes > 0)
3896
- parts.push(fmtBytes2(tool.outputBytes));
3897
- if (typeof tool.outputLines === "number" && tool.outputLines > 0)
3898
- parts.push(`${tool.outputLines}L`);
3899
- return parts.join(" ");
3900
- }
3901
- function truncToWidth(s2, width) {
3902
- if (width <= 0) return "";
3903
- if (s2.length <= width) return s2;
3904
- if (width === 1) return "\u2026";
3905
- return `${s2.slice(0, width - 1)}\u2026`;
3906
- }
3907
- function formatRow(e, now) {
3908
- const toolElapsed = e.currentTool ? now - e.currentTool.startedAt : 0;
3909
- const taskElapsed = now - e.startedAt;
3910
- const toolSeg = e.currentTool ? `\u2192 ${e.currentTool.name} (${fmtElapsed2(toolElapsed)})` : "\xB7";
3911
- const recentTools = (e.recentTools ?? []).slice(-2).map(fmtRecentTool).join(" | ");
3912
- const head = `${e.name.slice(0, 14).padEnd(14)} \xB7 ${toolSeg} \xB7 ${e.iterations}it ${e.toolCalls}tc \xB7 ${fmtElapsed2(taskElapsed)}`;
3913
- return recentTools ? `${head} | last: ${recentTools}` : head;
3914
- }
3915
- function activityStripRows(entries, now, maxRows, width) {
3916
- const bodyWidth = Math.max(0, width - 2);
3917
- const running = Object.values(entries).filter((e) => e.status === "running").sort((a, b) => a.startedAt - b.startedAt);
3918
- const rows = [];
3919
- const overflow = running.length > maxRows;
3920
- const shown = overflow ? running.slice(0, maxRows - 1) : running.slice(0, maxRows);
3921
- for (const e of shown) rows.push(truncToWidth(formatRow(e, now), bodyWidth));
3922
- if (overflow)
3923
- rows.push(truncToWidth(`\u2026+${running.length - (maxRows - 1)} more running`, bodyWidth));
3924
- while (rows.length < maxRows) rows.push("");
3925
- return rows;
3926
- }
3927
- var LiveActivityStrip = React6.memo(function LiveActivityStrip2({
3928
- entries,
3929
- nowTick,
3930
- maxRows = 4
3931
- }) {
3932
- const { stdout } = useStdout();
3933
- const width = Math.max(10, (stdout?.columns ?? 80) - 2);
3934
- const hasEntries = Object.keys(entries).length > 0;
3935
- const rows = hasEntries ? activityStripRows(entries, Date.now(), maxRows, width) : new Array(maxRows).fill("");
3936
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingX: 1, children: rows.map((text, slot) => (
3937
- // biome-ignore lint/suspicious/noArrayIndexKey: fixed-height slots, index IS the identity
3938
- /* @__PURE__ */ jsx(Box, { height: 1, children: text === "" ? /* @__PURE__ */ jsx(Text, { children: " " }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3939
- /* @__PURE__ */ jsx(Text, { color: theme.accent, children: "\u25CF " }),
3940
- /* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "truncate", children: text })
3941
- ] }) }, `strip-${slot}`)
3942
- )) });
3943
- });
3944
3996
  var MAX_VISIBLE = 10;
3945
3997
  function getVisibleWindow(selected, total) {
3946
3998
  const half = Math.floor(MAX_VISIBLE / 2);
@@ -4031,7 +4083,7 @@ function ModelPicker({
4031
4083
  hint ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) : null
4032
4084
  ] });
4033
4085
  }
4034
- var fmtElapsed3 = (ms) => {
4086
+ var fmtElapsed2 = (ms) => {
4035
4087
  const s2 = Math.floor(ms / 1e3);
4036
4088
  const m = Math.floor(s2 / 60);
4037
4089
  const h = Math.floor(m / 60);
@@ -4073,7 +4125,7 @@ function PhaseMonitor({
4073
4125
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
4074
4126
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4075
4127
  "\u23F1 ",
4076
- fmtElapsed3(elapsedMs)
4128
+ fmtElapsed2(elapsedMs)
4077
4129
  ] }),
4078
4130
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
4079
4131
  /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
@@ -4098,7 +4150,7 @@ function PhaseMonitor({
4098
4150
  const s2 = fmtPhase(phase.status);
4099
4151
  const phaseKey = Object.keys(phases).find((k) => phases[k] === phase) ?? String(i);
4100
4152
  const isRunning = runningPhaseIds.includes(phaseKey);
4101
- const elapsed = phase.startedAt ? fmtElapsed3(nowTick - phase.startedAt) : "\u2014";
4153
+ const elapsed = phase.startedAt ? fmtElapsed2(nowTick - phase.startedAt) : "\u2014";
4102
4154
  const progress = phase.totalTasks > 0 ? `${phase.completedTasks}/${phase.totalTasks}` : "\u2014";
4103
4155
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
4104
4156
  /* @__PURE__ */ jsx(Text, { color: s2.color, bold: true, children: s2.icon }),
@@ -4124,7 +4176,7 @@ function PhaseMonitor({
4124
4176
  /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate phases \xB7 Esc close" }) })
4125
4177
  ] });
4126
4178
  }
4127
- var fmtElapsed4 = (ms) => {
4179
+ var fmtElapsed3 = (ms) => {
4128
4180
  const s2 = Math.floor(ms / 1e3);
4129
4181
  const m = Math.floor(s2 / 60);
4130
4182
  const h = Math.floor(m / 60);
@@ -4187,7 +4239,7 @@ function PhasePanel({ phases, nowTick }) {
4187
4239
  const phaseKey = Object.keys(phases).find((k) => phases[k] === phase) ?? String(i);
4188
4240
  const st2 = s(phase.status);
4189
4241
  const progress = phase.totalTasks > 0 ? `${phase.completedTasks}/${phase.totalTasks}` : "";
4190
- const elapsed = phase.startedAt ? fmtElapsed4(nowTick - phase.startedAt) : "";
4242
+ const elapsed = phase.startedAt ? fmtElapsed3(nowTick - phase.startedAt) : "";
4191
4243
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
4192
4244
  /* @__PURE__ */ jsx(Text, { color: st2.color, children: st2.icon }),
4193
4245
  /* @__PURE__ */ jsx(Text, { children: phase.name.slice(0, 14).padEnd(14) }),
@@ -4316,7 +4368,7 @@ function ProcessListMonitor() {
4316
4368
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: String(p.pid).padEnd(7) }),
4317
4369
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: p.name.padEnd(6) }),
4318
4370
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${age}s`.padEnd(7) }),
4319
- /* @__PURE__ */ jsx(Text, { color: isSelected ? "red" : void 0, bold: isSelected, children: cmd }),
4371
+ /* @__PURE__ */ jsx(Text, { ...isSelected ? { color: "red" } : {}, bold: isSelected, children: cmd }),
4320
4372
  p.killed ? /* @__PURE__ */ jsx(Text, { color: "red", children: "[killed]" }) : null
4321
4373
  ] }, p.pid);
4322
4374
  }),
@@ -4383,12 +4435,15 @@ function GoalPanel({ goal }) {
4383
4435
  ] }) }),
4384
4436
  goal.deliverables.map((d, i) => {
4385
4437
  const done = /^\[[x✓]\]|✅|\(done\)/i.test(d);
4386
- return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: done ? "green" : void 0, dimColor: !done, children: [
4387
- " ",
4388
- done ? "\u2713" : "\u25CB",
4389
- " ",
4390
- d
4391
- ] }) }, i);
4438
+ return (
4439
+ // biome-ignore lint/suspicious/noArrayIndexKey: deliverables are stable text strings
4440
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { ...done ? { color: "green" } : {}, dimColor: !done, children: [
4441
+ " ",
4442
+ done ? "\u2713" : "\u25CB",
4443
+ " ",
4444
+ d
4445
+ ] }) }, i)
4446
+ );
4392
4447
  })
4393
4448
  ] }),
4394
4449
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
@@ -4439,6 +4494,7 @@ var LOG_LEVELS = ["error", "warn", "info", "debug", "trace"];
4439
4494
  var AUDIT_LEVELS = ["minimal", "standard", "full"];
4440
4495
  var COMPACTOR_STRATEGIES = ["hybrid", "intelligent", "selective"];
4441
4496
  var MAX_ITERATIONS_PRESETS = [100, 200, 500, 1e3, 0];
4497
+ var ENHANCE_DELAY_PRESETS = [3e4, 45e3, 6e4, 9e4, 12e4];
4442
4498
  function formatSettingsDelay(ms) {
4443
4499
  if (ms === 0) return "disabled";
4444
4500
  if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
@@ -4448,12 +4504,16 @@ function formatMaxIterations(n) {
4448
4504
  if (n === 0) return "unlimited";
4449
4505
  return String(n);
4450
4506
  }
4507
+ function formatEnhanceDelay(ms) {
4508
+ return `${Math.round(ms / 1e3)}s`;
4509
+ }
4451
4510
  var MODE_DESC = {
4452
4511
  off: "Agent stops after each turn (normal)",
4453
4512
  suggest: "Shows next-step suggestions after each turn",
4454
4513
  auto: "Self-driving \u2014 agent continues automatically"
4455
4514
  };
4456
- var SETTINGS_FIELD_COUNT = 19;
4515
+ var SETTINGS_FIELD_COUNT = 22;
4516
+ var CONFIG_SCOPES = ["global", "project"];
4457
4517
  function SettingsPicker({
4458
4518
  field,
4459
4519
  mode,
@@ -4475,6 +4535,9 @@ function SettingsPicker({
4475
4535
  auditLevel,
4476
4536
  indexOnStart,
4477
4537
  maxIterations,
4538
+ enhanceDelayMs,
4539
+ debugStream,
4540
+ configScope,
4478
4541
  hint
4479
4542
  }) {
4480
4543
  const boolVal = (v) => v ? "on" : "off";
@@ -4585,6 +4648,23 @@ function SettingsPicker({
4585
4648
  label: "Max iterations",
4586
4649
  value: formatMaxIterations(maxIterations),
4587
4650
  detail: "100\u20131000 or unlimited (0)"
4651
+ },
4652
+ {
4653
+ label: "Refine preview countdown",
4654
+ value: formatEnhanceDelay(enhanceDelayMs),
4655
+ detail: "Timeout for prompt refinement preview (30s\u2013120s)"
4656
+ },
4657
+ // ── Debug ──
4658
+ { section: "Debug" },
4659
+ {
4660
+ label: "Stream debug logging",
4661
+ value: boolVal(debugStream),
4662
+ detail: "Hex-dump raw SSE bytes to stderr"
4663
+ },
4664
+ {
4665
+ label: "Config scope",
4666
+ value: configScope,
4667
+ detail: "global (~/.wrongstack/) or project (.wrongstack/)"
4588
4668
  }
4589
4669
  ];
4590
4670
  const fieldRowIndex = [];
@@ -4619,7 +4699,7 @@ function SettingsPicker({
4619
4699
  };
4620
4700
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
4621
4701
  /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Settings \u2501\u2501" }),
4622
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 field \xB7 \u2190/\u2192 change (instant save) \xB7 Esc close" }),
4702
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 field \xB7 \u2190/\u2192 change (instant save) \xB7 Esc / F5 close" }),
4623
4703
  hasAbove ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2191 ${windowStart} field${windowStart === 1 ? "" : "s"} above` }) : null,
4624
4704
  rows.map((row, i) => {
4625
4705
  const fieldAtRow = fieldRowIndex.indexOf(i);
@@ -4836,7 +4916,7 @@ function TodosMonitor({ todos }) {
4836
4916
  )
4837
4917
  ] });
4838
4918
  }
4839
- var fmtElapsed5 = (ms) => {
4919
+ var fmtElapsed4 = (ms) => {
4840
4920
  const s2 = Math.floor(ms / 1e3);
4841
4921
  const m = Math.floor(s2 / 60);
4842
4922
  const h = Math.floor(m / 60);
@@ -4905,7 +4985,7 @@ function WorktreeMonitor({
4905
4985
  recent.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No worktrees. They appear when AutoPhase runs with isolation on." }) : recent.map((w) => {
4906
4986
  const s2 = fmt(w.status);
4907
4987
  const short = w.branch.replace(/^wstack\/ap\//, "");
4908
- const elapsed = w.allocatedAt ? fmtElapsed5(nowTick - w.allocatedAt) : "\u2014";
4988
+ const elapsed = w.allocatedAt ? fmtElapsed4(nowTick - w.allocatedAt) : "\u2014";
4909
4989
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
4910
4990
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
4911
4991
  /* @__PURE__ */ jsx(Text, { color: s2.color, bold: true, children: s2.icon }),
@@ -4955,7 +5035,7 @@ function WorktreeMonitor({
4955
5035
  ] })
4956
5036
  ] });
4957
5037
  }
4958
- var fmtElapsed6 = (ms) => {
5038
+ var fmtElapsed5 = (ms) => {
4959
5039
  const s2 = Math.floor(ms / 1e3);
4960
5040
  const m = Math.floor(s2 / 60);
4961
5041
  const h = Math.floor(m / 60);
@@ -5021,7 +5101,7 @@ function WorktreePanel({
5021
5101
  list.map((w) => {
5022
5102
  const s2 = st(w.status);
5023
5103
  const conflict = w.status === "needs-review";
5024
- const elapsed = w.allocatedAt ? fmtElapsed6(nowTick - w.allocatedAt) : "";
5104
+ const elapsed = w.allocatedAt ? fmtElapsed5(nowTick - w.allocatedAt) : "";
5025
5105
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
5026
5106
  /* @__PURE__ */ jsx(Text, { color: s2.color, children: s2.icon }),
5027
5107
  /* @__PURE__ */ jsx(Text, { children: w.branch.replace(/^wstack\/ap\//, "").slice(0, 18).padEnd(18) }),
@@ -6182,6 +6262,10 @@ function buildSteeringPreamble(snapshot, newDirection) {
6182
6262
  function reducer(state, action) {
6183
6263
  switch (action.type) {
6184
6264
  case "addEntry": {
6265
+ const e = action.entry;
6266
+ if ((e.kind === "user" || e.kind === "assistant" || e.kind === "info" || e.kind === "warn" || e.kind === "error" || e.kind === "turn-summary") && !e.text?.trim()) {
6267
+ return state;
6268
+ }
6185
6269
  const appended = [...state.entries, { ...action.entry, id: state.nextId }];
6186
6270
  return { ...state, entries: appended, nextId: state.nextId + 1 };
6187
6271
  }
@@ -6517,6 +6601,9 @@ function reducer(state, action) {
6517
6601
  auditLevel: action.auditLevel,
6518
6602
  indexOnStart: action.indexOnStart,
6519
6603
  maxIterations: action.maxIterations,
6604
+ enhanceDelayMs: action.enhanceDelayMs,
6605
+ debugStream: action.debugStream,
6606
+ configScope: action.configScope,
6520
6607
  hint: void 0
6521
6608
  }
6522
6609
  };
@@ -6582,12 +6669,26 @@ function reducer(state, action) {
6582
6669
  return { ...state, settingsPicker: { ...sp, auditLevel: expectDefined(AUDIT_LEVELS[next]), hint: void 0 } };
6583
6670
  }
6584
6671
  if (f === 17) return { ...state, settingsPicker: { ...sp, indexOnStart: !sp.indexOnStart, hint: void 0 } };
6585
- {
6672
+ if (f === 18) {
6586
6673
  const j = MAX_ITERATIONS_PRESETS.indexOf(sp.maxIterations);
6587
6674
  const base = j < 0 ? 0 : j;
6588
6675
  const next = (base + action.delta + MAX_ITERATIONS_PRESETS.length) % MAX_ITERATIONS_PRESETS.length;
6589
6676
  return { ...state, settingsPicker: { ...sp, maxIterations: expectDefined(MAX_ITERATIONS_PRESETS[next]), hint: void 0 } };
6590
6677
  }
6678
+ if (f === 19) {
6679
+ const ej = ENHANCE_DELAY_PRESETS.indexOf(sp.enhanceDelayMs);
6680
+ const ebase = ej < 0 ? 0 : ej;
6681
+ const enext = (ebase + action.delta + ENHANCE_DELAY_PRESETS.length) % ENHANCE_DELAY_PRESETS.length;
6682
+ return { ...state, settingsPicker: { ...sp, enhanceDelayMs: expectDefined(ENHANCE_DELAY_PRESETS[enext]), hint: void 0 } };
6683
+ }
6684
+ if (f === 20) return { ...state, settingsPicker: { ...sp, debugStream: !sp.debugStream, hint: void 0 } };
6685
+ if (f === 21) {
6686
+ const i = CONFIG_SCOPES.indexOf(sp.configScope);
6687
+ const base = i < 0 ? 0 : i;
6688
+ const next = (base + action.delta + CONFIG_SCOPES.length) % CONFIG_SCOPES.length;
6689
+ return { ...state, settingsPicker: { ...sp, configScope: expectDefined(CONFIG_SCOPES[next]), hint: void 0 } };
6690
+ }
6691
+ return state;
6591
6692
  }
6592
6693
  case "settingsHint":
6593
6694
  return { ...state, settingsPicker: { ...state.settingsPicker, hint: action.text } };
@@ -6832,6 +6933,7 @@ function reducer(state, action) {
6832
6933
  ctxPct: action.load,
6833
6934
  ctxTokens: action.tokens,
6834
6935
  ctxMaxTokens: action.maxContext,
6936
+ ctxCost: action.ctxCost,
6835
6937
  lastEventAt: Date.now()
6836
6938
  }
6837
6939
  }
@@ -7242,6 +7344,22 @@ function reducer(state, action) {
7242
7344
  }
7243
7345
  };
7244
7346
  }
7347
+ case "debugStreamStats": {
7348
+ return {
7349
+ ...state,
7350
+ debugStreamStats: {
7351
+ chunkCount: action.chunkCount,
7352
+ lastChunkSize: action.lastChunkSize,
7353
+ lastDeltaMs: action.lastDeltaMs,
7354
+ totalBytes: action.totalBytes,
7355
+ lastChunkAt: action.lastChunkAt
7356
+ }
7357
+ };
7358
+ }
7359
+ case "debugStreamStatsClear": {
7360
+ if (state.debugStreamStats === null) return state;
7361
+ return { ...state, debugStreamStats: null };
7362
+ }
7245
7363
  }
7246
7364
  }
7247
7365
  var INPUT_PROMPT = "\u203A ";
@@ -7316,7 +7434,9 @@ function App({
7316
7434
  initialAsk,
7317
7435
  sessionsDir,
7318
7436
  modeLabel,
7319
- getModeLabel
7437
+ getModeLabel,
7438
+ registerDebugStreamCallback,
7439
+ restoreDebugStreamCallback
7320
7440
  }) {
7321
7441
  const { exit } = useApp();
7322
7442
  const { stdout } = useStdout();
@@ -7343,7 +7463,10 @@ function App({
7343
7463
  if (!projectRoot) return;
7344
7464
  const goalPath = resolveWstackPaths({ projectRoot }).projectGoal;
7345
7465
  loadGoal(goalPath).then((goal) => {
7346
- if (!goal) return;
7466
+ if (!goal) {
7467
+ dispatch({ type: "goalSummary", summary: null });
7468
+ return;
7469
+ }
7347
7470
  const lastEntry = goal.journal?.[goal.journal.length - 1];
7348
7471
  dispatch({
7349
7472
  type: "goalSummary",
@@ -7423,7 +7546,7 @@ function App({
7423
7546
  searchQuery: ""
7424
7547
  },
7425
7548
  autonomyPicker: { open: false, options: [], selected: 0 },
7426
- 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 },
7549
+ 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, enhanceDelayMs: 6e4, debugStream: false, configScope: "global" },
7427
7550
  confirmQueue: [],
7428
7551
  enhance: null,
7429
7552
  enhanceEnabled,
@@ -7462,7 +7585,8 @@ function App({
7462
7585
  scrollOffset: 0,
7463
7586
  totalLines: 0,
7464
7587
  viewportRows: 0,
7465
- pendingNewLines: 0
7588
+ pendingNewLines: 0,
7589
+ debugStreamStats: null
7466
7590
  });
7467
7591
  const builderRef = useRef(null);
7468
7592
  if (builderRef.current === null) {
@@ -7475,7 +7599,7 @@ function App({
7475
7599
  const inputGateRef = useRef(false);
7476
7600
  const lastEnterAtRef = useRef(0);
7477
7601
  const tokenPreviewsRef = useRef(/* @__PURE__ */ new Map());
7478
- const projectName = React6.useMemo(() => {
7602
+ const projectName = React5.useMemo(() => {
7479
7603
  const base = path2.basename(projectRoot);
7480
7604
  return base && base !== path2.sep ? base : void 0;
7481
7605
  }, [projectRoot]);
@@ -7491,7 +7615,7 @@ function App({
7491
7615
  const draftRef = useRef({ buffer: state.buffer, cursor: state.cursor });
7492
7616
  draftRef.current = { buffer: state.buffer, cursor: state.cursor };
7493
7617
  const handleKeyRef = useRef(null);
7494
- const handleRewindTo = React6.useCallback(
7618
+ const handleRewindTo = React5.useCallback(
7495
7619
  async (checkpointIndex) => {
7496
7620
  const sessionId = agent.ctx.session.id;
7497
7621
  if (!sessionId) return;
@@ -7510,7 +7634,7 @@ function App({
7510
7634
  dispatch({ type: "clearInput" });
7511
7635
  };
7512
7636
  const startedAtRef = useRef(Date.now());
7513
- const [nowTick, setNowTick] = React6.useState(Date.now());
7637
+ const [nowTick, setNowTick] = React5.useState(Date.now());
7514
7638
  useEffect(() => {
7515
7639
  const t = setInterval(() => setNowTick(Date.now()), 1e4);
7516
7640
  return () => clearInterval(t);
@@ -7536,7 +7660,43 @@ function App({
7536
7660
  const t = setInterval(poll, 2e3);
7537
7661
  return () => clearInterval(t);
7538
7662
  }, [agent.ctx.todos]);
7539
- const [gitInfo, setGitInfo] = React6.useState(null);
7663
+ const staleGuardRef = useRef(JSON.stringify({ a: "", y: false, m: "", model: "", provider: "" }));
7664
+ useEffect(() => {
7665
+ const poll = () => {
7666
+ const a = getAutonomy?.() ?? "off";
7667
+ const y = getYolo?.() ?? false;
7668
+ const m = getModeLabel?.() ?? "";
7669
+ const curModel = agent.ctx.model;
7670
+ const curProvider = agent.ctx.provider?.id ?? "";
7671
+ const snap = JSON.stringify({ a, y, m, model: curModel, provider: curProvider });
7672
+ if (snap !== staleGuardRef.current) {
7673
+ staleGuardRef.current = snap;
7674
+ if (a !== autonomyLive) setAutonomyLive(a);
7675
+ if (y !== yoloLive) setYoloLive(y);
7676
+ if (m !== liveModeLabel) setLiveModeLabel(m);
7677
+ if (curModel !== liveModel) setLiveModel(curModel);
7678
+ if (curProvider !== liveProvider) setLiveProvider(curProvider);
7679
+ if (a === "eternal" && getEternalEngine) void runEternalLoopRef.current();
7680
+ if (a === "eternal-parallel" && getParallelEngine) void runParallelLoopRef.current();
7681
+ }
7682
+ };
7683
+ const t = setInterval(poll, 2e3);
7684
+ return () => clearInterval(t);
7685
+ }, [
7686
+ getAutonomy,
7687
+ getYolo,
7688
+ getModeLabel,
7689
+ getEternalEngine,
7690
+ getParallelEngine,
7691
+ autonomyLive,
7692
+ yoloLive,
7693
+ liveModeLabel,
7694
+ liveModel,
7695
+ liveProvider,
7696
+ agent.ctx.model,
7697
+ agent.ctx.provider
7698
+ ]);
7699
+ const [gitInfo, setGitInfo] = React5.useState(null);
7540
7700
  useEffect(() => {
7541
7701
  let cancelled = false;
7542
7702
  const refresh = () => {
@@ -7647,7 +7807,7 @@ function App({
7647
7807
  } catch {
7648
7808
  }
7649
7809
  }, []);
7650
- React6.useLayoutEffect(() => {
7810
+ React5.useLayoutEffect(() => {
7651
7811
  const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || state.enhanceBusy || state.enhance != null || state.escConfirm != null || state.confirmQueue.length > 0;
7652
7812
  const overlayClosed = prevAnyOverlayOpen.current && !anyOpenNow;
7653
7813
  const newEntryCommitted = state.entries.length > prevEntriesCount.current;
@@ -7728,7 +7888,10 @@ function App({
7728
7888
  logLevel: sp.logLevel,
7729
7889
  auditLevel: sp.auditLevel,
7730
7890
  indexOnStart: sp.indexOnStart,
7731
- maxIterations: sp.maxIterations
7891
+ maxIterations: sp.maxIterations,
7892
+ enhanceDelayMs: sp.enhanceDelayMs,
7893
+ debugStream: sp.debugStream,
7894
+ configScope: sp.configScope
7732
7895
  });
7733
7896
  }
7734
7897
  if (prev.help) dispatch({ type: "toggleHelp" });
@@ -7746,7 +7909,7 @@ function App({
7746
7909
  process.stdout.off("resize", handleResize);
7747
7910
  };
7748
7911
  }, [eraseLiveRegion]);
7749
- React6.useLayoutEffect(() => {
7912
+ React5.useLayoutEffect(() => {
7750
7913
  if (state.enhanceBusy || state.enhance != null) eraseLiveRegion();
7751
7914
  });
7752
7915
  useEffect(() => {
@@ -7950,7 +8113,7 @@ function App({
7950
8113
  const subagents = Object.values(s2.fleet).filter((e) => e.status === "running").map((e) => ({ label: e.name, status: e.status, tool: e.currentTool?.name }));
7951
8114
  const subagentsTerminated = subagents.length;
7952
8115
  const partialAssistantText = streamingTextRef.current.slice(-1500);
7953
- activeCtrlRef.current?.abort();
8116
+ activeCtrlRef.current?.abort("user interrupt (/steer)");
7954
8117
  dispatch({
7955
8118
  type: "steerStart",
7956
8119
  snapshot: { runningTools, subagents, subagentsTerminated, partialAssistantText }
@@ -8033,12 +8196,12 @@ function App({
8033
8196
  slashRegistry.unregister("agents");
8034
8197
  };
8035
8198
  }, [slashRegistry]);
8036
- const openModelPicker = React6.useCallback(async () => {
8199
+ const openModelPicker = React5.useCallback(async () => {
8037
8200
  if (!getPickableProviders) return;
8038
8201
  const providers = await getPickableProviders();
8039
8202
  dispatch({ type: "modelPickerOpen", providers });
8040
8203
  }, [getPickableProviders]);
8041
- const openSettings = React6.useCallback(() => {
8204
+ const openSettings = React5.useCallback(() => {
8042
8205
  if (!getSettings) return;
8043
8206
  const s2 = getSettings();
8044
8207
  dispatch({
@@ -8061,7 +8224,10 @@ function App({
8061
8224
  logLevel: s2.logLevel ?? "info",
8062
8225
  auditLevel: s2.auditLevel ?? "standard",
8063
8226
  indexOnStart: s2.indexOnStart ?? true,
8064
- maxIterations: s2.maxIterations ?? 500
8227
+ maxIterations: s2.maxIterations ?? 500,
8228
+ enhanceDelayMs: s2.enhanceDelayMs ?? 6e4,
8229
+ debugStream: s2.debugStream ?? false,
8230
+ configScope: s2.configScope ?? "global"
8065
8231
  });
8066
8232
  }, [getSettings]);
8067
8233
  const settingsAutoSaveGateRef = useRef(true);
@@ -8097,7 +8263,10 @@ function App({
8097
8263
  logLevel: sp.logLevel,
8098
8264
  auditLevel: sp.auditLevel,
8099
8265
  indexOnStart: sp.indexOnStart,
8100
- maxIterations: sp.maxIterations
8266
+ maxIterations: sp.maxIterations,
8267
+ enhanceDelayMs: sp.enhanceDelayMs,
8268
+ debugStream: sp.debugStream,
8269
+ configScope: sp.configScope
8101
8270
  })).then((err) => {
8102
8271
  if (err) dispatch({ type: "settingsHint", text: err });
8103
8272
  });
@@ -8122,6 +8291,7 @@ function App({
8122
8291
  state.settingsPicker.auditLevel,
8123
8292
  state.settingsPicker.indexOnStart,
8124
8293
  state.settingsPicker.maxIterations,
8294
+ state.settingsPicker.enhanceDelayMs,
8125
8295
  saveSettings
8126
8296
  ]);
8127
8297
  useEffect(() => {
@@ -8331,6 +8501,33 @@ function App({
8331
8501
  if (flushTimerRef.current) clearTimeout(flushTimerRef.current);
8332
8502
  };
8333
8503
  }, [events, agent.ctx.todos]);
8504
+ useEffect(() => {
8505
+ if (!registerDebugStreamCallback) return;
8506
+ let cancelled = false;
8507
+ registerDebugStreamCallback((stats) => {
8508
+ if (cancelled) return;
8509
+ dispatch({
8510
+ type: "debugStreamStats",
8511
+ chunkCount: stats.chunkCount,
8512
+ lastChunkSize: stats.lastChunkSize,
8513
+ lastDeltaMs: stats.lastDeltaMs,
8514
+ totalBytes: stats.totalBytes,
8515
+ lastChunkAt: stats.lastChunkAt
8516
+ });
8517
+ });
8518
+ const offResp = events.on("provider.response", () => {
8519
+ dispatch({ type: "debugStreamStatsClear" });
8520
+ });
8521
+ const offErr = events.on("provider.error", () => {
8522
+ dispatch({ type: "debugStreamStatsClear" });
8523
+ });
8524
+ return () => {
8525
+ cancelled = true;
8526
+ offResp();
8527
+ offErr();
8528
+ restoreDebugStreamCallback?.();
8529
+ };
8530
+ }, [events, registerDebugStreamCallback, restoreDebugStreamCallback]);
8334
8531
  const enhanceEnabledRef = useRef(state.enhanceEnabled);
8335
8532
  useEffect(() => {
8336
8533
  enhanceEnabledRef.current = state.enhanceEnabled;
@@ -8404,7 +8601,7 @@ function App({
8404
8601
  return;
8405
8602
  }
8406
8603
  if (activeCtrlRef.current) {
8407
- activeCtrlRef.current.abort();
8604
+ activeCtrlRef.current.abort("user interrupt (Ctrl+C)");
8408
8605
  dispatch({ type: "status", status: "aborting" });
8409
8606
  if (director) {
8410
8607
  const cap = new Promise((resolve) => {
@@ -8650,7 +8847,7 @@ function App({
8650
8847
  return;
8651
8848
  }
8652
8849
  if (state.settingsPicker.open) {
8653
- if (key.escape || key.ctrl && input === "s") {
8850
+ if (key.escape || key.ctrl && input === "s" || key.fn === 5) {
8654
8851
  dispatch({ type: "settingsClose" });
8655
8852
  return;
8656
8853
  }
@@ -8890,7 +9087,10 @@ function App({
8890
9087
  logLevel: cfg.logLevel ?? "info",
8891
9088
  auditLevel: cfg.auditLevel ?? "standard",
8892
9089
  indexOnStart: cfg.indexOnStart ?? true,
8893
- maxIterations: cfg.maxIterations ?? 500
9090
+ maxIterations: cfg.maxIterations ?? 500,
9091
+ enhanceDelayMs: cfg.enhanceDelayMs ?? 6e4,
9092
+ debugStream: cfg.debugStream ?? false,
9093
+ configScope: cfg.configScope ?? "global"
8894
9094
  });
8895
9095
  }
8896
9096
  return;
@@ -8979,7 +9179,10 @@ function App({
8979
9179
  logLevel: cfg.logLevel ?? "info",
8980
9180
  auditLevel: cfg.auditLevel ?? "standard",
8981
9181
  indexOnStart: cfg.indexOnStart ?? true,
8982
- maxIterations: cfg.maxIterations ?? 500
9182
+ maxIterations: cfg.maxIterations ?? 500,
9183
+ enhanceDelayMs: cfg.enhanceDelayMs ?? 6e4,
9184
+ debugStream: cfg.debugStream ?? false,
9185
+ configScope: cfg.configScope ?? "global"
8983
9186
  });
8984
9187
  }
8985
9188
  return;
@@ -9206,7 +9409,8 @@ function App({
9206
9409
  }
9207
9410
  dispatch({ type: "streamReset" });
9208
9411
  if (result.status === "aborted") {
9209
- dispatch({ type: "addEntry", entry: { kind: "warn", text: "Aborted." } });
9412
+ const reason = result.abortReason ? `Aborted (${result.abortReason}).` : "Aborted.";
9413
+ dispatch({ type: "addEntry", entry: { kind: "warn", text: reason } });
9210
9414
  } else if (result.status === "failed") {
9211
9415
  const err = result.error;
9212
9416
  const text = err ? `Failed [${err.severity}${err.recoverable ? ", recoverable" : ""}]: ${err.describe()}` : "Failed.";
@@ -9628,7 +9832,6 @@ User message:
9628
9832
  }
9629
9833
  ),
9630
9834
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [
9631
- /* @__PURE__ */ jsx(LiveActivityStrip, { entries: state.fleet, nowTick }),
9632
9835
  /* @__PURE__ */ jsx(
9633
9836
  Input,
9634
9837
  {
@@ -9702,6 +9905,9 @@ User message:
9702
9905
  auditLevel: state.settingsPicker.auditLevel,
9703
9906
  indexOnStart: state.settingsPicker.indexOnStart,
9704
9907
  maxIterations: state.settingsPicker.maxIterations,
9908
+ enhanceDelayMs: state.settingsPicker.enhanceDelayMs,
9909
+ debugStream: state.settingsPicker.debugStream,
9910
+ configScope: state.settingsPicker.configScope,
9705
9911
  hint: state.settingsPicker.hint
9706
9912
  }
9707
9913
  ) : null,
@@ -9759,7 +9965,7 @@ User message:
9759
9965
  const escConfirm = state.escConfirm;
9760
9966
  if (!escConfirm) return;
9761
9967
  const { snapshot } = escConfirm;
9762
- activeCtrlRef.current?.abort();
9968
+ activeCtrlRef.current?.abort("user interrupt (Esc)");
9763
9969
  dispatch({ type: "status", status: "aborting" });
9764
9970
  dispatch({ type: "steerStart", snapshot });
9765
9971
  if (director && snapshot.subagentsTerminated > 0) {
@@ -9840,7 +10046,8 @@ User message:
9840
10046
  eternalStage: state.eternalStage,
9841
10047
  goalSummary: state.goalSummary,
9842
10048
  indexState,
9843
- modeLabel: liveModeLabel || void 0
10049
+ modeLabel: liveModeLabel || void 0,
10050
+ debugStreamStats: state.debugStreamStats
9844
10051
  }
9845
10052
  ),
9846
10053
  state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, {}) : null,
@@ -10066,7 +10273,7 @@ async function runTui(opts) {
10066
10273
  let instance;
10067
10274
  try {
10068
10275
  instance = render(
10069
- React6.createElement(App, {
10276
+ React5.createElement(App, {
10070
10277
  agent: opts.agent,
10071
10278
  slashRegistry: opts.slashRegistry,
10072
10279
  attachments: opts.attachments,
@@ -10115,7 +10322,9 @@ async function runTui(opts) {
10115
10322
  chime: opts.chime,
10116
10323
  confirmExit: opts.confirmExit,
10117
10324
  modeLabel: opts.modeLabel,
10118
- getModeLabel: opts.getModeLabel
10325
+ getModeLabel: opts.getModeLabel,
10326
+ registerDebugStreamCallback: opts.registerDebugStreamCallback,
10327
+ restoreDebugStreamCallback: opts.restoreDebugStreamCallback
10119
10328
  }),
10120
10329
  { exitOnCtrlC: false, stdin: inkStdin }
10121
10330
  );