@wrongstack/tui 0.107.2 → 0.119.1

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
- import { expectDefined, writeErr, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, buildChildEnv } from '@wrongstack/core';
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, useReducer, useRef, useMemo, useCallback } from 'react';
4
+ import React6, { 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) => (
@@ -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
  }
@@ -826,11 +857,16 @@ 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
864
  /* @__PURE__ */ jsx(Text, { bold: selected, color: selected ? "magenta" : void 0, children: entry.name }),
833
865
  modelLabel ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: modelLabel }) : null,
866
+ entry.provider || entry.model ? /* @__PURE__ */ jsxs(Text, { color: "blue", children: [
867
+ entry.provider ? `${entry.provider}/` : "",
868
+ entry.model ?? ""
869
+ ] }) : null,
834
870
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
835
871
  "L",
836
872
  entry.iterations,
@@ -839,6 +875,7 @@ function AgentRow({
839
875
  "t"
840
876
  ] }),
841
877
  entry.ctxPct !== void 0 ? /* @__PURE__ */ jsx(ContextBar, { pct: entry.ctxPct, tokens: entry.ctxTokens, maxTokens: entry.ctxMaxTokens }) : null,
878
+ ctxCostStr ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: ctxCostStr }) : null,
842
879
  entry.status === "running" && entry.currentTool ? /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
843
880
  "\u2192 ",
844
881
  entry.currentTool.name,
@@ -877,6 +914,26 @@ function AgentDetail({
877
914
  lastTool.ok === false ? " \u2717" : ""
878
915
  ] }) : null
879
916
  ] }) : null,
917
+ entry.provider || entry.model ? /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
918
+ "provider: ",
919
+ entry.provider || "(leader)",
920
+ " \xB7 model: ",
921
+ entry.model || "\u2014"
922
+ ] }) }) : null,
923
+ entry.cost > 0 || entry.ctxCost && entry.ctxCost > 0 ? /* @__PURE__ */ jsxs(Box, { children: [
924
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "cost: " }),
925
+ entry.cost > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
926
+ "$",
927
+ entry.cost.toFixed(4),
928
+ " total"
929
+ ] }) : null,
930
+ entry.cost > 0 && entry.ctxCost && entry.ctxCost > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }) : null,
931
+ entry.ctxCost && entry.ctxCost > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
932
+ "$",
933
+ entry.ctxCost.toFixed(4),
934
+ " ctx"
935
+ ] }) : null
936
+ ] }) : null,
880
937
  entry.status === "running" && streamTail ? /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
881
938
  ">",
882
939
  " ",
@@ -913,10 +970,10 @@ function AgentsMonitor({
913
970
  const live = useMemo(() => selectLiveAgents(all, nowTick), [all, nowTick]);
914
971
  const [selectedIndex, setSelectedIndex] = useState(0);
915
972
  const safeIndex = Math.min(selectedIndex, Math.max(0, live.length - 1));
916
- useInput((input, key) => {
917
- if (key.upArrow || input === "k") {
973
+ useInput((_input, key) => {
974
+ if (key.upArrow) {
918
975
  setSelectedIndex((prev) => Math.max(0, prev - 1));
919
- } else if (key.downArrow || input === "j") {
976
+ } else if (key.downArrow) {
920
977
  setSelectedIndex((prev) => Math.min(live.length - 1, prev + 1));
921
978
  }
922
979
  });
@@ -949,6 +1006,20 @@ function AgentsMonitor({
949
1006
  ] }) : null,
950
1007
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 \u2191\u2193 nav \xB7 Ctrl+G / F3 close" })
951
1008
  ] }),
1009
+ live.length > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1010
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "models" }),
1011
+ (() => {
1012
+ const seen = /* @__PURE__ */ new Map();
1013
+ for (const e of live) {
1014
+ if (e.model) seen.set(e.name ?? e.id, `${e.provider ?? "?"}/${e.model}`);
1015
+ }
1016
+ return [...seen.entries()].slice(0, 4).map(([name, mod]) => /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1017
+ name,
1018
+ ":",
1019
+ mod
1020
+ ] }, name));
1021
+ })()
1022
+ ] }) : null,
952
1023
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
953
1024
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "shown" }),
954
1025
  /* @__PURE__ */ jsx(Text, { color: "magenta", children: live.length }),
@@ -2430,7 +2501,10 @@ function MarkdownView({
2430
2501
  rows.push(
2431
2502
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
2432
2503
  /* @__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 })
2504
+ /[\u2500-\u257F]/.test(qContent) ? /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: [...qContent].slice(0, (contentWidth ?? termWidth) - 2).map((ch, ci) => (
2505
+ /* biome-ignore lint/suspicious/noArrayIndexKey: characters are not reorderable */
2506
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ch }, ci)
2507
+ )) }) : /* @__PURE__ */ jsx(InlineLine, { tokens: parseInline(qContent), dim: true })
2434
2508
  ] }, `q${key++}`)
2435
2509
  );
2436
2510
  continue;
@@ -2459,7 +2533,10 @@ function MarkdownView({
2459
2533
  const maxW = contentWidth ?? termWidth;
2460
2534
  const chars = [...line].slice(0, maxW);
2461
2535
  rows.push(
2462
- /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: chars.map((ch, ci) => /* @__PURE__ */ jsx(Text, { children: ch }, ci)) }, `bx${key++}`)
2536
+ /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: chars.map((ch, ci) => (
2537
+ /* biome-ignore lint/suspicious/noArrayIndexKey: characters are not reorderable */
2538
+ /* @__PURE__ */ jsx(Text, { children: ch }, ci)
2539
+ )) }, `bx${key++}`)
2463
2540
  );
2464
2541
  continue;
2465
2542
  }
@@ -3084,7 +3161,8 @@ function CodeBlock({
3084
3161
  const hidden = Math.max(0, lines.length - MAX_CODE_LINES);
3085
3162
  if (hidden > 0) lines = lines.slice(0, MAX_CODE_LINES);
3086
3163
  const gutterW = String(lines.length).length;
3087
- const maxW = Math.max(20, Math.min(contentWidth - 6 - gutterW - 1, 120));
3164
+ const boxWidth = Math.max(22, contentWidth - 2);
3165
+ const maxW = Math.max(20, Math.min(boxWidth - 4 - gutterW - 1, 120));
3088
3166
  let carry = {};
3089
3167
  const rows = lines.map((raw) => {
3090
3168
  const display = raw.length > maxW ? `${raw.slice(0, maxW - 1)}\u2026` : raw;
@@ -3096,6 +3174,8 @@ function CodeBlock({
3096
3174
  Box,
3097
3175
  {
3098
3176
  flexDirection: "column",
3177
+ width: boxWidth,
3178
+ flexShrink: 0,
3099
3179
  marginLeft: 2,
3100
3180
  marginY: 0,
3101
3181
  borderStyle: "round",
@@ -3764,6 +3844,7 @@ function isHomeEnd(data) {
3764
3844
  return null;
3765
3845
  }
3766
3846
  function isBackspaceOrDelete(data) {
3847
+ if (data === "\x1B\x7F" || data === "\x1B\b") return "metaBackspace";
3767
3848
  if (data === "\x7F" || data === "\b") return "backspace";
3768
3849
  if (data === "\x1B[3~") return "delete";
3769
3850
  return null;
@@ -3800,17 +3881,42 @@ var Input = memo(function Input2({
3800
3881
  cursor,
3801
3882
  disabled,
3802
3883
  hint,
3884
+ hidden,
3885
+ placeholderHeight,
3803
3886
  onKey
3804
3887
  }) {
3888
+ const suppressInkEscRef = useRef(false);
3805
3889
  useInput((input, key) => {
3806
3890
  if (disabled) return;
3891
+ if (key.escape && suppressInkEscRef.current) {
3892
+ suppressInkEscRef.current = false;
3893
+ return;
3894
+ }
3807
3895
  onKey(input, key);
3808
3896
  });
3809
3897
  const { stdin } = useStdin();
3810
3898
  useEffect(() => {
3811
3899
  if (!stdin || disabled) return;
3900
+ let escTimer = null;
3812
3901
  const handleData = (data) => {
3813
3902
  const s2 = data.toString();
3903
+ if (s2 === "\x1B") {
3904
+ escTimer = setTimeout(() => {
3905
+ escTimer = null;
3906
+ suppressInkEscRef.current = true;
3907
+ onKey("", { ...EMPTY_KEY, escape: true });
3908
+ }, 10);
3909
+ return;
3910
+ }
3911
+ if (escTimer !== null) {
3912
+ clearTimeout(escTimer);
3913
+ escTimer = null;
3914
+ if (s2 === "\x7F" || s2 === "\b") {
3915
+ onKey("", { ...EMPTY_KEY, backspace: true, ctrl: true });
3916
+ return;
3917
+ }
3918
+ return;
3919
+ }
3814
3920
  const homeEnd = isHomeEnd(s2);
3815
3921
  if (homeEnd === "home") {
3816
3922
  onKey("", { ...EMPTY_KEY, home: true });
@@ -3829,6 +3935,10 @@ var Input = memo(function Input2({
3829
3935
  onKey("", { ...EMPTY_KEY, delete: true });
3830
3936
  return;
3831
3937
  }
3938
+ if (bsdel === "metaBackspace") {
3939
+ onKey("", { ...EMPTY_KEY, backspace: true, ctrl: true });
3940
+ return;
3941
+ }
3832
3942
  const wheelDelta = parseMouseWheel(s2);
3833
3943
  if (wheelDelta !== null) {
3834
3944
  onKey("", { ...EMPTY_KEY, wheelDeltaY: wheelDelta });
@@ -3839,6 +3949,7 @@ var Input = memo(function Input2({
3839
3949
  };
3840
3950
  stdin.on("data", handleData);
3841
3951
  return () => {
3952
+ if (escTimer !== null) clearTimeout(escTimer);
3842
3953
  stdin.off("data", handleData);
3843
3954
  };
3844
3955
  }, [stdin, disabled, onKey]);
@@ -3855,6 +3966,9 @@ var Input = memo(function Input2({
3855
3966
  }, [stdout]);
3856
3967
  const promptColor = disabled ? "red" : "cyan";
3857
3968
  const rows = layoutInputRows(prompt, value, cursor, cols);
3969
+ if (hidden) {
3970
+ return /* @__PURE__ */ jsx(Box, { height: Math.max(1, placeholderHeight ?? rows.length) });
3971
+ }
3858
3972
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
3859
3973
  rows.map(
3860
3974
  (row, i) => row.length === 0 ? (
@@ -4375,12 +4489,15 @@ function GoalPanel({ goal }) {
4375
4489
  ] }) }),
4376
4490
  goal.deliverables.map((d, i) => {
4377
4491
  const done = /^\[[x✓]\]|✅|\(done\)/i.test(d);
4378
- return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: done ? "green" : void 0, dimColor: !done, children: [
4379
- " ",
4380
- done ? "\u2713" : "\u25CB",
4381
- " ",
4382
- d
4383
- ] }) }, i);
4492
+ return (
4493
+ // biome-ignore lint/suspicious/noArrayIndexKey: deliverables are stable text strings
4494
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: done ? "green" : void 0, dimColor: !done, children: [
4495
+ " ",
4496
+ done ? "\u2713" : "\u25CB",
4497
+ " ",
4498
+ d
4499
+ ] }) }, i)
4500
+ );
4384
4501
  })
4385
4502
  ] }),
4386
4503
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
@@ -4431,6 +4548,7 @@ var LOG_LEVELS = ["error", "warn", "info", "debug", "trace"];
4431
4548
  var AUDIT_LEVELS = ["minimal", "standard", "full"];
4432
4549
  var COMPACTOR_STRATEGIES = ["hybrid", "intelligent", "selective"];
4433
4550
  var MAX_ITERATIONS_PRESETS = [100, 200, 500, 1e3, 0];
4551
+ var ENHANCE_DELAY_PRESETS = [3e4, 45e3, 6e4, 9e4, 12e4];
4434
4552
  function formatSettingsDelay(ms) {
4435
4553
  if (ms === 0) return "disabled";
4436
4554
  if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
@@ -4440,12 +4558,16 @@ function formatMaxIterations(n) {
4440
4558
  if (n === 0) return "unlimited";
4441
4559
  return String(n);
4442
4560
  }
4561
+ function formatEnhanceDelay(ms) {
4562
+ return `${Math.round(ms / 1e3)}s`;
4563
+ }
4443
4564
  var MODE_DESC = {
4444
4565
  off: "Agent stops after each turn (normal)",
4445
4566
  suggest: "Shows next-step suggestions after each turn",
4446
4567
  auto: "Self-driving \u2014 agent continues automatically"
4447
4568
  };
4448
- var SETTINGS_FIELD_COUNT = 19;
4569
+ var SETTINGS_FIELD_COUNT = 22;
4570
+ var CONFIG_SCOPES = ["global", "project"];
4449
4571
  function SettingsPicker({
4450
4572
  field,
4451
4573
  mode,
@@ -4467,6 +4589,9 @@ function SettingsPicker({
4467
4589
  auditLevel,
4468
4590
  indexOnStart,
4469
4591
  maxIterations,
4592
+ enhanceDelayMs,
4593
+ debugStream,
4594
+ configScope,
4470
4595
  hint
4471
4596
  }) {
4472
4597
  const boolVal = (v) => v ? "on" : "off";
@@ -4577,6 +4702,23 @@ function SettingsPicker({
4577
4702
  label: "Max iterations",
4578
4703
  value: formatMaxIterations(maxIterations),
4579
4704
  detail: "100\u20131000 or unlimited (0)"
4705
+ },
4706
+ {
4707
+ label: "Refine preview countdown",
4708
+ value: formatEnhanceDelay(enhanceDelayMs),
4709
+ detail: "Timeout for prompt refinement preview (30s\u2013120s)"
4710
+ },
4711
+ // ── Debug ──
4712
+ { section: "Debug" },
4713
+ {
4714
+ label: "Stream debug logging",
4715
+ value: boolVal(debugStream),
4716
+ detail: "Hex-dump raw SSE bytes to stderr"
4717
+ },
4718
+ {
4719
+ label: "Config scope",
4720
+ value: configScope,
4721
+ detail: "global (~/.wrongstack/) or project (.wrongstack/)"
4580
4722
  }
4581
4723
  ];
4582
4724
  const fieldRowIndex = [];
@@ -4611,7 +4753,7 @@ function SettingsPicker({
4611
4753
  };
4612
4754
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
4613
4755
  /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Settings \u2501\u2501" }),
4614
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 field \xB7 \u2190/\u2192 change (instant save) \xB7 Esc close" }),
4756
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 field \xB7 \u2190/\u2192 change (instant save) \xB7 Esc / F5 close" }),
4615
4757
  hasAbove ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2191 ${windowStart} field${windowStart === 1 ? "" : "s"} above` }) : null,
4616
4758
  rows.map((row, i) => {
4617
4759
  const fieldAtRow = fieldRowIndex.indexOf(i);
@@ -6174,6 +6316,10 @@ function buildSteeringPreamble(snapshot, newDirection) {
6174
6316
  function reducer(state, action) {
6175
6317
  switch (action.type) {
6176
6318
  case "addEntry": {
6319
+ const e = action.entry;
6320
+ if ((e.kind === "user" || e.kind === "assistant" || e.kind === "info" || e.kind === "warn" || e.kind === "error" || e.kind === "turn-summary") && !e.text?.trim()) {
6321
+ return state;
6322
+ }
6177
6323
  const appended = [...state.entries, { ...action.entry, id: state.nextId }];
6178
6324
  return { ...state, entries: appended, nextId: state.nextId + 1 };
6179
6325
  }
@@ -6509,6 +6655,9 @@ function reducer(state, action) {
6509
6655
  auditLevel: action.auditLevel,
6510
6656
  indexOnStart: action.indexOnStart,
6511
6657
  maxIterations: action.maxIterations,
6658
+ enhanceDelayMs: action.enhanceDelayMs,
6659
+ debugStream: action.debugStream,
6660
+ configScope: action.configScope,
6512
6661
  hint: void 0
6513
6662
  }
6514
6663
  };
@@ -6574,12 +6723,26 @@ function reducer(state, action) {
6574
6723
  return { ...state, settingsPicker: { ...sp, auditLevel: expectDefined(AUDIT_LEVELS[next]), hint: void 0 } };
6575
6724
  }
6576
6725
  if (f === 17) return { ...state, settingsPicker: { ...sp, indexOnStart: !sp.indexOnStart, hint: void 0 } };
6577
- {
6726
+ if (f === 18) {
6578
6727
  const j = MAX_ITERATIONS_PRESETS.indexOf(sp.maxIterations);
6579
6728
  const base = j < 0 ? 0 : j;
6580
6729
  const next = (base + action.delta + MAX_ITERATIONS_PRESETS.length) % MAX_ITERATIONS_PRESETS.length;
6581
6730
  return { ...state, settingsPicker: { ...sp, maxIterations: expectDefined(MAX_ITERATIONS_PRESETS[next]), hint: void 0 } };
6582
6731
  }
6732
+ if (f === 19) {
6733
+ const ej = ENHANCE_DELAY_PRESETS.indexOf(sp.enhanceDelayMs);
6734
+ const ebase = ej < 0 ? 0 : ej;
6735
+ const enext = (ebase + action.delta + ENHANCE_DELAY_PRESETS.length) % ENHANCE_DELAY_PRESETS.length;
6736
+ return { ...state, settingsPicker: { ...sp, enhanceDelayMs: expectDefined(ENHANCE_DELAY_PRESETS[enext]), hint: void 0 } };
6737
+ }
6738
+ if (f === 20) return { ...state, settingsPicker: { ...sp, debugStream: !sp.debugStream, hint: void 0 } };
6739
+ if (f === 21) {
6740
+ const i = CONFIG_SCOPES.indexOf(sp.configScope);
6741
+ const base = i < 0 ? 0 : i;
6742
+ const next = (base + action.delta + CONFIG_SCOPES.length) % CONFIG_SCOPES.length;
6743
+ return { ...state, settingsPicker: { ...sp, configScope: expectDefined(CONFIG_SCOPES[next]), hint: void 0 } };
6744
+ }
6745
+ return state;
6583
6746
  }
6584
6747
  case "settingsHint":
6585
6748
  return { ...state, settingsPicker: { ...state.settingsPicker, hint: action.text } };
@@ -6824,6 +6987,7 @@ function reducer(state, action) {
6824
6987
  ctxPct: action.load,
6825
6988
  ctxTokens: action.tokens,
6826
6989
  ctxMaxTokens: action.maxContext,
6990
+ ctxCost: action.ctxCost,
6827
6991
  lastEventAt: Date.now()
6828
6992
  }
6829
6993
  }
@@ -7234,6 +7398,22 @@ function reducer(state, action) {
7234
7398
  }
7235
7399
  };
7236
7400
  }
7401
+ case "debugStreamStats": {
7402
+ return {
7403
+ ...state,
7404
+ debugStreamStats: {
7405
+ chunkCount: action.chunkCount,
7406
+ lastChunkSize: action.lastChunkSize,
7407
+ lastDeltaMs: action.lastDeltaMs,
7408
+ totalBytes: action.totalBytes,
7409
+ lastChunkAt: action.lastChunkAt
7410
+ }
7411
+ };
7412
+ }
7413
+ case "debugStreamStatsClear": {
7414
+ if (state.debugStreamStats === null) return state;
7415
+ return { ...state, debugStreamStats: null };
7416
+ }
7237
7417
  }
7238
7418
  }
7239
7419
  var INPUT_PROMPT = "\u203A ";
@@ -7308,7 +7488,9 @@ function App({
7308
7488
  initialAsk,
7309
7489
  sessionsDir,
7310
7490
  modeLabel,
7311
- getModeLabel
7491
+ getModeLabel,
7492
+ registerDebugStreamCallback,
7493
+ restoreDebugStreamCallback
7312
7494
  }) {
7313
7495
  const { exit } = useApp();
7314
7496
  const { stdout } = useStdout();
@@ -7331,32 +7513,36 @@ function App({
7331
7513
  setStatuslineHiddenItems(hiddenItems);
7332
7514
  }, [setStatuslineHiddenItems, hiddenItems]);
7333
7515
  const projectRoot = agent.ctx.projectRoot;
7334
- useEffect(() => {
7516
+ const refreshGoalSummary = useCallback(() => {
7335
7517
  if (!projectRoot) return;
7336
- const goalPath = path2.join(projectRoot, ".wrongstack", "goal.json");
7337
- fs2.readFile(goalPath, "utf8").then((raw) => {
7338
- const goal = JSON.parse(raw);
7339
- if (goal?.goal && typeof goal.iterations === "number") {
7340
- const lastEntry = goal.journal?.[goal.journal.length - 1];
7341
- dispatch({
7342
- type: "goalSummary",
7343
- summary: {
7344
- goal: goal.goal,
7345
- refinedGoal: goal.refinedGoal,
7346
- goalState: goal.goalState ?? "active",
7347
- iterations: goal.iterations,
7348
- progress: goal.progress,
7349
- progressNote: goal.progressNote,
7350
- progressTrend: goal.progressTrend,
7351
- deliverables: goal.deliverables,
7352
- lastTask: lastEntry?.task,
7353
- lastStatus: lastEntry?.status
7354
- }
7355
- });
7518
+ const goalPath = resolveWstackPaths({ projectRoot }).projectGoal;
7519
+ loadGoal(goalPath).then((goal) => {
7520
+ if (!goal) {
7521
+ dispatch({ type: "goalSummary", summary: null });
7522
+ return;
7356
7523
  }
7524
+ const lastEntry = goal.journal?.[goal.journal.length - 1];
7525
+ dispatch({
7526
+ type: "goalSummary",
7527
+ summary: {
7528
+ goal: goal.goal,
7529
+ refinedGoal: goal.refinedGoal,
7530
+ goalState: goal.goalState ?? "active",
7531
+ iterations: goal.iterations,
7532
+ progress: goal.progress,
7533
+ progressNote: goal.progressNote,
7534
+ progressTrend: goal.progressTrend,
7535
+ deliverables: goal.deliverables,
7536
+ lastTask: lastEntry?.task,
7537
+ lastStatus: lastEntry?.status
7538
+ }
7539
+ });
7357
7540
  }).catch(() => {
7358
7541
  });
7359
7542
  }, [projectRoot]);
7543
+ useEffect(() => {
7544
+ refreshGoalSummary();
7545
+ }, [refreshGoalSummary]);
7360
7546
  const restoredEntries = (() => {
7361
7547
  const msgs = agent.ctx.messages;
7362
7548
  if (!msgs || msgs.length === 0) return [];
@@ -7414,7 +7600,7 @@ function App({
7414
7600
  searchQuery: ""
7415
7601
  },
7416
7602
  autonomyPicker: { open: false, options: [], selected: 0 },
7417
- 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 },
7603
+ 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" },
7418
7604
  confirmQueue: [],
7419
7605
  enhance: null,
7420
7606
  enhanceEnabled,
@@ -7453,7 +7639,8 @@ function App({
7453
7639
  scrollOffset: 0,
7454
7640
  totalLines: 0,
7455
7641
  viewportRows: 0,
7456
- pendingNewLines: 0
7642
+ pendingNewLines: 0,
7643
+ debugStreamStats: null
7457
7644
  });
7458
7645
  const builderRef = useRef(null);
7459
7646
  if (builderRef.current === null) {
@@ -7506,6 +7693,9 @@ function App({
7506
7693
  const t = setInterval(() => setNowTick(Date.now()), 1e4);
7507
7694
  return () => clearInterval(t);
7508
7695
  }, []);
7696
+ useEffect(() => {
7697
+ if (state.goalPanelOpen) refreshGoalSummary();
7698
+ }, [state.goalPanelOpen, nowTick, refreshGoalSummary]);
7509
7699
  const [enhanceDots, setEnhanceDots] = useState(0);
7510
7700
  useEffect(() => {
7511
7701
  if (!state.enhanceBusy) return;
@@ -7524,6 +7714,42 @@ function App({
7524
7714
  const t = setInterval(poll, 2e3);
7525
7715
  return () => clearInterval(t);
7526
7716
  }, [agent.ctx.todos]);
7717
+ const staleGuardRef = useRef(JSON.stringify({ a: "", y: false, m: "", model: "", provider: "" }));
7718
+ useEffect(() => {
7719
+ const poll = () => {
7720
+ const a = getAutonomy?.() ?? "off";
7721
+ const y = getYolo?.() ?? false;
7722
+ const m = getModeLabel?.() ?? "";
7723
+ const curModel = agent.ctx.model;
7724
+ const curProvider = agent.ctx.provider?.id ?? "";
7725
+ const snap = JSON.stringify({ a, y, m, model: curModel, provider: curProvider });
7726
+ if (snap !== staleGuardRef.current) {
7727
+ staleGuardRef.current = snap;
7728
+ if (a !== autonomyLive) setAutonomyLive(a);
7729
+ if (y !== yoloLive) setYoloLive(y);
7730
+ if (m !== liveModeLabel) setLiveModeLabel(m);
7731
+ if (curModel !== liveModel) setLiveModel(curModel);
7732
+ if (curProvider !== liveProvider) setLiveProvider(curProvider);
7733
+ if (a === "eternal" && getEternalEngine) void runEternalLoopRef.current();
7734
+ if (a === "eternal-parallel" && getParallelEngine) void runParallelLoopRef.current();
7735
+ }
7736
+ };
7737
+ const t = setInterval(poll, 2e3);
7738
+ return () => clearInterval(t);
7739
+ }, [
7740
+ getAutonomy,
7741
+ getYolo,
7742
+ getModeLabel,
7743
+ getEternalEngine,
7744
+ getParallelEngine,
7745
+ autonomyLive,
7746
+ yoloLive,
7747
+ liveModeLabel,
7748
+ liveModel,
7749
+ liveProvider,
7750
+ agent.ctx.model,
7751
+ agent.ctx.provider
7752
+ ]);
7527
7753
  const [gitInfo, setGitInfo] = React6.useState(null);
7528
7754
  useEffect(() => {
7529
7755
  let cancelled = false;
@@ -7716,7 +7942,10 @@ function App({
7716
7942
  logLevel: sp.logLevel,
7717
7943
  auditLevel: sp.auditLevel,
7718
7944
  indexOnStart: sp.indexOnStart,
7719
- maxIterations: sp.maxIterations
7945
+ maxIterations: sp.maxIterations,
7946
+ enhanceDelayMs: sp.enhanceDelayMs,
7947
+ debugStream: sp.debugStream,
7948
+ configScope: sp.configScope
7720
7949
  });
7721
7950
  }
7722
7951
  if (prev.help) dispatch({ type: "toggleHelp" });
@@ -7938,7 +8167,7 @@ function App({
7938
8167
  const subagents = Object.values(s2.fleet).filter((e) => e.status === "running").map((e) => ({ label: e.name, status: e.status, tool: e.currentTool?.name }));
7939
8168
  const subagentsTerminated = subagents.length;
7940
8169
  const partialAssistantText = streamingTextRef.current.slice(-1500);
7941
- activeCtrlRef.current?.abort();
8170
+ activeCtrlRef.current?.abort("user interrupt (/steer)");
7942
8171
  dispatch({
7943
8172
  type: "steerStart",
7944
8173
  snapshot: { runningTools, subagents, subagentsTerminated, partialAssistantText }
@@ -8049,7 +8278,10 @@ function App({
8049
8278
  logLevel: s2.logLevel ?? "info",
8050
8279
  auditLevel: s2.auditLevel ?? "standard",
8051
8280
  indexOnStart: s2.indexOnStart ?? true,
8052
- maxIterations: s2.maxIterations ?? 500
8281
+ maxIterations: s2.maxIterations ?? 500,
8282
+ enhanceDelayMs: s2.enhanceDelayMs ?? 6e4,
8283
+ debugStream: s2.debugStream ?? false,
8284
+ configScope: s2.configScope ?? "global"
8053
8285
  });
8054
8286
  }, [getSettings]);
8055
8287
  const settingsAutoSaveGateRef = useRef(true);
@@ -8085,7 +8317,10 @@ function App({
8085
8317
  logLevel: sp.logLevel,
8086
8318
  auditLevel: sp.auditLevel,
8087
8319
  indexOnStart: sp.indexOnStart,
8088
- maxIterations: sp.maxIterations
8320
+ maxIterations: sp.maxIterations,
8321
+ enhanceDelayMs: sp.enhanceDelayMs,
8322
+ debugStream: sp.debugStream,
8323
+ configScope: sp.configScope
8089
8324
  })).then((err) => {
8090
8325
  if (err) dispatch({ type: "settingsHint", text: err });
8091
8326
  });
@@ -8110,6 +8345,7 @@ function App({
8110
8345
  state.settingsPicker.auditLevel,
8111
8346
  state.settingsPicker.indexOnStart,
8112
8347
  state.settingsPicker.maxIterations,
8348
+ state.settingsPicker.enhanceDelayMs,
8113
8349
  saveSettings
8114
8350
  ]);
8115
8351
  useEffect(() => {
@@ -8319,6 +8555,33 @@ function App({
8319
8555
  if (flushTimerRef.current) clearTimeout(flushTimerRef.current);
8320
8556
  };
8321
8557
  }, [events, agent.ctx.todos]);
8558
+ useEffect(() => {
8559
+ if (!registerDebugStreamCallback) return;
8560
+ let cancelled = false;
8561
+ registerDebugStreamCallback((stats) => {
8562
+ if (cancelled) return;
8563
+ dispatch({
8564
+ type: "debugStreamStats",
8565
+ chunkCount: stats.chunkCount,
8566
+ lastChunkSize: stats.lastChunkSize,
8567
+ lastDeltaMs: stats.lastDeltaMs,
8568
+ totalBytes: stats.totalBytes,
8569
+ lastChunkAt: stats.lastChunkAt
8570
+ });
8571
+ });
8572
+ const offResp = events.on("provider.response", () => {
8573
+ dispatch({ type: "debugStreamStatsClear" });
8574
+ });
8575
+ const offErr = events.on("provider.error", () => {
8576
+ dispatch({ type: "debugStreamStatsClear" });
8577
+ });
8578
+ return () => {
8579
+ cancelled = true;
8580
+ offResp();
8581
+ offErr();
8582
+ restoreDebugStreamCallback?.();
8583
+ };
8584
+ }, [events, registerDebugStreamCallback, restoreDebugStreamCallback]);
8322
8585
  const enhanceEnabledRef = useRef(state.enhanceEnabled);
8323
8586
  useEffect(() => {
8324
8587
  enhanceEnabledRef.current = state.enhanceEnabled;
@@ -8392,7 +8655,7 @@ function App({
8392
8655
  return;
8393
8656
  }
8394
8657
  if (activeCtrlRef.current) {
8395
- activeCtrlRef.current.abort();
8658
+ activeCtrlRef.current.abort("user interrupt (Ctrl+C)");
8396
8659
  dispatch({ type: "status", status: "aborting" });
8397
8660
  if (director) {
8398
8661
  const cap = new Promise((resolve) => {
@@ -8638,7 +8901,7 @@ function App({
8638
8901
  return;
8639
8902
  }
8640
8903
  if (state.settingsPicker.open) {
8641
- if (key.escape || key.ctrl && input === "s") {
8904
+ if (key.escape || key.ctrl && input === "s" || key.fn === 5) {
8642
8905
  dispatch({ type: "settingsClose" });
8643
8906
  return;
8644
8907
  }
@@ -8878,7 +9141,10 @@ function App({
8878
9141
  logLevel: cfg.logLevel ?? "info",
8879
9142
  auditLevel: cfg.auditLevel ?? "standard",
8880
9143
  indexOnStart: cfg.indexOnStart ?? true,
8881
- maxIterations: cfg.maxIterations ?? 500
9144
+ maxIterations: cfg.maxIterations ?? 500,
9145
+ enhanceDelayMs: cfg.enhanceDelayMs ?? 6e4,
9146
+ debugStream: cfg.debugStream ?? false,
9147
+ configScope: cfg.configScope ?? "global"
8882
9148
  });
8883
9149
  }
8884
9150
  return;
@@ -8967,7 +9233,10 @@ function App({
8967
9233
  logLevel: cfg.logLevel ?? "info",
8968
9234
  auditLevel: cfg.auditLevel ?? "standard",
8969
9235
  indexOnStart: cfg.indexOnStart ?? true,
8970
- maxIterations: cfg.maxIterations ?? 500
9236
+ maxIterations: cfg.maxIterations ?? 500,
9237
+ enhanceDelayMs: cfg.enhanceDelayMs ?? 6e4,
9238
+ debugStream: cfg.debugStream ?? false,
9239
+ configScope: cfg.configScope ?? "global"
8971
9240
  });
8972
9241
  }
8973
9242
  return;
@@ -8981,18 +9250,10 @@ function App({
8981
9250
  dispatch({ type: "toggleMonitor" });
8982
9251
  return;
8983
9252
  }
8984
- if (state.worktreeMonitorOpen) {
8985
- dispatch({ type: "worktreeMonitorToggle" });
8986
- return;
8987
- }
8988
9253
  if (state.todosMonitorOpen) {
8989
9254
  dispatch({ type: "toggleTodosMonitor" });
8990
9255
  return;
8991
9256
  }
8992
- if (state.autoPhase?.monitorOpen) {
8993
- dispatch({ type: "autoPhaseMonitorToggle" });
8994
- return;
8995
- }
8996
9257
  if (state.settingsPicker.open) {
8997
9258
  dispatch({ type: "settingsClose" });
8998
9259
  return;
@@ -9010,6 +9271,9 @@ function App({
9010
9271
  return;
9011
9272
  }
9012
9273
  }
9274
+ if (state.processListOpen) {
9275
+ return;
9276
+ }
9013
9277
  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) {
9014
9278
  dispatch({ type: "toggleHelp" });
9015
9279
  return;
@@ -9199,7 +9463,8 @@ function App({
9199
9463
  }
9200
9464
  dispatch({ type: "streamReset" });
9201
9465
  if (result.status === "aborted") {
9202
- dispatch({ type: "addEntry", entry: { kind: "warn", text: "Aborted." } });
9466
+ const reason = result.abortReason ? `Aborted (${result.abortReason}).` : "Aborted.";
9467
+ dispatch({ type: "addEntry", entry: { kind: "warn", text: reason } });
9203
9468
  } else if (result.status === "failed") {
9204
9469
  const err = result.error;
9205
9470
  const text = err ? `Failed [${err.severity}${err.recoverable ? ", recoverable" : ""}]: ${err.describe()}` : "Failed.";
@@ -9610,8 +9875,7 @@ User message:
9610
9875
  stdout?.columns ?? 80
9611
9876
  );
9612
9877
  const inputHeight = Math.max(1, inputCellRows.length);
9613
- const monitorOpen = state.monitorOpen || state.agentsMonitorOpen || state.worktreeMonitorOpen || state.todosMonitorOpen || state.queuePanelOpen || state.processListOpen || state.goalPanelOpen || state.helpOpen;
9614
- const hideInput = enhanceActive || monitorOpen;
9878
+ const hideInput = enhanceActive || state.helpOpen || state.processListOpen;
9615
9879
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 0, children: [
9616
9880
  /* @__PURE__ */ jsx(
9617
9881
  History,
@@ -9623,12 +9887,14 @@ User message:
9623
9887
  ),
9624
9888
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [
9625
9889
  /* @__PURE__ */ jsx(LiveActivityStrip, { entries: state.fleet, nowTick }),
9626
- hideInput ? /* @__PURE__ */ jsx(Box, { height: inputHeight }) : /* @__PURE__ */ jsx(
9890
+ /* @__PURE__ */ jsx(
9627
9891
  Input,
9628
9892
  {
9629
9893
  prompt: INPUT_PROMPT,
9630
9894
  value: state.buffer,
9631
9895
  cursor: state.cursor,
9896
+ hidden: hideInput,
9897
+ placeholderHeight: inputHeight,
9632
9898
  disabled: state.status === "aborting" && !state.steeringPending || state.confirmQueue.length > 0,
9633
9899
  hint: inputHint,
9634
9900
  onKey: stableOnKey
@@ -9694,6 +9960,9 @@ User message:
9694
9960
  auditLevel: state.settingsPicker.auditLevel,
9695
9961
  indexOnStart: state.settingsPicker.indexOnStart,
9696
9962
  maxIterations: state.settingsPicker.maxIterations,
9963
+ enhanceDelayMs: state.settingsPicker.enhanceDelayMs,
9964
+ debugStream: state.settingsPicker.debugStream,
9965
+ configScope: state.settingsPicker.configScope,
9697
9966
  hint: state.settingsPicker.hint
9698
9967
  }
9699
9968
  ) : null,
@@ -9751,7 +10020,7 @@ User message:
9751
10020
  const escConfirm = state.escConfirm;
9752
10021
  if (!escConfirm) return;
9753
10022
  const { snapshot } = escConfirm;
9754
- activeCtrlRef.current?.abort();
10023
+ activeCtrlRef.current?.abort("user interrupt (Esc)");
9755
10024
  dispatch({ type: "status", status: "aborting" });
9756
10025
  dispatch({ type: "steerStart", snapshot });
9757
10026
  if (director && snapshot.subagentsTerminated > 0) {
@@ -9832,7 +10101,8 @@ User message:
9832
10101
  eternalStage: state.eternalStage,
9833
10102
  goalSummary: state.goalSummary,
9834
10103
  indexState,
9835
- modeLabel: liveModeLabel || void 0
10104
+ modeLabel: liveModeLabel || void 0,
10105
+ debugStreamStats: state.debugStreamStats
9836
10106
  }
9837
10107
  ),
9838
10108
  state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, {}) : null,
@@ -10107,7 +10377,9 @@ async function runTui(opts) {
10107
10377
  chime: opts.chime,
10108
10378
  confirmExit: opts.confirmExit,
10109
10379
  modeLabel: opts.modeLabel,
10110
- getModeLabel: opts.getModeLabel
10380
+ getModeLabel: opts.getModeLabel,
10381
+ registerDebugStreamCallback: opts.registerDebugStreamCallback,
10382
+ restoreDebugStreamCallback: opts.restoreDebugStreamCallback
10111
10383
  }),
10112
10384
  { exitOnCtrlC: false, stdin: inkStdin }
10113
10385
  );