@wrongstack/tui 0.309.1 → 0.310.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
@@ -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",
@@ -3272,7 +3272,19 @@ function buildPrimaryChips(p) {
3272
3272
  STATUSLINE_ICONS.cache,
3273
3273
  " ",
3274
3274
  (cache2.hitRatio * 100).toFixed(0),
3275
- "%"
3275
+ "%",
3276
+ /* @__PURE__ */ jsxs5(Text, { children: [
3277
+ " r",
3278
+ fmtTok(cache2.readTokens)
3279
+ ] }),
3280
+ /* @__PURE__ */ jsxs5(Text, { children: [
3281
+ " w",
3282
+ fmtTok(cache2.writeTokens)
3283
+ ] }),
3284
+ cache2.savedUsd > 0 ? /* @__PURE__ */ jsxs5(Text, { children: [
3285
+ " ~$",
3286
+ cache2.savedUsd.toFixed(2)
3287
+ ] }) : null
3276
3288
  ] }) : null
3277
3289
  ] });
3278
3290
  })() : null,
@@ -5439,6 +5451,7 @@ function MetricsSection({ data }) {
5439
5451
  function CacheSection({ data }) {
5440
5452
  const cs = data.cacheStats ?? { readTokens: 0, writeTokens: 0, hitRatio: 0, savedUsd: 0 };
5441
5453
  const hasCache = cs.readTokens > 0 || cs.writeTokens > 0;
5454
+ const hasTtlSplit = cs.cacheWrite5m !== void 0 && cs.cacheWrite1h !== void 0;
5442
5455
  const coverage = Math.max(0, data.cacheCoverageTokens ?? 0);
5443
5456
  const max = data.ctxMaxTokens ?? 0;
5444
5457
  const covBarLen = 30;
@@ -5455,8 +5468,58 @@ function CacheSection({ data }) {
5455
5468
  /* @__PURE__ */ jsx10(Text, { color: theme.textMuted, children: " \xB7 read " }),
5456
5469
  /* @__PURE__ */ jsx10(Text, { color: theme.textPrimary, children: cs.readTokens.toLocaleString("en-US") }),
5457
5470
  /* @__PURE__ */ jsx10(Text, { color: theme.textMuted, children: " \xB7 write " }),
5458
- /* @__PURE__ */ jsx10(Text, { color: theme.textSecondary, children: cs.writeTokens.toLocaleString("en-US") })
5471
+ /* @__PURE__ */ jsx10(Text, { color: theme.textSecondary, children: cs.writeTokens.toLocaleString("en-US") }),
5472
+ cs.savedUsd > 0 ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
5473
+ /* @__PURE__ */ jsx10(Text, { color: theme.textMuted, children: " \xB7 saved " }),
5474
+ /* @__PURE__ */ jsxs9(Text, { color: theme.success, children: [
5475
+ "~$",
5476
+ cs.savedUsd.toFixed(2)
5477
+ ] })
5478
+ ] }) : null
5459
5479
  ] }),
5480
+ hasTtlSplit ? /* @__PURE__ */ jsxs9(Box, { children: [
5481
+ /* @__PURE__ */ jsx10(Text, { color: theme.textMuted, children: "Write TTL " }),
5482
+ /* @__PURE__ */ jsxs9(Text, { color: theme.textPrimary, children: [
5483
+ "5m ",
5484
+ (cs.cacheWrite5m ?? 0).toLocaleString("en-US")
5485
+ ] }),
5486
+ /* @__PURE__ */ jsx10(Text, { color: theme.textMuted, children: " \xB7 " }),
5487
+ /* @__PURE__ */ jsxs9(Text, { color: theme.textSecondary, children: [
5488
+ "1h ",
5489
+ (cs.cacheWrite1h ?? 0).toLocaleString("en-US")
5490
+ ] })
5491
+ ] }) : null,
5492
+ (data.providerCacheStats?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs9(Box, { flexDirection: "column", children: [
5493
+ /* @__PURE__ */ jsx10(Text, { color: theme.textMuted, children: "Providers" }),
5494
+ data.providerCacheStats?.map((provider) => /* @__PURE__ */ jsxs9(Text, { children: [
5495
+ /* @__PURE__ */ jsxs9(Text, { color: theme.textPrimary, children: [
5496
+ " ",
5497
+ provider.provider
5498
+ ] }),
5499
+ /* @__PURE__ */ jsx10(Text, { color: theme.textMuted, children: " \xB7 " }),
5500
+ /* @__PURE__ */ jsxs9(Text, { color: provider.cacheRead > 0 ? theme.success : theme.textMuted, children: [
5501
+ (provider.hitRatio * 100).toFixed(1),
5502
+ "%"
5503
+ ] }),
5504
+ /* @__PURE__ */ jsx10(Text, { color: theme.textMuted, children: " \xB7 read " }),
5505
+ /* @__PURE__ */ jsx10(Text, { color: theme.textPrimary, children: provider.cacheRead.toLocaleString("en-US") }),
5506
+ /* @__PURE__ */ jsx10(Text, { color: theme.textMuted, children: " \xB7 write " }),
5507
+ /* @__PURE__ */ jsx10(Text, { color: theme.textSecondary, children: provider.cacheWrite.toLocaleString("en-US") }),
5508
+ provider.cacheWrite5m !== void 0 && provider.cacheWrite1h !== void 0 ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
5509
+ /* @__PURE__ */ jsx10(Text, { color: theme.textMuted, children: " (" }),
5510
+ /* @__PURE__ */ jsxs9(Text, { color: theme.textPrimary, children: [
5511
+ "5m ",
5512
+ provider.cacheWrite5m.toLocaleString("en-US")
5513
+ ] }),
5514
+ /* @__PURE__ */ jsx10(Text, { color: theme.textMuted, children: ", " }),
5515
+ /* @__PURE__ */ jsxs9(Text, { color: theme.textSecondary, children: [
5516
+ "1h ",
5517
+ provider.cacheWrite1h.toLocaleString("en-US")
5518
+ ] }),
5519
+ /* @__PURE__ */ jsx10(Text, { color: theme.textMuted, children: ")" })
5520
+ ] }) : null
5521
+ ] }, provider.provider))
5522
+ ] }) : null,
5460
5523
  coverage > 0 && max > 0 ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
5461
5524
  /* @__PURE__ */ jsxs9(Box, { marginTop: 1, children: [
5462
5525
  /* @__PURE__ */ jsx10(Text, { color: theme.textMuted, children: "Coverage " }),
@@ -10316,6 +10379,7 @@ function AppStatusRegion({
10316
10379
  })(),
10317
10380
  cacheStats: cacheStats ?? { readTokens: 0, writeTokens: 0, hitRatio: 0, savedUsd: 0 },
10318
10381
  cacheCoverageTokens: cacheCoverageTokens ?? 0,
10382
+ providerCacheStats: cacheStats?.providers ?? [],
10319
10383
  fleetEntries: Object.values(state.fleet).map((e) => ({
10320
10384
  name: e.name,
10321
10385
  status: e.status,
@@ -14924,7 +14988,7 @@ function ConnectionsPanel({
14924
14988
  const [loading, setLoading] = useState17(true);
14925
14989
  const [error, setError] = useState17(null);
14926
14990
  const cancelledRef = useRef3(false);
14927
- const fetch = useCallback4(async () => {
14991
+ const fetch2 = useCallback4(async () => {
14928
14992
  try {
14929
14993
  const r = await collectConnectionsHealth(projectRoot);
14930
14994
  if (cancelledRef.current) return;
@@ -14939,18 +15003,18 @@ function ConnectionsPanel({
14939
15003
  }, [projectRoot]);
14940
15004
  useEffect16(() => {
14941
15005
  cancelledRef.current = false;
14942
- void fetch();
14943
- const timer = setInterval(() => void fetch(), REFRESH_MS);
15006
+ void fetch2();
15007
+ const timer = setInterval(() => void fetch2(), REFRESH_MS);
14944
15008
  return () => {
14945
15009
  cancelledRef.current = true;
14946
15010
  clearInterval(timer);
14947
15011
  };
14948
- }, [fetch]);
15012
+ }, [fetch2]);
14949
15013
  const shortcutsEnabled = usePanelShortcutsEnabled();
14950
15014
  useInput((_input, _key) => {
14951
15015
  if (!shortcutsEnabled) return;
14952
15016
  if (_input === "q") onClose();
14953
- if (_input === "r") void fetch();
15017
+ if (_input === "r") void fetch2();
14954
15018
  });
14955
15019
  const overallLabel = report ? report.overall === "healthy" ? "All required services healthy" : report.overall === "degraded" ? "Some services degraded" : "Service errors detected" : "Checking\u2026";
14956
15020
  return /* @__PURE__ */ jsxs40(
@@ -17659,6 +17723,7 @@ var MODE_DESC = {
17659
17723
  };
17660
17724
  var SETTINGS_FIELD_COUNT = TOTAL_SETTINGS_FIELD_COUNT;
17661
17725
  var THINKING_WORD_FIELD = 22;
17726
+ var WRONGPROXY_URL_FIELD = 60;
17662
17727
  var THINKING_WORD_PRESETS = [
17663
17728
  "thinking",
17664
17729
  "random",
@@ -17792,8 +17857,17 @@ var SETTINGS_FIELD_LABELS = [
17792
17857
  // 56 (P panel id index 10)
17793
17858
  "Kanban placement",
17794
17859
  // 57 (P panel id index 11)
17795
- "Connections placement"
17860
+ "Connections placement",
17796
17861
  // 58 (P panel id index 12)
17862
+ // WrongProxy / WrongTrace (appended at the end so existing field
17863
+ // indices 0–58 — including the panel-position block at 46–58 — are
17864
+ // not shifted by the addition). The runtime probe in
17865
+ // `packages/cli/src/wiring/proxy-probe.ts` reacts to these via the
17866
+ // WS `prefs.update` pipeline; the picker is just the user surface.
17867
+ "WrongProxy / WrongTrace",
17868
+ // 59
17869
+ "WrongProxy URL"
17870
+ // 60
17797
17871
  ];
17798
17872
  function resolveSettingsFieldValue(field, input) {
17799
17873
  const raw = input.trim().toLowerCase();
@@ -17820,7 +17894,10 @@ function resolveSettingsFieldValue(field, input) {
17820
17894
  // [40, 'showAgentSwarmPanel'] — now an enum, see ENUM_FIELDS below
17821
17895
  [42, "readSymbols"],
17822
17896
  [43, "showSageMemoryInject"],
17823
- [45, "nextStepsTool"]
17897
+ [45, "nextStepsTool"],
17898
+ // WrongProxy / WrongTrace master switch (field 59). The companion
17899
+ // URL field (60) is text-typed, see resolveSettingsFieldValue.
17900
+ [59, "wrongProxyEnabled"]
17824
17901
  ]);
17825
17902
  const boolKey = BOOL_FIELDS.get(field);
17826
17903
  if (boolKey) {
@@ -17981,6 +18058,16 @@ function resolveSettingsFieldValue(field, input) {
17981
18058
  }
17982
18059
  return { ok: true, patch: { thinkingWord: word }, label, displayValue: word };
17983
18060
  }
18061
+ if (field === 60) {
18062
+ const url = input.trim();
18063
+ if (url.length === 0) {
18064
+ return { ok: false, error: `"${input}" is not a valid proxy URL. Use http://host:port or https://host:port.` };
18065
+ }
18066
+ if (!/^https?:\/\//i.test(url)) {
18067
+ return { ok: false, error: `"${input}" is not a valid proxy URL. Use http://host:port or https://host:port.` };
18068
+ }
18069
+ return { ok: true, patch: { wrongProxyUrl: url }, label, displayValue: url };
18070
+ }
17984
18071
  if (field >= PANEL_POSITION_FIELD_START && field - PANEL_POSITION_FIELD_START < PANEL_IDS.length) {
17985
18072
  const panelId = PANEL_IDS[field - PANEL_POSITION_FIELD_START];
17986
18073
  if (raw === "bottom" || raw === "sidebar") {
@@ -18022,7 +18109,10 @@ function getSettingsFieldValue(values, field) {
18022
18109
  // [40, 'showAgentSwarmPanel'] — now an enum, see ENUM_KEYS below
18023
18110
  [42, "readSymbols"],
18024
18111
  [43, "showSageMemoryInject"],
18025
- [45, "nextStepsTool"]
18112
+ [45, "nextStepsTool"],
18113
+ // WrongProxy / WrongTrace master switch (field 59). The companion
18114
+ // URL field (60) is text-typed, see getSettingsFieldValue.
18115
+ [59, "wrongProxyEnabled"]
18026
18116
  ];
18027
18117
  for (const [f, key] of BOOL_KEYS) {
18028
18118
  if (field !== f) continue;
@@ -18068,6 +18158,9 @@ function getSettingsFieldValue(values, field) {
18068
18158
  if (field === 22) {
18069
18159
  return { ok: true, label, displayValue: values.thinkingWord };
18070
18160
  }
18161
+ if (field === 60) {
18162
+ return { ok: true, label, displayValue: values.wrongProxyUrl };
18163
+ }
18071
18164
  if (field >= PANEL_POSITION_FIELD_START && field - PANEL_POSITION_FIELD_START < PANEL_IDS.length) {
18072
18165
  const panelId = PANEL_IDS[field - PANEL_POSITION_FIELD_START];
18073
18166
  return {
@@ -18126,6 +18219,16 @@ var SETTINGS_SECTIONS = [
18126
18219
  {
18127
18220
  name: "Panels",
18128
18221
  fields: [46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58]
18222
+ },
18223
+ {
18224
+ // WrongProxy / WrongTrace — fields 59 (master switch) + 60 (URL).
18225
+ // Sits in its own section because the runtime probe + base-URL
18226
+ // rewrite it controls live outside the picker's normal "feature
18227
+ // toggle" semantics; matching the WebUI's IntegrationsSection
18228
+ // keeps the cross-surface contract obvious. Appended at the end
18229
+ // to avoid shifting the panel-position block 46–58 indices.
18230
+ name: "Integrations",
18231
+ fields: [59, 60]
18129
18232
  }
18130
18233
  ];
18131
18234
  function formatAllSettingsSummary(values) {
@@ -18195,7 +18298,15 @@ var SETTINGS_DEFAULTS = Object.freeze({
18195
18298
  readSymbols: false,
18196
18299
  showSageMemoryInject: true,
18197
18300
  sageMemoryInjectThreshold: 0.85,
18198
- nextStepsTool: false
18301
+ nextStepsTool: false,
18302
+ // WrongProxy / WrongTrace. Defaults mirror the WebUI `LocalPrefs`
18303
+ // DEFAULTS in `packages/webui/src/stores/local-prefs.ts` (master
18304
+ // switch off, URL 'http://localhost:8000'). Required keys here
18305
+ // because `SettingsPickerValues` (mapped from `SettingsPickerPatch`)
18306
+ // makes every key mandatory — omitting them here breaks the type
18307
+ // contract for the entire TUI settings module. See Chimera review.
18308
+ wrongProxyEnabled: false,
18309
+ wrongProxyUrl: "http://localhost:8000"
18199
18310
  });
18200
18311
  function resetSettingsFieldValue(field) {
18201
18312
  const result = getSettingsFieldValue(SETTINGS_DEFAULTS, field);
@@ -18251,7 +18362,14 @@ function buildResetPatch(field) {
18251
18362
  [42, "readSymbols"],
18252
18363
  [43, "showSageMemoryInject"],
18253
18364
  [44, "sageMemoryInjectThreshold"],
18254
- [45, "nextStepsTool"]
18365
+ [45, "nextStepsTool"],
18366
+ // WrongProxy / WrongTrace: appended at the end so the existing
18367
+ // 46+13 = 59 entries are not shifted (the picker caps + scrolls;
18368
+ // field 59 is boolean, 60 is text). See `Settings.wrongProxy*`
18369
+ // and `ToolsConfig.wrongProxy?: WrongProxyToolConfig` for the
18370
+ // canonical persistence shape.
18371
+ [59, "wrongProxyEnabled"],
18372
+ [60, "wrongProxyUrl"]
18255
18373
  ];
18256
18374
  for (const [f, key] of KEY_MAP) {
18257
18375
  if (f === field) {
@@ -18380,6 +18498,8 @@ function SettingsPicker({
18380
18498
  thinkingWord,
18381
18499
  thinkingWordEditing,
18382
18500
  thinkingWordDraft,
18501
+ wrongProxyUrlEditing,
18502
+ wrongProxyUrlDraft,
18383
18503
  reasoningMode,
18384
18504
  reasoningEffort,
18385
18505
  reasoningPreserve,
@@ -18404,6 +18524,13 @@ function SettingsPicker({
18404
18524
  nextStepsTool,
18405
18525
  readSymbols,
18406
18526
  panelPositions,
18527
+ // WrongProxy / WrongTrace: pick these from the same slice as
18528
+ // `nextStepsTool` and `readSymbols`. The runtime probe (see
18529
+ // `runtime-controller-deps.ts:applyLiveSettings`) reads them off the
18530
+ // same object, so adding both at the destructuring level keeps the
18531
+ // mid-session effect coherent.
18532
+ wrongProxyEnabled,
18533
+ wrongProxyUrl,
18407
18534
  inputHeight,
18408
18535
  hint,
18409
18536
  onLayoutComputed
@@ -18673,7 +18800,25 @@ function SettingsPicker({
18673
18800
  label: SETTINGS_FIELD_LABELS[45 + i] ?? panelId,
18674
18801
  value: panelPositions[panelId],
18675
18802
  detail: panelPositions[panelId] === "sidebar" ? "Render in right sidebar" : "Render in lower F-key region"
18676
- }))
18803
+ })),
18804
+ // ── Integrations (fields 59–60) ─────────────────────────────────────
18805
+ // WrongProxy / WrongTrace. Appended at the end so the existing
18806
+ // field indices 0–58 (including the panel-position block 46–58)
18807
+ // remain stable — see the SettingsPicker "Appended to preserve"
18808
+ // comment above. The runtime probe (see
18809
+ // `runtime-controller-deps.ts:applyLiveSettings`) reads both keys
18810
+ // from the same SettingsPicker slice, so adding them at the
18811
+ // destructuring level keeps the mid-session effect coherent.
18812
+ {
18813
+ label: "WrongProxy / WrongTrace",
18814
+ value: boolVal(wrongProxyEnabled),
18815
+ detail: "Route base URLs through the local proxy (default http://localhost:8000). openai-codex is excluded."
18816
+ },
18817
+ {
18818
+ label: "WrongProxy URL",
18819
+ value: wrongProxyUrlEditing ? `${wrongProxyUrlDraft ?? ""}\u258F` : wrongProxyUrl,
18820
+ 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."
18821
+ }
18677
18822
  ];
18678
18823
  const fieldRowIndex = [];
18679
18824
  for (let i = 0; i < rows.length; i++) {
@@ -20017,6 +20162,10 @@ function AppViewPickers({
20017
20162
  nextStepsTool: state.settingsPicker.nextStepsTool,
20018
20163
  readSymbols: state.settingsPicker.readSymbols,
20019
20164
  panelPositions: state.settingsPicker.panelPositions,
20165
+ wrongProxyEnabled: state.settingsPicker.wrongProxyEnabled,
20166
+ wrongProxyUrl: state.settingsPicker.wrongProxyUrl,
20167
+ wrongProxyUrlEditing: state.settingsPicker.wrongProxyUrlEditing,
20168
+ wrongProxyUrlDraft: state.settingsPicker.wrongProxyUrlDraft,
20020
20169
  inputHeight,
20021
20170
  filter: state.settingsPicker.filter,
20022
20171
  hint: state.settingsPicker.hint
@@ -20901,6 +21050,7 @@ function SidebarContent({
20901
21050
  const cs = cacheStats ?? { readTokens: 0, writeTokens: 0, hitRatio: 0, savedUsd: 0 };
20902
21051
  const hasCacheActivity = cs.readTokens > 0 || cs.writeTokens > 0;
20903
21052
  const cacheHitPct = Math.round(cs.hitRatio * 100);
21053
+ const activeProviderCache = cs.providers?.find((entry) => entry.provider === provider);
20904
21054
  const cacheCoveragePct = contextWindow && contextWindow.max > 0 ? Math.min(100, Math.round(cacheCoverageTokens / contextWindow.max * 100)) : 0;
20905
21055
  const MAX_SIDEBAR_AGENTS = 12;
20906
21056
  const allEntries = Object.values(entries);
@@ -21065,6 +21215,16 @@ function SidebarContent({
21065
21215
  )
21066
21216
  ) })
21067
21217
  ] }),
21218
+ /* @__PURE__ */ jsx78(
21219
+ SidebarStatRow,
21220
+ {
21221
+ label: `${activeProviderCache?.provider ?? provider ?? "provider"} hit`,
21222
+ value: activeProviderCache ? `${(activeProviderCache.hitRatio * 100).toFixed(1)}%` : `${cacheHitPct}%`,
21223
+ color: theme.success,
21224
+ accent: theme.success,
21225
+ innerWidth: bodyWidth
21226
+ }
21227
+ ),
21068
21228
  /* @__PURE__ */ jsx78(
21069
21229
  SidebarStatRow,
21070
21230
  {
@@ -21084,7 +21244,39 @@ function SidebarContent({
21084
21244
  accent: theme.warn,
21085
21245
  innerWidth: bodyWidth
21086
21246
  }
21087
- )
21247
+ ),
21248
+ cs.cacheWrite5m !== void 0 && cs.cacheWrite1h !== void 0 ? /* @__PURE__ */ jsxs77(Fragment25, { children: [
21249
+ /* @__PURE__ */ jsx78(
21250
+ SidebarStatRow,
21251
+ {
21252
+ label: "write 5m",
21253
+ value: fmtTok(cs.cacheWrite5m),
21254
+ color: theme.textSecondary,
21255
+ accent: theme.warn,
21256
+ innerWidth: bodyWidth
21257
+ }
21258
+ ),
21259
+ /* @__PURE__ */ jsx78(
21260
+ SidebarStatRow,
21261
+ {
21262
+ label: "write 1h",
21263
+ value: fmtTok(cs.cacheWrite1h),
21264
+ color: theme.textSecondary,
21265
+ accent: theme.warn,
21266
+ innerWidth: bodyWidth
21267
+ }
21268
+ )
21269
+ ] }) : null,
21270
+ cs.savedUsd > 0 ? /* @__PURE__ */ jsx78(
21271
+ SidebarStatRow,
21272
+ {
21273
+ label: "saved",
21274
+ value: `~${cs.savedUsd.toFixed(2)}`,
21275
+ color: theme.success,
21276
+ accent: theme.success,
21277
+ innerWidth: bodyWidth
21278
+ }
21279
+ ) : null
21088
21280
  ] }) : null,
21089
21281
  cacheCoverageTokens > 0 && contextWindow ? /* @__PURE__ */ jsx78(
21090
21282
  SidebarStatRow,
@@ -21359,56 +21551,7 @@ function SidebarContent({
21359
21551
  })
21360
21552
  }
21361
21553
  ) : null
21362
- ] }) }) : null,
21363
- (() => {
21364
- const liveTools = [];
21365
- for (const entry of Object.values(entries)) {
21366
- if (entry.currentTool?.name) {
21367
- liveTools.push({
21368
- agent: entry.name || entry.id,
21369
- name: entry.currentTool.name,
21370
- status: "RUNNING",
21371
- color: theme.success
21372
- });
21373
- } else if (entry.recentTools && entry.recentTools.length > 0) {
21374
- const lastTool = entry.recentTools[entry.recentTools.length - 1];
21375
- if (lastTool?.name) {
21376
- liveTools.push({
21377
- agent: entry.name || entry.id,
21378
- name: lastTool.name,
21379
- status: lastTool.ok === false ? "FAIL" : "OK",
21380
- color: lastTool.ok === false ? theme.error : theme.textMuted
21381
- });
21382
- }
21383
- }
21384
- }
21385
- if (liveTools.length === 0) return null;
21386
- return /* @__PURE__ */ jsx78(Card, { innerWidth, marginBottom: 0, accent: theme.accent, children: (bodyWidth) => /* @__PURE__ */ jsxs77(Fragment25, { children: [
21387
- /* @__PURE__ */ jsx78(
21388
- SidebarSectionHeader,
21389
- {
21390
- glyph: glyphs.tools,
21391
- label: "TOOL TICKER",
21392
- color: theme.accent,
21393
- badge: liveTools.some((t) => t.status === "RUNNING") ? "STREAM" : "IDLE",
21394
- badgeColor: liveTools.some((t) => t.status === "RUNNING") ? theme.success : theme.textMuted,
21395
- innerWidth: bodyWidth,
21396
- pill: true
21397
- }
21398
- ),
21399
- liveTools.slice(0, 3).map((item, i) => /* @__PURE__ */ jsx78(
21400
- SidebarStatRow,
21401
- {
21402
- label: `${glyphs.zap} ${item.name}`,
21403
- value: item.status,
21404
- color: item.color,
21405
- accent: item.color,
21406
- innerWidth: bodyWidth
21407
- },
21408
- `ticker-${i}`
21409
- ))
21410
- ] }) });
21411
- })()
21554
+ ] }) }) : null
21412
21555
  ] });
21413
21556
  }
21414
21557
 
@@ -22348,6 +22491,18 @@ function CoordinatorPanelSidebar({
22348
22491
  }
22349
22492
  );
22350
22493
  }
22494
+ function wrongProxyVisual(status) {
22495
+ switch (status) {
22496
+ case "ok":
22497
+ return { glyph: glyphs.success, color: theme.success, pill: "LIVE" };
22498
+ case "warn":
22499
+ return { glyph: glyphs.warning, color: theme.warn, pill: "WARN" };
22500
+ case "down":
22501
+ return { glyph: glyphs.failure, color: theme.error, pill: "DOWN" };
22502
+ default:
22503
+ return { glyph: "?", color: theme.textMuted, pill: "?" };
22504
+ }
22505
+ }
22351
22506
  function ConnectionsPanelSidebar({
22352
22507
  connections,
22353
22508
  width
@@ -22445,6 +22600,77 @@ function ConnectionsPanelSidebar({
22445
22600
  }
22446
22601
  );
22447
22602
  }
22603
+ function WrongProxyPanelSidebar({
22604
+ proxy,
22605
+ width
22606
+ }) {
22607
+ const inner = Math.max(8, width);
22608
+ const bodyWidth = inner >= 18 ? inner - 4 : inner;
22609
+ const visual = proxy ? wrongProxyVisual(proxy.status) : wrongProxyVisual("unknown");
22610
+ const showLatency = inner >= METRIC_MIN_BODY_WIDTH;
22611
+ const latencyLabel = proxy?.latencyMs !== void 0 ? `${proxy.latencyMs}ms` : "";
22612
+ const urlLabel = proxy?.url ?? "\u2014";
22613
+ return /* @__PURE__ */ jsxs80(
22614
+ SidebarPanelFrame,
22615
+ {
22616
+ accent: visual.color,
22617
+ icon: glyphs.link,
22618
+ title: "WRONGPROXY",
22619
+ width,
22620
+ kicker: "proxy daemon",
22621
+ pillLabel: inner >= PILL_MIN_INNER_WIDTH ? `${visual.glyph} ${visual.pill}` : void 0,
22622
+ pillColor: visual.color,
22623
+ right: inner < PILL_MIN_INNER_WIDTH ? /* @__PURE__ */ jsxs80(Text, { color: visual.color, bold: true, children: [
22624
+ visual.glyph,
22625
+ " ",
22626
+ visual.pill
22627
+ ] }) : void 0,
22628
+ children: [
22629
+ /* @__PURE__ */ jsx81(
22630
+ SidebarSectionHeader,
22631
+ {
22632
+ glyph: glyphs.link,
22633
+ label: "ENDPOINT",
22634
+ color: visual.color,
22635
+ innerWidth: bodyWidth
22636
+ }
22637
+ ),
22638
+ /* @__PURE__ */ jsxs80(Box, { flexDirection: "row", width: bodyWidth, children: [
22639
+ /* @__PURE__ */ jsx81(Text, { color: visual.color, children: visual.glyph }),
22640
+ /* @__PURE__ */ jsxs80(Text, { color: theme.textPrimary, bold: true, wrap: "truncate", children: [
22641
+ " ",
22642
+ trunc(
22643
+ urlLabel,
22644
+ Math.max(
22645
+ 3,
22646
+ bodyWidth - (showLatency && latencyLabel ? displayWidth(latencyLabel) + 1 : 2)
22647
+ )
22648
+ )
22649
+ ] }),
22650
+ showLatency && latencyLabel ? /* @__PURE__ */ jsxs80(Fragment27, { children: [
22651
+ /* @__PURE__ */ jsx81(Box, { flexGrow: 1 }),
22652
+ /* @__PURE__ */ jsx81(Text, { color: theme.textMuted, children: latencyLabel })
22653
+ ] }) : null
22654
+ ] }),
22655
+ proxy?.detail ? /* @__PURE__ */ jsxs80(Text, { color: proxy.status === "down" ? theme.error : theme.warn, wrap: "truncate", children: [
22656
+ glyphs.warning,
22657
+ " ",
22658
+ trunc(proxy.detail, bodyWidth - 2)
22659
+ ] }) : null,
22660
+ /* @__PURE__ */ jsx81(
22661
+ SidebarStatRow,
22662
+ {
22663
+ label: `${glyphs.dividerDot} mode`,
22664
+ value: "routing",
22665
+ color: theme.textMuted,
22666
+ innerWidth: bodyWidth
22667
+ }
22668
+ ),
22669
+ /* @__PURE__ */ jsx81(Text, { color: theme.textMuted, wrap: "truncate", dimColor: true, children: trunc("openai-codex excluded by spec", bodyWidth) })
22670
+ ]
22671
+ }
22672
+ );
22673
+ }
22448
22674
 
22449
22675
  // src/app-view-sidebar.tsx
22450
22676
  import { jsx as jsx82, jsxs as jsxs81 } from "react/jsx-runtime";
@@ -22458,7 +22684,9 @@ function AppViewSidebar({
22458
22684
  sidebarProcessData,
22459
22685
  sidebarConnectionsData,
22460
22686
  sidebarKanbanData,
22461
- sidebarPlanData
22687
+ sidebarPlanData,
22688
+ sidebarWrongProxyEnabled,
22689
+ sidebarWrongProxyData
22462
22690
  }) {
22463
22691
  const { agent } = host;
22464
22692
  const { state, activity, environment, statusbar, liveTodos, liveSettings } = runtime;
@@ -22572,6 +22800,7 @@ function AppViewSidebar({
22572
22800
  }
22573
22801
  ) : null,
22574
22802
  sidebarSlotVisible("connections") ? /* @__PURE__ */ jsx82(ConnectionsPanelSidebar, { connections: sidebarConnectionsData, width: sidebarContentWidth }) : null,
22803
+ sidebarWrongProxyEnabled ? /* @__PURE__ */ jsx82(WrongProxyPanelSidebar, { proxy: sidebarWrongProxyData, width: sidebarContentWidth }) : null,
22575
22804
  hiddenSidebarPanelCount > 0 ? /* @__PURE__ */ jsxs81(Text, { color: theme.textMuted, children: [
22576
22805
  "+",
22577
22806
  hiddenSidebarPanelCount,
@@ -26943,6 +27172,55 @@ function useSidebarConnections(projectRoot, enabled = true) {
26943
27172
  }, [projectRoot, enabled]);
26944
27173
  return connections;
26945
27174
  }
27175
+ function useSidebarWrongProxy(url, enabled = true) {
27176
+ const [data, setData] = useState24(null);
27177
+ useEffect26(() => {
27178
+ if (!enabled || !url) {
27179
+ setData(null);
27180
+ return;
27181
+ }
27182
+ let cancelled = false;
27183
+ const probe = async () => {
27184
+ const trimmed = url.trim().replace(/\/+$/, "");
27185
+ const healthUrl = `${trimmed}/api/health`;
27186
+ const startedAt = Date.now();
27187
+ try {
27188
+ const res = await fetch(healthUrl, {
27189
+ method: "GET",
27190
+ // 2s budget — matches the runtime probe in proxy-probe.ts so a
27191
+ // hung localhost:8000 cannot stall the render loop. The TUI
27192
+ // never sees AbortError unless the caller tears us down.
27193
+ signal: AbortSignal.timeout(2e3),
27194
+ headers: { accept: "application/json" }
27195
+ });
27196
+ if (cancelled) return;
27197
+ const ok = res.ok && res.status >= 200 && res.status < 300;
27198
+ setData({
27199
+ url: trimmed,
27200
+ status: ok ? "ok" : "warn",
27201
+ latencyMs: Date.now() - startedAt,
27202
+ detail: ok ? void 0 : `HTTP ${res.status}`
27203
+ });
27204
+ } catch (error) {
27205
+ if (cancelled) return;
27206
+ const message = error instanceof Error ? error.message : String(error);
27207
+ setData({
27208
+ url: trimmed,
27209
+ status: "down",
27210
+ latencyMs: Date.now() - startedAt,
27211
+ detail: message || "unreachable"
27212
+ });
27213
+ }
27214
+ };
27215
+ void probe();
27216
+ const id = setInterval(() => void probe(), 8e3);
27217
+ return () => {
27218
+ cancelled = true;
27219
+ clearInterval(id);
27220
+ };
27221
+ }, [url, enabled]);
27222
+ return data;
27223
+ }
26946
27224
  function useSidebarKanban(projectRoot, enabled = true) {
26947
27225
  const [data, setData] = useState24({ columns: [], totalActive: 0, activeCardTitles: [] });
26948
27226
  useEffect26(() => {
@@ -26994,11 +27272,7 @@ function useSidebarKanban(projectRoot, enabled = true) {
26994
27272
  import { jsx as jsx98, jsxs as jsxs95 } from "react/jsx-runtime";
26995
27273
  var INPUT_PROMPT = DEFAULT_INPUT_PROMPT;
26996
27274
  function AppView({ host, runtime }) {
26997
- const {
26998
- agent,
26999
- appVersion,
27000
- setSuggestions
27001
- } = host;
27275
+ const { agent, appVersion, setSuggestions } = host;
27002
27276
  const {
27003
27277
  state,
27004
27278
  activity,
@@ -27033,6 +27307,12 @@ function AppView({ host, runtime }) {
27033
27307
  sidebarSlotVisible("connections")
27034
27308
  );
27035
27309
  const sidebarKanbanData = useSidebarKanban(agent.ctx.projectRoot, sidebarSlotVisible("kanban"));
27310
+ const wrongProxyEnabled = state.settingsPicker.open ? state.settingsPicker.wrongProxyEnabled === true : liveSettings?.wrongProxyEnabled === true;
27311
+ const wrongProxyUrl = state.settingsPicker.open ? state.settingsPicker.wrongProxyUrl : liveSettings?.wrongProxyUrl;
27312
+ const sidebarWrongProxyData = useSidebarWrongProxy(
27313
+ wrongProxyUrl,
27314
+ wrongProxyEnabled && sidebarWidth > 0
27315
+ );
27036
27316
  const sidebarPlanData = usePlanPanelData(
27037
27317
  agent.ctx.projectRoot,
27038
27318
  agent.ctx.session?.id ?? null,
@@ -27122,7 +27402,9 @@ function AppView({ host, runtime }) {
27122
27402
  sidebarProcessData,
27123
27403
  sidebarConnectionsData,
27124
27404
  sidebarKanbanData,
27125
- sidebarPlanData
27405
+ sidebarPlanData,
27406
+ sidebarWrongProxyEnabled: wrongProxyEnabled,
27407
+ sidebarWrongProxyData
27126
27408
  }
27127
27409
  )
27128
27410
  ] })
@@ -27711,7 +27993,12 @@ function routeSettingsOverlayKey(host, input, key, isEnter) {
27711
27993
  showSageMemoryInject: config.showSageMemoryInject ?? false,
27712
27994
  sageMemoryInjectThreshold: config.sageMemoryInjectThreshold ?? 0.85,
27713
27995
  nextStepsTool: config.nextStepsTool ?? false,
27714
- readSymbols: config.readSymbols ?? false
27996
+ readSymbols: config.readSymbols ?? false,
27997
+ // WrongProxy / WrongTrace: hydrate from the persisted Config (CLI
27998
+ // adapter owns the read/write — see LiveSettingsInput + the
27999
+ // tui-settings-adapter.ts branch tree).
28000
+ wrongProxyEnabled: config.wrongProxyEnabled ?? false,
28001
+ wrongProxyUrl: config.wrongProxyUrl ?? "http://localhost:8000"
27715
28002
  });
27716
28003
  return true;
27717
28004
  }
@@ -30307,6 +30594,32 @@ function tryToolsSettingsPickerKeys(host, input, key, isEnter, debouncedEnter2)
30307
30594
  }
30308
30595
  return true;
30309
30596
  }
30597
+ if (sp.wrongProxyUrlEditing) {
30598
+ if (key.escape) {
30599
+ dispatch({ type: "settingsWrongProxyUrlEditCancel" });
30600
+ return true;
30601
+ }
30602
+ if (isEnter) {
30603
+ if (debouncedEnter2(host)) return true;
30604
+ dispatch({ type: "settingsWrongProxyUrlEditCommit" });
30605
+ return true;
30606
+ }
30607
+ if (key.backspace) {
30608
+ dispatch({
30609
+ type: "settingsWrongProxyUrlEditChange",
30610
+ draft: sp.wrongProxyUrlDraft.slice(0, -1)
30611
+ });
30612
+ return true;
30613
+ }
30614
+ if (input && input.length === 1 && input.charCodeAt(0) >= 32 && input.charCodeAt(0) < 127) {
30615
+ dispatch({
30616
+ type: "settingsWrongProxyUrlEditChange",
30617
+ draft: sp.wrongProxyUrlDraft + input
30618
+ });
30619
+ return true;
30620
+ }
30621
+ return true;
30622
+ }
30310
30623
  if (key.escape || key.ctrl && input === "s") {
30311
30624
  dispatch({ type: "settingsClose" });
30312
30625
  return true;
@@ -31076,6 +31389,8 @@ function useAppPickerKeys({
31076
31389
  }
31077
31390
  if (sp.field === THINKING_WORD_FIELD) {
31078
31391
  dispatch({ type: "settingsThinkingEditStart" });
31392
+ } else if (sp.field === WRONGPROXY_URL_FIELD) {
31393
+ dispatch({ type: "settingsWrongProxyUrlEditStart" });
31079
31394
  } else {
31080
31395
  dispatch({ type: "settingsValueChange", delta: 1 });
31081
31396
  }
@@ -32141,7 +32456,19 @@ function createInitialState(options) {
32141
32456
  showSageMemoryInject: true,
32142
32457
  sageMemoryInjectThreshold: 0.85,
32143
32458
  nextStepsTool: false,
32144
- readSymbols: false
32459
+ readSymbols: false,
32460
+ // WrongProxy / WrongTrace. Defaults match the WebUI LocalPrefs
32461
+ // (master switch off; URL 'http://localhost:8000'). The runtime
32462
+ // probe in `packages/cli/src/wiring/proxy-probe.ts` reacts via
32463
+ // the WS `prefs.update` pipeline — these initial values only
32464
+ // cover the slice before the first `settingsOpen` action lands.
32465
+ wrongProxyEnabled: false,
32466
+ wrongProxyUrl: "http://localhost:8000",
32467
+ // WrongProxy URL text-edit state (field 60). The reducer quartet
32468
+ // at `reducers/settings-values.ts:733-801` seeds the draft from
32469
+ // `wrongProxyUrl` on Start and clears it on Commit/Cancel.
32470
+ wrongProxyUrlEditing: false,
32471
+ wrongProxyUrlDraft: ""
32145
32472
  },
32146
32473
  statuslinePicker: {
32147
32474
  open: false,
@@ -34535,13 +34862,31 @@ function reduceSettingsPanel(state, action) {
34535
34862
  sageMemoryInjectThreshold: action.sageMemoryInjectThreshold,
34536
34863
  nextStepsTool: action.nextStepsTool,
34537
34864
  readSymbols: action.readSymbols,
34865
+ // WrongProxy / WrongTrace: hydrate from the persisted boot-time
34866
+ // values pushed into the action by the TUI settings adapter.
34867
+ // The runtime probe (see `packages/cli/src/wiring/proxy-probe.ts`)
34868
+ // reacts to these via the WS `prefs.update` pipeline.
34869
+ wrongProxyEnabled: action.wrongProxyEnabled,
34870
+ wrongProxyUrl: action.wrongProxyUrl,
34871
+ // WrongProxy URL text-edit state (field 60): always defaults to
34872
+ // 'not editing' + empty draft on open. The Start action seeds
34873
+ // the draft from `wrongProxyUrl` at the reducer boundary.
34874
+ wrongProxyUrlEditing: false,
34875
+ wrongProxyUrlDraft: "",
34538
34876
  hint: void 0
34539
34877
  }
34540
34878
  };
34541
34879
  case "settingsClose":
34542
34880
  return {
34543
34881
  ...state,
34544
- settingsPicker: { ...state.settingsPicker, open: false, hint: void 0 }
34882
+ ...closePanels(state),
34883
+ settingsPicker: {
34884
+ ...state.settingsPicker,
34885
+ open: false,
34886
+ filter: "",
34887
+ wrongProxyUrlEditing: false,
34888
+ wrongProxyUrlDraft: ""
34889
+ }
34545
34890
  };
34546
34891
  case "settingsFieldMove": {
34547
34892
  const next = (state.settingsPicker.field + action.delta + SETTINGS_FIELD_COUNT) % SETTINGS_FIELD_COUNT;
@@ -34553,6 +34898,8 @@ function reduceSettingsPanel(state, action) {
34553
34898
  lastSettingsField: next,
34554
34899
  thinkingWordEditing: false,
34555
34900
  thinkingWordDraft: "",
34901
+ wrongProxyUrlEditing: false,
34902
+ wrongProxyUrlDraft: "",
34556
34903
  hint: void 0
34557
34904
  }
34558
34905
  };
@@ -34565,6 +34912,8 @@ function reduceSettingsPanel(state, action) {
34565
34912
  ...state.settingsPicker,
34566
34913
  field,
34567
34914
  lastSettingsField: field,
34915
+ wrongProxyUrlEditing: false,
34916
+ wrongProxyUrlDraft: "",
34568
34917
  hint: void 0
34569
34918
  }
34570
34919
  };
@@ -34582,6 +34931,7 @@ function reduceSettingsPanel(state, action) {
34582
34931
 
34583
34932
  // src/reducers/settings-values.ts
34584
34933
  import { expectDefined as expectDefined4 } from "@wrongstack/core/utils";
34934
+ import { MAX_WRONGPROXY_URL_LENGTH } from "@wrongstack/core/types";
34585
34935
  var settingsValueActionTypes = [
34586
34936
  "settingsValueChange",
34587
34937
  "settingsValueSet",
@@ -34589,7 +34939,16 @@ var settingsValueActionTypes = [
34589
34939
  "settingsThinkingEditStart",
34590
34940
  "settingsThinkingEditChange",
34591
34941
  "settingsThinkingEditCommit",
34592
- "settingsThinkingEditCancel"
34942
+ "settingsThinkingEditCancel",
34943
+ // WrongProxy / WrongTrace URL (field 60) text-edit quartet.
34944
+ // Adding these here routes them into `reduceSettingsValues` via the
34945
+ // `SettingsValueAction` extract — without these, the actions hit the
34946
+ // generic `reduce*` path and the `action satisfies never` narrowing
34947
+ // marks the whole `Action` union as unassignable to `never`.
34948
+ "settingsWrongProxyUrlEditStart",
34949
+ "settingsWrongProxyUrlEditChange",
34950
+ "settingsWrongProxyUrlEditCommit",
34951
+ "settingsWrongProxyUrlEditCancel"
34593
34952
  ];
34594
34953
  var settingsValueActionTypeSet = new Set(settingsValueActionTypes);
34595
34954
  function isSettingsValueAction(action) {
@@ -35024,6 +35383,11 @@ function reduceSettingsValues(state, action) {
35024
35383
  ...state,
35025
35384
  settingsPicker: { ...sp, nextStepsTool: !sp.nextStepsTool, hint: void 0 }
35026
35385
  };
35386
+ if (f === 59)
35387
+ return {
35388
+ ...state,
35389
+ settingsPicker: { ...sp, wrongProxyEnabled: !sp.wrongProxyEnabled, hint: void 0 }
35390
+ };
35027
35391
  if (f >= PANEL_POSITION_FIELD_START && f - PANEL_POSITION_FIELD_START < PANEL_IDS.length) {
35028
35392
  const PANEL_POSITION_CYCLE = ["bottom", "sidebar"];
35029
35393
  const panelId = PANEL_IDS[f - PANEL_POSITION_FIELD_START];
@@ -35122,6 +35486,76 @@ function reduceSettingsValues(state, action) {
35122
35486
  hint: void 0
35123
35487
  }
35124
35488
  };
35489
+ // WrongProxy URL (field 60) free-text edit quartet. Mirrors the
35490
+ // `settingsThinkingEdit*` shape: Enter on the row opens the edit
35491
+ // (seeding the draft with the current URL), Change caps the draft
35492
+ // (URLs don't have a strict length cap, but we cap to a sane 2 KiB
35493
+ // to prevent pathological input), Commit validates the scheme and
35494
+ // applies the new value, Cancel discards. Empty draft on commit
35495
+ // keeps the current URL (same semantics as the thinking-word edit).
35496
+ case "settingsWrongProxyUrlEditStart":
35497
+ return {
35498
+ ...state,
35499
+ settingsPicker: {
35500
+ ...state.settingsPicker,
35501
+ wrongProxyUrlEditing: true,
35502
+ // Seed the draft with the current URL so the user edits from it.
35503
+ wrongProxyUrlDraft: state.settingsPicker.wrongProxyUrl,
35504
+ hint: void 0
35505
+ }
35506
+ };
35507
+ case "settingsWrongProxyUrlEditChange":
35508
+ return {
35509
+ ...state,
35510
+ settingsPicker: {
35511
+ ...state.settingsPicker,
35512
+ // Cap to a sane 2 KiB so a runaway paste can't bloat the slice.
35513
+ wrongProxyUrlDraft: action.draft.slice(0, MAX_WRONGPROXY_URL_LENGTH),
35514
+ hint: void 0
35515
+ }
35516
+ };
35517
+ case "settingsWrongProxyUrlEditCommit": {
35518
+ const sp = state.settingsPicker;
35519
+ const raw = sp.wrongProxyUrlDraft.trim();
35520
+ if (raw.length === 0) {
35521
+ return {
35522
+ ...state,
35523
+ settingsPicker: {
35524
+ ...sp,
35525
+ wrongProxyUrlEditing: false,
35526
+ wrongProxyUrlDraft: "",
35527
+ hint: void 0
35528
+ }
35529
+ };
35530
+ }
35531
+ let valid = false;
35532
+ try {
35533
+ const parsed = new URL(raw);
35534
+ valid = parsed.protocol === "http:" || parsed.protocol === "https:";
35535
+ } catch {
35536
+ valid = false;
35537
+ }
35538
+ return {
35539
+ ...state,
35540
+ settingsPicker: {
35541
+ ...sp,
35542
+ wrongProxyUrl: valid ? raw : sp.wrongProxyUrl,
35543
+ wrongProxyUrlEditing: false,
35544
+ wrongProxyUrlDraft: "",
35545
+ hint: valid ? void 0 : "Invalid URL \u2014 use http://host:port or https://host:port (no whitespace)."
35546
+ }
35547
+ };
35548
+ }
35549
+ case "settingsWrongProxyUrlEditCancel":
35550
+ return {
35551
+ ...state,
35552
+ settingsPicker: {
35553
+ ...state.settingsPicker,
35554
+ wrongProxyUrlEditing: false,
35555
+ wrongProxyUrlDraft: "",
35556
+ hint: void 0
35557
+ }
35558
+ };
35125
35559
  default:
35126
35560
  void action;
35127
35561
  return state;
@@ -35133,6 +35567,7 @@ var SIDEBAR_MISSION_MAX_WRAP_LINES = 10;
35133
35567
  function computeMaxSidebarScroll(state, viewportHeight = 20, effectiveSwarmOnSidebar) {
35134
35568
  let contentHeight = 0;
35135
35569
  contentHeight += 14;
35570
+ contentHeight += 11;
35136
35571
  contentHeight += 5;
35137
35572
  const swarmOnSidebar = effectiveSwarmOnSidebar ?? state.settingsPicker.showAgentSwarmPanel === "sidebar";
35138
35573
  if (swarmOnSidebar) {
@@ -41328,7 +41763,12 @@ function usePanelControllers({
41328
41763
  showSageMemoryInject: s2.showSageMemoryInject ?? false,
41329
41764
  sageMemoryInjectThreshold: s2.sageMemoryInjectThreshold ?? 0.85,
41330
41765
  nextStepsTool: s2.nextStepsTool ?? false,
41331
- readSymbols: s2.readSymbols ?? false
41766
+ readSymbols: s2.readSymbols ?? false,
41767
+ // WrongProxy / WrongTrace: hydrate from the picker state slice.
41768
+ // Same defaults as the WebUI LocalPrefs and the CLI adapter's
41769
+ // branch tree — see tui-settings-adapter.ts.
41770
+ wrongProxyEnabled: s2.wrongProxyEnabled ?? false,
41771
+ wrongProxyUrl: s2.wrongProxyUrl ?? "http://localhost:8000"
41332
41772
  });
41333
41773
  }, [getSettings]);
41334
41774
  const refreshPluginPicker = React11.useCallback(() => {
@@ -42660,7 +43100,13 @@ function useSettingsAutoSave(state, saveSettings, dispatch) {
42660
43100
  showSageMemoryInject: sp.showSageMemoryInject,
42661
43101
  sageMemoryInjectThreshold: sp.sageMemoryInjectThreshold,
42662
43102
  nextStepsTool: sp.nextStepsTool,
42663
- readSymbols: sp.readSymbols
43103
+ readSymbols: sp.readSymbols,
43104
+ // WrongProxy / WrongTrace: persist the picker-state values to
43105
+ // the same Config keys the adapter exposes (see LiveSettingsInput
43106
+ // and the tui-settings-adapter.ts branch tree). Optional with
43107
+ // `??` fallback — older persisted configs may not have the keys.
43108
+ wrongProxyEnabled: sp.wrongProxyEnabled ?? false,
43109
+ wrongProxyUrl: sp.wrongProxyUrl ?? "http://localhost:8000"
42664
43110
  })
42665
43111
  ).then((err) => {
42666
43112
  if (err) dispatch({ type: "settingsHint", text: err });
@@ -42714,6 +43160,8 @@ function useSettingsAutoSave(state, saveSettings, dispatch) {
42714
43160
  state.settingsPicker.sageMemoryInjectThreshold,
42715
43161
  state.settingsPicker.nextStepsTool,
42716
43162
  state.settingsPicker.readSymbols,
43163
+ state.settingsPicker.wrongProxyEnabled,
43164
+ state.settingsPicker.wrongProxyUrl,
42717
43165
  saveSettings
42718
43166
  ]);
42719
43167
  }
@@ -46254,6 +46702,50 @@ function createRunTuiClientRegistration(opts) {
46254
46702
  return { register, unregister };
46255
46703
  }
46256
46704
 
46705
+ // src/run-tui-teardown.ts
46706
+ var DEFAULT_CLOSE_BUDGET_MS = 1500;
46707
+ function createDurableTeardown(opts) {
46708
+ const closeBudgetMs = opts.closeBudgetMs ?? DEFAULT_CLOSE_BUDGET_MS;
46709
+ const salvageSync = () => {
46710
+ try {
46711
+ opts.getSession()?.flushSync?.();
46712
+ } catch {
46713
+ }
46714
+ };
46715
+ const awaitCloseBounded = async () => {
46716
+ await Promise.race([
46717
+ (async () => {
46718
+ try {
46719
+ await opts.getSession()?.close();
46720
+ } catch {
46721
+ }
46722
+ })(),
46723
+ new Promise((resolve) => {
46724
+ const t = setTimeout(resolve, closeBudgetMs);
46725
+ t.unref();
46726
+ })
46727
+ ]);
46728
+ };
46729
+ const awaitDurableClose = async () => {
46730
+ salvageSync();
46731
+ try {
46732
+ opts.cleanup();
46733
+ } catch {
46734
+ }
46735
+ await awaitCloseBounded();
46736
+ salvageSync();
46737
+ };
46738
+ const shutdownViaSignal = async (exitCode) => {
46739
+ try {
46740
+ opts.killChildren?.();
46741
+ } catch {
46742
+ }
46743
+ await awaitDurableClose();
46744
+ opts.exit(exitCode);
46745
+ };
46746
+ return { shutdownViaSignal, awaitDurableClose, salvageSync };
46747
+ }
46748
+
46257
46749
  // src/run-tui-title-controller.ts
46258
46750
  import * as path11 from "node:path";
46259
46751
 
@@ -46508,6 +47000,24 @@ async function runTui(opts) {
46508
47000
  unsilenceTerminal();
46509
47001
  }
46510
47002
  };
47003
+ let resolveRun;
47004
+ let runExitCode = 0;
47005
+ const finishRun = (code) => {
47006
+ detachListeners();
47007
+ if (!resolveRun) return;
47008
+ const resolve = resolveRun;
47009
+ resolveRun = void 0;
47010
+ resolve(code);
47011
+ };
47012
+ const durableTeardown = createDurableTeardown({
47013
+ getSession: () => opts.agent.ctx.session,
47014
+ killChildren: () => getProcessRegistry10().killAll({ force: true, preserveBackground: true }),
47015
+ cleanup,
47016
+ // Resolve the host instead of process.exit so CLI teardown (plugin
47017
+ // stop, Telegram lock.release, vector-memory close) still runs.
47018
+ // Rapid Ctrl+C and the hung-unmount timer still hard-exit.
47019
+ exit: (code) => finishRun(code)
47020
+ });
46511
47021
  const RAPID_EXIT_WINDOW_MS = 2e3;
46512
47022
  const RAPID_EXIT_THRESHOLD = 2;
46513
47023
  let ctrlCPressTimestamps = [];
@@ -46518,21 +47028,20 @@ async function runTui(opts) {
46518
47028
  getProcessRegistry10().killAll({ force: true, preserveBackground: true });
46519
47029
  } catch {
46520
47030
  }
46521
- try {
46522
- opts.agent.ctx.session.flushSync?.();
46523
- } catch {
46524
- }
47031
+ durableTeardown.salvageSync();
46525
47032
  cleanup();
46526
47033
  process.exit(130);
46527
47034
  };
46528
47035
  const signals = ["SIGTERM", "SIGHUP", "SIGINT"];
46529
47036
  const signalHandler = () => {
47037
+ runExitCode = 143;
46530
47038
  inkInstance?.unmount();
47039
+ void durableTeardown.shutdownViaSignal(143);
47040
+ };
47041
+ const exitHandler = () => {
47042
+ durableTeardown.salvageSync();
46531
47043
  cleanup();
46532
- const sig = setTimeout(() => process.exit(143), 5e3);
46533
- sig.unref();
46534
47044
  };
46535
- const exitHandler = () => cleanup();
46536
47045
  const sigintHandler = () => {
46537
47046
  const now = Date.now();
46538
47047
  ctrlCPressTimestamps = ctrlCPressTimestamps.filter((t) => now - t < RAPID_EXIT_WINDOW_MS);
@@ -46542,10 +47051,9 @@ async function runTui(opts) {
46542
47051
  forceExitViaRapidCtrlC();
46543
47052
  return;
46544
47053
  }
47054
+ runExitCode = 130;
46545
47055
  inkInstance?.unmount();
46546
- cleanup();
46547
- const sig = setTimeout(() => process.exit(130), 5e3);
46548
- sig.unref();
47056
+ void durableTeardown.shutdownViaSignal(130);
46549
47057
  };
46550
47058
  process.on("SIGINT", sigintHandler);
46551
47059
  for (const s2 of ["SIGTERM", "SIGHUP", "SIGBREAK"]) {
@@ -46584,24 +47092,32 @@ async function runTui(opts) {
46584
47092
  };
46585
47093
  void tuiClientRegistration.register();
46586
47094
  return new Promise((resolve) => {
46587
- let exitCode = 0;
47095
+ resolveRun = resolve;
46588
47096
  let hardExitTimer = null;
46589
47097
  const onExit = (code) => {
46590
- exitCode = code;
47098
+ runExitCode = code;
46591
47099
  };
46592
47100
  const settle = (code) => {
46593
47101
  if (hardExitTimer) {
46594
47102
  clearTimeout(hardExitTimer);
46595
47103
  hardExitTimer = null;
46596
47104
  }
46597
- cleanup();
46598
- detachListeners();
46599
- resolve(code);
47105
+ void durableTeardown.awaitDurableClose().then(
47106
+ () => finishRun(code),
47107
+ () => finishRun(code)
47108
+ );
46600
47109
  };
46601
47110
  const requestExit = (code) => {
46602
47111
  onExit(code);
46603
47112
  inkInstance?.unmount();
46604
- hardExitTimer = setTimeout(() => process.exit(code), 5e3);
47113
+ hardExitTimer = setTimeout(() => {
47114
+ durableTeardown.salvageSync();
47115
+ try {
47116
+ getProcessRegistry10().killAll({ force: true, preserveBackground: true });
47117
+ } catch {
47118
+ }
47119
+ process.exit(code);
47120
+ }, 5e3);
46605
47121
  hardExitTimer.unref();
46606
47122
  };
46607
47123
  opts.requestExit = requestExit;
@@ -46772,7 +47288,7 @@ async function runTui(opts) {
46772
47288
  detachResize = () => stdout.off("resize", onResize2);
46773
47289
  instance.waitUntilExit().then(() => {
46774
47290
  detachResize?.();
46775
- settle(exitCode);
47291
+ settle(runExitCode);
46776
47292
  }).catch(() => {
46777
47293
  detachResize?.();
46778
47294
  settle(1);