@wrongstack/tui 0.310.0 → 0.313.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
@@ -1936,7 +1936,7 @@ var PANEL_IDS = Object.freeze([
1936
1936
  "connections"
1937
1937
  // Ctrl+N
1938
1938
  ]);
1939
- var TOTAL_SETTINGS_FIELD_COUNT = PANEL_POSITION_FIELD_START + PANEL_IDS.length;
1939
+ var TOTAL_SETTINGS_FIELD_COUNT = PANEL_POSITION_FIELD_START + PANEL_IDS.length + 2;
1940
1940
  var DEFAULT_PANEL_POSITIONS = Object.freeze({
1941
1941
  projectPicker: "bottom",
1942
1942
  fleet: "bottom",
@@ -2601,20 +2601,23 @@ var ACCENT = "#fab387";
2601
2601
  var DIM = "#313244";
2602
2602
  var BREATHE_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
2603
2603
  var DOTS_FRAMES = ["", ".", "..", "...", "..", "."];
2604
- var ANIMATION_STYLES = ["rainbow", "wave", "pulse", "dots", "breathe"];
2604
+ var ANIMATION_STYLES = ["rainbow", "wave", "pulse", "dots", "breathe", "static"];
2605
2605
  var ANIMATION_STYLE_DESCS = {
2606
2606
  rainbow: "Per-glyph hue cycle (default)",
2607
2607
  wave: "Single-color brightness sweep",
2608
2608
  pulse: "Whole-text brightness pulse",
2609
2609
  dots: "Trailing dots ellipsis (. .. ... .. .)",
2610
- breathe: "Spinning braille glyph prefix, flat text"
2610
+ breathe: "Spinning braille glyph prefix, flat text",
2611
+ static: "No animation \u2014 flat working indicator"
2611
2612
  };
2612
2613
  var CYCLE_INTERVAL_SECONDS = 12;
2613
2614
  var COLOR_TICK_MS = 120;
2614
2615
  function colorPhaseFromTime(elapsedMs) {
2615
2616
  return Math.floor(Math.max(0, elapsedMs) / COLOR_TICK_MS);
2616
2617
  }
2617
- var CYCLE_ORDER = ["wave", "pulse", "dots", "breathe"];
2618
+ var CYCLE_ORDER = ANIMATION_STYLES.filter(
2619
+ (style) => style !== "rainbow"
2620
+ );
2618
2621
  function styleForCycleTick(tick) {
2619
2622
  const idx = Math.max(0, Math.floor(tick / CYCLE_INTERVAL_SECONDS));
2620
2623
  const i = idx % CYCLE_ORDER.length;
@@ -2747,9 +2750,10 @@ function ThinkingChip({
2747
2750
  phase,
2748
2751
  cycleTick
2749
2752
  }) {
2750
- const { time: colorTime } = useAnimation({ interval: COLOR_TICK_MS, isActive: true });
2751
- const colorPhase = colorPhaseFromTime(colorTime);
2752
2753
  const live = style === "cycle" ? styleForCycleTick(cycleTick) : style;
2754
+ const animateColor = live === "rainbow" || live === "wave" || live === "pulse";
2755
+ const { time: colorTime } = useAnimation({ interval: COLOR_TICK_MS, isActive: animateColor });
2756
+ const colorPhase = colorPhaseFromTime(animateColor ? colorTime : 0);
2753
2757
  if (live === "rainbow") {
2754
2758
  const chars = Array.from(text);
2755
2759
  return /* @__PURE__ */ jsx5(Text, { bold: true, children: chars.map((ch, i) => /* @__PURE__ */ jsx5(Text, { color: rainbowColor(i, colorPhase), children: ch }, i)) });
@@ -2761,6 +2765,9 @@ function ThinkingChip({
2761
2765
  if (live === "pulse") {
2762
2766
  return /* @__PURE__ */ jsx5(Text, { bold: true, color: pulseColor(colorPhase), children: text });
2763
2767
  }
2768
+ if (live === "static") {
2769
+ return /* @__PURE__ */ jsx5(Text, { bold: true, color: "green", children: text });
2770
+ }
2764
2771
  if (live === "dots") {
2765
2772
  const stripped = stripTrailingDots(text);
2766
2773
  const idx2 = phase % DOTS_FRAMES.length;
@@ -3272,7 +3279,19 @@ function buildPrimaryChips(p) {
3272
3279
  STATUSLINE_ICONS.cache,
3273
3280
  " ",
3274
3281
  (cache2.hitRatio * 100).toFixed(0),
3275
- "%"
3282
+ "%",
3283
+ /* @__PURE__ */ jsxs5(Text, { children: [
3284
+ " r",
3285
+ fmtTok(cache2.readTokens)
3286
+ ] }),
3287
+ /* @__PURE__ */ jsxs5(Text, { children: [
3288
+ " w",
3289
+ fmtTok(cache2.writeTokens)
3290
+ ] }),
3291
+ cache2.savedUsd > 0 ? /* @__PURE__ */ jsxs5(Text, { children: [
3292
+ " ~$",
3293
+ cache2.savedUsd.toFixed(2)
3294
+ ] }) : null
3276
3295
  ] }) : null
3277
3296
  ] });
3278
3297
  })() : null,
@@ -3815,7 +3834,7 @@ function StatusBar({
3815
3834
  fleet?.running ?? 0,
3816
3835
  thinkingWord
3817
3836
  );
3818
- const statePrefix = state === "idle" || state === "aborting" ? "\u25CF" : spinner;
3837
+ const statePrefix = state === "idle" || state === "aborting" ? "\u25CF" : animationStyle === "static" ? "\u25CF" : spinner;
3819
3838
  const thinking = state === "running" || state === "streaming";
3820
3839
  const hasAutoProceed = autoProceedCountdown != null && autoProceedCountdown > 0;
3821
3840
  const fleetHasActivity = fleet && (fleet.running > 0 || fleet.idle > 0 || fleet.pending > 0 || fleet.completed > 0) || subagentCount > 0;
@@ -14976,7 +14995,7 @@ function ConnectionsPanel({
14976
14995
  const [loading, setLoading] = useState17(true);
14977
14996
  const [error, setError] = useState17(null);
14978
14997
  const cancelledRef = useRef3(false);
14979
- const fetch = useCallback4(async () => {
14998
+ const fetch2 = useCallback4(async () => {
14980
14999
  try {
14981
15000
  const r = await collectConnectionsHealth(projectRoot);
14982
15001
  if (cancelledRef.current) return;
@@ -14991,18 +15010,18 @@ function ConnectionsPanel({
14991
15010
  }, [projectRoot]);
14992
15011
  useEffect16(() => {
14993
15012
  cancelledRef.current = false;
14994
- void fetch();
14995
- const timer = setInterval(() => void fetch(), REFRESH_MS);
15013
+ void fetch2();
15014
+ const timer = setInterval(() => void fetch2(), REFRESH_MS);
14996
15015
  return () => {
14997
15016
  cancelledRef.current = true;
14998
15017
  clearInterval(timer);
14999
15018
  };
15000
- }, [fetch]);
15019
+ }, [fetch2]);
15001
15020
  const shortcutsEnabled = usePanelShortcutsEnabled();
15002
15021
  useInput((_input, _key) => {
15003
15022
  if (!shortcutsEnabled) return;
15004
15023
  if (_input === "q") onClose();
15005
- if (_input === "r") void fetch();
15024
+ if (_input === "r") void fetch2();
15006
15025
  });
15007
15026
  const overallLabel = report ? report.overall === "healthy" ? "All required services healthy" : report.overall === "degraded" ? "Some services degraded" : "Service errors detected" : "Checking\u2026";
15008
15027
  return /* @__PURE__ */ jsxs40(
@@ -17711,6 +17730,7 @@ var MODE_DESC = {
17711
17730
  };
17712
17731
  var SETTINGS_FIELD_COUNT = TOTAL_SETTINGS_FIELD_COUNT;
17713
17732
  var THINKING_WORD_FIELD = 22;
17733
+ var WRONGPROXY_URL_FIELD = 60;
17714
17734
  var THINKING_WORD_PRESETS = [
17715
17735
  "thinking",
17716
17736
  "random",
@@ -17844,8 +17864,17 @@ var SETTINGS_FIELD_LABELS = [
17844
17864
  // 56 (P panel id index 10)
17845
17865
  "Kanban placement",
17846
17866
  // 57 (P panel id index 11)
17847
- "Connections placement"
17867
+ "Connections placement",
17848
17868
  // 58 (P panel id index 12)
17869
+ // WrongProxy / WrongTrace (appended at the end so existing field
17870
+ // indices 0–58 — including the panel-position block at 46–58 — are
17871
+ // not shifted by the addition). The runtime probe in
17872
+ // `packages/cli/src/wiring/proxy-probe.ts` reacts to these via the
17873
+ // WS `prefs.update` pipeline; the picker is just the user surface.
17874
+ "WrongProxy / WrongTrace",
17875
+ // 59
17876
+ "WrongProxy URL"
17877
+ // 60
17849
17878
  ];
17850
17879
  function resolveSettingsFieldValue(field, input) {
17851
17880
  const raw = input.trim().toLowerCase();
@@ -17872,7 +17901,10 @@ function resolveSettingsFieldValue(field, input) {
17872
17901
  // [40, 'showAgentSwarmPanel'] — now an enum, see ENUM_FIELDS below
17873
17902
  [42, "readSymbols"],
17874
17903
  [43, "showSageMemoryInject"],
17875
- [45, "nextStepsTool"]
17904
+ [45, "nextStepsTool"],
17905
+ // WrongProxy / WrongTrace master switch (field 59). The companion
17906
+ // URL field (60) is text-typed, see resolveSettingsFieldValue.
17907
+ [59, "wrongProxyEnabled"]
17876
17908
  ]);
17877
17909
  const boolKey = BOOL_FIELDS.get(field);
17878
17910
  if (boolKey) {
@@ -18033,6 +18065,16 @@ function resolveSettingsFieldValue(field, input) {
18033
18065
  }
18034
18066
  return { ok: true, patch: { thinkingWord: word }, label, displayValue: word };
18035
18067
  }
18068
+ if (field === 60) {
18069
+ const url = input.trim();
18070
+ if (url.length === 0) {
18071
+ return { ok: false, error: `"${input}" is not a valid proxy URL. Use http://host:port or https://host:port.` };
18072
+ }
18073
+ if (!/^https?:\/\//i.test(url)) {
18074
+ return { ok: false, error: `"${input}" is not a valid proxy URL. Use http://host:port or https://host:port.` };
18075
+ }
18076
+ return { ok: true, patch: { wrongProxyUrl: url }, label, displayValue: url };
18077
+ }
18036
18078
  if (field >= PANEL_POSITION_FIELD_START && field - PANEL_POSITION_FIELD_START < PANEL_IDS.length) {
18037
18079
  const panelId = PANEL_IDS[field - PANEL_POSITION_FIELD_START];
18038
18080
  if (raw === "bottom" || raw === "sidebar") {
@@ -18074,7 +18116,10 @@ function getSettingsFieldValue(values, field) {
18074
18116
  // [40, 'showAgentSwarmPanel'] — now an enum, see ENUM_KEYS below
18075
18117
  [42, "readSymbols"],
18076
18118
  [43, "showSageMemoryInject"],
18077
- [45, "nextStepsTool"]
18119
+ [45, "nextStepsTool"],
18120
+ // WrongProxy / WrongTrace master switch (field 59). The companion
18121
+ // URL field (60) is text-typed, see getSettingsFieldValue.
18122
+ [59, "wrongProxyEnabled"]
18078
18123
  ];
18079
18124
  for (const [f, key] of BOOL_KEYS) {
18080
18125
  if (field !== f) continue;
@@ -18120,6 +18165,9 @@ function getSettingsFieldValue(values, field) {
18120
18165
  if (field === 22) {
18121
18166
  return { ok: true, label, displayValue: values.thinkingWord };
18122
18167
  }
18168
+ if (field === 60) {
18169
+ return { ok: true, label, displayValue: values.wrongProxyUrl };
18170
+ }
18123
18171
  if (field >= PANEL_POSITION_FIELD_START && field - PANEL_POSITION_FIELD_START < PANEL_IDS.length) {
18124
18172
  const panelId = PANEL_IDS[field - PANEL_POSITION_FIELD_START];
18125
18173
  return {
@@ -18178,6 +18226,16 @@ var SETTINGS_SECTIONS = [
18178
18226
  {
18179
18227
  name: "Panels",
18180
18228
  fields: [46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58]
18229
+ },
18230
+ {
18231
+ // WrongProxy / WrongTrace — fields 59 (master switch) + 60 (URL).
18232
+ // Sits in its own section because the runtime probe + base-URL
18233
+ // rewrite it controls live outside the picker's normal "feature
18234
+ // toggle" semantics; matching the WebUI's IntegrationsSection
18235
+ // keeps the cross-surface contract obvious. Appended at the end
18236
+ // to avoid shifting the panel-position block 46–58 indices.
18237
+ name: "Integrations",
18238
+ fields: [59, 60]
18181
18239
  }
18182
18240
  ];
18183
18241
  function formatAllSettingsSummary(values) {
@@ -18247,7 +18305,15 @@ var SETTINGS_DEFAULTS = Object.freeze({
18247
18305
  readSymbols: false,
18248
18306
  showSageMemoryInject: true,
18249
18307
  sageMemoryInjectThreshold: 0.85,
18250
- nextStepsTool: false
18308
+ nextStepsTool: false,
18309
+ // WrongProxy / WrongTrace. Defaults mirror the WebUI `LocalPrefs`
18310
+ // DEFAULTS in `packages/webui/src/stores/local-prefs.ts` (master
18311
+ // switch off, URL 'http://localhost:3444'). Required keys here
18312
+ // because `SettingsPickerValues` (mapped from `SettingsPickerPatch`)
18313
+ // makes every key mandatory — omitting them here breaks the type
18314
+ // contract for the entire TUI settings module. See Chimera review.
18315
+ wrongProxyEnabled: false,
18316
+ wrongProxyUrl: "http://localhost:3444"
18251
18317
  });
18252
18318
  function resetSettingsFieldValue(field) {
18253
18319
  const result = getSettingsFieldValue(SETTINGS_DEFAULTS, field);
@@ -18303,7 +18369,14 @@ function buildResetPatch(field) {
18303
18369
  [42, "readSymbols"],
18304
18370
  [43, "showSageMemoryInject"],
18305
18371
  [44, "sageMemoryInjectThreshold"],
18306
- [45, "nextStepsTool"]
18372
+ [45, "nextStepsTool"],
18373
+ // WrongProxy / WrongTrace: appended at the end so the existing
18374
+ // 46+13 = 59 entries are not shifted (the picker caps + scrolls;
18375
+ // field 59 is boolean, 60 is text). See `Settings.wrongProxy*`
18376
+ // and `ToolsConfig.wrongProxy?: WrongProxyToolConfig` for the
18377
+ // canonical persistence shape.
18378
+ [59, "wrongProxyEnabled"],
18379
+ [60, "wrongProxyUrl"]
18307
18380
  ];
18308
18381
  for (const [f, key] of KEY_MAP) {
18309
18382
  if (f === field) {
@@ -18432,6 +18505,8 @@ function SettingsPicker({
18432
18505
  thinkingWord,
18433
18506
  thinkingWordEditing,
18434
18507
  thinkingWordDraft,
18508
+ wrongProxyUrlEditing,
18509
+ wrongProxyUrlDraft,
18435
18510
  reasoningMode,
18436
18511
  reasoningEffort,
18437
18512
  reasoningPreserve,
@@ -18456,6 +18531,13 @@ function SettingsPicker({
18456
18531
  nextStepsTool,
18457
18532
  readSymbols,
18458
18533
  panelPositions,
18534
+ // WrongProxy / WrongTrace: pick these from the same slice as
18535
+ // `nextStepsTool` and `readSymbols`. The runtime probe (see
18536
+ // `runtime-controller-deps.ts:applyLiveSettings`) reads them off the
18537
+ // same object, so adding both at the destructuring level keeps the
18538
+ // mid-session effect coherent.
18539
+ wrongProxyEnabled,
18540
+ wrongProxyUrl,
18459
18541
  inputHeight,
18460
18542
  hint,
18461
18543
  onLayoutComputed
@@ -18725,7 +18807,25 @@ function SettingsPicker({
18725
18807
  label: SETTINGS_FIELD_LABELS[45 + i] ?? panelId,
18726
18808
  value: panelPositions[panelId],
18727
18809
  detail: panelPositions[panelId] === "sidebar" ? "Render in right sidebar" : "Render in lower F-key region"
18728
- }))
18810
+ })),
18811
+ // ── Integrations (fields 59–60) ─────────────────────────────────────
18812
+ // WrongProxy / WrongTrace. Appended at the end so the existing
18813
+ // field indices 0–58 (including the panel-position block 46–58)
18814
+ // remain stable — see the SettingsPicker "Appended to preserve"
18815
+ // comment above. The runtime probe (see
18816
+ // `runtime-controller-deps.ts:applyLiveSettings`) reads both keys
18817
+ // from the same SettingsPicker slice, so adding them at the
18818
+ // destructuring level keeps the mid-session effect coherent.
18819
+ {
18820
+ label: "WrongProxy / WrongTrace",
18821
+ value: boolVal(wrongProxyEnabled),
18822
+ detail: "Route base URLs through the local proxy (default http://localhost:3444). openai-codex is excluded."
18823
+ },
18824
+ {
18825
+ label: "WrongProxy URL",
18826
+ value: wrongProxyUrlEditing ? `${wrongProxyUrlDraft ?? ""}\u258F` : wrongProxyUrl,
18827
+ detail: wrongProxyUrlEditing ? "type a URL \xB7 Enter \u2713 \xB7 Esc \u2717 (http://host:port or https://host:port)" : "Where the local proxy daemon listens. Probed at <url>/api/health every 30s."
18828
+ }
18729
18829
  ];
18730
18830
  const fieldRowIndex = [];
18731
18831
  for (let i = 0; i < rows.length; i++) {
@@ -20069,6 +20169,10 @@ function AppViewPickers({
20069
20169
  nextStepsTool: state.settingsPicker.nextStepsTool,
20070
20170
  readSymbols: state.settingsPicker.readSymbols,
20071
20171
  panelPositions: state.settingsPicker.panelPositions,
20172
+ wrongProxyEnabled: state.settingsPicker.wrongProxyEnabled,
20173
+ wrongProxyUrl: state.settingsPicker.wrongProxyUrl,
20174
+ wrongProxyUrlEditing: state.settingsPicker.wrongProxyUrlEditing,
20175
+ wrongProxyUrlDraft: state.settingsPicker.wrongProxyUrlDraft,
20072
20176
  inputHeight,
20073
20177
  filter: state.settingsPicker.filter,
20074
20178
  hint: state.settingsPicker.hint
@@ -21147,7 +21251,39 @@ function SidebarContent({
21147
21251
  accent: theme.warn,
21148
21252
  innerWidth: bodyWidth
21149
21253
  }
21150
- )
21254
+ ),
21255
+ cs.cacheWrite5m !== void 0 && cs.cacheWrite1h !== void 0 ? /* @__PURE__ */ jsxs77(Fragment25, { children: [
21256
+ /* @__PURE__ */ jsx78(
21257
+ SidebarStatRow,
21258
+ {
21259
+ label: "write 5m",
21260
+ value: fmtTok(cs.cacheWrite5m),
21261
+ color: theme.textSecondary,
21262
+ accent: theme.warn,
21263
+ innerWidth: bodyWidth
21264
+ }
21265
+ ),
21266
+ /* @__PURE__ */ jsx78(
21267
+ SidebarStatRow,
21268
+ {
21269
+ label: "write 1h",
21270
+ value: fmtTok(cs.cacheWrite1h),
21271
+ color: theme.textSecondary,
21272
+ accent: theme.warn,
21273
+ innerWidth: bodyWidth
21274
+ }
21275
+ )
21276
+ ] }) : null,
21277
+ cs.savedUsd > 0 ? /* @__PURE__ */ jsx78(
21278
+ SidebarStatRow,
21279
+ {
21280
+ label: "saved",
21281
+ value: `~${cs.savedUsd.toFixed(2)}`,
21282
+ color: theme.success,
21283
+ accent: theme.success,
21284
+ innerWidth: bodyWidth
21285
+ }
21286
+ ) : null
21151
21287
  ] }) : null,
21152
21288
  cacheCoverageTokens > 0 && contextWindow ? /* @__PURE__ */ jsx78(
21153
21289
  SidebarStatRow,
@@ -21422,56 +21558,7 @@ function SidebarContent({
21422
21558
  })
21423
21559
  }
21424
21560
  ) : null
21425
- ] }) }) : null,
21426
- (() => {
21427
- const liveTools = [];
21428
- for (const entry of Object.values(entries)) {
21429
- if (entry.currentTool?.name) {
21430
- liveTools.push({
21431
- agent: entry.name || entry.id,
21432
- name: entry.currentTool.name,
21433
- status: "RUNNING",
21434
- color: theme.success
21435
- });
21436
- } else if (entry.recentTools && entry.recentTools.length > 0) {
21437
- const lastTool = entry.recentTools[entry.recentTools.length - 1];
21438
- if (lastTool?.name) {
21439
- liveTools.push({
21440
- agent: entry.name || entry.id,
21441
- name: lastTool.name,
21442
- status: lastTool.ok === false ? "FAIL" : "OK",
21443
- color: lastTool.ok === false ? theme.error : theme.textMuted
21444
- });
21445
- }
21446
- }
21447
- }
21448
- if (liveTools.length === 0) return null;
21449
- return /* @__PURE__ */ jsx78(Card, { innerWidth, marginBottom: 0, accent: theme.accent, children: (bodyWidth) => /* @__PURE__ */ jsxs77(Fragment25, { children: [
21450
- /* @__PURE__ */ jsx78(
21451
- SidebarSectionHeader,
21452
- {
21453
- glyph: glyphs.tools,
21454
- label: "TOOL TICKER",
21455
- color: theme.accent,
21456
- badge: liveTools.some((t) => t.status === "RUNNING") ? "STREAM" : "IDLE",
21457
- badgeColor: liveTools.some((t) => t.status === "RUNNING") ? theme.success : theme.textMuted,
21458
- innerWidth: bodyWidth,
21459
- pill: true
21460
- }
21461
- ),
21462
- liveTools.slice(0, 3).map((item, i) => /* @__PURE__ */ jsx78(
21463
- SidebarStatRow,
21464
- {
21465
- label: `${glyphs.zap} ${item.name}`,
21466
- value: item.status,
21467
- color: item.color,
21468
- accent: item.color,
21469
- innerWidth: bodyWidth
21470
- },
21471
- `ticker-${i}`
21472
- ))
21473
- ] }) });
21474
- })()
21561
+ ] }) }) : null
21475
21562
  ] });
21476
21563
  }
21477
21564
 
@@ -22411,6 +22498,18 @@ function CoordinatorPanelSidebar({
22411
22498
  }
22412
22499
  );
22413
22500
  }
22501
+ function wrongProxyVisual(status) {
22502
+ switch (status) {
22503
+ case "ok":
22504
+ return { glyph: glyphs.success, color: theme.success, pill: "LIVE" };
22505
+ case "warn":
22506
+ return { glyph: glyphs.warning, color: theme.warn, pill: "WARN" };
22507
+ case "down":
22508
+ return { glyph: glyphs.failure, color: theme.error, pill: "DOWN" };
22509
+ default:
22510
+ return { glyph: "?", color: theme.textMuted, pill: "?" };
22511
+ }
22512
+ }
22414
22513
  function ConnectionsPanelSidebar({
22415
22514
  connections,
22416
22515
  width
@@ -22508,6 +22607,81 @@ function ConnectionsPanelSidebar({
22508
22607
  }
22509
22608
  );
22510
22609
  }
22610
+ function WrongProxyPanelSidebar({
22611
+ proxy,
22612
+ width
22613
+ }) {
22614
+ const inner = Math.max(8, width);
22615
+ const bodyWidth = inner >= 18 ? inner - 4 : inner;
22616
+ const visual = proxy ? wrongProxyVisual(proxy.status) : wrongProxyVisual("unknown");
22617
+ const showLatency = inner >= METRIC_MIN_BODY_WIDTH;
22618
+ const latencyLabel = proxy?.latencyMs !== void 0 ? `${proxy.latencyMs}ms` : "";
22619
+ const urlLabel = proxy?.url ?? "\u2014";
22620
+ const ipcLabel = `${glyphs.dividerDot} ipc`;
22621
+ const ipcValue = proxy?.socketPath ? trunc(proxy.socketPath, Math.max(4, bodyWidth - displayWidth(ipcLabel) - 3)) : null;
22622
+ const daemonLabel = `${glyphs.dividerDot} daemon`;
22623
+ const daemonValue = proxy?.version ? trunc(proxy.version, Math.max(4, bodyWidth - displayWidth(daemonLabel) - 3)) : null;
22624
+ return /* @__PURE__ */ jsxs80(
22625
+ SidebarPanelFrame,
22626
+ {
22627
+ accent: visual.color,
22628
+ icon: glyphs.link,
22629
+ title: "WRONGPROXY",
22630
+ width,
22631
+ kicker: "proxy daemon",
22632
+ pillLabel: inner >= PILL_MIN_INNER_WIDTH ? `${visual.glyph} ${visual.pill}` : void 0,
22633
+ pillColor: visual.color,
22634
+ right: inner < PILL_MIN_INNER_WIDTH ? /* @__PURE__ */ jsxs80(Text, { color: visual.color, bold: true, children: [
22635
+ visual.glyph,
22636
+ " ",
22637
+ visual.pill
22638
+ ] }) : void 0,
22639
+ children: [
22640
+ /* @__PURE__ */ jsxs80(Box, { flexDirection: "row", width: bodyWidth, children: [
22641
+ /* @__PURE__ */ jsx81(Text, { color: visual.color, children: visual.glyph }),
22642
+ /* @__PURE__ */ jsxs80(Text, { color: theme.textPrimary, bold: true, wrap: "truncate", children: [
22643
+ " ",
22644
+ trunc(
22645
+ urlLabel,
22646
+ Math.max(
22647
+ 3,
22648
+ bodyWidth - (showLatency && latencyLabel ? displayWidth(latencyLabel) + 1 : 2)
22649
+ )
22650
+ )
22651
+ ] }),
22652
+ showLatency && latencyLabel ? /* @__PURE__ */ jsxs80(Fragment27, { children: [
22653
+ /* @__PURE__ */ jsx81(Box, { flexGrow: 1 }),
22654
+ /* @__PURE__ */ jsx81(Text, { color: theme.textMuted, children: latencyLabel })
22655
+ ] }) : null
22656
+ ] }),
22657
+ proxy?.detail ? /* @__PURE__ */ jsxs80(Text, { color: proxy.status === "down" ? theme.error : theme.warn, wrap: "truncate", children: [
22658
+ glyphs.warning,
22659
+ " ",
22660
+ trunc(proxy.detail, bodyWidth - 2)
22661
+ ] }) : null,
22662
+ ipcValue ? /* @__PURE__ */ jsx81(
22663
+ SidebarStatRow,
22664
+ {
22665
+ label: ipcLabel,
22666
+ value: ipcValue,
22667
+ color: theme.textMuted,
22668
+ innerWidth: bodyWidth,
22669
+ valueMuted: true
22670
+ }
22671
+ ) : null,
22672
+ daemonValue ? /* @__PURE__ */ jsx81(
22673
+ SidebarStatRow,
22674
+ {
22675
+ label: daemonLabel,
22676
+ value: daemonValue,
22677
+ color: theme.textPrimary,
22678
+ innerWidth: bodyWidth
22679
+ }
22680
+ ) : null
22681
+ ]
22682
+ }
22683
+ );
22684
+ }
22511
22685
 
22512
22686
  // src/app-view-sidebar.tsx
22513
22687
  import { jsx as jsx82, jsxs as jsxs81 } from "react/jsx-runtime";
@@ -22521,7 +22695,9 @@ function AppViewSidebar({
22521
22695
  sidebarProcessData,
22522
22696
  sidebarConnectionsData,
22523
22697
  sidebarKanbanData,
22524
- sidebarPlanData
22698
+ sidebarPlanData,
22699
+ sidebarWrongProxyEnabled,
22700
+ sidebarWrongProxyData
22525
22701
  }) {
22526
22702
  const { agent } = host;
22527
22703
  const { state, activity, environment, statusbar, liveTodos, liveSettings } = runtime;
@@ -22635,6 +22811,7 @@ function AppViewSidebar({
22635
22811
  }
22636
22812
  ) : null,
22637
22813
  sidebarSlotVisible("connections") ? /* @__PURE__ */ jsx82(ConnectionsPanelSidebar, { connections: sidebarConnectionsData, width: sidebarContentWidth }) : null,
22814
+ sidebarWrongProxyEnabled ? /* @__PURE__ */ jsx82(WrongProxyPanelSidebar, { proxy: sidebarWrongProxyData, width: sidebarContentWidth }) : null,
22638
22815
  hiddenSidebarPanelCount > 0 ? /* @__PURE__ */ jsxs81(Text, { color: theme.textMuted, children: [
22639
22816
  "+",
22640
22817
  hiddenSidebarPanelCount,
@@ -22985,6 +23162,13 @@ function renderWorking(word, style, spinner, phase, colorPhase, maxWidth) {
22985
23162
  width: displayWidth(text2)
22986
23163
  };
22987
23164
  }
23165
+ if (style === "static") {
23166
+ const text2 = truncateDisplay(word, maxWidth);
23167
+ return {
23168
+ node: /* @__PURE__ */ jsx83(Text, { bold: true, color: "green", children: text2 }),
23169
+ width: displayWidth(text2)
23170
+ };
23171
+ }
22988
23172
  if (style === "breathe") {
22989
23173
  const text2 = truncateDisplay(`${spinner} ${word}`, maxWidth);
22990
23174
  return {
@@ -23057,6 +23241,7 @@ function ComposerTopRail({
23057
23241
  }) {
23058
23242
  const available = Math.max(0, width - 2);
23059
23243
  const icon = glyphs.brand;
23244
+ const staticMode = animationStyle === "static";
23060
23245
  const requestedStatusWidth = composerStatusReservedWidth(status);
23061
23246
  const statusWidth = Math.min(requestedStatusWidth, Math.max(0, available - 3));
23062
23247
  const statusSectionW = statusWidth > 0 ? statusWidth + 3 : 0;
@@ -23071,7 +23256,7 @@ function ComposerTopRail({
23071
23256
  "\u256D",
23072
23257
  title ? /* @__PURE__ */ jsxs83(Fragment29, { children: [
23073
23258
  /* @__PURE__ */ jsx84(Text, { children: "\u2500 " }),
23074
- /* @__PURE__ */ jsx84(ComposerActivityIcon, { status, idleGlyph: glyphs.brand, disabled }),
23259
+ staticMode ? /* @__PURE__ */ jsx84(Text, { bold: true, children: glyphs.brand }) : /* @__PURE__ */ jsx84(ComposerActivityIcon, { status, idleGlyph: glyphs.brand, disabled }),
23075
23260
  /* @__PURE__ */ jsx84(Text, { children: ` ${title}` }),
23076
23261
  workingTimeChip ? /* @__PURE__ */ jsx84(Text, { dimColor: true, children: workingTimeChip }) : null,
23077
23262
  /* @__PURE__ */ jsx84(Text, { children: " " })
@@ -24457,7 +24642,7 @@ import { memo as memo6, useRef as useRef8 } from "react";
24457
24642
 
24458
24643
  // src/components/history/entry.tsx
24459
24644
  import { hasOpenTodos } from "@wrongstack/core/utils";
24460
- import { parseNextSteps } from "@wrongstack/tools/next-steps";
24645
+ import { parseNextSteps as parseNextSteps2 } from "@wrongstack/tools/next-steps";
24461
24646
  import React8, { useEffect as useEffect23, useMemo as useMemo8 } from "react";
24462
24647
 
24463
24648
  // src/history-entry.ts
@@ -25773,6 +25958,63 @@ function ToolEntry({
25773
25958
  ] });
25774
25959
  }
25775
25960
 
25961
+ // src/components/history/wrap-geometry.ts
25962
+ import { parseNextSteps } from "@wrongstack/tools/next-steps";
25963
+ import stringWidth2 from "string-width";
25964
+ import wrapAnsi from "wrap-ansi";
25965
+ var USER_LABEL = "\u{1F464} USER ";
25966
+ var INFO_PREFIX = "\u2139 ";
25967
+ function hasWrapMap(entry) {
25968
+ return entry.kind === "assistant" || entry.kind === "thinking" || entry.kind === "user" || entry.kind === "info";
25969
+ }
25970
+ function textContentWidth(termWidth) {
25971
+ return Math.max(1, termWidth - MESSAGE_PANEL_CHROME_WIDTH);
25972
+ }
25973
+ function infoContentWidth(termWidth) {
25974
+ return Math.max(1, termWidth);
25975
+ }
25976
+ function buildBodyRowMap(kind, rawText, termWidth) {
25977
+ const prefix = kind === "user" ? USER_LABEL : kind === "info" ? INFO_PREFIX : "";
25978
+ const prefixChars = prefix.length;
25979
+ const prefixWidth = stringWidth2(prefix);
25980
+ const base = sanitizeTerminalText(
25981
+ kind === "assistant" ? parseNextSteps(rawText, true).stripped : rawText
25982
+ );
25983
+ const width = kind === "info" ? infoContentWidth(termWidth) : textContentWidth(termWidth);
25984
+ const rows = [];
25985
+ const lines = base.split("\n");
25986
+ for (let i = 0; i < lines.length; i++) {
25987
+ const line = lines[i] ?? "";
25988
+ if (line.length === 0) {
25989
+ rows.push({ line: i, start: 0, end: 0 });
25990
+ continue;
25991
+ }
25992
+ const sub = i === 0 ? prefixChars : 0;
25993
+ const wrapped = i === 0 && prefixChars > 0 ? prefix + line : line;
25994
+ const segments = wrapAnsi(wrapped, width, { trim: false, hard: true }).split("\n");
25995
+ let offset = 0;
25996
+ for (const seg of segments) {
25997
+ rows.push({
25998
+ line: i,
25999
+ start: Math.max(0, offset - sub),
26000
+ end: Math.max(0, offset + seg.length - sub)
26001
+ });
26002
+ offset += seg.length;
26003
+ }
26004
+ }
26005
+ return { rows, text: base, prefixWidth };
26006
+ }
26007
+ function resolveRowCol(map, row, col, atEnd) {
26008
+ const last = map.rows.length - 1;
26009
+ if (last < 0) return { line: 0, offset: 0 };
26010
+ const idx = Math.max(0, Math.min(row, last));
26011
+ const span = map.rows[idx] ?? { line: 0, start: 0, end: 0 };
26012
+ const textCol = idx === 0 && map.prefixWidth > 0 ? Math.max(0, col - map.prefixWidth) : col;
26013
+ const segLen = span.end - span.start;
26014
+ const within = atEnd ? Math.max(0, Math.min(textCol + 1, segLen)) : Math.max(0, Math.min(textCol, segLen));
26015
+ return { line: span.line, offset: span.start + within };
26016
+ }
26017
+
25776
26018
  // src/components/history/entry.tsx
25777
26019
  import { Fragment as Fragment33, jsx as jsx94, jsxs as jsxs91 } from "react/jsx-runtime";
25778
26020
  var Entry = React8.memo(function Entry2({
@@ -25790,7 +26032,7 @@ var Entry = React8.memo(function Entry2({
25790
26032
  const isFinalTurnEntry = entry.kind === "assistant" && entry.final === true;
25791
26033
  const nextSteps = useMemo8(() => {
25792
26034
  if (entry.kind !== "assistant") return { steps: [], stripped: "" };
25793
- return parseNextSteps(entry.text, true);
26035
+ return parseNextSteps2(entry.text, true);
25794
26036
  }, [entry.kind, entry.text]);
25795
26037
  useEffect23(() => {
25796
26038
  if (!setSuggestions) return;
@@ -25798,7 +26040,7 @@ var Entry = React8.memo(function Entry2({
25798
26040
  if (openTodos) return;
25799
26041
  if (!isFinalTurnEntry) return;
25800
26042
  const text = entry.text ?? "";
25801
- const { texts } = parseNextSteps(text, true);
26043
+ const { texts } = parseNextSteps2(text, true);
25802
26044
  if (texts.length > 0) setSuggestions(texts);
25803
26045
  }, [
25804
26046
  entry.kind,
@@ -25820,7 +26062,7 @@ var Entry = React8.memo(function Entry2({
25820
26062
  borderColor: theme.user,
25821
26063
  paddingLeft: 1,
25822
26064
  children: /* @__PURE__ */ jsxs91(Text, { children: [
25823
- /* @__PURE__ */ jsx94(Text, { bold: true, color: theme.user, children: "\u{1F464} USER " }),
26065
+ /* @__PURE__ */ jsx94(Text, { bold: true, color: theme.user, children: USER_LABEL }),
25824
26066
  /* @__PURE__ */ jsx94(Text, { color: "white", children: sanitizeTerminalText(entry.text) }),
25825
26067
  entry.queued ? /* @__PURE__ */ jsx94(Text, { dimColor: true, children: " (queued)" }) : null,
25826
26068
  entry.pasteContent ? /* @__PURE__ */ jsxs91(Fragment33, { children: [
@@ -25967,7 +26209,7 @@ var Entry = React8.memo(function Entry2({
25967
26209
  }
25968
26210
  case "info": {
25969
26211
  return /* @__PURE__ */ jsxs91(Text, { dimColor: true, children: [
25970
- /* @__PURE__ */ jsx94(Text, { children: "\u2139 " }),
26212
+ /* @__PURE__ */ jsx94(Text, { children: INFO_PREFIX }),
25971
26213
  sanitizeTerminalText(entry.text)
25972
26214
  ] });
25973
26215
  }
@@ -26216,7 +26458,7 @@ var History = memo6(function History2({
26216
26458
  });
26217
26459
 
26218
26460
  // src/components/history/use-history-controller.ts
26219
- import { useMemo as useMemo9, useRef as useRef9, useCallback as useCallback5, useEffect as useEffect24 } from "react";
26461
+ import { useCallback as useCallback5, useEffect as useEffect24, useMemo as useMemo9, useRef as useRef9 } from "react";
26220
26462
 
26221
26463
  // src/clipboard.ts
26222
26464
  import {
@@ -26282,6 +26524,38 @@ function selectionHitAt(row, spans) {
26282
26524
  return null;
26283
26525
  }
26284
26526
 
26527
+ // src/components/history/selection-band-store.ts
26528
+ function sameBand(a, b) {
26529
+ if (a === b) return true;
26530
+ if (a === null || b === null) return false;
26531
+ return a.topRow === b.topRow && a.bottomRow === b.bottomRow && a.headRow === b.headRow;
26532
+ }
26533
+ function bandFromSelection(anchor, head) {
26534
+ return {
26535
+ topRow: Math.min(anchor.row, head.row),
26536
+ bottomRow: Math.max(anchor.row, head.row),
26537
+ headRow: head.row
26538
+ };
26539
+ }
26540
+ function createSelectionBandStore() {
26541
+ let snapshot = null;
26542
+ const listeners = /* @__PURE__ */ new Set();
26543
+ return {
26544
+ subscribe(listener) {
26545
+ listeners.add(listener);
26546
+ return () => {
26547
+ listeners.delete(listener);
26548
+ };
26549
+ },
26550
+ getSnapshot: () => snapshot,
26551
+ publish(next) {
26552
+ if (sameBand(snapshot, next)) return;
26553
+ snapshot = next;
26554
+ for (const listener of listeners) listener();
26555
+ }
26556
+ };
26557
+ }
26558
+
26285
26559
  // src/components/history/selection-helpers.ts
26286
26560
  function normalizeSelection(a, b, inProgress) {
26287
26561
  const topLeft = { row: Math.min(a.row, b.row), col: Math.min(a.col, b.col) };
@@ -26314,7 +26588,7 @@ function selectionToSlices(opts) {
26314
26588
  return result;
26315
26589
  }
26316
26590
  function assembleSelectionText(opts) {
26317
- const { slices, entriesById, toolGroupsByHeadId } = opts;
26591
+ const { slices, entriesById, toolGroupsByHeadId, termWidth } = opts;
26318
26592
  if (slices.length === 0) return "";
26319
26593
  const byEntry = /* @__PURE__ */ new Map();
26320
26594
  for (const slice of slices) {
@@ -26337,6 +26611,36 @@ function assembleSelectionText(opts) {
26337
26611
  segments.push(fullText);
26338
26612
  continue;
26339
26613
  }
26614
+ const single = entries[0];
26615
+ const map = termWidth !== void 0 && hasWrapMap(single) && !(single.kind === "user" && (single.pasteContent || single.queued)) ? buildBodyRowMap(single.kind, single.text, termWidth) : null;
26616
+ if (map !== null && map.text.length > 0) {
26617
+ const lines = map.text.split("\n");
26618
+ for (const slice of entrySlices) {
26619
+ const a = resolveRowCol(map, slice.startRow, slice.startCol, false);
26620
+ const b = resolveRowCol(map, slice.endRow, slice.endCol, true);
26621
+ if (b.line < a.line) continue;
26622
+ const collected = [];
26623
+ for (let ln = a.line; ln <= b.line && ln < lines.length; ln++) {
26624
+ const line = lines[ln] ?? "";
26625
+ if (ln === a.line && ln === b.line) {
26626
+ const start = Math.max(0, Math.min(line.length, a.offset));
26627
+ const end = Math.max(start, Math.min(line.length, b.offset));
26628
+ if (end > start) collected.push(line.slice(start, end));
26629
+ } else if (ln === a.line) {
26630
+ const start = Math.max(0, Math.min(line.length, a.offset));
26631
+ if (start < line.length) collected.push(line.slice(start));
26632
+ } else if (ln === b.line) {
26633
+ const end = Math.max(0, Math.min(line.length, b.offset));
26634
+ if (end > 0) collected.push(line.slice(0, end));
26635
+ } else {
26636
+ collected.push(line);
26637
+ }
26638
+ }
26639
+ const seg = collected.join("\n");
26640
+ if (seg.length > 0) segments.push(seg);
26641
+ }
26642
+ continue;
26643
+ }
26340
26644
  for (const slice of entrySlices) {
26341
26645
  const rows = fullText.split("\n");
26342
26646
  const collected = [];
@@ -26379,6 +26683,7 @@ function useHistoryController(opts) {
26379
26683
  selectionRef,
26380
26684
  entriesByIdRef,
26381
26685
  toolStreamRef,
26686
+ selectionBandStore,
26382
26687
  setAnchor,
26383
26688
  setMountBump,
26384
26689
  controllerRef
@@ -26389,15 +26694,36 @@ function useHistoryController(opts) {
26389
26694
  effectiveAnchorRef.current = effectiveAnchor;
26390
26695
  const groupIdsRef = useRef9(groupIds);
26391
26696
  groupIdsRef.current = groupIds;
26392
- const applyAnchor = useCallback5((next) => {
26393
- const ids = groupIdsRef.current;
26394
- const nextId = next ? ids[next.index] : void 0;
26395
- const normalized = next && nextId !== void 0 ? { index: next.index, clip: Math.max(0, next.clip) } : null;
26396
- selectionRef.current = { anchor: null, head: null, inProgress: false };
26397
- effectiveAnchorRef.current = normalized;
26398
- setAnchor(normalized && nextId !== void 0 ? { id: nextId, clip: normalized.clip } : null);
26399
- setMountBump(0);
26400
- }, [selectionRef, setAnchor, setMountBump]);
26697
+ const publishBand = useCallback5(() => {
26698
+ const cur = selectionRef.current;
26699
+ selectionBandStore?.publish(
26700
+ cur.anchor !== null && cur.head !== null && cur.inProgress ? bandFromSelection(cur.anchor, cur.head) : null
26701
+ );
26702
+ }, [selectionBandStore, selectionRef]);
26703
+ const gutterWidthForEntry = (entryId) => {
26704
+ if (entryId === void 0) return 0;
26705
+ const entry = entriesByIdRef.current.get(entryId);
26706
+ if (!entry) return 0;
26707
+ return entry.kind === "assistant" || entry.kind === "thinking" || entry.kind === "user" ? MESSAGE_PANEL_CHROME_WIDTH : 0;
26708
+ };
26709
+ const toBodyLocalCol = (row, col) => {
26710
+ const hit = selectionHitAt(row, mountedGroupSpansRef.current);
26711
+ const gutter = gutterWidthForEntry(hit?.entryId);
26712
+ return gutter === 0 ? col : Math.max(0, col - gutter);
26713
+ };
26714
+ const applyAnchor = useCallback5(
26715
+ (next) => {
26716
+ const ids = groupIdsRef.current;
26717
+ const nextId = next ? ids[next.index] : void 0;
26718
+ const normalized = next && nextId !== void 0 ? { index: next.index, clip: Math.max(0, next.clip) } : null;
26719
+ selectionRef.current = { anchor: null, head: null, inProgress: false };
26720
+ publishBand();
26721
+ effectiveAnchorRef.current = normalized;
26722
+ setAnchor(normalized && nextId !== void 0 ? { id: nextId, clip: normalized.clip } : null);
26723
+ setMountBump(0);
26724
+ },
26725
+ [selectionRef, publishBand, setAnchor, setMountBump]
26726
+ );
26401
26727
  const controller = useMemo9(
26402
26728
  () => ({
26403
26729
  scrollBy: (deltaUp) => {
@@ -26443,18 +26769,22 @@ function useHistoryController(opts) {
26443
26769
  beginSelection: (row, col) => {
26444
26770
  if (isOutOfBand(col, termWidth)) {
26445
26771
  selectionRef.current = { anchor: null, head: null, inProgress: false };
26772
+ publishBand();
26446
26773
  return;
26447
26774
  }
26448
26775
  const cardHit = selectionHitAt(row, mountedGroupSpansRef.current);
26449
26776
  if (cardHit === null) {
26450
26777
  selectionRef.current = { anchor: null, head: null, inProgress: false };
26778
+ publishBand();
26451
26779
  return;
26452
26780
  }
26781
+ const bodyCol = toBodyLocalCol(row, col);
26453
26782
  selectionRef.current = {
26454
- anchor: { row, col },
26455
- head: { row, col },
26783
+ anchor: { row, col: bodyCol },
26784
+ head: { row, col: bodyCol },
26456
26785
  inProgress: true
26457
26786
  };
26787
+ publishBand();
26458
26788
  },
26459
26789
  extendSelection: (row, col) => {
26460
26790
  const cur = selectionRef.current;
@@ -26462,25 +26792,31 @@ function useHistoryController(opts) {
26462
26792
  if (cur.inProgress === false) return;
26463
26793
  if (isOutOfBand(col, termWidth) || row < 0 || row >= vp) {
26464
26794
  selectionRef.current = { anchor: null, head: null, inProgress: false };
26795
+ publishBand();
26465
26796
  return;
26466
26797
  }
26467
- selectionRef.current = { ...cur, head: { row, col } };
26798
+ selectionRef.current = { ...cur, head: { row, col: toBodyLocalCol(row, col) } };
26799
+ publishBand();
26468
26800
  },
26469
26801
  endSelection: () => {
26470
26802
  const cur = selectionRef.current;
26471
26803
  if (cur.anchor === null) return;
26472
26804
  selectionRef.current = { ...cur, inProgress: false };
26805
+ publishBand();
26473
26806
  },
26807
+ hasSelection: () => selectionRef.current.anchor !== null,
26474
26808
  clearSelection: () => {
26475
26809
  const cur = selectionRef.current;
26476
26810
  if (cur.anchor === null && cur.head === null) return;
26477
26811
  selectionRef.current = { anchor: null, head: null, inProgress: false };
26812
+ publishBand();
26478
26813
  },
26479
26814
  commitSelection: async () => {
26480
26815
  const cur = selectionRef.current;
26481
26816
  if (cur.anchor === null || cur.head === null) return false;
26482
26817
  if (cur.anchor.row === cur.head.row && cur.anchor.col === cur.head.col) {
26483
26818
  selectionRef.current = { anchor: null, head: null, inProgress: false };
26819
+ publishBand();
26484
26820
  return false;
26485
26821
  }
26486
26822
  const rect = normalizeSelection(cur.anchor, cur.head, cur.inProgress);
@@ -26498,9 +26834,13 @@ function useHistoryController(opts) {
26498
26834
  const text = assembleSelectionText({
26499
26835
  slices,
26500
26836
  entriesById: entriesByIdRef.current,
26837
+ // Wrap-aware row→line translation (v1.1 M4): termWidth is the same
26838
+ // render width the card geometry was built with.
26839
+ termWidth,
26501
26840
  ...toolGroupsByHeadId.size > 0 ? { toolGroupsByHeadId } : {}
26502
26841
  });
26503
26842
  selectionRef.current = { anchor: null, head: null, inProgress: false };
26843
+ publishBand();
26504
26844
  if (text.length === 0) return false;
26505
26845
  try {
26506
26846
  return await writeClipboardText(text);
@@ -26509,7 +26849,18 @@ function useHistoryController(opts) {
26509
26849
  }
26510
26850
  }
26511
26851
  }),
26512
- [applyAnchor, termWidth, vp, copyHitsRef, liveToolCopyHitRef, mountedGroupSpansRef, selectionRef, entriesByIdRef, toolStreamRef]
26852
+ [
26853
+ applyAnchor,
26854
+ publishBand,
26855
+ termWidth,
26856
+ vp,
26857
+ copyHitsRef,
26858
+ liveToolCopyHitRef,
26859
+ mountedGroupSpansRef,
26860
+ selectionRef,
26861
+ entriesByIdRef,
26862
+ toolStreamRef
26863
+ ]
26513
26864
  );
26514
26865
  useEffect24(() => {
26515
26866
  if (!controllerRef) return void 0;
@@ -26528,14 +26879,18 @@ function useHistoryController(opts) {
26528
26879
  }
26529
26880
 
26530
26881
  // src/components/history/scrollbar-rail.tsx
26882
+ import { useSyncExternalStore as useSyncExternalStore2 } from "react";
26531
26883
  import { jsx as jsx96, jsxs as jsxs93 } from "react/jsx-runtime";
26884
+ var NO_BAND = createSelectionBandStore();
26532
26885
  function Scrollbar({
26533
26886
  rows,
26534
26887
  offset,
26535
26888
  total,
26536
26889
  copyHits,
26537
- copiedEntryId
26890
+ copiedEntryId,
26891
+ selectionBandStore = NO_BAND
26538
26892
  }) {
26893
+ const band = useSyncExternalStore2(selectionBandStore.subscribe, selectionBandStore.getSnapshot);
26539
26894
  const { top: thumbTop, size: thumbSize, scrollable } = scrollbarThumb(rows, offset, total);
26540
26895
  const cells = [];
26541
26896
  for (let i = 0; i < rows; i++) {
@@ -26545,6 +26900,8 @@ function Scrollbar({
26545
26900
  for (const hit of copyHits) copyByRow.set(hit.startRow, hit);
26546
26901
  return /* @__PURE__ */ jsx96(Box, { flexDirection: "column", flexShrink: 0, children: cells.map((cell, row) => {
26547
26902
  const copyHit = copyByRow.get(row);
26903
+ const inBand = band !== null && row >= band.topRow && row <= band.bottomRow && row < rows;
26904
+ const isHead = band !== null && row === band.headRow && row < rows;
26548
26905
  return /* @__PURE__ */ jsxs93(Box, { flexDirection: "row", children: [
26549
26906
  /* @__PURE__ */ jsx96(
26550
26907
  Text,
@@ -26553,7 +26910,7 @@ function Scrollbar({
26553
26910
  children: copyHit ? COPY_ICON : " "
26554
26911
  }
26555
26912
  ),
26556
- /* @__PURE__ */ jsx96(Text, { children: " " }),
26913
+ /* @__PURE__ */ jsx96(Text, { ...inBand ? { color: theme.accent } : {}, children: isHead ? "\u2588" : inBand ? "\u258C" : " " }),
26557
26914
  /* @__PURE__ */ jsx96(
26558
26915
  Text,
26559
26916
  {
@@ -26615,6 +26972,11 @@ var ScrollableHistory = memo7(function ScrollableHistory2({
26615
26972
  const heightCacheRef = useRef10(null);
26616
26973
  if (heightCacheRef.current === null) heightCacheRef.current = new EntryHeightCache();
26617
26974
  const heightCache = heightCacheRef.current;
26975
+ const selectionBandStoreRef = useRef10(null);
26976
+ if (selectionBandStoreRef.current === null) {
26977
+ selectionBandStoreRef.current = createSelectionBandStore();
26978
+ }
26979
+ const selectionBandStore = selectionBandStoreRef.current;
26618
26980
  const entryNodeRefs = useRef10(/* @__PURE__ */ new Map());
26619
26981
  const measuredGroupIdsRef = useRef10(/* @__PURE__ */ new Set());
26620
26982
  const copyHitsRef = useRef10([]);
@@ -26739,6 +27101,7 @@ var ScrollableHistory = memo7(function ScrollableHistory2({
26739
27101
  selectionRef,
26740
27102
  entriesByIdRef,
26741
27103
  toolStreamRef,
27104
+ selectionBandStore,
26742
27105
  setAnchor,
26743
27106
  setMountBump,
26744
27107
  controllerRef
@@ -26947,7 +27310,8 @@ var ScrollableHistory = memo7(function ScrollableHistory2({
26947
27310
  offset: offsetFromBottom,
26948
27311
  total: totalRows,
26949
27312
  copyHits: copyRailHits,
26950
- copiedEntryId
27313
+ copiedEntryId,
27314
+ selectionBandStore
26951
27315
  }
26952
27316
  )
26953
27317
  ] });
@@ -27006,6 +27370,75 @@ function useSidebarConnections(projectRoot, enabled = true) {
27006
27370
  }, [projectRoot, enabled]);
27007
27371
  return connections;
27008
27372
  }
27373
+ function useSidebarWrongProxy(url, enabled = true) {
27374
+ const [data, setData] = useState24(null);
27375
+ useEffect26(() => {
27376
+ if (!enabled || !url) {
27377
+ setData(null);
27378
+ return;
27379
+ }
27380
+ let cancelled = false;
27381
+ const probe = async () => {
27382
+ const trimmed = url.trim().replace(/\/+$/, "");
27383
+ const healthUrl = `${trimmed}/api/health`;
27384
+ const startedAt = Date.now();
27385
+ try {
27386
+ const res = await fetch(healthUrl, {
27387
+ method: "GET",
27388
+ // 2s budget — matches the runtime probe in proxy-probe.ts so a
27389
+ // hung localhost:3444 cannot stall the render loop. The TUI
27390
+ // never sees AbortError unless the caller tears us down.
27391
+ signal: AbortSignal.timeout(2e3),
27392
+ headers: { accept: "application/json" }
27393
+ });
27394
+ if (cancelled) return;
27395
+ const ok = res.ok && res.status >= 200 && res.status < 300;
27396
+ let socketPath;
27397
+ let version;
27398
+ if (ok) {
27399
+ try {
27400
+ const body = await res.json();
27401
+ if (body != null && typeof body === "object") {
27402
+ const { socket_path, version: reported } = body;
27403
+ if (typeof socket_path === "string" && socket_path.length > 0) {
27404
+ socketPath = socket_path;
27405
+ }
27406
+ if (typeof reported === "string" && reported.length > 0) {
27407
+ version = reported;
27408
+ }
27409
+ }
27410
+ } catch {
27411
+ }
27412
+ }
27413
+ if (cancelled) return;
27414
+ setData({
27415
+ url: trimmed,
27416
+ status: ok ? "ok" : "warn",
27417
+ latencyMs: Date.now() - startedAt,
27418
+ detail: ok ? void 0 : `HTTP ${res.status}`,
27419
+ socketPath,
27420
+ version
27421
+ });
27422
+ } catch (error) {
27423
+ if (cancelled) return;
27424
+ const message = error instanceof Error ? error.message : String(error);
27425
+ setData({
27426
+ url: trimmed,
27427
+ status: "down",
27428
+ latencyMs: Date.now() - startedAt,
27429
+ detail: message || "unreachable"
27430
+ });
27431
+ }
27432
+ };
27433
+ void probe();
27434
+ const id = setInterval(() => void probe(), 8e3);
27435
+ return () => {
27436
+ cancelled = true;
27437
+ clearInterval(id);
27438
+ };
27439
+ }, [url, enabled]);
27440
+ return data;
27441
+ }
27009
27442
  function useSidebarKanban(projectRoot, enabled = true) {
27010
27443
  const [data, setData] = useState24({ columns: [], totalActive: 0, activeCardTitles: [] });
27011
27444
  useEffect26(() => {
@@ -27057,11 +27490,7 @@ function useSidebarKanban(projectRoot, enabled = true) {
27057
27490
  import { jsx as jsx98, jsxs as jsxs95 } from "react/jsx-runtime";
27058
27491
  var INPUT_PROMPT = DEFAULT_INPUT_PROMPT;
27059
27492
  function AppView({ host, runtime }) {
27060
- const {
27061
- agent,
27062
- appVersion,
27063
- setSuggestions
27064
- } = host;
27493
+ const { agent, appVersion, setSuggestions } = host;
27065
27494
  const {
27066
27495
  state,
27067
27496
  activity,
@@ -27096,6 +27525,12 @@ function AppView({ host, runtime }) {
27096
27525
  sidebarSlotVisible("connections")
27097
27526
  );
27098
27527
  const sidebarKanbanData = useSidebarKanban(agent.ctx.projectRoot, sidebarSlotVisible("kanban"));
27528
+ const wrongProxyEnabled = state.settingsPicker.open ? state.settingsPicker.wrongProxyEnabled === true : liveSettings?.wrongProxyEnabled === true;
27529
+ const wrongProxyUrl = state.settingsPicker.open ? state.settingsPicker.wrongProxyUrl : liveSettings?.wrongProxyUrl;
27530
+ const sidebarWrongProxyData = useSidebarWrongProxy(
27531
+ wrongProxyUrl,
27532
+ wrongProxyEnabled && sidebarWidth > 0
27533
+ );
27099
27534
  const sidebarPlanData = usePlanPanelData(
27100
27535
  agent.ctx.projectRoot,
27101
27536
  agent.ctx.session?.id ?? null,
@@ -27185,7 +27620,9 @@ function AppView({ host, runtime }) {
27185
27620
  sidebarProcessData,
27186
27621
  sidebarConnectionsData,
27187
27622
  sidebarKanbanData,
27188
- sidebarPlanData
27623
+ sidebarPlanData,
27624
+ sidebarWrongProxyEnabled: wrongProxyEnabled,
27625
+ sidebarWrongProxyData
27189
27626
  }
27190
27627
  )
27191
27628
  ] })
@@ -27774,7 +28211,12 @@ function routeSettingsOverlayKey(host, input, key, isEnter) {
27774
28211
  showSageMemoryInject: config.showSageMemoryInject ?? false,
27775
28212
  sageMemoryInjectThreshold: config.sageMemoryInjectThreshold ?? 0.85,
27776
28213
  nextStepsTool: config.nextStepsTool ?? false,
27777
- readSymbols: config.readSymbols ?? false
28214
+ readSymbols: config.readSymbols ?? false,
28215
+ // WrongProxy / WrongTrace: hydrate from the persisted Config (CLI
28216
+ // adapter owns the read/write — see LiveSettingsInput + the
28217
+ // tui-settings-adapter.ts branch tree).
28218
+ wrongProxyEnabled: config.wrongProxyEnabled ?? false,
28219
+ wrongProxyUrl: config.wrongProxyUrl ?? "http://localhost:3444"
27778
28220
  });
27779
28221
  return true;
27780
28222
  }
@@ -28369,7 +28811,7 @@ function createAppKeyHandler(options) {
28369
28811
  return;
28370
28812
  }
28371
28813
  if (!overlayOpen) {
28372
- if (mouseMode && key.mouse?.kind === "press" && key.mouse.button === "right") {
28814
+ if (key.mouse?.kind === "press" && key.mouse.button === "right") {
28373
28815
  const region = hitRegion(
28374
28816
  { termRows, termCols: historyWidth, viewportRows: state.viewportRows },
28375
28817
  key.mouse.x,
@@ -28409,7 +28851,7 @@ function createAppKeyHandler(options) {
28409
28851
  return;
28410
28852
  }
28411
28853
  }
28412
- if ((key.mouse?.kind === "press" || mouseMode && key.mouse?.kind === "move") && key.mouse.button === "left") {
28854
+ if ((key.mouse?.kind === "press" || key.mouse?.kind === "move") && key.mouse.button === "left") {
28413
28855
  const region = hitRegion(
28414
28856
  { termRows, termCols: historyWidth, viewportRows: state.viewportRows },
28415
28857
  key.mouse.x,
@@ -28423,7 +28865,7 @@ function createAppKeyHandler(options) {
28423
28865
  }).catch(() => null);
28424
28866
  return;
28425
28867
  }
28426
- if (mouseMode && region?.kind === "history" && key.mouse.x <= historyWidth - SCROLLBAR_HIT_WIDTH) {
28868
+ if (region?.kind === "history" && key.mouse.x <= historyWidth - SCROLLBAR_HIT_WIDTH) {
28427
28869
  if (key.mouse.kind === "press") {
28428
28870
  historyScrollRef.current?.beginSelection(region.row, key.mouse.x - 1);
28429
28871
  return;
@@ -28439,8 +28881,15 @@ function createAppKeyHandler(options) {
28439
28881
  return;
28440
28882
  }
28441
28883
  }
28442
- if (mouseMode && key.mouse?.kind === "release" && key.mouse.button === "left") {
28443
- historyScrollRef.current?.endSelection();
28884
+ if (key.mouse?.kind === "release" && key.mouse.button === "left") {
28885
+ if (historyScrollRef.current?.hasSelection()) {
28886
+ detach(
28887
+ historyScrollRef.current?.commitSelection().then((copied) => {
28888
+ if (copied) onHistoryCopy?.(SELECTION_COPY_ID);
28889
+ }),
28890
+ "Copy"
28891
+ );
28892
+ }
28444
28893
  }
28445
28894
  if (mouseMode && key.mouse?.kind === "press" && key.mouse.button === "left" && statusBarWrapRef.current) {
28446
28895
  const sbHeight = measureElement(statusBarWrapRef.current).height;
@@ -29215,6 +29664,19 @@ import {
29215
29664
  } from "@wrongstack/core/execution";
29216
29665
 
29217
29666
  // src/submit-controller.ts
29667
+ async function waitForIdleSettle(isIdle, deadlineMs, opts = {}) {
29668
+ const maxPollMs = opts.maxPollMs ?? 150;
29669
+ const start = Date.now();
29670
+ let pollMs = 20;
29671
+ while (!isIdle()) {
29672
+ const remaining = deadlineMs - (Date.now() - start);
29673
+ if (remaining <= 0) return false;
29674
+ const wait = Math.min(Math.max(pollMs, 1), maxPollMs, remaining);
29675
+ await new Promise((resolve) => setTimeout(resolve, wait));
29676
+ pollMs = Math.min(pollMs * 2, maxPollMs);
29677
+ }
29678
+ return true;
29679
+ }
29218
29680
  var topicAdvisorByAgent = /* @__PURE__ */ new WeakMap();
29219
29681
  function topicAdvisorFor(agent) {
29220
29682
  let advisor = topicAdvisorByAgent.get(agent);
@@ -29468,10 +29930,10 @@ function createSubmitController(host) {
29468
29930
  if (b) {
29469
29931
  b.appendText(res.runText);
29470
29932
  const blocks2 = await b.submit();
29471
- const start = Date.now();
29472
- while (stateRef.current.status !== "idle" && Date.now() - start < 1500) {
29473
- await new Promise((r) => setTimeout(r, 25));
29474
- }
29933
+ await waitForIdleSettle(
29934
+ () => stateRef.current.status === "idle",
29935
+ 1500
29936
+ );
29475
29937
  try {
29476
29938
  await runBlocks(blocks2);
29477
29939
  } finally {
@@ -29708,10 +30170,10 @@ User message:
29708
30170
  if (b) {
29709
30171
  b.appendText(preamble);
29710
30172
  const steerBlocks = await b.submit();
29711
- const start = Date.now();
29712
- while (stateRef.current.status !== "idle" && Date.now() - start < 1500) {
29713
- await new Promise((r) => setTimeout(r, 25));
29714
- }
30173
+ await waitForIdleSettle(
30174
+ () => stateRef.current.status === "idle",
30175
+ 1500
30176
+ );
29715
30177
  try {
29716
30178
  stampJournalMarker();
29717
30179
  await runBlocks(steerBlocks);
@@ -30370,6 +30832,32 @@ function tryToolsSettingsPickerKeys(host, input, key, isEnter, debouncedEnter2)
30370
30832
  }
30371
30833
  return true;
30372
30834
  }
30835
+ if (sp.wrongProxyUrlEditing) {
30836
+ if (key.escape) {
30837
+ dispatch({ type: "settingsWrongProxyUrlEditCancel" });
30838
+ return true;
30839
+ }
30840
+ if (isEnter) {
30841
+ if (debouncedEnter2(host)) return true;
30842
+ dispatch({ type: "settingsWrongProxyUrlEditCommit" });
30843
+ return true;
30844
+ }
30845
+ if (key.backspace) {
30846
+ dispatch({
30847
+ type: "settingsWrongProxyUrlEditChange",
30848
+ draft: sp.wrongProxyUrlDraft.slice(0, -1)
30849
+ });
30850
+ return true;
30851
+ }
30852
+ if (input && input.length === 1 && input.charCodeAt(0) >= 32 && input.charCodeAt(0) < 127) {
30853
+ dispatch({
30854
+ type: "settingsWrongProxyUrlEditChange",
30855
+ draft: sp.wrongProxyUrlDraft + input
30856
+ });
30857
+ return true;
30858
+ }
30859
+ return true;
30860
+ }
30373
30861
  if (key.escape || key.ctrl && input === "s") {
30374
30862
  dispatch({ type: "settingsClose" });
30375
30863
  return true;
@@ -31139,6 +31627,8 @@ function useAppPickerKeys({
31139
31627
  }
31140
31628
  if (sp.field === THINKING_WORD_FIELD) {
31141
31629
  dispatch({ type: "settingsThinkingEditStart" });
31630
+ } else if (sp.field === WRONGPROXY_URL_FIELD) {
31631
+ dispatch({ type: "settingsWrongProxyUrlEditStart" });
31142
31632
  } else {
31143
31633
  dispatch({ type: "settingsValueChange", delta: 1 });
31144
31634
  }
@@ -32204,7 +32694,19 @@ function createInitialState(options) {
32204
32694
  showSageMemoryInject: true,
32205
32695
  sageMemoryInjectThreshold: 0.85,
32206
32696
  nextStepsTool: false,
32207
- readSymbols: false
32697
+ readSymbols: false,
32698
+ // WrongProxy / WrongTrace. Defaults match the WebUI LocalPrefs
32699
+ // (master switch off; URL 'http://localhost:3444'). The runtime
32700
+ // probe in `packages/cli/src/wiring/proxy-probe.ts` reacts via
32701
+ // the WS `prefs.update` pipeline — these initial values only
32702
+ // cover the slice before the first `settingsOpen` action lands.
32703
+ wrongProxyEnabled: false,
32704
+ wrongProxyUrl: "http://localhost:3444",
32705
+ // WrongProxy URL text-edit state (field 60). The reducer quartet
32706
+ // at `reducers/settings-values.ts:733-801` seeds the draft from
32707
+ // `wrongProxyUrl` on Start and clears it on Commit/Cancel.
32708
+ wrongProxyUrlEditing: false,
32709
+ wrongProxyUrlDraft: ""
32208
32710
  },
32209
32711
  statuslinePicker: {
32210
32712
  open: false,
@@ -34598,13 +35100,31 @@ function reduceSettingsPanel(state, action) {
34598
35100
  sageMemoryInjectThreshold: action.sageMemoryInjectThreshold,
34599
35101
  nextStepsTool: action.nextStepsTool,
34600
35102
  readSymbols: action.readSymbols,
35103
+ // WrongProxy / WrongTrace: hydrate from the persisted boot-time
35104
+ // values pushed into the action by the TUI settings adapter.
35105
+ // The runtime probe (see `packages/cli/src/wiring/proxy-probe.ts`)
35106
+ // reacts to these via the WS `prefs.update` pipeline.
35107
+ wrongProxyEnabled: action.wrongProxyEnabled,
35108
+ wrongProxyUrl: action.wrongProxyUrl,
35109
+ // WrongProxy URL text-edit state (field 60): always defaults to
35110
+ // 'not editing' + empty draft on open. The Start action seeds
35111
+ // the draft from `wrongProxyUrl` at the reducer boundary.
35112
+ wrongProxyUrlEditing: false,
35113
+ wrongProxyUrlDraft: "",
34601
35114
  hint: void 0
34602
35115
  }
34603
35116
  };
34604
35117
  case "settingsClose":
34605
35118
  return {
34606
35119
  ...state,
34607
- settingsPicker: { ...state.settingsPicker, open: false, hint: void 0 }
35120
+ ...closePanels(state),
35121
+ settingsPicker: {
35122
+ ...state.settingsPicker,
35123
+ open: false,
35124
+ filter: "",
35125
+ wrongProxyUrlEditing: false,
35126
+ wrongProxyUrlDraft: ""
35127
+ }
34608
35128
  };
34609
35129
  case "settingsFieldMove": {
34610
35130
  const next = (state.settingsPicker.field + action.delta + SETTINGS_FIELD_COUNT) % SETTINGS_FIELD_COUNT;
@@ -34616,6 +35136,8 @@ function reduceSettingsPanel(state, action) {
34616
35136
  lastSettingsField: next,
34617
35137
  thinkingWordEditing: false,
34618
35138
  thinkingWordDraft: "",
35139
+ wrongProxyUrlEditing: false,
35140
+ wrongProxyUrlDraft: "",
34619
35141
  hint: void 0
34620
35142
  }
34621
35143
  };
@@ -34628,6 +35150,8 @@ function reduceSettingsPanel(state, action) {
34628
35150
  ...state.settingsPicker,
34629
35151
  field,
34630
35152
  lastSettingsField: field,
35153
+ wrongProxyUrlEditing: false,
35154
+ wrongProxyUrlDraft: "",
34631
35155
  hint: void 0
34632
35156
  }
34633
35157
  };
@@ -34645,6 +35169,7 @@ function reduceSettingsPanel(state, action) {
34645
35169
 
34646
35170
  // src/reducers/settings-values.ts
34647
35171
  import { expectDefined as expectDefined4 } from "@wrongstack/core/utils";
35172
+ import { MAX_WRONGPROXY_URL_LENGTH } from "@wrongstack/core/types";
34648
35173
  var settingsValueActionTypes = [
34649
35174
  "settingsValueChange",
34650
35175
  "settingsValueSet",
@@ -34652,7 +35177,16 @@ var settingsValueActionTypes = [
34652
35177
  "settingsThinkingEditStart",
34653
35178
  "settingsThinkingEditChange",
34654
35179
  "settingsThinkingEditCommit",
34655
- "settingsThinkingEditCancel"
35180
+ "settingsThinkingEditCancel",
35181
+ // WrongProxy / WrongTrace URL (field 60) text-edit quartet.
35182
+ // Adding these here routes them into `reduceSettingsValues` via the
35183
+ // `SettingsValueAction` extract — without these, the actions hit the
35184
+ // generic `reduce*` path and the `action satisfies never` narrowing
35185
+ // marks the whole `Action` union as unassignable to `never`.
35186
+ "settingsWrongProxyUrlEditStart",
35187
+ "settingsWrongProxyUrlEditChange",
35188
+ "settingsWrongProxyUrlEditCommit",
35189
+ "settingsWrongProxyUrlEditCancel"
34656
35190
  ];
34657
35191
  var settingsValueActionTypeSet = new Set(settingsValueActionTypes);
34658
35192
  function isSettingsValueAction(action) {
@@ -35087,6 +35621,11 @@ function reduceSettingsValues(state, action) {
35087
35621
  ...state,
35088
35622
  settingsPicker: { ...sp, nextStepsTool: !sp.nextStepsTool, hint: void 0 }
35089
35623
  };
35624
+ if (f === 59)
35625
+ return {
35626
+ ...state,
35627
+ settingsPicker: { ...sp, wrongProxyEnabled: !sp.wrongProxyEnabled, hint: void 0 }
35628
+ };
35090
35629
  if (f >= PANEL_POSITION_FIELD_START && f - PANEL_POSITION_FIELD_START < PANEL_IDS.length) {
35091
35630
  const PANEL_POSITION_CYCLE = ["bottom", "sidebar"];
35092
35631
  const panelId = PANEL_IDS[f - PANEL_POSITION_FIELD_START];
@@ -35185,6 +35724,76 @@ function reduceSettingsValues(state, action) {
35185
35724
  hint: void 0
35186
35725
  }
35187
35726
  };
35727
+ // WrongProxy URL (field 60) free-text edit quartet. Mirrors the
35728
+ // `settingsThinkingEdit*` shape: Enter on the row opens the edit
35729
+ // (seeding the draft with the current URL), Change caps the draft
35730
+ // (URLs don't have a strict length cap, but we cap to a sane 2 KiB
35731
+ // to prevent pathological input), Commit validates the scheme and
35732
+ // applies the new value, Cancel discards. Empty draft on commit
35733
+ // keeps the current URL (same semantics as the thinking-word edit).
35734
+ case "settingsWrongProxyUrlEditStart":
35735
+ return {
35736
+ ...state,
35737
+ settingsPicker: {
35738
+ ...state.settingsPicker,
35739
+ wrongProxyUrlEditing: true,
35740
+ // Seed the draft with the current URL so the user edits from it.
35741
+ wrongProxyUrlDraft: state.settingsPicker.wrongProxyUrl,
35742
+ hint: void 0
35743
+ }
35744
+ };
35745
+ case "settingsWrongProxyUrlEditChange":
35746
+ return {
35747
+ ...state,
35748
+ settingsPicker: {
35749
+ ...state.settingsPicker,
35750
+ // Cap to a sane 2 KiB so a runaway paste can't bloat the slice.
35751
+ wrongProxyUrlDraft: action.draft.slice(0, MAX_WRONGPROXY_URL_LENGTH),
35752
+ hint: void 0
35753
+ }
35754
+ };
35755
+ case "settingsWrongProxyUrlEditCommit": {
35756
+ const sp = state.settingsPicker;
35757
+ const raw = sp.wrongProxyUrlDraft.trim();
35758
+ if (raw.length === 0) {
35759
+ return {
35760
+ ...state,
35761
+ settingsPicker: {
35762
+ ...sp,
35763
+ wrongProxyUrlEditing: false,
35764
+ wrongProxyUrlDraft: "",
35765
+ hint: void 0
35766
+ }
35767
+ };
35768
+ }
35769
+ let valid = false;
35770
+ try {
35771
+ const parsed = new URL(raw);
35772
+ valid = parsed.protocol === "http:" || parsed.protocol === "https:";
35773
+ } catch {
35774
+ valid = false;
35775
+ }
35776
+ return {
35777
+ ...state,
35778
+ settingsPicker: {
35779
+ ...sp,
35780
+ wrongProxyUrl: valid ? raw : sp.wrongProxyUrl,
35781
+ wrongProxyUrlEditing: false,
35782
+ wrongProxyUrlDraft: "",
35783
+ hint: valid ? void 0 : "Invalid URL \u2014 use http://host:port or https://host:port (no whitespace)."
35784
+ }
35785
+ };
35786
+ }
35787
+ case "settingsWrongProxyUrlEditCancel":
35788
+ return {
35789
+ ...state,
35790
+ settingsPicker: {
35791
+ ...state.settingsPicker,
35792
+ wrongProxyUrlEditing: false,
35793
+ wrongProxyUrlDraft: "",
35794
+ hint: void 0
35795
+ }
35796
+ };
35188
35797
  default:
35189
35798
  void action;
35190
35799
  return state;
@@ -35196,6 +35805,7 @@ var SIDEBAR_MISSION_MAX_WRAP_LINES = 10;
35196
35805
  function computeMaxSidebarScroll(state, viewportHeight = 20, effectiveSwarmOnSidebar) {
35197
35806
  let contentHeight = 0;
35198
35807
  contentHeight += 14;
35808
+ contentHeight += 11;
35199
35809
  contentHeight += 5;
35200
35810
  const swarmOnSidebar = effectiveSwarmOnSidebar ?? state.settingsPicker.showAgentSwarmPanel === "sidebar";
35201
35811
  if (swarmOnSidebar) {
@@ -36230,6 +36840,10 @@ import {
36230
36840
  useEffect as useEffect31,
36231
36841
  useRef as useRef15
36232
36842
  } from "react";
36843
+ var BETWEEN_ITERATION_YIELD_MS = 200;
36844
+ function yieldBetweenIterations(ms = BETWEEN_ITERATION_YIELD_MS) {
36845
+ return new Promise((resolve) => setTimeout(resolve, ms));
36846
+ }
36233
36847
  function useAutonomyDrivers({
36234
36848
  getEternalEngine,
36235
36849
  getParallelEngine,
@@ -36276,7 +36890,7 @@ function useAutonomyDrivers({
36276
36890
  }
36277
36891
  if (!mountedRef.current) break;
36278
36892
  dispatch({ type: "status", status: "idle" });
36279
- await new Promise((r) => setTimeout(r, 200));
36893
+ await yieldBetweenIterations();
36280
36894
  }
36281
36895
  } finally {
36282
36896
  eternalLoopRunningRef.current = false;
@@ -36319,7 +36933,7 @@ function useAutonomyDrivers({
36319
36933
  }
36320
36934
  if (!mountedRef.current) break;
36321
36935
  dispatch({ type: "status", status: "idle" });
36322
- await new Promise((r) => setTimeout(r, 200));
36936
+ await yieldBetweenIterations();
36323
36937
  }
36324
36938
  } finally {
36325
36939
  parallelLoopRunningRef.current = false;
@@ -40792,7 +41406,7 @@ function useMouseTracking(input) {
40792
41406
  native: nativeMouse,
40793
41407
  protocol
40794
41408
  });
40795
- const mouseTrackingSequence = mouseTrackingOn ? mouseMode ? MOUSE_DRAG_ON : MOUSE_CLICK_ON : MOUSE_OFF;
41409
+ const mouseTrackingSequence = mouseTrackingOn ? MOUSE_DRAG_ON : MOUSE_OFF;
40796
41410
  const mouseWrittenRef = useRef30(null);
40797
41411
  useEffect49(() => {
40798
41412
  if (mouseWrittenRef.current === mouseTrackingSequence) return;
@@ -41391,7 +42005,12 @@ function usePanelControllers({
41391
42005
  showSageMemoryInject: s2.showSageMemoryInject ?? false,
41392
42006
  sageMemoryInjectThreshold: s2.sageMemoryInjectThreshold ?? 0.85,
41393
42007
  nextStepsTool: s2.nextStepsTool ?? false,
41394
- readSymbols: s2.readSymbols ?? false
42008
+ readSymbols: s2.readSymbols ?? false,
42009
+ // WrongProxy / WrongTrace: hydrate from the picker state slice.
42010
+ // Same defaults as the WebUI LocalPrefs and the CLI adapter's
42011
+ // branch tree — see tui-settings-adapter.ts.
42012
+ wrongProxyEnabled: s2.wrongProxyEnabled ?? false,
42013
+ wrongProxyUrl: s2.wrongProxyUrl ?? "http://localhost:3444"
41395
42014
  });
41396
42015
  }, [getSettings]);
41397
42016
  const refreshPluginPicker = React11.useCallback(() => {
@@ -42364,36 +42983,9 @@ function useQueueManager({
42364
42983
  midRunSendPickerRef
42365
42984
  }) {
42366
42985
  const persistState = useRef34({ running: false, pending: null });
42367
- useEffect53(() => {
42368
- if (!queueStore) return;
42369
- let cancelled = false;
42370
- queueStore.read().then((items) => {
42371
- if (cancelled || items.length === 0) return;
42372
- for (const item of items) {
42373
- dispatch({
42374
- type: "enqueue",
42375
- item: {
42376
- displayText: item.displayText,
42377
- blocks: item.blocks,
42378
- ...item.shouldRefine !== void 0 ? { shouldRefine: item.shouldRefine } : {},
42379
- ...item.journalRaw !== void 0 ? { journalRaw: item.journalRaw } : {}
42380
- }
42381
- });
42382
- }
42383
- dispatch({
42384
- type: "addEntry",
42385
- entry: {
42386
- kind: "info",
42387
- text: `Restored ${items.length} queued message${items.length === 1 ? "" : "s"} from a previous run.`
42388
- }
42389
- });
42390
- }).catch(() => void 0);
42391
- return () => {
42392
- cancelled = true;
42393
- };
42394
- }, [queueStore]);
42395
- useEffect53(() => {
42396
- if (!queueStore) return;
42986
+ const hydrated = useRef34(!queueStore);
42987
+ const pendingBeforeHydration = useRef34(false);
42988
+ const writeQueue = (store) => {
42397
42989
  const raw = stateRef.current.queue.map(
42398
42990
  ({
42399
42991
  displayText,
@@ -42408,7 +43000,7 @@ function useQueueManager({
42408
43000
  })
42409
43001
  );
42410
43002
  const persistence = persistState.current;
42411
- persistence.pending = { store: queueStore, items: raw };
43003
+ persistence.pending = { store, items: raw };
42412
43004
  if (persistence.running) return;
42413
43005
  persistence.running = true;
42414
43006
  void (async () => {
@@ -42422,6 +43014,49 @@ function useQueueManager({
42422
43014
  persistence.running = false;
42423
43015
  }
42424
43016
  })();
43017
+ };
43018
+ useEffect53(() => {
43019
+ if (!queueStore) return;
43020
+ let cancelled = false;
43021
+ queueStore.read().then((items) => {
43022
+ if (cancelled) return;
43023
+ hydrated.current = true;
43024
+ for (const item of items) {
43025
+ dispatch({
43026
+ type: "enqueue",
43027
+ item: {
43028
+ displayText: item.displayText,
43029
+ blocks: item.blocks,
43030
+ ...item.shouldRefine !== void 0 ? { shouldRefine: item.shouldRefine } : {},
43031
+ ...item.journalRaw !== void 0 ? { journalRaw: item.journalRaw } : {}
43032
+ }
43033
+ });
43034
+ }
43035
+ if (items.length > 0) {
43036
+ dispatch({
43037
+ type: "addEntry",
43038
+ entry: {
43039
+ kind: "info",
43040
+ text: `Restored ${items.length} queued message${items.length === 1 ? "" : "s"} from a previous run.`
43041
+ }
43042
+ });
43043
+ }
43044
+ if (pendingBeforeHydration.current && items.length === 0) {
43045
+ pendingBeforeHydration.current = false;
43046
+ writeQueue(queueStore);
43047
+ }
43048
+ }).catch(() => void 0);
43049
+ return () => {
43050
+ cancelled = true;
43051
+ };
43052
+ }, [queueStore]);
43053
+ useEffect53(() => {
43054
+ if (!queueStore) return;
43055
+ if (!hydrated.current) {
43056
+ pendingBeforeHydration.current = true;
43057
+ return;
43058
+ }
43059
+ writeQueue(queueStore);
42425
43060
  }, [stateRef.current.queue, queueStore, stateRef]);
42426
43061
  useEffect53(() => {
42427
43062
  onQueueChange?.(stateRef.current.queue.map((q) => q.displayText));
@@ -42723,7 +43358,13 @@ function useSettingsAutoSave(state, saveSettings, dispatch) {
42723
43358
  showSageMemoryInject: sp.showSageMemoryInject,
42724
43359
  sageMemoryInjectThreshold: sp.sageMemoryInjectThreshold,
42725
43360
  nextStepsTool: sp.nextStepsTool,
42726
- readSymbols: sp.readSymbols
43361
+ readSymbols: sp.readSymbols,
43362
+ // WrongProxy / WrongTrace: persist the picker-state values to
43363
+ // the same Config keys the adapter exposes (see LiveSettingsInput
43364
+ // and the tui-settings-adapter.ts branch tree). Optional with
43365
+ // `??` fallback — older persisted configs may not have the keys.
43366
+ wrongProxyEnabled: sp.wrongProxyEnabled ?? false,
43367
+ wrongProxyUrl: sp.wrongProxyUrl ?? "http://localhost:3444"
42727
43368
  })
42728
43369
  ).then((err) => {
42729
43370
  if (err) dispatch({ type: "settingsHint", text: err });
@@ -42777,6 +43418,8 @@ function useSettingsAutoSave(state, saveSettings, dispatch) {
42777
43418
  state.settingsPicker.sageMemoryInjectThreshold,
42778
43419
  state.settingsPicker.nextStepsTool,
42779
43420
  state.settingsPicker.readSymbols,
43421
+ state.settingsPicker.wrongProxyEnabled,
43422
+ state.settingsPicker.wrongProxyUrl,
42780
43423
  saveSettings
42781
43424
  ]);
42782
43425
  }
@@ -46044,7 +46687,7 @@ function App(props) {
46044
46687
 
46045
46688
  // src/components/suggestions.ts
46046
46689
  import {
46047
- parseNextSteps as parseNextSteps2,
46690
+ parseNextSteps as parseNextSteps3,
46048
46691
  stripNextStepsBlock as stripNextStepsBlock2
46049
46692
  } from "@wrongstack/tools/next-steps";
46050
46693
 
@@ -46913,7 +47556,7 @@ async function runTui(opts) {
46913
47556
  export {
46914
47557
  buildGoalPreamble2 as buildGoalPreamble,
46915
47558
  parseInline,
46916
- parseNextSteps2 as parseNextSteps,
47559
+ parseNextSteps3 as parseNextSteps,
46917
47560
  replaySessionMessages,
46918
47561
  runTui
46919
47562
  };