@wrongstack/tui 0.296.2 → 0.296.4

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.
Files changed (47) hide show
  1. package/dist/app-action-type.d.ts +6 -5
  2. package/dist/app-action-type.d.ts.map +1 -1
  3. package/dist/app-initial-state.d.ts +1 -1
  4. package/dist/app-initial-state.d.ts.map +1 -1
  5. package/dist/app-settings-type.d.ts +2 -0
  6. package/dist/app-settings-type.d.ts.map +1 -1
  7. package/dist/app-state.d.ts +6 -4
  8. package/dist/app-state.d.ts.map +1 -1
  9. package/dist/app-view.d.ts.map +1 -1
  10. package/dist/components/history/entry.d.ts.map +1 -1
  11. package/dist/components/history/sage-output-format.d.ts +38 -0
  12. package/dist/components/history/sage-output-format.d.ts.map +1 -1
  13. package/dist/components/history/tool-group.d.ts.map +1 -1
  14. package/dist/components/history/utils.d.ts +4 -2
  15. package/dist/components/history/utils.d.ts.map +1 -1
  16. package/dist/components/refine-failure-panel.d.ts +2 -1
  17. package/dist/components/refine-failure-panel.d.ts.map +1 -1
  18. package/dist/components/settings-picker-model.d.ts +2 -1
  19. package/dist/components/settings-picker-model.d.ts.map +1 -1
  20. package/dist/components/settings-picker.d.ts +4 -2
  21. package/dist/components/settings-picker.d.ts.map +1 -1
  22. package/dist/connections-health.d.ts.map +1 -1
  23. package/dist/history-archive.d.ts +0 -1
  24. package/dist/history-archive.d.ts.map +1 -1
  25. package/dist/history-entry.d.ts +50 -0
  26. package/dist/history-entry.d.ts.map +1 -1
  27. package/dist/hooks/use-git-session-status.d.ts.map +1 -1
  28. package/dist/hooks/use-panel-controllers.d.ts +1 -1
  29. package/dist/hooks/use-panel-controllers.d.ts.map +1 -1
  30. package/dist/hooks/use-provider-event-bridge.d.ts.map +1 -1
  31. package/dist/hooks/use-settings-auto-save.d.ts +1 -1
  32. package/dist/hooks/use-settings-auto-save.d.ts.map +1 -1
  33. package/dist/hooks/use-statusbar-view-model.d.ts.map +1 -1
  34. package/dist/hooks/use-subagent-events.d.ts.map +1 -1
  35. package/dist/index.js +1520 -1069
  36. package/dist/index.js.map +4 -4
  37. package/dist/layout-engine.d.ts +4 -0
  38. package/dist/layout-engine.d.ts.map +1 -1
  39. package/dist/memory-context-monitor.d.ts +6 -0
  40. package/dist/memory-context-monitor.d.ts.map +1 -1
  41. package/dist/overlay-key-router.d.ts +1 -1
  42. package/dist/overlay-key-router.d.ts.map +1 -1
  43. package/dist/reducers/settings-panel.d.ts.map +1 -1
  44. package/dist/reducers/settings-values.d.ts.map +1 -1
  45. package/dist/settings-contracts.d.ts +5 -0
  46. package/dist/settings-contracts.d.ts.map +1 -1
  47. package/package.json +7 -7
package/dist/index.js CHANGED
@@ -636,6 +636,7 @@ function applyMemoryInjectorRun(state, payload) {
636
636
  kind: stringValue(item.kind) ?? "unknown",
637
637
  text: capMemoryDetailText(stringValue(item.text) ?? ""),
638
638
  score: numberValue(item.score),
639
+ relationStrength: numberValue(item.relationStrength),
639
640
  confidence: numberValue(item.confidence),
640
641
  freshness: numberValue(item.freshness),
641
642
  importance: numberValue(item.importance),
@@ -731,6 +732,7 @@ function placeholder(id, at, state) {
731
732
  kind: "unknown",
732
733
  text: "Details unavailable in this TUI process.",
733
734
  score: 0,
735
+ relationStrength: 0,
734
736
  confidence: 0,
735
737
  freshness: 0,
736
738
  importance: 0,
@@ -3459,62 +3461,6 @@ function cyclePreset(presets, current, delta) {
3459
3461
  return presets[next];
3460
3462
  }
3461
3463
 
3462
- // src/components/settings-picker-jumps.ts
3463
- var SETTINGS_PICKER_JUMP_CHORDS = Object.freeze([
3464
- { mod: "ctrl", letter: "i", field: 20, label: "Index on session start" },
3465
- { mod: "ctrl", letter: "m", field: 21, label: "Multi-diff summary" },
3466
- { mod: "ctrl", letter: "w", field: 22, label: "Thinking word" },
3467
- { mod: "ctrl", letter: "r", field: 17, label: "Refine preview countdown" },
3468
- { mod: "ctrl", letter: "e", field: 18, label: "Refine" },
3469
- { mod: "ctrl", letter: "n", field: 23, label: "Reasoning mode" },
3470
- { mod: "ctrl", letter: "l", field: 30, label: "Max concurrent" },
3471
- { mod: "ctrl", letter: "d", field: 34, label: "Statusline" },
3472
- { mod: "alt", letter: "a", field: 0, label: "Default autonomy mode" },
3473
- { mod: "alt", letter: "y", field: 3, label: "YOLO mode" },
3474
- { mod: "alt", letter: "c", field: 5, label: "Completion chime" },
3475
- { mod: "alt", letter: "s", field: 6, label: "Confirm before exit" },
3476
- { mod: "alt", letter: "t", field: 13, label: "Token-saving mode" },
3477
- { mod: "alt", letter: "x", field: 29, label: "Context mode" },
3478
- { mod: "alt-shift", letter: "l", field: 31, label: "Log level" },
3479
- { mod: "alt-shift", letter: "a", field: 32, label: "Audit level" },
3480
- { mod: "alt-shift", letter: "b", field: 33, label: "Stream debug logging" },
3481
- { mod: "alt-shift", letter: "g", field: 35, label: "Config scope" },
3482
- { mod: "alt", letter: "n", field: 36, label: "Animation" },
3483
- { mod: "alt", letter: "p", field: 40, label: "Show agent swarm panel" },
3484
- { mod: "alt", letter: "r", field: 41, label: "Pre-refine countdown" }
3485
- ]);
3486
- function settingsPickerJumpField(mod, letter) {
3487
- const lower = letter.toLowerCase();
3488
- const chord = SETTINGS_PICKER_JUMP_CHORDS.find((c) => c.mod === mod && c.letter === lower);
3489
- return chord?.field;
3490
- }
3491
- function settingsPickerSlug(label) {
3492
- return label.toLowerCase().replace(/\s+/g, "-");
3493
- }
3494
- function settingsPickerJumpByName(name) {
3495
- const query = settingsPickerSlug(name);
3496
- if (!query) return void 0;
3497
- const exact = SETTINGS_PICKER_JUMP_CHORDS.find((c) => settingsPickerSlug(c.label) === query);
3498
- if (exact) return exact.field;
3499
- const queryTokens = query.split("-");
3500
- for (const c of SETTINGS_PICKER_JUMP_CHORDS) {
3501
- const labelTokens = settingsPickerSlug(c.label).split("-");
3502
- if (queryTokens.every((t, i) => labelTokens[i] === t)) {
3503
- return c.field;
3504
- }
3505
- }
3506
- for (const c of SETTINGS_PICKER_JUMP_CHORDS) {
3507
- const labelTokens = settingsPickerSlug(c.label).split("-");
3508
- if (labelTokens.some((t) => t === query || t.startsWith(`${query}-`))) {
3509
- return c.field;
3510
- }
3511
- }
3512
- return void 0;
3513
- }
3514
- function settingsPickerJumpNames() {
3515
- return SETTINGS_PICKER_JUMP_CHORDS.map((c) => settingsPickerSlug(c.label));
3516
- }
3517
-
3518
3464
  // src/components/settings-picker-filter.ts
3519
3465
  function fuzzyMatch(haystack, needle) {
3520
3466
  const lower = haystack.toLowerCase();
@@ -3614,75 +3560,60 @@ function buildSettingsFilterState(filter, chords) {
3614
3560
  };
3615
3561
  }
3616
3562
 
3617
- // src/components/settings-picker-row-list.tsx
3618
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
3619
- function renderPickerRow(row, fieldIdx, selected, filterActive, highlightSegments) {
3620
- const labelStr = row.label ?? "";
3621
- const segments = highlightSegments(labelStr);
3622
- const padNeeded = Math.max(0, 26 - labelStr.length);
3623
- return /* @__PURE__ */ jsxs7(Text, { inverse: selected, ...selected ? { color: "yellow" } : {}, children: [
3624
- selected ? "\u203A " : " ",
3625
- filterActive ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
3626
- segments.map((seg, j) => /* @__PURE__ */ jsx8(Text, { bold: true, ...seg.match ? { color: "yellow" } : { dimColor: true }, children: seg.text }, j)),
3627
- /* @__PURE__ */ jsx8(Text, { bold: true, dimColor: true, children: " ".repeat(padNeeded) })
3628
- ] }) : /* @__PURE__ */ jsx8(Text, { bold: true, children: labelStr.padEnd(26) }),
3629
- /* @__PURE__ */ jsx8(Text, { color: "cyan", children: String(row.value ?? "").padEnd(12) }),
3630
- /* @__PURE__ */ jsx8(Text, { dimColor: true, children: row.detail ?? "" })
3631
- ] }, `row-${row.label ?? fieldIdx}`);
3563
+ // src/components/settings-picker-jumps.ts
3564
+ var SETTINGS_PICKER_JUMP_CHORDS = Object.freeze([
3565
+ { mod: "ctrl", letter: "i", field: 20, label: "Index on session start" },
3566
+ { mod: "ctrl", letter: "m", field: 21, label: "Multi-diff summary" },
3567
+ { mod: "ctrl", letter: "w", field: 22, label: "Thinking word" },
3568
+ { mod: "ctrl", letter: "r", field: 17, label: "Refine preview countdown" },
3569
+ { mod: "ctrl", letter: "e", field: 18, label: "Refine" },
3570
+ { mod: "ctrl", letter: "n", field: 23, label: "Reasoning mode" },
3571
+ { mod: "ctrl", letter: "l", field: 30, label: "Max concurrent" },
3572
+ { mod: "ctrl", letter: "d", field: 34, label: "Statusline" },
3573
+ { mod: "alt", letter: "a", field: 0, label: "Default autonomy mode" },
3574
+ { mod: "alt", letter: "y", field: 3, label: "YOLO mode" },
3575
+ { mod: "alt", letter: "c", field: 5, label: "Completion chime" },
3576
+ { mod: "alt", letter: "s", field: 6, label: "Confirm before exit" },
3577
+ { mod: "alt", letter: "t", field: 13, label: "Token-saving mode" },
3578
+ { mod: "alt", letter: "x", field: 29, label: "Context mode" },
3579
+ { mod: "alt-shift", letter: "l", field: 31, label: "Log level" },
3580
+ { mod: "alt-shift", letter: "a", field: 32, label: "Audit level" },
3581
+ { mod: "alt-shift", letter: "b", field: 33, label: "Stream debug logging" },
3582
+ { mod: "alt-shift", letter: "g", field: 35, label: "Config scope" },
3583
+ { mod: "alt", letter: "n", field: 36, label: "Animation" },
3584
+ { mod: "alt", letter: "p", field: 40, label: "Show agent swarm panel" },
3585
+ { mod: "alt", letter: "r", field: 41, label: "Pre-refine countdown" }
3586
+ ]);
3587
+ function settingsPickerJumpField(mod, letter) {
3588
+ const lower = letter.toLowerCase();
3589
+ const chord = SETTINGS_PICKER_JUMP_CHORDS.find((c) => c.mod === mod && c.letter === lower);
3590
+ return chord?.field;
3632
3591
  }
3633
- function buildVisibleSectionHeaders(rows, fieldRowIndex, windowStart, windowEnd) {
3634
- const sectionFields = [];
3635
- let curHeader = -1;
3636
- for (let i = 0; i < rows.length; i++) {
3637
- if (rows[i]?.section) curHeader = i;
3638
- else if (curHeader >= 0) {
3639
- const fieldIdx = fieldRowIndex.indexOf(i);
3640
- if (fieldIdx === -1) continue;
3641
- const entry = sectionFields.find((s2) => s2.headerIdx === curHeader);
3642
- if (entry) {
3643
- entry.fieldEnd = fieldIdx + 1;
3644
- } else {
3645
- sectionFields.push({ headerIdx: curHeader, fieldStart: fieldIdx, fieldEnd: fieldIdx + 1 });
3646
- }
3647
- }
3648
- }
3649
- return new Set(
3650
- sectionFields.filter((section) => section.fieldStart < windowEnd && section.fieldEnd > windowStart).map((section) => section.headerIdx)
3651
- );
3592
+ function settingsPickerSlug(label) {
3593
+ return label.toLowerCase().replace(/\s+/g, "-");
3652
3594
  }
3653
- function SettingsPickerRowList({
3654
- rows,
3655
- field,
3656
- fieldRowIndex,
3657
- filterActive,
3658
- rankedResults,
3659
- highlightSegments,
3660
- windowStart,
3661
- windowEnd
3662
- }) {
3663
- if (filterActive) {
3664
- return /* @__PURE__ */ jsx8(Fragment4, { children: rankedResults.map((result) => {
3665
- const fieldIdx = result.chord.field;
3666
- const rowIdx = fieldRowIndex[fieldIdx] ?? -1;
3667
- const row = rows[rowIdx];
3668
- if (!row?.label) return null;
3669
- return renderPickerRow(row, fieldIdx, fieldIdx === field, true, highlightSegments);
3670
- }) });
3595
+ function settingsPickerJumpByName(name) {
3596
+ const query = settingsPickerSlug(name);
3597
+ if (!query) return void 0;
3598
+ const exact = SETTINGS_PICKER_JUMP_CHORDS.find((c) => settingsPickerSlug(c.label) === query);
3599
+ if (exact) return exact.field;
3600
+ const queryTokens = query.split("-");
3601
+ for (const c of SETTINGS_PICKER_JUMP_CHORDS) {
3602
+ const labelTokens = settingsPickerSlug(c.label).split("-");
3603
+ if (queryTokens.every((t, i) => labelTokens[i] === t)) {
3604
+ return c.field;
3605
+ }
3671
3606
  }
3672
- const visibleSections = buildVisibleSectionHeaders(rows, fieldRowIndex, windowStart, windowEnd);
3673
- return /* @__PURE__ */ jsx8(Fragment4, { children: rows.map((row, i) => {
3674
- const fieldAtRow = fieldRowIndex.indexOf(i);
3675
- if (fieldAtRow === -1) {
3676
- if (!visibleSections.has(i)) return null;
3677
- return /* @__PURE__ */ jsxs7(Text, { bold: true, color: "green", children: [
3678
- "\u2500\u2500 ",
3679
- row.section,
3680
- " \u2500\u2500"
3681
- ] }, `section-${row.section ?? i}`);
3607
+ for (const c of SETTINGS_PICKER_JUMP_CHORDS) {
3608
+ const labelTokens = settingsPickerSlug(c.label).split("-");
3609
+ if (labelTokens.some((t) => t === query || t.startsWith(`${query}-`))) {
3610
+ return c.field;
3682
3611
  }
3683
- if (fieldAtRow < windowStart || fieldAtRow >= windowEnd) return null;
3684
- return renderPickerRow(row, fieldAtRow, fieldAtRow === field, false, highlightSegments);
3685
- }) });
3612
+ }
3613
+ return void 0;
3614
+ }
3615
+ function settingsPickerJumpNames() {
3616
+ return SETTINGS_PICKER_JUMP_CHORDS.map((c) => settingsPickerSlug(c.label));
3686
3617
  }
3687
3618
 
3688
3619
  // src/components/settings-picker-model.ts
@@ -3699,7 +3630,15 @@ var CONTEXT_MODE_DESCS = {
3699
3630
  };
3700
3631
  var STATUSLINE_MODES = ["minimum", "detailed", "no-color"];
3701
3632
  var REASONING_MODES = ["auto", "on", "off"];
3702
- var REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
3633
+ var REASONING_EFFORTS = [
3634
+ "none",
3635
+ "minimal",
3636
+ "low",
3637
+ "medium",
3638
+ "high",
3639
+ "xhigh",
3640
+ "max"
3641
+ ];
3703
3642
  var CACHE_TTLS = ["default", "5m", "1h"];
3704
3643
  var STATUSLINE_MODE_DESCS = {
3705
3644
  minimum: "Single line with essential chips only",
@@ -3718,7 +3657,14 @@ function formatBreakerTimeout(ms) {
3718
3657
  }
3719
3658
  var MULTI_DIFF_SUMMARY_THRESHOLD_PRESETS = [3, 5, 8, 10, 15, 0];
3720
3659
  var ENHANCE_LANGUAGES = ["original", "english"];
3721
- var TOKEN_SAVING_TIERS = ["auto", "off", "minimal", "light", "medium", "aggressive"];
3660
+ var TOKEN_SAVING_TIERS = [
3661
+ "auto",
3662
+ "off",
3663
+ "minimal",
3664
+ "light",
3665
+ "medium",
3666
+ "aggressive"
3667
+ ];
3722
3668
  var TOKEN_SAVING_TIER_DESCS = {
3723
3669
  auto: "By window \u2014 full tools, lean prompt on small models (cache-safe)",
3724
3670
  off: "All tools enabled (full prompt)",
@@ -3762,7 +3708,7 @@ var MODE_DESC = {
3762
3708
  suggest: "Shows next-step suggestions after each turn",
3763
3709
  auto: "Self-driving \u2014 agent continues automatically"
3764
3710
  };
3765
- var SETTINGS_FIELD_COUNT = 42;
3711
+ var SETTINGS_FIELD_COUNT = 43;
3766
3712
  var THINKING_WORD_FIELD = 22;
3767
3713
  var THINKING_WORD_PRESETS = [
3768
3714
  "thinking",
@@ -3863,8 +3809,10 @@ var SETTINGS_FIELD_LABELS = [
3863
3809
  // 39
3864
3810
  "Show agent swarm panel",
3865
3811
  // 40
3866
- "Pre-refine countdown"
3812
+ "Pre-refine countdown",
3867
3813
  // 41
3814
+ "Read symbols"
3815
+ // 42
3868
3816
  ];
3869
3817
  function resolveSettingsFieldValue(field, input) {
3870
3818
  const raw = input.trim().toLowerCase();
@@ -3888,15 +3836,26 @@ function resolveSettingsFieldValue(field, input) {
3888
3836
  [33, "debugStream"],
3889
3837
  [37, "breakerEnabled"],
3890
3838
  [39, "showModelReasoning"],
3891
- [40, "showAgentSwarmPanel"]
3839
+ [40, "showAgentSwarmPanel"],
3840
+ [42, "readSymbols"]
3892
3841
  ]);
3893
3842
  const boolKey = BOOL_FIELDS.get(field);
3894
3843
  if (boolKey) {
3895
3844
  if (["on", "true", "yes", "1"].includes(raw)) {
3896
- return { ok: true, patch: { [boolKey]: true }, label, displayValue: "on" };
3845
+ return {
3846
+ ok: true,
3847
+ patch: { [boolKey]: true },
3848
+ label,
3849
+ displayValue: "on"
3850
+ };
3897
3851
  }
3898
3852
  if (["off", "false", "no", "0"].includes(raw)) {
3899
- return { ok: true, patch: { [boolKey]: false }, label, displayValue: "off" };
3853
+ return {
3854
+ ok: true,
3855
+ patch: { [boolKey]: false },
3856
+ label,
3857
+ displayValue: "off"
3858
+ };
3900
3859
  }
3901
3860
  return { ok: false, error: `Invalid value "${input}" for ${label}. Use on or off.` };
3902
3861
  }
@@ -3904,7 +3863,12 @@ function resolveSettingsFieldValue(field, input) {
3904
3863
  const legacy = ["on", "true", "yes", "1"].includes(raw) ? "full" : ["false", "no", "0"].includes(raw) ? "off" : void 0;
3905
3864
  const match = FLEET_CHAT_MODES.includes(raw) ? raw : legacy;
3906
3865
  if (match) {
3907
- return { ok: true, patch: { fleetChat: match }, label, displayValue: match };
3866
+ return {
3867
+ ok: true,
3868
+ patch: { fleetChat: match },
3869
+ label,
3870
+ displayValue: match
3871
+ };
3908
3872
  }
3909
3873
  return {
3910
3874
  ok: false,
@@ -3930,7 +3894,12 @@ function resolveSettingsFieldValue(field, input) {
3930
3894
  if (field !== f) continue;
3931
3895
  const match = values.find((v) => v.toLowerCase() === raw);
3932
3896
  if (match) {
3933
- return { ok: true, patch: { [key]: match }, label, displayValue: match };
3897
+ return {
3898
+ ok: true,
3899
+ patch: { [key]: match },
3900
+ label,
3901
+ displayValue: match
3902
+ };
3934
3903
  }
3935
3904
  return {
3936
3905
  ok: false,
@@ -3944,7 +3913,12 @@ function resolveSettingsFieldValue(field, input) {
3944
3913
  [16, "autoProceedMaxIterations", AUTO_PROCEED_MAX_PRESETS, (n) => formatMaxIterations(n)],
3945
3914
  [17, "enhanceDelayMs", ENHANCE_DELAY_PRESETS, (n) => formatEnhanceDelay(n)],
3946
3915
  [41, "preRefineSeconds", PRE_REFINE_SECONDS_PRESETS, (n) => formatPreRefineSeconds(n)],
3947
- [21, "multiDiffSummaryThreshold", MULTI_DIFF_SUMMARY_THRESHOLD_PRESETS, (n) => formatMultiDiffSummaryThreshold(n)],
3916
+ [
3917
+ 21,
3918
+ "multiDiffSummaryThreshold",
3919
+ MULTI_DIFF_SUMMARY_THRESHOLD_PRESETS,
3920
+ (n) => formatMultiDiffSummaryThreshold(n)
3921
+ ],
3948
3922
  [30, "maxConcurrent", MAX_CONCURRENT_PRESETS, (n) => presetLabel(n, "runtime default")],
3949
3923
  [38, "breakerAutoKillResetMs", BREAKER_TIMEOUT_PRESETS, (n) => formatBreakerTimeout(n)]
3950
3924
  ];
@@ -3952,11 +3926,21 @@ function resolveSettingsFieldValue(field, input) {
3952
3926
  if (field !== f) continue;
3953
3927
  const asNum = Number.parseInt(raw, 10);
3954
3928
  if (!Number.isNaN(asNum) && presets.includes(asNum)) {
3955
- return { ok: true, patch: { [key]: asNum }, label, displayValue: fmt2(asNum) };
3929
+ return {
3930
+ ok: true,
3931
+ patch: { [key]: asNum },
3932
+ label,
3933
+ displayValue: fmt2(asNum)
3934
+ };
3956
3935
  }
3957
3936
  const byName = presets.find((p) => fmt2(p).toLowerCase() === raw);
3958
3937
  if (byName !== void 0) {
3959
- return { ok: true, patch: { [key]: byName }, label, displayValue: fmt2(byName) };
3938
+ return {
3939
+ ok: true,
3940
+ patch: { [key]: byName },
3941
+ label,
3942
+ displayValue: fmt2(byName)
3943
+ };
3960
3944
  }
3961
3945
  const options = presets.map((p) => fmt2(p)).join(", ");
3962
3946
  return {
@@ -4003,7 +3987,8 @@ function getSettingsFieldValue(values, field) {
4003
3987
  [33, "debugStream"],
4004
3988
  [37, "breakerEnabled"],
4005
3989
  [39, "showModelReasoning"],
4006
- [40, "showAgentSwarmPanel"]
3990
+ [40, "showAgentSwarmPanel"],
3991
+ [42, "readSymbols"]
4007
3992
  ];
4008
3993
  for (const [f, key] of BOOL_KEYS) {
4009
3994
  if (field !== f) continue;
@@ -4064,7 +4049,7 @@ var SETTINGS_SECTIONS = [
4064
4049
  },
4065
4050
  {
4066
4051
  name: "Tools",
4067
- fields: [15, 16, 17, 18, 19, 20, 21, 22]
4052
+ fields: [15, 16, 17, 18, 19, 20, 21, 22, 42]
4068
4053
  },
4069
4054
  {
4070
4055
  name: "Reasoning",
@@ -4153,7 +4138,8 @@ var SETTINGS_DEFAULTS = Object.freeze({
4153
4138
  breakerEnabled: false,
4154
4139
  breakerAutoKillResetMs: 6e4,
4155
4140
  showModelReasoning: true,
4156
- showAgentSwarmPanel: true
4141
+ showAgentSwarmPanel: true,
4142
+ readSymbols: false
4157
4143
  });
4158
4144
  function resetSettingsFieldValue(field) {
4159
4145
  const result = getSettingsFieldValue(SETTINGS_DEFAULTS, field);
@@ -4205,7 +4191,8 @@ function buildResetPatch(field) {
4205
4191
  [38, "breakerAutoKillResetMs"],
4206
4192
  [39, "showModelReasoning"],
4207
4193
  [40, "showAgentSwarmPanel"],
4208
- [41, "preRefineSeconds"]
4194
+ [41, "preRefineSeconds"],
4195
+ [42, "readSymbols"]
4209
4196
  ];
4210
4197
  for (const [f, key] of KEY_MAP) {
4211
4198
  if (f === field) {
@@ -4215,6 +4202,77 @@ function buildResetPatch(field) {
4215
4202
  return null;
4216
4203
  }
4217
4204
 
4205
+ // src/components/settings-picker-row-list.tsx
4206
+ import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4207
+ function renderPickerRow(row, fieldIdx, selected, filterActive, highlightSegments) {
4208
+ const labelStr = row.label ?? "";
4209
+ const segments = highlightSegments(labelStr);
4210
+ const padNeeded = Math.max(0, 26 - labelStr.length);
4211
+ return /* @__PURE__ */ jsxs7(Text, { inverse: selected, ...selected ? { color: "yellow" } : {}, children: [
4212
+ selected ? "\u203A " : " ",
4213
+ filterActive ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
4214
+ segments.map((seg, j) => /* @__PURE__ */ jsx8(Text, { bold: true, ...seg.match ? { color: "yellow" } : { dimColor: true }, children: seg.text }, j)),
4215
+ /* @__PURE__ */ jsx8(Text, { bold: true, dimColor: true, children: " ".repeat(padNeeded) })
4216
+ ] }) : /* @__PURE__ */ jsx8(Text, { bold: true, children: labelStr.padEnd(26) }),
4217
+ /* @__PURE__ */ jsx8(Text, { color: "cyan", children: String(row.value ?? "").padEnd(12) }),
4218
+ /* @__PURE__ */ jsx8(Text, { dimColor: true, children: row.detail ?? "" })
4219
+ ] }, `row-${row.label ?? fieldIdx}`);
4220
+ }
4221
+ function buildVisibleSectionHeaders(rows, fieldRowIndex, windowStart, windowEnd) {
4222
+ const sectionFields = [];
4223
+ let curHeader = -1;
4224
+ for (let i = 0; i < rows.length; i++) {
4225
+ if (rows[i]?.section) curHeader = i;
4226
+ else if (curHeader >= 0) {
4227
+ const fieldIdx = fieldRowIndex.indexOf(i);
4228
+ if (fieldIdx === -1) continue;
4229
+ const entry = sectionFields.find((s2) => s2.headerIdx === curHeader);
4230
+ if (entry) {
4231
+ entry.fieldEnd = fieldIdx + 1;
4232
+ } else {
4233
+ sectionFields.push({ headerIdx: curHeader, fieldStart: fieldIdx, fieldEnd: fieldIdx + 1 });
4234
+ }
4235
+ }
4236
+ }
4237
+ return new Set(
4238
+ sectionFields.filter((section) => section.fieldStart < windowEnd && section.fieldEnd > windowStart).map((section) => section.headerIdx)
4239
+ );
4240
+ }
4241
+ function SettingsPickerRowList({
4242
+ rows,
4243
+ field,
4244
+ fieldRowIndex,
4245
+ filterActive,
4246
+ rankedResults,
4247
+ highlightSegments,
4248
+ windowStart,
4249
+ windowEnd
4250
+ }) {
4251
+ if (filterActive) {
4252
+ return /* @__PURE__ */ jsx8(Fragment4, { children: rankedResults.map((result) => {
4253
+ const fieldIdx = result.chord.field;
4254
+ const rowIdx = fieldRowIndex[fieldIdx] ?? -1;
4255
+ const row = rows[rowIdx];
4256
+ if (!row?.label) return null;
4257
+ return renderPickerRow(row, fieldIdx, fieldIdx === field, true, highlightSegments);
4258
+ }) });
4259
+ }
4260
+ const visibleSections = buildVisibleSectionHeaders(rows, fieldRowIndex, windowStart, windowEnd);
4261
+ return /* @__PURE__ */ jsx8(Fragment4, { children: rows.map((row, i) => {
4262
+ const fieldAtRow = fieldRowIndex.indexOf(i);
4263
+ if (fieldAtRow === -1) {
4264
+ if (!visibleSections.has(i)) return null;
4265
+ return /* @__PURE__ */ jsxs7(Text, { bold: true, color: "green", children: [
4266
+ "\u2500\u2500 ",
4267
+ row.section,
4268
+ " \u2500\u2500"
4269
+ ] }, `section-${row.section ?? i}`);
4270
+ }
4271
+ if (fieldAtRow < windowStart || fieldAtRow >= windowEnd) return null;
4272
+ return renderPickerRow(row, fieldAtRow, fieldAtRow === field, false, highlightSegments);
4273
+ }) });
4274
+ }
4275
+
4218
4276
  // src/components/settings-picker.tsx
4219
4277
  import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4220
4278
  function SettingsPicker({
@@ -4265,6 +4323,7 @@ function SettingsPicker({
4265
4323
  breakerAutoKillResetMs,
4266
4324
  showModelReasoning,
4267
4325
  showAgentSwarmPanel,
4326
+ readSymbols,
4268
4327
  hint
4269
4328
  }) {
4270
4329
  const boolVal = (v) => v ? "on" : "off";
@@ -4496,6 +4555,13 @@ function SettingsPicker({
4496
4555
  label: "Pre-refine countdown",
4497
4556
  value: formatPreRefineSeconds(preRefineSeconds),
4498
4557
  detail: "Grace period before refiner starts (0 = skip)"
4558
+ },
4559
+ // Appended to preserve the established field indices used by settings
4560
+ // reducers, slash commands, and persisted lastSettingsField values.
4561
+ {
4562
+ label: "Read symbols",
4563
+ value: boolVal(readSymbols),
4564
+ detail: "Include codebase-index symbols in read tool results"
4499
4565
  }
4500
4566
  ];
4501
4567
  const fieldRowIndex = [];
@@ -4513,7 +4579,14 @@ function SettingsPicker({
4513
4579
  const hasBelow = windowEnd < totalFields;
4514
4580
  return /* @__PURE__ */ jsxs8(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
4515
4581
  /* @__PURE__ */ jsx9(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Settings \u2501\u2501" }),
4516
- filterActive ? /* @__PURE__ */ jsx9(Text, { color: "yellow", bold: true, children: `Filter: ${filter} (${filteredFieldIndices.length} match${filteredFieldIndices.length === 1 ? "" : "es"})` }) : /* @__PURE__ */ jsx9(Text, { dimColor: true, children: "\u2191/\u2193 field \xB7 \u2190/\u2192 change + autosave \xB7 `/` to search \xB7 F5 to close" }),
4582
+ filterActive ? /* @__PURE__ */ jsx9(
4583
+ Text,
4584
+ {
4585
+ color: "yellow",
4586
+ bold: true,
4587
+ children: `Filter: ${filter} (${filteredFieldIndices.length} match${filteredFieldIndices.length === 1 ? "" : "es"})`
4588
+ }
4589
+ ) : /* @__PURE__ */ jsx9(Text, { dimColor: true, children: "\u2191/\u2193 field \xB7 \u2190/\u2192 change + autosave \xB7 `/` to search \xB7 F5 to close" }),
4517
4590
  hasAbove && !filterActive ? /* @__PURE__ */ jsx9(Text, { dimColor: true, children: ` \u2191 ${windowStart} field${windowStart === 1 ? "" : "s"} above` }) : null,
4518
4591
  filterActive && filteredFieldIndices.length === 0 ? /* @__PURE__ */ jsx9(Text, { dimColor: true, italic: true, children: "No matching settings rows." }) : null,
4519
4592
  /* @__PURE__ */ jsx9(
@@ -4529,7 +4602,13 @@ function SettingsPicker({
4529
4602
  windowEnd
4530
4603
  }
4531
4604
  ),
4532
- hasBelow && !filterActive ? /* @__PURE__ */ jsx9(Text, { dimColor: true, children: ` \u2193 ${totalFields - windowEnd} field${totalFields - windowEnd === 1 ? "" : "s"} below` }) : null,
4605
+ hasBelow && !filterActive ? /* @__PURE__ */ jsx9(
4606
+ Text,
4607
+ {
4608
+ dimColor: true,
4609
+ children: ` \u2193 ${totalFields - windowEnd} field${totalFields - windowEnd === 1 ? "" : "s"} below`
4610
+ }
4611
+ ) : null,
4533
4612
  /* @__PURE__ */ jsx9(Text, { dimColor: true, children: configScope === "project" ? "Persisted to <project>/.wrongstack/config.json" : `Persisted to ${profileConfigPath}` }),
4534
4613
  hint ? /* @__PURE__ */ jsx9(Text, { color: "yellow", children: hint }) : null
4535
4614
  ] });
@@ -10317,6 +10396,35 @@ function useAppRuntimeRefs(attachments, state) {
10317
10396
  // src/hooks/use-app-session-state.ts
10318
10397
  import { useEffect as useEffect18, useMemo as useMemo6, useReducer } from "react";
10319
10398
 
10399
+ // src/checkpoint-retention.ts
10400
+ var TUI_CHECKPOINTS_MAX_ENTRIES = 200;
10401
+ var TUI_CHECKPOINTS_MAX_BYTES = 256 * 1024;
10402
+ function entryBytes(entry) {
10403
+ try {
10404
+ return Buffer.byteLength(JSON.stringify(entry), "utf8");
10405
+ } catch {
10406
+ return TUI_CHECKPOINTS_MAX_BYTES + 1;
10407
+ }
10408
+ }
10409
+ function retainCheckpoints(checkpoints, budget = {}) {
10410
+ const maxEntries = Math.max(1, Math.floor(budget.maxEntries ?? TUI_CHECKPOINTS_MAX_ENTRIES));
10411
+ const maxBytes = Math.max(1, Math.floor(budget.maxBytes ?? TUI_CHECKPOINTS_MAX_BYTES));
10412
+ let keepFrom = checkpoints.length;
10413
+ let retainedBytes = 0;
10414
+ let retainedCount = 0;
10415
+ while (keepFrom > 0 && retainedCount < maxEntries) {
10416
+ const next = checkpoints[keepFrom - 1];
10417
+ if (!next) break;
10418
+ const bytes = entryBytes(next);
10419
+ if (retainedCount > 0 && retainedBytes + bytes > maxBytes) break;
10420
+ retainedBytes += bytes;
10421
+ retainedCount += 1;
10422
+ keepFrom -= 1;
10423
+ }
10424
+ if (keepFrom === 0) return checkpoints;
10425
+ return checkpoints.slice(keepFrom);
10426
+ }
10427
+
10320
10428
  // src/components/history/replay.ts
10321
10429
  import { SESSION_MARKER_EVENT_TYPES, sessionEventToMarker } from "@wrongstack/core/types";
10322
10430
  var MARKER_EVENT_TYPES = SESSION_MARKER_EVENT_TYPES;
@@ -10526,79 +10634,6 @@ function eventToEntry(ev, pendingTools, completedTools) {
10526
10634
  }
10527
10635
  }
10528
10636
 
10529
- // src/rehydrate-history.ts
10530
- function contentBlocksText(content) {
10531
- if (typeof content === "string") return content;
10532
- if (!Array.isArray(content)) return "";
10533
- return content.filter(
10534
- (block) => !!block && typeof block === "object" && block.type === "text" && typeof block.text === "string"
10535
- ).map((block) => block.text).join("");
10536
- }
10537
- function rehydrateHistory(messages, startId, toolCalls) {
10538
- const entries = [];
10539
- const toolCallsById = /* @__PURE__ */ new Map();
10540
- if (toolCalls) {
10541
- for (const tc of toolCalls) {
10542
- if (!toolCallsById.has(tc.id)) toolCallsById.set(tc.id, tc);
10543
- }
10544
- }
10545
- const consumed = /* @__PURE__ */ new Set();
10546
- const fallback = [];
10547
- let nextId = startId;
10548
- const textOf = (msg) => contentBlocksText(msg.content);
10549
- const toolEntryFor = (tc) => ({
10550
- id: nextId++,
10551
- kind: "tool",
10552
- name: tc.name,
10553
- durationMs: tc.durationMs,
10554
- ok: tc.ok,
10555
- outputBytes: tc.outputBytes,
10556
- outputTokens: tc.outputTokens,
10557
- outputLines: tc.outputLines
10558
- });
10559
- const pushAssistantText = (parts) => {
10560
- const text = parts.join("").trim();
10561
- if (text) entries.push({ id: nextId++, kind: "assistant", text });
10562
- parts.length = 0;
10563
- };
10564
- for (const msg of messages) {
10565
- if (msg.role === "system") continue;
10566
- if (msg.role === "user") {
10567
- const text = textOf(msg).trim();
10568
- if (text) entries.push({ id: nextId++, kind: "user", text });
10569
- continue;
10570
- }
10571
- if (msg.role === "assistant") {
10572
- if (!Array.isArray(msg.content)) {
10573
- const text = textOf(msg).trim();
10574
- if (text) entries.push({ id: nextId++, kind: "assistant", text });
10575
- continue;
10576
- }
10577
- const textParts = [];
10578
- for (const block of msg.content) {
10579
- if (block.type === "text") {
10580
- textParts.push(block.text);
10581
- continue;
10582
- }
10583
- if (block.type !== "tool_use") continue;
10584
- pushAssistantText(textParts);
10585
- const tc = toolCallsById.get(block.id);
10586
- if (!tc) continue;
10587
- entries.push(toolEntryFor(tc));
10588
- consumed.add(block.id);
10589
- }
10590
- pushAssistantText(textParts);
10591
- }
10592
- }
10593
- if (toolCalls) {
10594
- for (const tc of toolCalls) {
10595
- if (!consumed.has(tc.id)) fallback.push(toolEntryFor(tc));
10596
- }
10597
- }
10598
- entries.push(...fallback);
10599
- return entries;
10600
- }
10601
-
10602
10637
  // src/history-retention.ts
10603
10638
  var TUI_HISTORY_MAX_ENTRIES = 400;
10604
10639
  var TUI_HISTORY_MAX_BYTES = 1024 * 1024;
@@ -10612,7 +10647,7 @@ function omittedCount(entry) {
10612
10647
  const count = Number(match[1]);
10613
10648
  return Number.isSafeInteger(count) && count > 0 ? count : null;
10614
10649
  }
10615
- function entryBytes(entry) {
10650
+ function entryBytes2(entry) {
10616
10651
  try {
10617
10652
  return Buffer.byteLength(JSON.stringify(entry), "utf8");
10618
10653
  } catch {
@@ -10627,8 +10662,8 @@ function oversizedEntryMarker(entry) {
10627
10662
  };
10628
10663
  }
10629
10664
  function retainTextWithinEntryBudget(value, maxBytes, createEntry, original) {
10630
- if (entryBytes(createEntry(value)) <= maxBytes) return createEntry(value);
10631
- if (entryBytes(createEntry(ENTRY_TRUNCATION_MARKER)) > maxBytes) {
10665
+ if (entryBytes2(createEntry(value)) <= maxBytes) return createEntry(value);
10666
+ if (entryBytes2(createEntry(ENTRY_TRUNCATION_MARKER)) > maxBytes) {
10632
10667
  return oversizedEntryMarker(original);
10633
10668
  }
10634
10669
  let low = 0;
@@ -10636,7 +10671,7 @@ function retainTextWithinEntryBudget(value, maxBytes, createEntry, original) {
10636
10671
  while (low < high) {
10637
10672
  const middle = Math.ceil((low + high) / 2);
10638
10673
  const candidate = `${value.slice(0, middle)}${ENTRY_TRUNCATION_MARKER}`;
10639
- if (entryBytes(createEntry(candidate)) <= maxBytes) low = middle;
10674
+ if (entryBytes2(createEntry(candidate)) <= maxBytes) low = middle;
10640
10675
  else high = middle - 1;
10641
10676
  }
10642
10677
  let end = low;
@@ -10645,7 +10680,7 @@ function retainTextWithinEntryBudget(value, maxBytes, createEntry, original) {
10645
10680
  return createEntry(`${value.slice(0, end)}${ENTRY_TRUNCATION_MARKER}`);
10646
10681
  }
10647
10682
  function retainTuiHistoryEntry(entry, maxBytes = TUI_HISTORY_MAX_ENTRY_BYTES) {
10648
- if (entryBytes(entry) <= maxBytes) return entry;
10683
+ if (entryBytes2(entry) <= maxBytes) return entry;
10649
10684
  switch (entry.kind) {
10650
10685
  case "user":
10651
10686
  return retainTextWithinEntryBudget(
@@ -10693,7 +10728,7 @@ function retainTuiHistoryEntry(entry, maxBytes = TUI_HISTORY_MAX_ENTRY_BYTES) {
10693
10728
  model: truncate3(entry.model),
10694
10729
  cwd: truncate3(entry.cwd)
10695
10730
  };
10696
- return entryBytes(compact) <= maxBytes ? compact : oversizedEntryMarker(entry);
10731
+ return entryBytes2(compact) <= maxBytes ? compact : oversizedEntryMarker(entry);
10697
10732
  }
10698
10733
  default:
10699
10734
  return oversizedEntryMarker(entry);
@@ -10734,7 +10769,7 @@ function retainTuiHistory(entries, budget = {}) {
10734
10769
  while (keepFrom > 0 && retainedCount < maxEntries) {
10735
10770
  const next = transcript[keepFrom - 1];
10736
10771
  if (!next) break;
10737
- const bytes = entryBytes(next);
10772
+ const bytes = entryBytes2(next);
10738
10773
  if (retainedCount > 0 && retainedBytes + bytes > maxBytes) break;
10739
10774
  retainedBytes += bytes;
10740
10775
  retainedCount += 1;
@@ -10759,33 +10794,77 @@ function retainTuiHistory(entries, budget = {}) {
10759
10794
  return retained;
10760
10795
  }
10761
10796
 
10762
- // src/checkpoint-retention.ts
10763
- var TUI_CHECKPOINTS_MAX_ENTRIES = 200;
10764
- var TUI_CHECKPOINTS_MAX_BYTES = 256 * 1024;
10765
- function entryBytes2(entry) {
10766
- try {
10767
- return Buffer.byteLength(JSON.stringify(entry), "utf8");
10768
- } catch {
10769
- return TUI_CHECKPOINTS_MAX_BYTES + 1;
10770
- }
10797
+ // src/rehydrate-history.ts
10798
+ function contentBlocksText(content) {
10799
+ if (typeof content === "string") return content;
10800
+ if (!Array.isArray(content)) return "";
10801
+ return content.filter(
10802
+ (block) => !!block && typeof block === "object" && block.type === "text" && typeof block.text === "string"
10803
+ ).map((block) => block.text).join("");
10771
10804
  }
10772
- function retainCheckpoints(checkpoints, budget = {}) {
10773
- const maxEntries = Math.max(1, Math.floor(budget.maxEntries ?? TUI_CHECKPOINTS_MAX_ENTRIES));
10774
- const maxBytes = Math.max(1, Math.floor(budget.maxBytes ?? TUI_CHECKPOINTS_MAX_BYTES));
10775
- let keepFrom = checkpoints.length;
10776
- let retainedBytes = 0;
10777
- let retainedCount = 0;
10778
- while (keepFrom > 0 && retainedCount < maxEntries) {
10779
- const next = checkpoints[keepFrom - 1];
10780
- if (!next) break;
10781
- const bytes = entryBytes2(next);
10782
- if (retainedCount > 0 && retainedBytes + bytes > maxBytes) break;
10783
- retainedBytes += bytes;
10784
- retainedCount += 1;
10785
- keepFrom -= 1;
10805
+ function rehydrateHistory(messages, startId, toolCalls) {
10806
+ const entries = [];
10807
+ const toolCallsById = /* @__PURE__ */ new Map();
10808
+ if (toolCalls) {
10809
+ for (const tc of toolCalls) {
10810
+ if (!toolCallsById.has(tc.id)) toolCallsById.set(tc.id, tc);
10811
+ }
10786
10812
  }
10787
- if (keepFrom === 0) return checkpoints;
10788
- return checkpoints.slice(keepFrom);
10813
+ const consumed = /* @__PURE__ */ new Set();
10814
+ const fallback = [];
10815
+ let nextId = startId;
10816
+ const textOf = (msg) => contentBlocksText(msg.content);
10817
+ const toolEntryFor = (tc) => ({
10818
+ id: nextId++,
10819
+ kind: "tool",
10820
+ name: tc.name,
10821
+ durationMs: tc.durationMs,
10822
+ ok: tc.ok,
10823
+ outputBytes: tc.outputBytes,
10824
+ outputTokens: tc.outputTokens,
10825
+ outputLines: tc.outputLines
10826
+ });
10827
+ const pushAssistantText = (parts) => {
10828
+ const text = parts.join("").trim();
10829
+ if (text) entries.push({ id: nextId++, kind: "assistant", text });
10830
+ parts.length = 0;
10831
+ };
10832
+ for (const msg of messages) {
10833
+ if (msg.role === "system") continue;
10834
+ if (msg.role === "user") {
10835
+ const text = textOf(msg).trim();
10836
+ if (text) entries.push({ id: nextId++, kind: "user", text });
10837
+ continue;
10838
+ }
10839
+ if (msg.role === "assistant") {
10840
+ if (!Array.isArray(msg.content)) {
10841
+ const text = textOf(msg).trim();
10842
+ if (text) entries.push({ id: nextId++, kind: "assistant", text });
10843
+ continue;
10844
+ }
10845
+ const textParts = [];
10846
+ for (const block of msg.content) {
10847
+ if (block.type === "text") {
10848
+ textParts.push(block.text);
10849
+ continue;
10850
+ }
10851
+ if (block.type !== "tool_use") continue;
10852
+ pushAssistantText(textParts);
10853
+ const tc = toolCallsById.get(block.id);
10854
+ if (!tc) continue;
10855
+ entries.push(toolEntryFor(tc));
10856
+ consumed.add(block.id);
10857
+ }
10858
+ pushAssistantText(textParts);
10859
+ }
10860
+ }
10861
+ if (toolCalls) {
10862
+ for (const tc of toolCalls) {
10863
+ if (!consumed.has(tc.id)) fallback.push(toolEntryFor(tc));
10864
+ }
10865
+ }
10866
+ entries.push(...fallback);
10867
+ return entries;
10789
10868
  }
10790
10869
 
10791
10870
  // src/app-initial-state.ts
@@ -10969,7 +11048,8 @@ function createInitialState(options) {
10969
11048
  breakerEnabled: false,
10970
11049
  breakerAutoKillResetMs: 6e4,
10971
11050
  showModelReasoning: true,
10972
- showAgentSwarmPanel: true
11051
+ showAgentSwarmPanel: true,
11052
+ readSymbols: false
10973
11053
  },
10974
11054
  statuslinePicker: {
10975
11055
  open: false,
@@ -10980,10 +11060,31 @@ function createInitialState(options) {
10980
11060
  },
10981
11061
  pluginPicker: { open: false, items: [], selected: 0, busy: false, hint: void 0 },
10982
11062
  mcpPicker: { open: false, items: [], selected: 0, busy: false, hint: void 0 },
10983
- toolsPicker: { open: false, items: [], selected: 0, busy: false, hint: void 0, filter: void 0 },
10984
- brainPanel: { open: false, riskLevel: "medium", log: [], selected: 0, hint: void 0, view: "settings", settings: void 0, row: 0, busy: false },
11063
+ toolsPicker: {
11064
+ open: false,
11065
+ items: [],
11066
+ selected: 0,
11067
+ busy: false,
11068
+ hint: void 0,
11069
+ filter: void 0
11070
+ },
11071
+ brainPanel: {
11072
+ open: false,
11073
+ riskLevel: "medium",
11074
+ log: [],
11075
+ selected: 0,
11076
+ hint: void 0,
11077
+ view: "settings",
11078
+ settings: void 0,
11079
+ row: 0,
11080
+ busy: false
11081
+ },
10985
11082
  helpPanel: { open: false, entries: [], selected: 0, filter: "", hint: void 0 },
10986
- shadowPanel: { open: false, shadow: { activeId: null, running: false, model: "", intervalMs: 3e4 }, hint: void 0 },
11083
+ shadowPanel: {
11084
+ open: false,
11085
+ shadow: { activeId: null, running: false, model: "", intervalMs: 3e4 },
11086
+ hint: void 0
11087
+ },
10987
11088
  authPanel: AUTH_PANEL_INITIAL,
10988
11089
  projectPicker: {
10989
11090
  open: false,
@@ -13242,6 +13343,7 @@ function reduceSettingsPanel(state, action) {
13242
13343
  breakerAutoKillResetMs: action.breakerAutoKillResetMs,
13243
13344
  showModelReasoning: action.showModelReasoning,
13244
13345
  showAgentSwarmPanel: action.showAgentSwarmPanel,
13346
+ readSymbols: action.readSymbols,
13245
13347
  hint: void 0
13246
13348
  }
13247
13349
  };
@@ -13671,6 +13773,24 @@ function reduceSettingsValues(state, action) {
13671
13773
  ...state,
13672
13774
  settingsPicker: { ...sp, showAgentSwarmPanel: !sp.showAgentSwarmPanel, hint: void 0 }
13673
13775
  };
13776
+ if (f === 41) {
13777
+ const j = PRE_REFINE_SECONDS_PRESETS.indexOf(sp.preRefineSeconds);
13778
+ const base = j < 0 ? 0 : j;
13779
+ const next = (base + action.delta + PRE_REFINE_SECONDS_PRESETS.length) % PRE_REFINE_SECONDS_PRESETS.length;
13780
+ return {
13781
+ ...state,
13782
+ settingsPicker: {
13783
+ ...sp,
13784
+ preRefineSeconds: expectDefined2(PRE_REFINE_SECONDS_PRESETS[next]),
13785
+ hint: void 0
13786
+ }
13787
+ };
13788
+ }
13789
+ if (f === 42)
13790
+ return {
13791
+ ...state,
13792
+ settingsPicker: { ...sp, readSymbols: !sp.readSymbols, hint: void 0 }
13793
+ };
13674
13794
  return state;
13675
13795
  }
13676
13796
  case "settingsValueSet": {
@@ -14075,6 +14195,7 @@ function visibleChars(text) {
14075
14195
  const cleaned = text.replace(/\x1b\[[0-9;]*m/g, "");
14076
14196
  return cleaned.length;
14077
14197
  }
14198
+ var MAX_MEMORY_PROOF_ENTRIES = 4;
14078
14199
  function computeEntryRows(kind, text, termWidth, meta) {
14079
14200
  const w = Math.max(MIN_CONTENT_WIDTH, termWidth);
14080
14201
  switch (kind) {
@@ -14165,10 +14286,17 @@ function computeEntryRows(kind, text, termWidth, meta) {
14165
14286
  return rows;
14166
14287
  }
14167
14288
  // ── Memory activation ──
14168
- // Bordered card: top (1) + header line (1) + optional injected line (1) +
14169
- // optional rejected line (1) + bottom (1)
14289
+ // Bordered card: top (1) + header (1) + bottom (1) + margin (1), plus an
14290
+ // optional query line, two rows per shown memory (verdict + score terms),
14291
+ // an optional overflow line, and an optional rejected line. Every added
14292
+ // line renders with wrap="truncate-end", so each is exactly one row at
14293
+ // any terminal width — keep it that way or this count drifts.
14170
14294
  case "memory-activation": {
14171
14295
  let rows = 4;
14296
+ if (meta?.memoryHasQuery) rows += 1;
14297
+ const proofs = Math.min(meta?.memoryInjectedCount ?? 0, MAX_MEMORY_PROOF_ENTRIES);
14298
+ rows += proofs * 2;
14299
+ if ((meta?.memoryInjectedCount ?? 0) > MAX_MEMORY_PROOF_ENTRIES) rows += 1;
14172
14300
  if (text) rows += 1;
14173
14301
  return rows;
14174
14302
  }
@@ -14802,7 +14930,7 @@ function useGitSessionStatus({
14802
14930
  }
14803
14931
  ]
14804
14932
  };
14805
- agent.ctx.messages.push(message);
14933
+ agent.ctx.state.appendMessage(message);
14806
14934
  void import("@wrongstack/core/storage").then(({ getSessionRegistry }) => getSessionRegistry()?.updateAgents([])).catch(() => void 0);
14807
14935
  }
14808
14936
  previousBranchRef.current = info.branch;
@@ -15513,9 +15641,7 @@ ${GROUNDED_NO_PROGRESS_STEER}` : suggestion;
15513
15641
 
15514
15642
  // src/hooks/use-panel-controllers.ts
15515
15643
  import { toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
15516
- import React2, {
15517
- useEffect as useEffect28
15518
- } from "react";
15644
+ import React2, { useEffect as useEffect28 } from "react";
15519
15645
 
15520
15646
  // src/on-panel-open.ts
15521
15647
  function createPanelOpenDispatcher(deps) {
@@ -15729,7 +15855,8 @@ function usePanelControllers({
15729
15855
  breakerEnabled: s2.breakerEnabled ?? false,
15730
15856
  breakerAutoKillResetMs: s2.breakerAutoKillResetMs ?? 6e4,
15731
15857
  showModelReasoning: s2.showModelReasoning ?? true,
15732
- showAgentSwarmPanel: s2.showAgentSwarmPanel ?? true
15858
+ showAgentSwarmPanel: s2.showAgentSwarmPanel ?? true,
15859
+ readSymbols: s2.readSymbols ?? false
15733
15860
  });
15734
15861
  }, [getSettings]);
15735
15862
  const refreshPluginPicker = React2.useCallback(() => {
@@ -16191,6 +16318,11 @@ function usePromptPicker({
16191
16318
 
16192
16319
  // src/hooks/use-provider-event-bridge.ts
16193
16320
  import { tuiStreamFlushMs } from "@wrongstack/core/utils";
16321
+ import {
16322
+ DEFAULT_MIN_IMPORTANCE,
16323
+ DEFAULT_MIN_SCORE,
16324
+ MIN_RELATION_STRENGTH
16325
+ } from "@wrongstack/sage";
16194
16326
  import {
16195
16327
  useEffect as useEffect29
16196
16328
  } from "react";
@@ -16373,6 +16505,13 @@ function canonicalStreamSegments(content) {
16373
16505
  }
16374
16506
  return segments;
16375
16507
  }
16508
+ function formatSageInjectionStats(run) {
16509
+ const parts = [`+${run.injectedChars} chars`];
16510
+ if (run.contextPressure >= 0.65) {
16511
+ parts.push(`ctx ${Math.round(run.contextPressure * 100)}%`);
16512
+ }
16513
+ return parts.join(" \xB7 ");
16514
+ }
16376
16515
  function useProviderEventBridge({
16377
16516
  events,
16378
16517
  agent,
@@ -16389,6 +16528,7 @@ function useProviderEventBridge({
16389
16528
  useEffect29(() => {
16390
16529
  const FLUSH_MS2 = tuiStreamFlushMs();
16391
16530
  let pendingToolStream = null;
16531
+ const pendingSageStats = /* @__PURE__ */ new Map();
16392
16532
  const appendStreamSegment = (kind, text) => {
16393
16533
  const last = streamSegmentsRef.current.at(-1);
16394
16534
  if (last?.kind === kind) {
@@ -16489,6 +16629,10 @@ function useProviderEventBridge({
16489
16629
  ok: e.ok,
16490
16630
  input: e.input,
16491
16631
  output: e.output,
16632
+ // SAGE-injected memory travels beside the output preview so it
16633
+ // always renders as a memory block, never as tool text.
16634
+ sageLines: e.sage,
16635
+ ...pendingSageStats.has(e.name) ? { sageStats: pendingSageStats.get(e.name) } : {},
16492
16636
  // Real model-visible sizes — forwarded so the size chip beside
16493
16637
  // the tool header can show what the model paid for instead of
16494
16638
  // the misleading preview-byte count we used to surface.
@@ -16498,6 +16642,7 @@ function useProviderEventBridge({
16498
16642
  }
16499
16643
  });
16500
16644
  }
16645
+ pendingSageStats.delete(e.name);
16501
16646
  dispatch({ type: "toolEnded", ...e.id !== void 0 ? { id: e.id } : {}, name: e.name });
16502
16647
  dispatch({ type: "toolStreamClear", name: e.name });
16503
16648
  dispatch({ type: "leaderToolEnd", name: e.name, ok: e.ok, durationMs: e.durationMs });
@@ -16634,6 +16779,38 @@ function useProviderEventBridge({
16634
16779
  setMemoryContextMonitor(
16635
16780
  (current) => applyMemoryInjectorRun(current, e)
16636
16781
  );
16782
+ if (e.injected.length > 0) {
16783
+ pendingSageStats.set(e.toolName, formatSageInjectionStats(e));
16784
+ }
16785
+ if (!e.error) return;
16786
+ dispatch({
16787
+ type: "addEntry",
16788
+ entry: {
16789
+ kind: "memory-activation",
16790
+ trigger: e.trigger,
16791
+ outcome: e.outcome,
16792
+ candidates: e.candidates,
16793
+ contextPressure: e.contextPressure,
16794
+ injectedChars: e.injectedChars,
16795
+ // The core event payload types declare these as REQUIRED, so the
16796
+ // `??` defaults are a stream-boundary fallback for legacy emitters
16797
+ // that omit them — not a current-build safety net.
16798
+ queryPreview: e.queryPreview ?? "",
16799
+ thresholds: e.thresholds ?? {
16800
+ minScore: DEFAULT_MIN_SCORE,
16801
+ minImportance: DEFAULT_MIN_IMPORTANCE,
16802
+ relationFloor: MIN_RELATION_STRENGTH
16803
+ },
16804
+ activated: e.activated.map((item) => ({
16805
+ ...item,
16806
+ metadataScore: item.metadataScore ?? 0,
16807
+ scoreTerms: item.scoreTerms ?? []
16808
+ })),
16809
+ injectedIds: e.injected.map((item) => item.id),
16810
+ rejected: e.rejected,
16811
+ ...e.error ? { error: e.error } : {}
16812
+ }
16813
+ });
16637
16814
  });
16638
16815
  const offMemoryContextSnapshot = events.onPattern(
16639
16816
  "memory.context_snapshot",
@@ -16648,6 +16825,7 @@ function useProviderEventBridge({
16648
16825
  const eventSessionId = payload?.sessionId;
16649
16826
  if (typeof eventSessionId !== "string" || eventSessionId !== agent.ctx.session.id) return;
16650
16827
  setMemoryContextMonitor(emptyMemoryContextMonitor());
16828
+ pendingSageStats.clear();
16651
16829
  });
16652
16830
  const offMemoryLifecycle = events.onPattern("memory.*", (event, payload) => {
16653
16831
  const lifecycle = memoryLifecycleEntry(event, payload);
@@ -17087,7 +17265,8 @@ function useSettingsAutoSave(state, saveSettings, dispatch) {
17087
17265
  breakerEnabled: sp.breakerEnabled,
17088
17266
  breakerAutoKillResetMs: sp.breakerAutoKillResetMs,
17089
17267
  showModelReasoning: sp.showModelReasoning,
17090
- showAgentSwarmPanel: sp.showAgentSwarmPanel
17268
+ showAgentSwarmPanel: sp.showAgentSwarmPanel,
17269
+ readSymbols: sp.readSymbols
17091
17270
  })
17092
17271
  ).then((err) => {
17093
17272
  if (err) dispatch({ type: "settingsHint", text: err });
@@ -17136,6 +17315,7 @@ function useSettingsAutoSave(state, saveSettings, dispatch) {
17136
17315
  state.settingsPicker.breakerAutoKillResetMs,
17137
17316
  state.settingsPicker.showModelReasoning,
17138
17317
  state.settingsPicker.showAgentSwarmPanel,
17318
+ state.settingsPicker.readSymbols,
17139
17319
  saveSettings
17140
17320
  ]);
17141
17321
  }
@@ -17248,6 +17428,25 @@ function useStableKeyHandler(handleKey) {
17248
17428
  import * as fs3 from "node:fs/promises";
17249
17429
  import { getContextBreakdown } from "@wrongstack/core/utils";
17250
17430
  import { useEffect as useEffect35, useMemo as useMemo7, useState as useState14 } from "react";
17431
+
17432
+ // src/context-fill.ts
17433
+ function resolveContextFill(inputs) {
17434
+ const { loopReportedTokens, perRequestTokens, localEstimate, maxContext } = inputs;
17435
+ const ceiling = maxContext > 0 ? maxContext : Number.POSITIVE_INFINITY;
17436
+ const clamp2 = (n) => Math.max(0, Math.min(n, ceiling));
17437
+ if (typeof loopReportedTokens === "number" && loopReportedTokens > 0) {
17438
+ return { used: clamp2(loopReportedTokens), needsLocalEstimate: false, source: "loop" };
17439
+ }
17440
+ if (perRequestTokens > 0 && perRequestTokens <= ceiling) {
17441
+ return { used: clamp2(perRequestTokens), needsLocalEstimate: false, source: "provider" };
17442
+ }
17443
+ if (typeof localEstimate === "number" && localEstimate > 0) {
17444
+ return { used: clamp2(localEstimate), needsLocalEstimate: true, source: "local" };
17445
+ }
17446
+ return { used: 0, needsLocalEstimate: true, source: "none" };
17447
+ }
17448
+
17449
+ // src/hooks/use-statusbar-view-model.ts
17251
17450
  function samePlanCounts(a, b) {
17252
17451
  return a === b || a !== null && b !== null && a.open === b.open && a.inProgress === b.inProgress && a.done === b.done;
17253
17452
  }
@@ -17267,22 +17466,35 @@ function useStatusbarViewModel({
17267
17466
  const maxContext = activeMaxContext ?? agent.ctx.provider.capabilities.maxContext;
17268
17467
  const perRequest = tokenCounter?.currentRequestTokens();
17269
17468
  const perRequestTokens = (perRequest?.input ?? 0) + (perRequest?.cacheRead ?? 0) + (perRequest?.cacheWrite ?? 0);
17270
- const cumulative = tokenCounter?.total();
17271
- const cumulativeTokens = (cumulative?.input ?? 0) + (cumulative?.cacheRead ?? 0) + (cumulative?.cacheWrite ?? 0);
17272
- const providerReportedTokens = perRequestTokens > 0 ? perRequestTokens : cumulativeTokens;
17273
- const needLocalEstimate = providerReportedTokens <= 0;
17469
+ const loopReportedTokens = state.leader.ctxTokens;
17470
+ const cheapFill = resolveContextFill({ loopReportedTokens, perRequestTokens, maxContext });
17471
+ const needLocalEstimate = cheapFill.needsLocalEstimate;
17472
+ const panelWantsBreakdown = state.contextPanelOpen;
17473
+ const needBreakdown = needLocalEstimate || panelWantsBreakdown;
17474
+ const conversationRevision = agent.ctx.state?.revision ?? 0;
17475
+ const messageCount = agent.ctx.messages?.length ?? 0;
17476
+ const toolDefCount = agent.ctx.tools?.length ?? 0;
17274
17477
  const contextBreakdown = useMemo7(() => {
17275
- if (!state.contextPanelOpen && !needLocalEstimate) return void 0;
17478
+ if (!needBreakdown) return void 0;
17276
17479
  try {
17277
17480
  return getContextBreakdown(agent.ctx);
17278
17481
  } catch {
17279
17482
  return void 0;
17280
17483
  }
17281
- }, [agent.ctx, state.contextPanelOpen, needLocalEstimate, state.contextChipVersion]);
17282
- const currentContextTokens = Math.min(
17283
- providerReportedTokens > 0 ? providerReportedTokens : contextBreakdown?.total ?? 0,
17484
+ }, [
17485
+ agent.ctx,
17486
+ needBreakdown,
17487
+ state.contextChipVersion,
17488
+ conversationRevision,
17489
+ messageCount,
17490
+ toolDefCount
17491
+ ]);
17492
+ const currentContextTokens = needLocalEstimate ? resolveContextFill({
17493
+ loopReportedTokens,
17494
+ perRequestTokens,
17495
+ localEstimate: contextBreakdown?.total,
17284
17496
  maxContext
17285
- );
17497
+ }).used : cheapFill.used;
17286
17498
  const contextWindow = useMemo7(() => {
17287
17499
  void state.contextChipVersion;
17288
17500
  return maxContext > 0 ? { used: currentContextTokens, max: maxContext } : void 0;
@@ -18275,6 +18487,9 @@ function useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId,
18275
18487
  /* @__PURE__ */ new Map()
18276
18488
  );
18277
18489
  const leaderCtxDispatchRef = useRef29(void 0);
18490
+ const leaderCtxFlushRef = useRef29(void 0);
18491
+ const latestLeaderCtxPctRef = useRef29(void 0);
18492
+ const ctxFlushRef = useRef29(/* @__PURE__ */ new Map());
18278
18493
  const gate = useFleetGenerationGate(sessionGenerationRef);
18279
18494
  const lbl = useCallback19(
18280
18495
  (id, name) => labelFor2(labelsRef, id, name),
@@ -18375,6 +18590,11 @@ function useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId,
18375
18590
  seen.delete(e.subagentId);
18376
18591
  labelsRef.current.delete(e.subagentId);
18377
18592
  ctxDispatchRef.current.delete(e.subagentId);
18593
+ const pendingFlush = ctxFlushRef.current.get(e.subagentId);
18594
+ if (pendingFlush) {
18595
+ clearTimeout(pendingFlush);
18596
+ ctxFlushRef.current.delete(e.subagentId);
18597
+ }
18378
18598
  gate.forget(e.subagentId);
18379
18599
  dispatch({ type: "fleetRemove", id: e.subagentId });
18380
18600
  });
@@ -18454,22 +18674,41 @@ function useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId,
18454
18674
  if (!isCurrentSession(e.sessionId)) return;
18455
18675
  if (!gate.isLive(e.subagentId)) return;
18456
18676
  seen.add(e.subagentId);
18457
- const now = Date.now();
18677
+ const emit = () => {
18678
+ ctxDispatchRef.current.set(e.subagentId, {
18679
+ at: Date.now(),
18680
+ load: e.load,
18681
+ tokens: e.tokens,
18682
+ max: e.maxContext
18683
+ });
18684
+ dispatch({
18685
+ type: "fleetCtxPct",
18686
+ id: e.subagentId,
18687
+ load: e.load,
18688
+ tokens: e.tokens,
18689
+ maxContext: e.maxContext
18690
+ });
18691
+ };
18458
18692
  const previous = ctxDispatchRef.current.get(e.subagentId);
18459
- if (previous && now - previous.at < 250) return;
18460
- ctxDispatchRef.current.set(e.subagentId, {
18461
- at: now,
18462
- load: e.load,
18463
- tokens: e.tokens,
18464
- max: e.maxContext
18465
- });
18466
- dispatch({
18467
- type: "fleetCtxPct",
18468
- id: e.subagentId,
18469
- load: e.load,
18470
- tokens: e.tokens,
18471
- maxContext: e.maxContext
18472
- });
18693
+ const elapsed = previous ? Date.now() - previous.at : Number.POSITIVE_INFINITY;
18694
+ if (elapsed < 250) {
18695
+ const pending2 = ctxFlushRef.current.get(e.subagentId);
18696
+ if (pending2) clearTimeout(pending2);
18697
+ const timer = setTimeout(() => {
18698
+ ctxFlushRef.current.delete(e.subagentId);
18699
+ if (!gate.isLive(e.subagentId)) return;
18700
+ emit();
18701
+ }, 250 - elapsed);
18702
+ timer.unref?.();
18703
+ ctxFlushRef.current.set(e.subagentId, timer);
18704
+ return;
18705
+ }
18706
+ const pending = ctxFlushRef.current.get(e.subagentId);
18707
+ if (pending) {
18708
+ clearTimeout(pending);
18709
+ ctxFlushRef.current.delete(e.subagentId);
18710
+ }
18711
+ emit();
18473
18712
  });
18474
18713
  const offConcurrencyChanged = events.on("concurrency.changed", (e) => {
18475
18714
  const { n, sessionId } = e;
@@ -18480,17 +18719,40 @@ function useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId,
18480
18719
  });
18481
18720
  const offLeaderCtxPct = events.on("ctx.pct", (e) => {
18482
18721
  if (!isCurrentSession(e.sessionId)) return;
18483
- const now = Date.now();
18484
- const previous = leaderCtxDispatchRef.current;
18485
- if (previous && now - previous.at < 250) return;
18486
- leaderCtxDispatchRef.current = {
18487
- at: now,
18488
- load: e.load,
18489
- tokens: e.tokens,
18490
- max: e.maxContext
18722
+ latestLeaderCtxPctRef.current = e;
18723
+ const emit = () => {
18724
+ const latest = latestLeaderCtxPctRef.current ?? e;
18725
+ leaderCtxDispatchRef.current = {
18726
+ at: Date.now(),
18727
+ load: latest.load,
18728
+ tokens: latest.tokens,
18729
+ max: latest.maxContext
18730
+ };
18731
+ setActiveMaxContext(latest.maxContext);
18732
+ dispatch({
18733
+ type: "leaderCtxPct",
18734
+ load: latest.load,
18735
+ tokens: latest.tokens,
18736
+ maxContext: latest.maxContext
18737
+ });
18491
18738
  };
18492
- setActiveMaxContext(e.maxContext);
18493
- dispatch({ type: "leaderCtxPct", load: e.load, tokens: e.tokens, maxContext: e.maxContext });
18739
+ const previous = leaderCtxDispatchRef.current;
18740
+ const elapsed = previous ? Date.now() - previous.at : Number.POSITIVE_INFINITY;
18741
+ if (elapsed < 250) {
18742
+ if (leaderCtxFlushRef.current) clearTimeout(leaderCtxFlushRef.current);
18743
+ const timer = setTimeout(() => {
18744
+ leaderCtxFlushRef.current = void 0;
18745
+ emit();
18746
+ }, 250 - elapsed);
18747
+ timer.unref?.();
18748
+ leaderCtxFlushRef.current = timer;
18749
+ return;
18750
+ }
18751
+ if (leaderCtxFlushRef.current) {
18752
+ clearTimeout(leaderCtxFlushRef.current);
18753
+ leaderCtxFlushRef.current = void 0;
18754
+ }
18755
+ emit();
18494
18756
  });
18495
18757
  const offLeaderMaxContext = events.on("ctx.max_context", (e) => {
18496
18758
  if (!isCurrentSession(e.sessionId)) return;
@@ -18527,6 +18789,12 @@ function useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId,
18527
18789
  offLeaderMaxContext();
18528
18790
  offCompactionFired();
18529
18791
  offTool();
18792
+ if (leaderCtxFlushRef.current) {
18793
+ clearTimeout(leaderCtxFlushRef.current);
18794
+ leaderCtxFlushRef.current = void 0;
18795
+ }
18796
+ for (const timer of ctxFlushRef.current.values()) clearTimeout(timer);
18797
+ ctxFlushRef.current.clear();
18530
18798
  for (const id of seen) {
18531
18799
  labelsRef.current.delete(id);
18532
18800
  ctxDispatchRef.current.delete(id);
@@ -25213,383 +25481,8 @@ function AuthPanel({ panel }) {
25213
25481
  ] });
25214
25482
  }
25215
25483
 
25216
- // src/components/connections-panel.tsx
25217
- import { useCallback as useCallback24, useEffect as useEffect61, useRef as useRef33, useState as useState34 } from "react";
25218
-
25219
- // src/connections-health.ts
25220
- import { createChronicleProjectAccess } from "@wrongstack/core/chronicle";
25221
- import {
25222
- isMailboxProjectServerAvailable,
25223
- MailboxProjectServerConnection
25224
- } from "@wrongstack/core/coordination";
25225
- import { resolveWstackPaths as resolveWstackPaths5 } from "@wrongstack/core/utils";
25226
- import { getKanbanServerConnection } from "@wrongstack/kanban";
25227
- import { isSageProjectServerAvailable, SageProjectServerConnection } from "@wrongstack/sage";
25228
- import { checkCodebaseIndexServerHealth, getIndexState as getIndexState2 } from "@wrongstack/tools";
25229
- async function collectConnectionsHealth(projectRoot) {
25230
- const services = await Promise.all([
25231
- chronicleHealth(projectRoot),
25232
- codebaseIndexHealth(projectRoot),
25233
- sageHealth(projectRoot),
25234
- kanbanHealth(projectRoot),
25235
- mailboxHealth(projectRoot)
25236
- ]);
25237
- const required = services.filter((s2) => s2.required);
25238
- const overall = required.some((s2) => s2.status === "error") ? "error" : required.some((s2) => s2.status !== "healthy") ? "degraded" : "healthy";
25239
- return { checkedAt: Date.now(), overall, services };
25240
- }
25241
- async function chronicleHealth(projectRoot) {
25242
- const startedAt = Date.now();
25243
- let access;
25244
- try {
25245
- access = createChronicleProjectAccess({ projectRoot });
25246
- const health2 = await access.call("ping", {}, { timeoutMs: 5e3 });
25247
- const quarantined = health2.quarantinedFamilies ?? [];
25248
- return {
25249
- id: "chronicle",
25250
- label: "Chronicle telemetry",
25251
- status: quarantined.length > 0 ? "degraded" : access.mode === "server" ? "healthy" : "degraded",
25252
- required: true,
25253
- mode: access.mode,
25254
- detail: quarantined.length > 0 ? `Serving, but ${quarantined.length} day(s) were quarantined for a broken chain: ${quarantined.map((family) => family.day).join(", ")}.` : access.mode === "server" ? "One project owner collects, processes, stores, and serves telemetry." : "Inline fallback is active; ownership is not shared across clients.",
25255
- ownerPid: health2.pid,
25256
- endpoint: health2.endpoint,
25257
- storage: health2.chronicleDirectory,
25258
- uptimeMs: health2.uptimeMs,
25259
- latencyMs: Date.now() - startedAt,
25260
- clients: health2.clients,
25261
- activeRequests: health2.activeRequests,
25262
- queuedWork: health2.journal.pendingEvents,
25263
- watcher: { active: health2.watcher.active, watchedFiles: health2.watcher.watchedFiles },
25264
- ...quarantined[0] ? { lastError: quarantined[0].reason } : health2.watcher.lastError ? { lastError: health2.watcher.lastError } : {}
25265
- };
25266
- } catch (error) {
25267
- return failureService("chronicle", "Chronicle telemetry", true, access?.mode ?? "unavailable", error, Date.now() - startedAt);
25268
- } finally {
25269
- await access?.close();
25270
- }
25271
- }
25272
- async function codebaseIndexHealth(projectRoot) {
25273
- const startedAt = Date.now();
25274
- try {
25275
- const health2 = await checkCodebaseIndexServerHealth(projectRoot, void 0, { timeoutMs: 2e3 });
25276
- const connection = getIndexState2().server;
25277
- return {
25278
- id: "codebase-index",
25279
- label: "Codebase index",
25280
- status: health2.status === "unresponsive" ? "error" : health2.status,
25281
- required: false,
25282
- mode: "project-server",
25283
- detail: health2.server?.activity.indexing ? `Indexing ${health2.server.activity.currentFile}/${health2.server.activity.totalFiles}.` : "Shared symbol index is ready for project queries.",
25284
- ownerPid: connection.pid,
25285
- endpoint: connection.endpoint,
25286
- storage: connection.indexDir ?? resolveWstackPaths5({ projectRoot }).projectCodebaseIndex,
25287
- uptimeMs: health2.server?.uptimeMs,
25288
- latencyMs: health2.latencyMs ?? Date.now() - startedAt,
25289
- clients: health2.server?.clients,
25290
- activeRequests: health2.server?.activeRequests,
25291
- queuedWork: health2.server?.queuedWrites,
25292
- watcher: health2.server ? { active: health2.server.watchingExternal, watchedFiles: health2.server.watchingClients } : void 0
25293
- };
25294
- } catch (error) {
25295
- if (isOfflineConnectionError(error)) {
25296
- return {
25297
- ...failureService("codebase-index", "Codebase index", false, "on-demand project-server", error, Date.now() - startedAt),
25298
- status: "offline",
25299
- detail: "Not running for this project; it starts on demand when indexing is used."
25300
- };
25301
- }
25302
- return failureService("codebase-index", "Codebase index", false, "project-server", error, Date.now() - startedAt);
25303
- }
25304
- }
25305
- async function sageHealth(projectRoot) {
25306
- const startedAt = Date.now();
25307
- if (!isSageProjectServerAvailable()) {
25308
- return {
25309
- id: "sage",
25310
- label: "SAGE memory",
25311
- status: "unavailable",
25312
- required: false,
25313
- mode: "unavailable",
25314
- detail: "The packaged SAGE project server is unavailable in this runtime."
25315
- };
25316
- }
25317
- const connection = new SageProjectServerConnection(projectRoot);
25318
- try {
25319
- const status = await connection.status();
25320
- if (!status) {
25321
- return {
25322
- id: "sage",
25323
- label: "SAGE memory",
25324
- status: "offline",
25325
- required: false,
25326
- mode: "on-demand project-server",
25327
- detail: "Not running for this project; it starts on demand when memory is used.",
25328
- endpoint: connection.getState().endpoint,
25329
- latencyMs: Date.now() - startedAt
25330
- };
25331
- }
25332
- const healthStatus = status.health.status === "ready" ? "healthy" : status.health.status === "degraded" ? "degraded" : "error";
25333
- return {
25334
- id: "sage",
25335
- label: "SAGE memory",
25336
- status: healthStatus,
25337
- required: false,
25338
- mode: "project-server",
25339
- detail: `Persistent project memory backend: ${status.health.backend}.`,
25340
- ownerPid: status.pid,
25341
- endpoint: status.endpoint,
25342
- storage: status.storageRoot,
25343
- latencyMs: Date.now() - startedAt,
25344
- clients: status.clients,
25345
- activeRequests: status.pendingRequests
25346
- };
25347
- } catch (error) {
25348
- return failureService("sage", "SAGE memory", false, "project-server", error, Date.now() - startedAt);
25349
- } finally {
25350
- connection.close();
25351
- }
25352
- }
25353
- async function kanbanHealth(projectRoot) {
25354
- const startedAt = Date.now();
25355
- if (process.env["WRONGSTACK_KANBAN_SERVER"] === "0") {
25356
- return {
25357
- id: "kanban",
25358
- label: "Kanban IPC",
25359
- status: "unavailable",
25360
- required: false,
25361
- mode: "disabled",
25362
- detail: "Kanban IPC daemon is disabled via WRONGSTACK_KANBAN_SERVER=0."
25363
- };
25364
- }
25365
- let connection;
25366
- try {
25367
- connection = await getKanbanServerConnection(projectRoot);
25368
- } catch (error) {
25369
- return failureService("kanban", "Kanban IPC", false, "project-server", error, Date.now() - startedAt);
25370
- }
25371
- if (!connection) {
25372
- return {
25373
- id: "kanban",
25374
- label: "Kanban IPC",
25375
- status: "unavailable",
25376
- required: false,
25377
- mode: "disabled",
25378
- detail: "Kanban IPC daemon is disabled in this runtime."
25379
- };
25380
- }
25381
- try {
25382
- const status = await connection.request("ping", {}, { timeoutMs: 5e3 });
25383
- return {
25384
- id: "kanban",
25385
- label: "Kanban IPC",
25386
- status: "healthy",
25387
- required: true,
25388
- mode: "project-server",
25389
- detail: `Single shared project-server owns kanban state (v${status.protocolVersion}).`,
25390
- ownerPid: status.pid,
25391
- endpoint: status.endpoint,
25392
- storage: status.databasePath,
25393
- latencyMs: Date.now() - startedAt,
25394
- clients: status.clients,
25395
- activeRequests: status.pendingRequests,
25396
- uptimeMs: Date.now() - new Date(status.startedAt).getTime()
25397
- };
25398
- } catch (error) {
25399
- return failureService("kanban", "Kanban IPC", true, "project-server", error, Date.now() - startedAt);
25400
- }
25401
- }
25402
- async function mailboxHealth(projectRoot) {
25403
- const startedAt = Date.now();
25404
- if (!isMailboxProjectServerAvailable()) {
25405
- return {
25406
- id: "mailbox",
25407
- label: "Mailbox IPC",
25408
- status: "unavailable",
25409
- required: false,
25410
- mode: "unavailable",
25411
- detail: "The packaged mailbox project server is unavailable in this runtime."
25412
- };
25413
- }
25414
- const connection = new MailboxProjectServerConnection(
25415
- resolveWstackPaths5({ projectRoot }).projectDir
25416
- );
25417
- try {
25418
- const status = await connection.probeStatus();
25419
- if (!status) {
25420
- return {
25421
- id: "mailbox",
25422
- label: "Mailbox IPC",
25423
- status: "offline",
25424
- required: false,
25425
- mode: "on-demand project-server",
25426
- detail: "Not running for this project; it starts on demand when mailbox is used.",
25427
- endpoint: connection.getState().endpoint,
25428
- latencyMs: Date.now() - startedAt
25429
- };
25430
- }
25431
- return {
25432
- id: "mailbox",
25433
- label: "Mailbox IPC",
25434
- status: "healthy",
25435
- required: true,
25436
- mode: "project-server",
25437
- detail: `Single shared project-server owns inter-agent mailbox state (v${status.protocolVersion}).`,
25438
- ownerPid: status.pid,
25439
- endpoint: status.endpoint,
25440
- storage: status.databasePath,
25441
- latencyMs: Date.now() - startedAt,
25442
- clients: status.clients,
25443
- activeRequests: status.pendingRequests,
25444
- uptimeMs: Date.now() - new Date(status.startedAt).getTime()
25445
- };
25446
- } catch (error) {
25447
- return failureService("mailbox", "Mailbox IPC", true, "project-server", error, Date.now() - startedAt);
25448
- } finally {
25449
- connection.close();
25450
- }
25451
- }
25452
- function isOfflineConnectionError(error) {
25453
- const message = error instanceof Error ? error.message : String(error);
25454
- return /(?:ENOENT|ECONNREFUSED|not found|not running|unavailable|connect failed)/iu.test(message);
25455
- }
25456
- function failureService(id, label, required, mode, error, latencyMs) {
25457
- const message = error instanceof Error ? error.message : String(error);
25458
- return {
25459
- id,
25460
- label,
25461
- status: "error",
25462
- required,
25463
- mode,
25464
- detail: message,
25465
- lastError: message,
25466
- ...latencyMs === void 0 ? {} : { latencyMs }
25467
- };
25468
- }
25469
-
25470
- // src/components/connections-panel.tsx
25471
- import { jsx as jsx40, jsxs as jsxs39 } from "react/jsx-runtime";
25472
- var REFRESH_MS = 8e3;
25473
- var STATUS_GLYPH = {
25474
- healthy: "\u25CF",
25475
- degraded: "\u25D0",
25476
- offline: "\u25CB",
25477
- unavailable: "\u2298",
25478
- error: "\u2717"
25479
- };
25480
- var STATUS_COLOR2 = {
25481
- healthy: "green",
25482
- degraded: "yellow",
25483
- offline: "gray",
25484
- unavailable: "gray",
25485
- error: "red"
25486
- };
25487
- var OVERALL_COLOR = {
25488
- healthy: "green",
25489
- degraded: "yellow",
25490
- error: "red"
25491
- };
25492
- function formatUptime(ms) {
25493
- if (ms < 1e3) return `${ms}ms`;
25494
- const s2 = Math.floor(ms / 1e3);
25495
- if (s2 < 60) return `${s2}s`;
25496
- const m = Math.floor(s2 / 60);
25497
- if (m < 60) return `${m}m`;
25498
- const h = Math.floor(m / 60);
25499
- return h < 24 ? `${h}h` : `${Math.floor(h / 24)}d`;
25500
- }
25501
- function formatLatency(ms) {
25502
- if (ms === void 0) return null;
25503
- if (ms < 1e3) return `${Math.round(ms)}ms`;
25504
- return `${(ms / 1e3).toFixed(1)}s`;
25505
- }
25506
- function ServiceRow({ service }) {
25507
- const glyph2 = STATUS_GLYPH[service.status];
25508
- const color = STATUS_COLOR2[service.status];
25509
- const latency = formatLatency(service.latencyMs);
25510
- const metaParts = [];
25511
- if (service.ownerPid !== void 0) metaParts.push(`PID ${service.ownerPid}`);
25512
- if (service.clients !== void 0) metaParts.push(`${service.clients} client${service.clients === 1 ? "" : "s"}`);
25513
- if (service.activeRequests !== void 0 && service.activeRequests > 0) {
25514
- metaParts.push(`${service.activeRequests} active`);
25515
- }
25516
- if (service.uptimeMs !== void 0) metaParts.push(`up ${formatUptime(service.uptimeMs)}`);
25517
- if (latency) metaParts.push(`${latency}`);
25518
- if (service.queuedWork !== void 0 && service.queuedWork > 0) {
25519
- metaParts.push(`${service.queuedWork} queued`);
25520
- }
25521
- return /* @__PURE__ */ jsxs39(Box, { flexDirection: "column", marginTop: 0, children: [
25522
- /* @__PURE__ */ jsxs39(Box, { gap: 1, children: [
25523
- /* @__PURE__ */ jsx40(Text, { color, children: glyph2 }),
25524
- /* @__PURE__ */ jsx40(Text, { bold: true, children: service.label }),
25525
- service.required ? /* @__PURE__ */ jsx40(Text, { dimColor: true, children: "required" }) : null,
25526
- /* @__PURE__ */ jsxs39(Text, { dimColor: true, children: [
25527
- "\xB7 ",
25528
- service.mode
25529
- ] })
25530
- ] }),
25531
- /* @__PURE__ */ jsx40(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx40(Text, { dimColor: true, children: service.detail }) }),
25532
- metaParts.length > 0 ? /* @__PURE__ */ jsx40(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx40(Text, { dimColor: true, children: metaParts.join(" \xB7 ") }) }) : null
25533
- ] });
25534
- }
25535
- function ConnectionsPanel({ projectRoot, onClose }) {
25536
- const [report, setReport] = useState34(null);
25537
- const [loading, setLoading] = useState34(true);
25538
- const [error, setError] = useState34(null);
25539
- const cancelledRef = useRef33(false);
25540
- const fetch = useCallback24(async () => {
25541
- try {
25542
- const r = await collectConnectionsHealth(projectRoot);
25543
- if (cancelledRef.current) return;
25544
- setReport(r);
25545
- setError(null);
25546
- } catch (e) {
25547
- if (cancelledRef.current) return;
25548
- setError(e instanceof Error ? e.message : String(e));
25549
- } finally {
25550
- if (!cancelledRef.current) setLoading(false);
25551
- }
25552
- }, [projectRoot]);
25553
- useEffect61(() => {
25554
- cancelledRef.current = false;
25555
- void fetch();
25556
- const timer = setInterval(() => void fetch(), REFRESH_MS);
25557
- return () => {
25558
- cancelledRef.current = true;
25559
- clearInterval(timer);
25560
- };
25561
- }, [fetch]);
25562
- useInput((_input, key) => {
25563
- if (key.escape || _input === "q") onClose();
25564
- if (_input === "r") void fetch();
25565
- });
25566
- const overallLabel = report ? report.overall === "healthy" ? "All required services healthy" : report.overall === "degraded" ? "Some services degraded" : "Service errors detected" : "Checking\u2026";
25567
- return /* @__PURE__ */ jsxs39(Box, { flexDirection: "column", borderStyle: "round", borderColor: report ? OVERALL_COLOR[report.overall] : "gray", paddingX: 1, marginY: 0, flexShrink: 0, children: [
25568
- /* @__PURE__ */ jsxs39(Box, { justifyContent: "space-between", children: [
25569
- /* @__PURE__ */ jsxs39(Box, { gap: 1, children: [
25570
- /* @__PURE__ */ jsx40(Text, { bold: true, children: "Service Connections" }),
25571
- report ? /* @__PURE__ */ jsxs39(Text, { color: OVERALL_COLOR[report.overall], children: [
25572
- "\u25CF ",
25573
- overallLabel
25574
- ] }) : null
25575
- ] }),
25576
- /* @__PURE__ */ jsx40(Text, { dimColor: true, children: "Esc/q close \xB7 r refresh" })
25577
- ] }),
25578
- loading && !report ? /* @__PURE__ */ jsx40(Box, { paddingY: 1, children: /* @__PURE__ */ jsx40(Text, { dimColor: true, children: "Checking service health\u2026" }) }) : null,
25579
- error ? /* @__PURE__ */ jsx40(Box, { paddingY: 1, children: /* @__PURE__ */ jsxs39(Text, { color: "red", children: [
25580
- "Error: ",
25581
- error
25582
- ] }) }) : null,
25583
- report ? /* @__PURE__ */ jsx40(Box, { flexDirection: "column", marginTop: 0, children: report.services.map((s2) => /* @__PURE__ */ jsx40(ServiceRow, { service: s2 }, s2.id)) }) : null,
25584
- report ? /* @__PURE__ */ jsx40(Box, { marginTop: 0, children: /* @__PURE__ */ jsxs39(Text, { dimColor: true, children: [
25585
- "Last checked ",
25586
- new Date(report.checkedAt).toLocaleTimeString()
25587
- ] }) }) : null
25588
- ] });
25589
- }
25590
-
25591
25484
  // src/components/brain-decision-prompt.tsx
25592
- import { jsx as jsx41, jsxs as jsxs40 } from "react/jsx-runtime";
25485
+ import { jsx as jsx40, jsxs as jsxs39 } from "react/jsx-runtime";
25593
25486
  function riskColor(risk) {
25594
25487
  switch (risk) {
25595
25488
  case "low":
@@ -25634,54 +25527,54 @@ function BrainDecisionPrompt({
25634
25527
  const option = Number.isInteger(index) && index >= 0 ? options[index] : void 0;
25635
25528
  if (option) onAnswer({ id: requestId, optionId: option.id, text: option.label });
25636
25529
  });
25637
- return /* @__PURE__ */ jsxs40(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [
25638
- /* @__PURE__ */ jsx41(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx41(Text, { bold: true, color: "magenta", children: "\u{1F9E0} BRAIN REQUIRES HUMAN DECISION" }) }),
25639
- /* @__PURE__ */ jsxs40(Box, { flexDirection: "row", gap: 1, children: [
25640
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: "Source:" }),
25641
- /* @__PURE__ */ jsx41(Text, { children: source }),
25642
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: "Risk:" }),
25643
- /* @__PURE__ */ jsx41(Text, { color, children: risk })
25530
+ return /* @__PURE__ */ jsxs39(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [
25531
+ /* @__PURE__ */ jsx40(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx40(Text, { bold: true, color: "magenta", children: "\u{1F9E0} BRAIN REQUIRES HUMAN DECISION" }) }),
25532
+ /* @__PURE__ */ jsxs39(Box, { flexDirection: "row", gap: 1, children: [
25533
+ /* @__PURE__ */ jsx40(Text, { dimColor: true, children: "Source:" }),
25534
+ /* @__PURE__ */ jsx40(Text, { children: source }),
25535
+ /* @__PURE__ */ jsx40(Text, { dimColor: true, children: "Risk:" }),
25536
+ /* @__PURE__ */ jsx40(Text, { color, children: risk })
25644
25537
  ] }),
25645
- /* @__PURE__ */ jsx41(Text, { color: "white", children: question }),
25646
- ctx.length > 0 ? /* @__PURE__ */ jsxs40(Box, { flexDirection: "column", marginTop: 1, children: [
25647
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: "Context:" }),
25648
- ctx.map((line, index) => /* @__PURE__ */ jsx41(Text, { dimColor: true, children: line }, index)),
25649
- dropped > 0 ? /* @__PURE__ */ jsxs40(Text, { dimColor: true, children: [
25538
+ /* @__PURE__ */ jsx40(Text, { color: "white", children: question }),
25539
+ ctx.length > 0 ? /* @__PURE__ */ jsxs39(Box, { flexDirection: "column", marginTop: 1, children: [
25540
+ /* @__PURE__ */ jsx40(Text, { dimColor: true, children: "Context:" }),
25541
+ ctx.map((line, index) => /* @__PURE__ */ jsx40(Text, { dimColor: true, children: line }, index)),
25542
+ dropped > 0 ? /* @__PURE__ */ jsxs39(Text, { dimColor: true, children: [
25650
25543
  "\u2026 (",
25651
25544
  dropped,
25652
25545
  " more)"
25653
25546
  ] }) : null
25654
25547
  ] }) : null,
25655
- options.length > 0 ? /* @__PURE__ */ jsxs40(Box, { flexDirection: "column", marginTop: 1, children: [
25656
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: "Options:" }),
25548
+ options.length > 0 ? /* @__PURE__ */ jsxs39(Box, { flexDirection: "column", marginTop: 1, children: [
25549
+ /* @__PURE__ */ jsx40(Text, { dimColor: true, children: "Options:" }),
25657
25550
  options.slice(0, 6).map((option, index) => {
25658
25551
  const key = optionKey(index);
25659
25552
  const optionColor = riskColor(String(option.risk ?? risk));
25660
- return /* @__PURE__ */ jsxs40(Box, { flexDirection: "column", children: [
25661
- /* @__PURE__ */ jsxs40(Text, { children: [
25662
- /* @__PURE__ */ jsxs40(Text, { bold: true, color: "yellow", children: [
25553
+ return /* @__PURE__ */ jsxs39(Box, { flexDirection: "column", children: [
25554
+ /* @__PURE__ */ jsxs39(Text, { children: [
25555
+ /* @__PURE__ */ jsxs39(Text, { bold: true, color: "yellow", children: [
25663
25556
  "[",
25664
25557
  key,
25665
25558
  "]"
25666
25559
  ] }),
25667
25560
  " ",
25668
- /* @__PURE__ */ jsx41(Text, { color: optionColor, children: option.label }),
25669
- option.recommended ? /* @__PURE__ */ jsx41(Text, { color: "green", children: " recommended" }) : null
25561
+ /* @__PURE__ */ jsx40(Text, { color: optionColor, children: option.label }),
25562
+ option.recommended ? /* @__PURE__ */ jsx40(Text, { color: "green", children: " recommended" }) : null
25670
25563
  ] }),
25671
- option.consequence ? /* @__PURE__ */ jsxs40(Text, { dimColor: true, children: [
25564
+ option.consequence ? /* @__PURE__ */ jsxs39(Text, { dimColor: true, children: [
25672
25565
  " ",
25673
25566
  option.consequence
25674
25567
  ] }) : null
25675
25568
  ] }, option.id);
25676
25569
  })
25677
25570
  ] }) : null,
25678
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
25679
- /* @__PURE__ */ jsx41(Text, { dimColor: true, children: "Press A/B/C or 1/2/3 to answer; Esc or D denies with the safe default." })
25571
+ /* @__PURE__ */ jsx40(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
25572
+ /* @__PURE__ */ jsx40(Text, { dimColor: true, children: "Press A/B/C or 1/2/3 to answer; Esc or D denies with the safe default." })
25680
25573
  ] });
25681
25574
  }
25682
25575
 
25683
25576
  // src/components/brain-panel.tsx
25684
- import { Fragment as Fragment14, jsx as jsx42, jsxs as jsxs41 } from "react/jsx-runtime";
25577
+ import { Fragment as Fragment14, jsx as jsx41, jsxs as jsxs40 } from "react/jsx-runtime";
25685
25578
  var CHROME_ROWS2 = 14;
25686
25579
  var RISK_DESCS = {
25687
25580
  off: "Human decides everything",
@@ -25911,19 +25804,19 @@ function BrainPanel({
25911
25804
  const above = windowStart;
25912
25805
  const below = total - windowEnd;
25913
25806
  const headerHint = activeView === "settings" ? "\u2191/\u2193 row \xB7 \u2190/\u2192 change \xB7 Enter pick/toggle \xB7 d remove \xB7 Tab log \xB7 Esc close" : editable ? "\u2191/\u2193 navigate log \xB7 Tab settings \xB7 Esc close" : "\u2191/\u2193 navigate log \xB7 \u2190/\u2192 change risk \xB7 Esc close";
25914
- return /* @__PURE__ */ jsxs41(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [
25915
- /* @__PURE__ */ jsx42(Text, { bold: true, color: "magenta", children: `\u2501\u2501 Brain \u2501\u2501${busy ? " \u2026" : ""}` }),
25916
- /* @__PURE__ */ jsx42(Text, { dimColor: true, children: headerHint }),
25917
- activeView === "settings" && settings ? /* @__PURE__ */ jsxs41(Box, { marginTop: 1, flexDirection: "column", children: [
25807
+ return /* @__PURE__ */ jsxs40(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [
25808
+ /* @__PURE__ */ jsx41(Text, { bold: true, color: "magenta", children: `\u2501\u2501 Brain \u2501\u2501${busy ? " \u2026" : ""}` }),
25809
+ /* @__PURE__ */ jsx41(Text, { dimColor: true, children: headerHint }),
25810
+ activeView === "settings" && settings ? /* @__PURE__ */ jsxs40(Box, { marginTop: 1, flexDirection: "column", children: [
25918
25811
  brainPanelRows(settings).map((r, i) => {
25919
25812
  const focused = i === (row ?? 0);
25920
25813
  const readOnly = BRAIN_READONLY_ROW_KINDS.has(r.kind);
25921
25814
  const { label, value, dim } = rowText(r, settings);
25922
25815
  const key = `${r.kind}-${"index" in r ? r.index : "key" in r ? r.key : 0}`;
25923
- return /* @__PURE__ */ jsxs41(Text, { wrap: "truncate-end", children: [
25924
- focused ? /* @__PURE__ */ jsx42(Text, { color: "magenta", children: "\u203A " }) : " ",
25925
- /* @__PURE__ */ jsx42(Text, { bold: focused, dimColor: readOnly, children: label.padEnd(18) }),
25926
- /* @__PURE__ */ jsx42(
25816
+ return /* @__PURE__ */ jsxs40(Text, { wrap: "truncate-end", children: [
25817
+ focused ? /* @__PURE__ */ jsx41(Text, { color: "magenta", children: "\u203A " }) : " ",
25818
+ /* @__PURE__ */ jsx41(Text, { bold: focused, dimColor: readOnly, children: label.padEnd(18) }),
25819
+ /* @__PURE__ */ jsx41(
25927
25820
  Text,
25928
25821
  {
25929
25822
  color: r.kind === "risk" ? RISK_COLORS2[settings.riskLevel] : focused && !readOnly ? "cyan" : void 0,
@@ -25933,24 +25826,24 @@ function BrainPanel({
25933
25826
  children: value
25934
25827
  }
25935
25828
  ),
25936
- dim ? /* @__PURE__ */ jsx42(Text, { dimColor: true, children: ` ${dim}` }) : null
25829
+ dim ? /* @__PURE__ */ jsx41(Text, { dimColor: true, children: ` ${dim}` }) : null
25937
25830
  ] }, key);
25938
25831
  }),
25939
- /* @__PURE__ */ jsx42(Box, { marginTop: 1, children: /* @__PURE__ */ jsx42(Text, { dimColor: true, children: "changes apply live and persist to the active profile config" }) })
25940
- ] }) : /* @__PURE__ */ jsxs41(Box, { marginTop: 1, flexDirection: "column", children: [
25941
- /* @__PURE__ */ jsxs41(Box, { children: [
25942
- /* @__PURE__ */ jsx42(Text, { bold: true, children: "Risk ceiling: " }),
25943
- /* @__PURE__ */ jsx42(Text, { color: RISK_COLORS2[riskLevel], bold: true, children: riskLevel.toUpperCase() }),
25944
- /* @__PURE__ */ jsx42(Text, { dimColor: true, children: ` ${RISK_DESCS[riskLevel]}` })
25832
+ /* @__PURE__ */ jsx41(Box, { marginTop: 1, children: /* @__PURE__ */ jsx41(Text, { dimColor: true, children: "changes apply live and persist to the active profile config" }) })
25833
+ ] }) : /* @__PURE__ */ jsxs40(Box, { marginTop: 1, flexDirection: "column", children: [
25834
+ /* @__PURE__ */ jsxs40(Box, { children: [
25835
+ /* @__PURE__ */ jsx41(Text, { bold: true, children: "Risk ceiling: " }),
25836
+ /* @__PURE__ */ jsx41(Text, { color: RISK_COLORS2[riskLevel], bold: true, children: riskLevel.toUpperCase() }),
25837
+ /* @__PURE__ */ jsx41(Text, { dimColor: true, children: ` ${RISK_DESCS[riskLevel]}` })
25945
25838
  ] }),
25946
- /* @__PURE__ */ jsxs41(Box, { marginTop: 1, flexDirection: "column", children: [
25947
- /* @__PURE__ */ jsx42(Text, { bold: true, color: "blue", children: "Recent decisions" }),
25948
- total === 0 ? /* @__PURE__ */ jsx42(Text, { dimColor: true, children: " No decisions recorded yet this session." }) : /* @__PURE__ */ jsxs41(Fragment14, { children: [
25949
- above > 0 ? /* @__PURE__ */ jsx42(Text, { dimColor: true, children: ` \u2191 ${above} more` }) : null,
25839
+ /* @__PURE__ */ jsxs40(Box, { marginTop: 1, flexDirection: "column", children: [
25840
+ /* @__PURE__ */ jsx41(Text, { bold: true, color: "blue", children: "Recent decisions" }),
25841
+ total === 0 ? /* @__PURE__ */ jsx41(Text, { dimColor: true, children: " No decisions recorded yet this session." }) : /* @__PURE__ */ jsxs40(Fragment14, { children: [
25842
+ above > 0 ? /* @__PURE__ */ jsx41(Text, { dimColor: true, children: ` \u2191 ${above} more` }) : null,
25950
25843
  log.slice(windowStart, windowEnd).map((entry, i) => {
25951
25844
  const index = windowStart + i;
25952
25845
  const focused = index === selected;
25953
- return /* @__PURE__ */ jsxs41(
25846
+ return /* @__PURE__ */ jsxs40(
25954
25847
  Text,
25955
25848
  {
25956
25849
  inverse: focused,
@@ -25958,25 +25851,25 @@ function BrainPanel({
25958
25851
  wrap: "truncate-end",
25959
25852
  children: [
25960
25853
  focused ? "\u203A " : " ",
25961
- /* @__PURE__ */ jsx42(Text, { dimColor: true, children: entry.age.padEnd(8) }),
25962
- /* @__PURE__ */ jsx42(Text, { color: "cyan", children: entry.kind.padEnd(12) }),
25963
- /* @__PURE__ */ jsx42(Text, { children: entry.question.length > 60 ? `${entry.question.slice(0, 57)}\u2026` : entry.question }),
25964
- entry.outcome ? /* @__PURE__ */ jsx42(Text, { dimColor: true, children: ` \u2192 ${entry.outcome.length > 20 ? `${entry.outcome.slice(0, 17)}\u2026` : entry.outcome}` }) : null
25854
+ /* @__PURE__ */ jsx41(Text, { dimColor: true, children: entry.age.padEnd(8) }),
25855
+ /* @__PURE__ */ jsx41(Text, { color: "cyan", children: entry.kind.padEnd(12) }),
25856
+ /* @__PURE__ */ jsx41(Text, { children: entry.question.length > 60 ? `${entry.question.slice(0, 57)}\u2026` : entry.question }),
25857
+ entry.outcome ? /* @__PURE__ */ jsx41(Text, { dimColor: true, children: ` \u2192 ${entry.outcome.length > 20 ? `${entry.outcome.slice(0, 17)}\u2026` : entry.outcome}` }) : null
25965
25858
  ]
25966
25859
  },
25967
25860
  `${entry.kind}-${i}`
25968
25861
  );
25969
25862
  }),
25970
- below > 0 ? /* @__PURE__ */ jsx42(Text, { dimColor: true, children: ` \u2193 ${below} more` }) : null
25863
+ below > 0 ? /* @__PURE__ */ jsx41(Text, { dimColor: true, children: ` \u2193 ${below} more` }) : null
25971
25864
  ] })
25972
25865
  ] })
25973
25866
  ] }),
25974
- hint ? /* @__PURE__ */ jsx42(Box, { marginTop: 1, children: /* @__PURE__ */ jsx42(Text, { dimColor: true, children: hint }) }) : null
25867
+ hint ? /* @__PURE__ */ jsx41(Box, { marginTop: 1, children: /* @__PURE__ */ jsx41(Text, { dimColor: true, children: hint }) }) : null
25975
25868
  ] });
25976
25869
  }
25977
25870
 
25978
25871
  // src/components/checkpoint-timeline.tsx
25979
- import { jsx as jsx43, jsxs as jsxs42 } from "react/jsx-runtime";
25872
+ import { jsx as jsx42, jsxs as jsxs41 } from "react/jsx-runtime";
25980
25873
  function CheckpointTimeline({
25981
25874
  checkpoints,
25982
25875
  selected,
@@ -25995,22 +25888,22 @@ function CheckpointTimeline({
25995
25888
  onConfirm(selected);
25996
25889
  }
25997
25890
  });
25998
- return /* @__PURE__ */ jsxs42(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, children: [
25999
- /* @__PURE__ */ jsxs42(Box, { marginBottom: 1, children: [
26000
- /* @__PURE__ */ jsx43(Text, { bold: true, color: "cyan", children: "\u27F2 Session Rewind" }),
26001
- /* @__PURE__ */ jsx43(Text, { dimColor: true, children: " \u2014 \u2191/\u2193 navigate \xB7 Enter rewind \xB7 Esc cancel" })
25891
+ return /* @__PURE__ */ jsxs41(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, children: [
25892
+ /* @__PURE__ */ jsxs41(Box, { marginBottom: 1, children: [
25893
+ /* @__PURE__ */ jsx42(Text, { bold: true, color: "cyan", children: "\u27F2 Session Rewind" }),
25894
+ /* @__PURE__ */ jsx42(Text, { dimColor: true, children: " \u2014 \u2191/\u2193 navigate \xB7 Enter rewind \xB7 Esc cancel" })
26002
25895
  ] }),
26003
- checkpoints.length === 0 ? /* @__PURE__ */ jsx43(Text, { dimColor: true, children: "No checkpoints in this session." }) : checkpoints.map((cp, i) => {
25896
+ checkpoints.length === 0 ? /* @__PURE__ */ jsx42(Text, { dimColor: true, children: "No checkpoints in this session." }) : checkpoints.map((cp, i) => {
26004
25897
  const isSelected = i === selected;
26005
25898
  const label = `[${cp.promptIndex}] ${cp.promptPreview}`;
26006
- return /* @__PURE__ */ jsxs42(Box, { children: [
26007
- /* @__PURE__ */ jsx43(Text, { bold: isSelected, ...isSelected ? { color: "cyan" } : {}, children: isSelected ? "\u25B8 " : " " }),
26008
- /* @__PURE__ */ jsx43(Text, { bold: isSelected, ...isSelected ? { color: "cyan" } : {}, children: label }),
26009
- /* @__PURE__ */ jsxs42(Text, { dimColor: true, children: [
25899
+ return /* @__PURE__ */ jsxs41(Box, { children: [
25900
+ /* @__PURE__ */ jsx42(Text, { bold: isSelected, ...isSelected ? { color: "cyan" } : {}, children: isSelected ? "\u25B8 " : " " }),
25901
+ /* @__PURE__ */ jsx42(Text, { bold: isSelected, ...isSelected ? { color: "cyan" } : {}, children: label }),
25902
+ /* @__PURE__ */ jsxs41(Text, { dimColor: true, children: [
26010
25903
  " ",
26011
25904
  new Date(cp.ts).toLocaleTimeString()
26012
25905
  ] }),
26013
- cp.fileCount > 0 && /* @__PURE__ */ jsxs42(Text, { dimColor: true, children: [
25906
+ cp.fileCount > 0 && /* @__PURE__ */ jsxs41(Text, { dimColor: true, children: [
26014
25907
  " ",
26015
25908
  "\xB7 ",
26016
25909
  cp.fileCount,
@@ -26023,7 +25916,7 @@ function CheckpointTimeline({
26023
25916
  }
26024
25917
 
26025
25918
  // src/components/clear-confirm-panel.tsx
26026
- import { jsx as jsx44, jsxs as jsxs43 } from "react/jsx-runtime";
25919
+ import { jsx as jsx43, jsxs as jsxs42 } from "react/jsx-runtime";
26027
25920
  function isClearConfirmation(value) {
26028
25921
  return value === "YES";
26029
25922
  }
@@ -26049,30 +25942,30 @@ function ClearConfirmPanel({
26049
25942
  leaderActive ? "leader run" : null,
26050
25943
  subagentCount > 0 ? `${subagentCount} sub-agent${subagentCount === 1 ? "" : "s"}` : null
26051
25944
  ].filter((part) => part !== null);
26052
- return /* @__PURE__ */ jsxs43(Box, { flexDirection: "column", borderStyle: "double", borderColor: "red", paddingX: 1, marginY: 1, children: [
26053
- /* @__PURE__ */ jsx44(Text, { bold: true, color: "red", children: "\u26A0 CLEAR BLOCKED \u2014 ACTIVE WORK" }),
26054
- /* @__PURE__ */ jsxs43(Box, { flexDirection: "column", marginTop: 1, children: [
26055
- /* @__PURE__ */ jsxs43(Text, { children: [
25945
+ return /* @__PURE__ */ jsxs42(Box, { flexDirection: "column", borderStyle: "double", borderColor: "red", paddingX: 1, marginY: 1, children: [
25946
+ /* @__PURE__ */ jsx43(Text, { bold: true, color: "red", children: "\u26A0 CLEAR BLOCKED \u2014 ACTIVE WORK" }),
25947
+ /* @__PURE__ */ jsxs42(Box, { flexDirection: "column", marginTop: 1, children: [
25948
+ /* @__PURE__ */ jsxs42(Text, { children: [
26056
25949
  "Still running:",
26057
25950
  " ",
26058
- /* @__PURE__ */ jsx44(Text, { bold: true, color: "yellow", children: activeParts.join(" + ") })
25951
+ /* @__PURE__ */ jsx43(Text, { bold: true, color: "yellow", children: activeParts.join(" + ") })
26059
25952
  ] }),
26060
- /* @__PURE__ */ jsx44(Text, { dimColor: true, children: "Clearing will stop this work and permanently reset the current session." })
25953
+ /* @__PURE__ */ jsx43(Text, { dimColor: true, children: "Clearing will stop this work and permanently reset the current session." })
26061
25954
  ] }),
26062
- /* @__PURE__ */ jsx44(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs43(Text, { children: [
25955
+ /* @__PURE__ */ jsx43(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs42(Text, { children: [
26063
25956
  "Type",
26064
25957
  " ",
26065
- /* @__PURE__ */ jsx44(Text, { bold: true, color: "green", children: "YES" }),
25958
+ /* @__PURE__ */ jsx43(Text, { bold: true, color: "green", children: "YES" }),
26066
25959
  " ",
26067
25960
  "and press Enter to clear, or press ",
26068
- /* @__PURE__ */ jsx44(Text, { bold: true, children: "Esc" }),
25961
+ /* @__PURE__ */ jsx43(Text, { bold: true, children: "Esc" }),
26069
25962
  " to cancel."
26070
25963
  ] }) }),
26071
- /* @__PURE__ */ jsxs43(Text, { children: [
25964
+ /* @__PURE__ */ jsxs42(Text, { children: [
26072
25965
  "Confirmation:",
26073
25966
  " ",
26074
- /* @__PURE__ */ jsx44(Text, { bold: true, color: isClearConfirmation(value) ? "green" : "yellow", children: value }),
26075
- /* @__PURE__ */ jsx44(Text, { color: "cyan", children: "\u2588" })
25967
+ /* @__PURE__ */ jsx43(Text, { bold: true, color: isClearConfirmation(value) ? "green" : "yellow", children: value }),
25968
+ /* @__PURE__ */ jsx43(Text, { color: "cyan", children: "\u2588" })
26076
25969
  ] })
26077
25970
  ] });
26078
25971
  }
@@ -26616,7 +26509,7 @@ function formatMatchHit(hit) {
26616
26509
 
26617
26510
  // src/components/history/stream-box.tsx
26618
26511
  import React5 from "react";
26619
- import { jsx as jsx45, jsxs as jsxs44 } from "react/jsx-runtime";
26512
+ import { jsx as jsx44, jsxs as jsxs43 } from "react/jsx-runtime";
26620
26513
  var MAX_STREAM_DISPLAY_CHARS = 480;
26621
26514
  var MAX_STREAM_LINES = 8;
26622
26515
  var WRITE_CREATE_STREAM_LINES = 3;
@@ -26656,7 +26549,7 @@ var ToolStreamBox = React5.memo(function ToolStreamBox2({
26656
26549
  const contentWidth = Math.max(20, Math.min(termWidth - 4, 100));
26657
26550
  const rows = streamBoxRows(text, streamLines, contentWidth);
26658
26551
  const isWritePreview = name === "write";
26659
- return /* @__PURE__ */ jsxs44(
26552
+ return /* @__PURE__ */ jsxs43(
26660
26553
  Box,
26661
26554
  {
26662
26555
  flexDirection: "column",
@@ -26668,16 +26561,16 @@ var ToolStreamBox = React5.memo(function ToolStreamBox2({
26668
26561
  borderColor: color,
26669
26562
  paddingLeft: 1,
26670
26563
  children: [
26671
- /* @__PURE__ */ jsxs44(Box, { flexDirection: "row", children: [
26672
- /* @__PURE__ */ jsxs44(Text, { color, children: [
26564
+ /* @__PURE__ */ jsxs43(Box, { flexDirection: "row", children: [
26565
+ /* @__PURE__ */ jsxs43(Text, { color, children: [
26673
26566
  glyph2,
26674
26567
  " "
26675
26568
  ] }),
26676
- /* @__PURE__ */ jsx45(Text, { bold: true, color, children: isWritePreview ? "write \xB7 creating file" : name }),
26677
- /* @__PURE__ */ jsx45(Text, { dimColor: true, children: ` \u23F1 ${fmtDuration2(elapsedMs)}` }),
26678
- hidden > 0 ? /* @__PURE__ */ jsx45(Text, { dimColor: true, children: ` (${totalLines} lines, showing last ${streamLines})` }) : null
26569
+ /* @__PURE__ */ jsx44(Text, { bold: true, color, children: isWritePreview ? "write \xB7 creating file" : name }),
26570
+ /* @__PURE__ */ jsx44(Text, { dimColor: true, children: ` \u23F1 ${fmtDuration2(elapsedMs)}` }),
26571
+ hidden > 0 ? /* @__PURE__ */ jsx44(Text, { dimColor: true, children: ` (${totalLines} lines, showing last ${streamLines})` }) : null
26679
26572
  ] }),
26680
- /* @__PURE__ */ jsx45(Box, { flexDirection: "column", marginLeft: 1, children: rows.map((r, i) => /* @__PURE__ */ jsx45(
26573
+ /* @__PURE__ */ jsx44(Box, { flexDirection: "column", marginLeft: 1, children: rows.map((r, i) => /* @__PURE__ */ jsx44(
26681
26574
  Text,
26682
26575
  {
26683
26576
  color: isWritePreview ? "gray" : void 0,
@@ -26702,27 +26595,27 @@ function tailForDisplay(text, maxChars) {
26702
26595
  }
26703
26596
 
26704
26597
  // src/components/history/visual-lines.tsx
26705
- import { Fragment as Fragment15, jsx as jsx46, jsxs as jsxs45 } from "react/jsx-runtime";
26598
+ import { Fragment as Fragment15, jsx as jsx45, jsxs as jsxs44 } from "react/jsx-runtime";
26706
26599
  var VISUAL_TEXT_BUDGET = 92;
26707
26600
  function ToolOutputLines({
26708
26601
  lines,
26709
26602
  hasFollowingBlock
26710
26603
  }) {
26711
- return /* @__PURE__ */ jsx46(Fragment15, { children: lines.map((line, i) => {
26604
+ return /* @__PURE__ */ jsx45(Fragment15, { children: lines.map((line, i) => {
26712
26605
  const branch = i === lines.length - 1 && !hasFollowingBlock ? " \u2514\u2500 " : " \u251C\u2500 ";
26713
26606
  const color = colorForVisualKind(line.kind);
26714
- return /* @__PURE__ */ jsxs45(Text, { children: [
26715
- /* @__PURE__ */ jsx46(Text, { dimColor: true, children: branch }),
26716
- line.marker ? /* @__PURE__ */ jsx46(Text, { color, bold: true, children: line.marker }) : null,
26717
- line.path ? /* @__PURE__ */ jsxs45(Fragment15, { children: [
26718
- /* @__PURE__ */ jsx46(Text, { color: "cyan", children: shortenPath(line.path, 56) }),
26719
- /* @__PURE__ */ jsx46(Text, { dimColor: true, children: " " })
26607
+ return /* @__PURE__ */ jsxs44(Text, { children: [
26608
+ /* @__PURE__ */ jsx45(Text, { dimColor: true, children: branch }),
26609
+ line.marker ? /* @__PURE__ */ jsx45(Text, { color, bold: true, children: line.marker }) : null,
26610
+ line.path ? /* @__PURE__ */ jsxs44(Fragment15, { children: [
26611
+ /* @__PURE__ */ jsx45(Text, { color: "cyan", children: shortenPath(line.path, 56) }),
26612
+ /* @__PURE__ */ jsx45(Text, { dimColor: true, children: " " })
26720
26613
  ] }) : null,
26721
- line.lineNo ? /* @__PURE__ */ jsxs45(Fragment15, { children: [
26722
- /* @__PURE__ */ jsx46(Text, { color: "yellow", children: String(line.lineNo).padStart(4, " ") }),
26723
- /* @__PURE__ */ jsx46(Text, { dimColor: true, children: " \u2502 " })
26614
+ line.lineNo ? /* @__PURE__ */ jsxs44(Fragment15, { children: [
26615
+ /* @__PURE__ */ jsx45(Text, { color: "yellow", children: String(line.lineNo).padStart(4, " ") }),
26616
+ /* @__PURE__ */ jsx45(Text, { dimColor: true, children: " \u2502 " })
26724
26617
  ] }) : null,
26725
- /* @__PURE__ */ jsx46(Text, { color, dimColor: line.kind === "meta" || line.kind === "stdout", children: truncMid(line.text, VISUAL_TEXT_BUDGET) })
26618
+ /* @__PURE__ */ jsx45(Text, { color, dimColor: line.kind === "meta" || line.kind === "stdout", children: truncMid(line.text, VISUAL_TEXT_BUDGET) })
26726
26619
  ] }, `${line.kind}-${i}`);
26727
26620
  }) });
26728
26621
  }
@@ -27554,14 +27447,44 @@ var SAGE_INJECTOR_HEADINGS = /* @__PURE__ */ new Set([
27554
27447
  "--- SAGE: related project knowledge (Memory Injector) ---"
27555
27448
  ]);
27556
27449
  var SAGE_MEMORY_LINE = /^- \[[^\]]+\](?:\[[^\]]+\])* <memory id="[^"]+">.*<\/memory>(?: .*)?$/;
27450
+ var SAGE_MEMORY_LINE_TRUNCATED = /^- \[[^\]]+\](?:\[[^\]]+\])* <memory id="[^"]+">.*…$/;
27451
+ var SAGE_MEMORY_DETAIL = /^- (?<labels>\[[^\]]+\](?:\[[^\]]+\])*) <memory id="(?<id>[^"]+)">(?<text>.*?)<\/memory>(?: \((?<anchor>[^)]+)\))?(?: \[(?<meta>[^\]]+)\])?$/;
27452
+ function unescapeFenceBody(value) {
27453
+ return value.replace(/&#39;/g, "'").replace(/&quot;/g, '"').replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&amp;/g, "&");
27454
+ }
27455
+ function parseMeta(meta) {
27456
+ if (!meta) return {};
27457
+ const relationMatch = /relation=([^;\]]+)/.exec(meta);
27458
+ const tagsMatch = /tags=([^;\]]+)/.exec(meta);
27459
+ return {
27460
+ relation: relationMatch?.[1]?.trim(),
27461
+ tags: tagsMatch?.[1]?.split(",").map((tag) => tag.trim()).filter(Boolean)
27462
+ };
27463
+ }
27464
+ function parseSageMemoryLine(line) {
27465
+ const match = SAGE_MEMORY_DETAIL.exec(line);
27466
+ if (!match?.groups) return null;
27467
+ const rawLabels = match.groups["labels"] ?? "";
27468
+ const labels = Array.from(rawLabels.matchAll(/\[([^\]]+)\]/g)).map((m) => m[1] ?? "").filter(Boolean);
27469
+ return {
27470
+ id: match.groups["id"] ?? "",
27471
+ text: unescapeFenceBody(match.groups["text"] ?? ""),
27472
+ labels,
27473
+ anchor: match.groups["anchor"]?.trim() || void 0,
27474
+ ...parseMeta(match.groups["meta"])
27475
+ };
27476
+ }
27557
27477
  function extractSageBlock(output) {
27558
27478
  const lines = output.split("\n");
27559
27479
  for (let sageIdx = lines.length - 1; sageIdx >= 0; sageIdx--) {
27560
27480
  if (!SAGE_INJECTOR_HEADINGS.has(lines[sageIdx] ?? "")) continue;
27561
27481
  const candidate = lines.slice(sageIdx);
27562
27482
  if (candidate.length < 2) continue;
27563
- const memoryLines = candidate.slice(1).filter((line) => line.trim().length > 0);
27564
- if (memoryLines.length === 0 || !memoryLines.every((line) => SAGE_MEMORY_LINE.test(line))) {
27483
+ const rawMemoryLines = candidate.slice(1);
27484
+ const memoryLines = rawMemoryLines.filter((line) => line.trim().length > 0);
27485
+ if (memoryLines.length === 0 || !memoryLines.every(
27486
+ (line, index) => SAGE_MEMORY_LINE.test(line) || index === memoryLines.length - 1 && SAGE_MEMORY_LINE_TRUNCATED.test(line)
27487
+ )) {
27565
27488
  continue;
27566
27489
  }
27567
27490
  let end = candidate.length;
@@ -27573,16 +27496,24 @@ function extractSageBlock(output) {
27573
27496
  }
27574
27497
  return { cleanOutput: output, sageLines: [] };
27575
27498
  }
27499
+ function resolveEntrySage(output, sageLines) {
27500
+ if (sageLines && sageLines.length > 0) {
27501
+ return { cleanOutput: output ?? "", sageLines: [...sageLines] };
27502
+ }
27503
+ return extractSageBlock(output ?? "");
27504
+ }
27576
27505
  function formatToolOutputSageWith(input) {
27577
27506
  const { toolName, output, ok, outputBytes, outputLines, formatToolOutput: formatToolOutput2 } = input;
27578
27507
  if (!output) {
27579
27508
  return {
27580
27509
  cleanOutput: "",
27581
27510
  outLines: formatToolOutput2(toolName, output, ok, outputBytes, outputLines),
27582
- sageLines: []
27511
+ // A tool can return nothing and still get memory injected, so the
27512
+ // structured lines are honoured even with an empty body.
27513
+ sageLines: input.sageLines ? [...input.sageLines] : []
27583
27514
  };
27584
27515
  }
27585
- const { cleanOutput, sageLines } = extractSageBlock(output);
27516
+ const { cleanOutput, sageLines } = resolveEntrySage(output, input.sageLines);
27586
27517
  const text = cleanOutput || (sageLines.length > 0 ? "" : output);
27587
27518
  return {
27588
27519
  cleanOutput: text,
@@ -27826,11 +27757,12 @@ function formatToolArgs(toolName, input) {
27826
27757
  }
27827
27758
 
27828
27759
  // src/components/history/utils.tsx
27829
- function formatToolOutputSage(toolName, output, ok, outputBytes, outputLines) {
27760
+ function formatToolOutputSage(toolName, output, ok, outputBytes, outputLines, sageLines) {
27830
27761
  return formatToolOutputSageWith({
27831
27762
  toolName,
27832
27763
  output,
27833
27764
  ok,
27765
+ sageLines,
27834
27766
  outputBytes,
27835
27767
  outputLines,
27836
27768
  formatToolOutput
@@ -28666,7 +28598,7 @@ function extractPatchHeaderPath(diffText) {
28666
28598
  }
28667
28599
 
28668
28600
  // src/components/history/code-block.tsx
28669
- import { Fragment as Fragment16, jsx as jsx47, jsxs as jsxs46 } from "react/jsx-runtime";
28601
+ import { Fragment as Fragment16, jsx as jsx46, jsxs as jsxs45 } from "react/jsx-runtime";
28670
28602
  var MAX_CODE_LINES = 80;
28671
28603
  var DIFF_MAX_LINES = 200;
28672
28604
  var MULTI_DIFF_MAX_FILES = 20;
@@ -28708,7 +28640,7 @@ function CodeBlock({
28708
28640
  carry = r.carry;
28709
28641
  return r.tokens;
28710
28642
  });
28711
- return /* @__PURE__ */ jsxs46(
28643
+ return /* @__PURE__ */ jsxs45(
28712
28644
  Box,
28713
28645
  {
28714
28646
  flexDirection: "column",
@@ -28720,10 +28652,10 @@ function CodeBlock({
28720
28652
  borderColor: theme.borderDefault,
28721
28653
  paddingX: 1,
28722
28654
  children: [
28723
- lang !== "plain" ? /* @__PURE__ */ jsx47(Text, { dimColor: true, children: lang }) : null,
28724
- rows.map((tokens, i) => /* @__PURE__ */ jsxs46(Text, { children: [
28725
- /* @__PURE__ */ jsx47(Text, { dimColor: true, children: `${String(i + 1).padStart(gutterW, " ")} ` }),
28726
- tokens.length === 0 ? " " : tokens.map((t, j) => /* @__PURE__ */ jsx47(
28655
+ lang !== "plain" ? /* @__PURE__ */ jsx46(Text, { dimColor: true, children: lang }) : null,
28656
+ rows.map((tokens, i) => /* @__PURE__ */ jsxs45(Text, { children: [
28657
+ /* @__PURE__ */ jsx46(Text, { dimColor: true, children: `${String(i + 1).padStart(gutterW, " ")} ` }),
28658
+ tokens.length === 0 ? " " : tokens.map((t, j) => /* @__PURE__ */ jsx46(
28727
28659
  Text,
28728
28660
  {
28729
28661
  dimColor: Boolean(t.dim),
@@ -28734,7 +28666,7 @@ function CodeBlock({
28734
28666
  j
28735
28667
  ))
28736
28668
  ] }, i)),
28737
- hidden > 0 ? /* @__PURE__ */ jsx47(Text, { dimColor: true, italic: true, children: `\u2026 +${hidden} more line${hidden === 1 ? "" : "s"}` }) : null
28669
+ hidden > 0 ? /* @__PURE__ */ jsx46(Text, { dimColor: true, italic: true, children: `\u2026 +${hidden} more line${hidden === 1 ? "" : "s"}` }) : null
28738
28670
  ]
28739
28671
  }
28740
28672
  );
@@ -28795,9 +28727,9 @@ function DiffFileBlock({
28795
28727
  useColor = true,
28796
28728
  contentWidth
28797
28729
  }) {
28798
- return /* @__PURE__ */ jsxs46(Box, { flexDirection: "column", marginTop: 1, children: [
28799
- /* @__PURE__ */ jsx47(Text, { dimColor: true, italic: true, children: path11 }),
28800
- /* @__PURE__ */ jsx47(
28730
+ return /* @__PURE__ */ jsxs45(Box, { flexDirection: "column", marginTop: 1, children: [
28731
+ /* @__PURE__ */ jsx46(Text, { dimColor: true, italic: true, children: path11 }),
28732
+ /* @__PURE__ */ jsx46(
28801
28733
  DiffBlock,
28802
28734
  {
28803
28735
  rows: preview.rows,
@@ -28831,7 +28763,7 @@ function applyWashTokens(tokens, onWash) {
28831
28763
  function renderTokens(tokens, onWash = false) {
28832
28764
  const effective = applyWashTokens(tokens, onWash);
28833
28765
  if (effective.length === 0) return " ";
28834
- return effective.map((t, j) => /* @__PURE__ */ jsx47(
28766
+ return effective.map((t, j) => /* @__PURE__ */ jsx46(
28835
28767
  Text,
28836
28768
  {
28837
28769
  dimColor: Boolean(t.dim),
@@ -28909,19 +28841,19 @@ function DiffBlock({
28909
28841
  const hiddenStats = [];
28910
28842
  if (hiddenAdded > 0) hiddenStats.push(`+${hiddenAdded}`);
28911
28843
  if (hiddenRemoved > 0) hiddenStats.push(`-${hiddenRemoved}`);
28912
- return /* @__PURE__ */ jsxs46(Box, { flexDirection: "column", marginLeft: 2, marginTop: 0, children: [
28913
- stats ? /* @__PURE__ */ jsxs46(Text, { children: [
28914
- /* @__PURE__ */ jsx47(Text, { dimColor: true, children: "\u23BF " }),
28915
- /* @__PURE__ */ jsx47(Text, { children: stats })
28844
+ return /* @__PURE__ */ jsxs45(Box, { flexDirection: "column", marginLeft: 2, marginTop: 0, children: [
28845
+ stats ? /* @__PURE__ */ jsxs45(Text, { children: [
28846
+ /* @__PURE__ */ jsx46(Text, { dimColor: true, children: "\u23BF " }),
28847
+ /* @__PURE__ */ jsx46(Text, { children: stats })
28916
28848
  ] }) : null,
28917
28849
  rows.map((row, i) => {
28918
28850
  const key = i;
28919
28851
  if (row.kind === "hunk") {
28920
28852
  if (i === 0) return null;
28921
- return /* @__PURE__ */ jsx47(Text, { dimColor: true, children: ` ${blank} \u22EF` }, key);
28853
+ return /* @__PURE__ */ jsx46(Text, { dimColor: true, children: ` ${blank} \u22EF` }, key);
28922
28854
  }
28923
28855
  if (row.kind === "meta") {
28924
- return /* @__PURE__ */ jsx47(Text, { dimColor: true, children: ` ${blank} ${row.text}` }, key);
28856
+ return /* @__PURE__ */ jsx46(Text, { dimColor: true, children: ` ${blank} ${row.text}` }, key);
28925
28857
  }
28926
28858
  const n = lineNoOf(row);
28927
28859
  const ln = typeof n === "number" ? String(n).padStart(gutterWidth, " ") : blank;
@@ -28929,8 +28861,8 @@ function DiffBlock({
28929
28861
  const tokens = highlightLine(body, lang).tokens;
28930
28862
  const segments = wrapTokens(tokens, bodyBudget);
28931
28863
  if (row.kind === "ctx") {
28932
- return /* @__PURE__ */ jsx47(Box, { flexDirection: "column", children: segments.map((seg, si) => /* @__PURE__ */ jsxs46(Text, { children: [
28933
- /* @__PURE__ */ jsx47(Text, { dimColor: true, children: si === 0 ? ` ${ln} ` : contPrefix }),
28864
+ return /* @__PURE__ */ jsx46(Box, { flexDirection: "column", children: segments.map((seg, si) => /* @__PURE__ */ jsxs45(Text, { children: [
28865
+ /* @__PURE__ */ jsx46(Text, { dimColor: true, children: si === 0 ? ` ${ln} ` : contPrefix }),
28934
28866
  renderTokens(seg)
28935
28867
  ] }, si)) }, key);
28936
28868
  }
@@ -28938,29 +28870,29 @@ function DiffBlock({
28938
28870
  const markerColor = row.kind === "add" ? theme.success : theme.error;
28939
28871
  if (useColor) {
28940
28872
  const bg = row.kind === "add" ? theme.diffAddBg : theme.diffDelBg;
28941
- return /* @__PURE__ */ jsx47(Box, { flexDirection: "column", children: segments.map((seg, si) => {
28873
+ return /* @__PURE__ */ jsx46(Box, { flexDirection: "column", children: segments.map((seg, si) => {
28942
28874
  const pad = padBody(seg);
28943
- return /* @__PURE__ */ jsxs46(Text, { backgroundColor: bg, children: [
28944
- si === 0 ? /* @__PURE__ */ jsxs46(Fragment16, { children: [
28945
- /* @__PURE__ */ jsx47(Text, { dimColor: true, children: ` ${ln} ` }),
28946
- /* @__PURE__ */ jsx47(Text, { color: markerColor, bold: true, children: marker }),
28947
- /* @__PURE__ */ jsx47(Text, { children: " " })
28948
- ] }) : /* @__PURE__ */ jsx47(Text, { dimColor: true, children: contPrefix }),
28875
+ return /* @__PURE__ */ jsxs45(Text, { backgroundColor: bg, children: [
28876
+ si === 0 ? /* @__PURE__ */ jsxs45(Fragment16, { children: [
28877
+ /* @__PURE__ */ jsx46(Text, { dimColor: true, children: ` ${ln} ` }),
28878
+ /* @__PURE__ */ jsx46(Text, { color: markerColor, bold: true, children: marker }),
28879
+ /* @__PURE__ */ jsx46(Text, { children: " " })
28880
+ ] }) : /* @__PURE__ */ jsx46(Text, { dimColor: true, children: contPrefix }),
28949
28881
  renderTokens(seg, true),
28950
- pad > 0 ? /* @__PURE__ */ jsx47(Text, { children: " ".repeat(pad) }) : null
28882
+ pad > 0 ? /* @__PURE__ */ jsx46(Text, { children: " ".repeat(pad) }) : null
28951
28883
  ] }, si);
28952
28884
  }) }, key);
28953
28885
  }
28954
- return /* @__PURE__ */ jsx47(Box, { flexDirection: "column", children: segments.map((seg, si) => /* @__PURE__ */ jsxs46(Text, { children: [
28955
- si === 0 ? /* @__PURE__ */ jsxs46(Fragment16, { children: [
28956
- /* @__PURE__ */ jsx47(Text, { dimColor: true, children: ` ${ln} ` }),
28957
- /* @__PURE__ */ jsx47(Text, { color: markerColor, bold: true, children: marker }),
28958
- /* @__PURE__ */ jsx47(Text, { children: " " })
28959
- ] }) : /* @__PURE__ */ jsx47(Text, { dimColor: true, children: contPrefix }),
28886
+ return /* @__PURE__ */ jsx46(Box, { flexDirection: "column", children: segments.map((seg, si) => /* @__PURE__ */ jsxs45(Text, { children: [
28887
+ si === 0 ? /* @__PURE__ */ jsxs45(Fragment16, { children: [
28888
+ /* @__PURE__ */ jsx46(Text, { dimColor: true, children: ` ${ln} ` }),
28889
+ /* @__PURE__ */ jsx46(Text, { color: markerColor, bold: true, children: marker }),
28890
+ /* @__PURE__ */ jsx46(Text, { children: " " })
28891
+ ] }) : /* @__PURE__ */ jsx46(Text, { dimColor: true, children: contPrefix }),
28960
28892
  renderTokens(seg)
28961
28893
  ] }, si)) }, key);
28962
28894
  }),
28963
- hidden > 0 ? /* @__PURE__ */ jsx47(Text, { dimColor: true, italic: true, children: ` ${blank} \u2026 ${hidden} more line${hidden === 1 ? "" : "s"}${hiddenStats.length > 0 ? ` (${hiddenStats.join(" ")} hidden)` : ""}` }) : null
28895
+ hidden > 0 ? /* @__PURE__ */ jsx46(Text, { dimColor: true, italic: true, children: ` ${blank} \u2026 ${hidden} more line${hidden === 1 ? "" : "s"}${hiddenStats.length > 0 ? ` (${hiddenStats.join(" ")} hidden)` : ""}` }) : null
28964
28896
  ] });
28965
28897
  }
28966
28898
  function parseUnifiedDiff(diff, maxLines) {
@@ -29028,7 +28960,7 @@ function extractMultiFileDiffs(toolName, output, input) {
29028
28960
  }
29029
28961
 
29030
28962
  // src/components/confirm-prompt.tsx
29031
- import { jsx as jsx48, jsxs as jsxs47 } from "react/jsx-runtime";
28963
+ import { jsx as jsx47, jsxs as jsxs46 } from "react/jsx-runtime";
29032
28964
  var BUTTON_COLOR = {
29033
28965
  yes: "green",
29034
28966
  no: "red",
@@ -29059,7 +28991,7 @@ function hasDiff(input) {
29059
28991
  var CONFIRM_DIFF_MAX_LINES = 20;
29060
28992
  function renderDiffLine(line) {
29061
28993
  const prefix = line.startsWith("+") ? "green" : line.startsWith("-") ? "red" : line.startsWith("@@") ? "cyan" : void 0;
29062
- return /* @__PURE__ */ jsxs47(Text, { ...prefix ? { color: prefix } : {}, children: [
28994
+ return /* @__PURE__ */ jsxs46(Text, { ...prefix ? { color: prefix } : {}, children: [
29063
28995
  line,
29064
28996
  "\n"
29065
28997
  ] }, line);
@@ -29067,7 +28999,7 @@ function renderDiffLine(line) {
29067
28999
  function renderDiff(diff, path11) {
29068
29000
  const preview = parseUnifiedDiff(diff, CONFIRM_DIFF_MAX_LINES);
29069
29001
  if (preview.rows.length > 0) {
29070
- return /* @__PURE__ */ jsx48(
29002
+ return /* @__PURE__ */ jsx47(
29071
29003
  DiffBlock,
29072
29004
  {
29073
29005
  rows: preview.rows,
@@ -29082,7 +29014,7 @@ function renderDiff(diff, path11) {
29082
29014
  );
29083
29015
  }
29084
29016
  const lines = diff.split("\n").filter((l) => l.length > 0).slice(0, CONFIRM_DIFF_MAX_LINES);
29085
- return /* @__PURE__ */ jsx48(Box, { flexDirection: "column", paddingX: 2, children: lines.map((l) => renderDiffLine(l)) });
29017
+ return /* @__PURE__ */ jsx47(Box, { flexDirection: "column", paddingX: 2, children: lines.map((l) => renderDiffLine(l)) });
29086
29018
  }
29087
29019
  function ConfirmPrompt({
29088
29020
  toolName,
@@ -29118,28 +29050,28 @@ function ConfirmPrompt({
29118
29050
  const inp = input;
29119
29051
  const diff = typeof inp?.diff === "string" ? inp.diff : "";
29120
29052
  const diffPath = typeof inp?.path === "string" ? inp.path : void 0;
29121
- return /* @__PURE__ */ jsxs47(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
29122
- /* @__PURE__ */ jsxs47(Box, { flexDirection: "row", children: [
29123
- /* @__PURE__ */ jsx48(Text, { bold: true, color: "yellow", children: "\u26A0 APPROVAL REQUIRED" }),
29124
- /* @__PURE__ */ jsx48(Text, { children: " " }),
29125
- /* @__PURE__ */ jsx48(Text, { bold: true, color: "white", children: toolName })
29053
+ return /* @__PURE__ */ jsxs46(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
29054
+ /* @__PURE__ */ jsxs46(Box, { flexDirection: "row", children: [
29055
+ /* @__PURE__ */ jsx47(Text, { bold: true, color: "yellow", children: "\u26A0 APPROVAL REQUIRED" }),
29056
+ /* @__PURE__ */ jsx47(Text, { children: " " }),
29057
+ /* @__PURE__ */ jsx47(Text, { bold: true, color: "white", children: toolName })
29126
29058
  ] }),
29127
- inputSummary ? /* @__PURE__ */ jsx48(Text, { dimColor: true, children: inputSummary }) : null,
29128
- boundaryReason ? /* @__PURE__ */ jsxs47(Text, { color: "yellow", children: [
29059
+ inputSummary ? /* @__PURE__ */ jsx47(Text, { dimColor: true, children: inputSummary }) : null,
29060
+ boundaryReason ? /* @__PURE__ */ jsxs46(Text, { color: "yellow", children: [
29129
29061
  "KANBAN BOUNDARY: ",
29130
29062
  boundaryReason
29131
29063
  ] }) : null,
29132
- showDiff && diff ? /* @__PURE__ */ jsx48(Box, { flexDirection: "column", marginY: 1, children: renderDiff(diff, diffPath) }) : null,
29133
- /* @__PURE__ */ jsx48(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
29134
- /* @__PURE__ */ jsx48(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx48(Text, { children: buttonLabels(suggestedPattern).map((l) => /* @__PURE__ */ jsxs47(React6.Fragment, { children: [
29135
- /* @__PURE__ */ jsx48(Text, { bold: true, color: BUTTON_COLOR[l.decision], children: l.bracket }),
29136
- /* @__PURE__ */ jsx48(Text, { dimColor: true, children: l.rest })
29064
+ showDiff && diff ? /* @__PURE__ */ jsx47(Box, { flexDirection: "column", marginY: 1, children: renderDiff(diff, diffPath) }) : null,
29065
+ /* @__PURE__ */ jsx47(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
29066
+ /* @__PURE__ */ jsx47(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx47(Text, { children: buttonLabels(suggestedPattern).map((l) => /* @__PURE__ */ jsxs46(React6.Fragment, { children: [
29067
+ /* @__PURE__ */ jsx47(Text, { bold: true, color: BUTTON_COLOR[l.decision], children: l.bracket }),
29068
+ /* @__PURE__ */ jsx47(Text, { dimColor: true, children: l.rest })
29137
29069
  ] }, l.decision)) }) }),
29138
- !boundaryReason ? /* @__PURE__ */ jsx48(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs47(Text, { dimColor: true, children: [
29070
+ !boundaryReason ? /* @__PURE__ */ jsx47(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs46(Text, { dimColor: true, children: [
29139
29071
  " ",
29140
29072
  "Tip: press",
29141
29073
  " ",
29142
- /* @__PURE__ */ jsx48(Text, { bold: true, color: "yellow", children: "Y" }),
29074
+ /* @__PURE__ */ jsx47(Text, { bold: true, color: "yellow", children: "Y" }),
29143
29075
  " ",
29144
29076
  "to enable YOLO mode",
29145
29077
  destructive ? " (skips this and future approvals)" : " (skips future approvals)",
@@ -29148,6 +29080,398 @@ function ConfirmPrompt({
29148
29080
  ] });
29149
29081
  }
29150
29082
 
29083
+ // src/components/connections-panel.tsx
29084
+ import { useCallback as useCallback24, useEffect as useEffect61, useRef as useRef33, useState as useState34 } from "react";
29085
+
29086
+ // src/connections-health.ts
29087
+ import { createChronicleProjectAccess } from "@wrongstack/core/chronicle";
29088
+ import {
29089
+ isMailboxProjectServerAvailable,
29090
+ MailboxProjectServerConnection
29091
+ } from "@wrongstack/core/coordination";
29092
+ import { resolveWstackPaths as resolveWstackPaths5 } from "@wrongstack/core/utils";
29093
+ import { getKanbanServerConnection } from "@wrongstack/kanban";
29094
+ import { isSageProjectServerAvailable, SageProjectServerConnection } from "@wrongstack/sage";
29095
+ import {
29096
+ checkCodebaseIndexServerHealth,
29097
+ getIndexState as getIndexState2,
29098
+ resolveProjectIndexDaemonAvailability
29099
+ } from "@wrongstack/tools";
29100
+ async function collectConnectionsHealth(projectRoot) {
29101
+ const services = await Promise.all([
29102
+ chronicleHealth(projectRoot),
29103
+ codebaseIndexHealth(projectRoot),
29104
+ sageHealth(projectRoot),
29105
+ kanbanHealth(projectRoot),
29106
+ mailboxHealth(projectRoot)
29107
+ ]);
29108
+ const required = services.filter((s2) => s2.required);
29109
+ const overall = required.some((s2) => s2.status === "error") ? "error" : required.some((s2) => s2.status !== "healthy") ? "degraded" : "healthy";
29110
+ return { checkedAt: Date.now(), overall, services };
29111
+ }
29112
+ async function chronicleHealth(projectRoot) {
29113
+ const startedAt = Date.now();
29114
+ let access;
29115
+ try {
29116
+ access = createChronicleProjectAccess({ projectRoot });
29117
+ const health2 = await access.call("ping", {}, { timeoutMs: 5e3 });
29118
+ const quarantined = health2.quarantinedFamilies ?? [];
29119
+ return {
29120
+ id: "chronicle",
29121
+ label: "Chronicle telemetry",
29122
+ status: quarantined.length > 0 ? "degraded" : access.mode === "server" ? "healthy" : "degraded",
29123
+ required: true,
29124
+ mode: access.mode,
29125
+ detail: quarantined.length > 0 ? `Serving, but ${quarantined.length} day(s) were quarantined for a broken chain: ${quarantined.map((family) => family.day).join(", ")}.` : access.mode === "server" ? "One project owner collects, processes, stores, and serves telemetry." : "Inline fallback is active; ownership is not shared across clients.",
29126
+ ownerPid: health2.pid,
29127
+ endpoint: health2.endpoint,
29128
+ storage: health2.chronicleDirectory,
29129
+ uptimeMs: health2.uptimeMs,
29130
+ latencyMs: Date.now() - startedAt,
29131
+ clients: health2.clients,
29132
+ activeRequests: health2.activeRequests,
29133
+ queuedWork: health2.journal.pendingEvents,
29134
+ watcher: { active: health2.watcher.active, watchedFiles: health2.watcher.watchedFiles },
29135
+ ...quarantined[0] ? { lastError: quarantined[0].reason } : health2.watcher.lastError ? { lastError: health2.watcher.lastError } : {}
29136
+ };
29137
+ } catch (error) {
29138
+ return failureService("chronicle", "Chronicle telemetry", true, access?.mode ?? "unavailable", error, Date.now() - startedAt);
29139
+ } finally {
29140
+ await access?.close();
29141
+ }
29142
+ }
29143
+ async function codebaseIndexHealth(projectRoot) {
29144
+ const startedAt = Date.now();
29145
+ const availability = resolveProjectIndexDaemonAvailability(projectRoot);
29146
+ if (availability.kind === "endpoint-invalid") {
29147
+ return {
29148
+ id: "codebase-index",
29149
+ label: "Codebase index",
29150
+ status: "unavailable",
29151
+ required: false,
29152
+ mode: "endpoint-invalid",
29153
+ detail: `Socket path is ${availability.byteLength} bytes \u2014 over this platform's ${availability.maxBytes}-byte sun_path limit. Queries fall back to a process-local index. Set a shorter TMPDIR to restore the shared daemon.`,
29154
+ endpoint: availability.endpoint,
29155
+ latencyMs: Date.now() - startedAt
29156
+ };
29157
+ }
29158
+ try {
29159
+ const health2 = await checkCodebaseIndexServerHealth(projectRoot, void 0, { timeoutMs: 2e3 });
29160
+ const connection = getIndexState2().server;
29161
+ return {
29162
+ id: "codebase-index",
29163
+ label: "Codebase index",
29164
+ status: health2.status === "unresponsive" ? "error" : health2.status,
29165
+ required: false,
29166
+ mode: "project-server",
29167
+ detail: health2.server?.activity.indexing ? `Indexing ${health2.server.activity.currentFile}/${health2.server.activity.totalFiles}.` : "Shared symbol index is ready for project queries.",
29168
+ ownerPid: connection.pid,
29169
+ endpoint: connection.endpoint,
29170
+ storage: connection.indexDir ?? resolveWstackPaths5({ projectRoot }).projectCodebaseIndex,
29171
+ uptimeMs: health2.server?.uptimeMs,
29172
+ latencyMs: health2.latencyMs ?? Date.now() - startedAt,
29173
+ clients: health2.server?.clients,
29174
+ activeRequests: health2.server?.activeRequests,
29175
+ queuedWork: health2.server?.queuedWrites,
29176
+ watcher: health2.server ? { active: health2.server.watchingExternal, watchedFiles: health2.server.watchingClients } : void 0
29177
+ };
29178
+ } catch (error) {
29179
+ if (isOfflineConnectionError(error)) {
29180
+ return {
29181
+ ...failureService("codebase-index", "Codebase index", false, "on-demand project-server", error, Date.now() - startedAt),
29182
+ status: "offline",
29183
+ detail: "Not running for this project; it starts on demand when indexing is used."
29184
+ };
29185
+ }
29186
+ return failureService("codebase-index", "Codebase index", false, "project-server", error, Date.now() - startedAt);
29187
+ }
29188
+ }
29189
+ async function sageHealth(projectRoot) {
29190
+ const startedAt = Date.now();
29191
+ if (!isSageProjectServerAvailable()) {
29192
+ return {
29193
+ id: "sage",
29194
+ label: "SAGE memory",
29195
+ status: "unavailable",
29196
+ required: false,
29197
+ mode: "unavailable",
29198
+ detail: "The packaged SAGE project server is unavailable in this runtime."
29199
+ };
29200
+ }
29201
+ const connection = new SageProjectServerConnection(projectRoot);
29202
+ try {
29203
+ const status = await connection.status();
29204
+ if (!status) {
29205
+ return {
29206
+ id: "sage",
29207
+ label: "SAGE memory",
29208
+ status: "offline",
29209
+ required: false,
29210
+ mode: "on-demand project-server",
29211
+ detail: "Not running for this project; it starts on demand when memory is used.",
29212
+ endpoint: connection.getState().endpoint,
29213
+ latencyMs: Date.now() - startedAt
29214
+ };
29215
+ }
29216
+ const healthStatus = status.health.status === "ready" ? "healthy" : status.health.status === "degraded" ? "degraded" : "error";
29217
+ return {
29218
+ id: "sage",
29219
+ label: "SAGE memory",
29220
+ status: healthStatus,
29221
+ required: false,
29222
+ mode: "project-server",
29223
+ detail: `Persistent project memory backend: ${status.health.backend}.`,
29224
+ ownerPid: status.pid,
29225
+ endpoint: status.endpoint,
29226
+ storage: status.storageRoot,
29227
+ latencyMs: Date.now() - startedAt,
29228
+ clients: status.clients,
29229
+ activeRequests: status.pendingRequests
29230
+ };
29231
+ } catch (error) {
29232
+ return failureService("sage", "SAGE memory", false, "project-server", error, Date.now() - startedAt);
29233
+ } finally {
29234
+ connection.close();
29235
+ }
29236
+ }
29237
+ async function kanbanHealth(projectRoot) {
29238
+ const startedAt = Date.now();
29239
+ if (process.env["WRONGSTACK_KANBAN_SERVER"] === "0") {
29240
+ return {
29241
+ id: "kanban",
29242
+ label: "Kanban IPC",
29243
+ status: "unavailable",
29244
+ required: false,
29245
+ mode: "disabled",
29246
+ detail: "Kanban IPC daemon is disabled via WRONGSTACK_KANBAN_SERVER=0."
29247
+ };
29248
+ }
29249
+ let connection;
29250
+ try {
29251
+ connection = await getKanbanServerConnection(projectRoot);
29252
+ } catch (error) {
29253
+ return failureService("kanban", "Kanban IPC", false, "project-server", error, Date.now() - startedAt);
29254
+ }
29255
+ if (!connection) {
29256
+ return {
29257
+ id: "kanban",
29258
+ label: "Kanban IPC",
29259
+ status: "unavailable",
29260
+ required: false,
29261
+ mode: "disabled",
29262
+ detail: "Kanban IPC daemon is disabled in this runtime."
29263
+ };
29264
+ }
29265
+ try {
29266
+ const status = await connection.request("ping", {}, { timeoutMs: 5e3 });
29267
+ return {
29268
+ id: "kanban",
29269
+ label: "Kanban IPC",
29270
+ status: "healthy",
29271
+ required: true,
29272
+ mode: "project-server",
29273
+ detail: `Single shared project-server owns kanban state (v${status.protocolVersion}).`,
29274
+ ownerPid: status.pid,
29275
+ endpoint: status.endpoint,
29276
+ storage: status.databasePath,
29277
+ latencyMs: Date.now() - startedAt,
29278
+ clients: status.clients,
29279
+ activeRequests: status.pendingRequests,
29280
+ uptimeMs: Date.now() - new Date(status.startedAt).getTime()
29281
+ };
29282
+ } catch (error) {
29283
+ return failureService("kanban", "Kanban IPC", true, "project-server", error, Date.now() - startedAt);
29284
+ }
29285
+ }
29286
+ async function mailboxHealth(projectRoot) {
29287
+ const startedAt = Date.now();
29288
+ if (!isMailboxProjectServerAvailable()) {
29289
+ return {
29290
+ id: "mailbox",
29291
+ label: "Mailbox IPC",
29292
+ status: "unavailable",
29293
+ required: false,
29294
+ mode: "unavailable",
29295
+ detail: "The packaged mailbox project server is unavailable in this runtime."
29296
+ };
29297
+ }
29298
+ const connection = new MailboxProjectServerConnection(
29299
+ resolveWstackPaths5({ projectRoot }).projectDir
29300
+ );
29301
+ try {
29302
+ const status = await connection.probeStatus();
29303
+ if (!status) {
29304
+ return {
29305
+ id: "mailbox",
29306
+ label: "Mailbox IPC",
29307
+ status: "offline",
29308
+ required: false,
29309
+ mode: "on-demand project-server",
29310
+ detail: "Not running for this project; it starts on demand when mailbox is used.",
29311
+ endpoint: connection.getState().endpoint,
29312
+ latencyMs: Date.now() - startedAt
29313
+ };
29314
+ }
29315
+ return {
29316
+ id: "mailbox",
29317
+ label: "Mailbox IPC",
29318
+ status: "healthy",
29319
+ required: true,
29320
+ mode: "project-server",
29321
+ detail: `Single shared project-server owns inter-agent mailbox state (v${status.protocolVersion}).`,
29322
+ ownerPid: status.pid,
29323
+ endpoint: status.endpoint,
29324
+ storage: status.databasePath,
29325
+ latencyMs: Date.now() - startedAt,
29326
+ clients: status.clients,
29327
+ activeRequests: status.pendingRequests,
29328
+ uptimeMs: Date.now() - new Date(status.startedAt).getTime()
29329
+ };
29330
+ } catch (error) {
29331
+ return failureService("mailbox", "Mailbox IPC", true, "project-server", error, Date.now() - startedAt);
29332
+ } finally {
29333
+ connection.close();
29334
+ }
29335
+ }
29336
+ function isOfflineConnectionError(error) {
29337
+ const message = error instanceof Error ? error.message : String(error);
29338
+ return /(?:ENOENT|ECONNREFUSED|not found|not running|unavailable|connect failed)/iu.test(message);
29339
+ }
29340
+ function failureService(id, label, required, mode, error, latencyMs) {
29341
+ const message = error instanceof Error ? error.message : String(error);
29342
+ return {
29343
+ id,
29344
+ label,
29345
+ status: "error",
29346
+ required,
29347
+ mode,
29348
+ detail: message,
29349
+ lastError: message,
29350
+ ...latencyMs === void 0 ? {} : { latencyMs }
29351
+ };
29352
+ }
29353
+
29354
+ // src/components/connections-panel.tsx
29355
+ import { jsx as jsx48, jsxs as jsxs47 } from "react/jsx-runtime";
29356
+ var REFRESH_MS = 8e3;
29357
+ var STATUS_GLYPH = {
29358
+ healthy: "\u25CF",
29359
+ degraded: "\u25D0",
29360
+ offline: "\u25CB",
29361
+ unavailable: "\u2298",
29362
+ error: "\u2717"
29363
+ };
29364
+ var STATUS_COLOR2 = {
29365
+ healthy: "green",
29366
+ degraded: "yellow",
29367
+ offline: "gray",
29368
+ unavailable: "gray",
29369
+ error: "red"
29370
+ };
29371
+ var OVERALL_COLOR = {
29372
+ healthy: "green",
29373
+ degraded: "yellow",
29374
+ error: "red"
29375
+ };
29376
+ function formatUptime(ms) {
29377
+ if (ms < 1e3) return `${ms}ms`;
29378
+ const s2 = Math.floor(ms / 1e3);
29379
+ if (s2 < 60) return `${s2}s`;
29380
+ const m = Math.floor(s2 / 60);
29381
+ if (m < 60) return `${m}m`;
29382
+ const h = Math.floor(m / 60);
29383
+ return h < 24 ? `${h}h` : `${Math.floor(h / 24)}d`;
29384
+ }
29385
+ function formatLatency(ms) {
29386
+ if (ms === void 0) return null;
29387
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
29388
+ return `${(ms / 1e3).toFixed(1)}s`;
29389
+ }
29390
+ function ServiceRow({ service }) {
29391
+ const glyph2 = STATUS_GLYPH[service.status];
29392
+ const color = STATUS_COLOR2[service.status];
29393
+ const latency = formatLatency(service.latencyMs);
29394
+ const metaParts = [];
29395
+ if (service.ownerPid !== void 0) metaParts.push(`PID ${service.ownerPid}`);
29396
+ if (service.clients !== void 0) metaParts.push(`${service.clients} client${service.clients === 1 ? "" : "s"}`);
29397
+ if (service.activeRequests !== void 0 && service.activeRequests > 0) {
29398
+ metaParts.push(`${service.activeRequests} active`);
29399
+ }
29400
+ if (service.uptimeMs !== void 0) metaParts.push(`up ${formatUptime(service.uptimeMs)}`);
29401
+ if (latency) metaParts.push(`${latency}`);
29402
+ if (service.queuedWork !== void 0 && service.queuedWork > 0) {
29403
+ metaParts.push(`${service.queuedWork} queued`);
29404
+ }
29405
+ return /* @__PURE__ */ jsxs47(Box, { flexDirection: "column", marginTop: 0, children: [
29406
+ /* @__PURE__ */ jsxs47(Box, { gap: 1, children: [
29407
+ /* @__PURE__ */ jsx48(Text, { color, children: glyph2 }),
29408
+ /* @__PURE__ */ jsx48(Text, { bold: true, children: service.label }),
29409
+ service.required ? /* @__PURE__ */ jsx48(Text, { dimColor: true, children: "required" }) : null,
29410
+ /* @__PURE__ */ jsxs47(Text, { dimColor: true, children: [
29411
+ "\xB7 ",
29412
+ service.mode
29413
+ ] })
29414
+ ] }),
29415
+ /* @__PURE__ */ jsx48(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx48(Text, { dimColor: true, children: service.detail }) }),
29416
+ metaParts.length > 0 ? /* @__PURE__ */ jsx48(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx48(Text, { dimColor: true, children: metaParts.join(" \xB7 ") }) }) : null
29417
+ ] });
29418
+ }
29419
+ function ConnectionsPanel({ projectRoot, onClose }) {
29420
+ const [report, setReport] = useState34(null);
29421
+ const [loading, setLoading] = useState34(true);
29422
+ const [error, setError] = useState34(null);
29423
+ const cancelledRef = useRef33(false);
29424
+ const fetch = useCallback24(async () => {
29425
+ try {
29426
+ const r = await collectConnectionsHealth(projectRoot);
29427
+ if (cancelledRef.current) return;
29428
+ setReport(r);
29429
+ setError(null);
29430
+ } catch (e) {
29431
+ if (cancelledRef.current) return;
29432
+ setError(e instanceof Error ? e.message : String(e));
29433
+ } finally {
29434
+ if (!cancelledRef.current) setLoading(false);
29435
+ }
29436
+ }, [projectRoot]);
29437
+ useEffect61(() => {
29438
+ cancelledRef.current = false;
29439
+ void fetch();
29440
+ const timer = setInterval(() => void fetch(), REFRESH_MS);
29441
+ return () => {
29442
+ cancelledRef.current = true;
29443
+ clearInterval(timer);
29444
+ };
29445
+ }, [fetch]);
29446
+ useInput((_input, key) => {
29447
+ if (key.escape || _input === "q") onClose();
29448
+ if (_input === "r") void fetch();
29449
+ });
29450
+ const overallLabel = report ? report.overall === "healthy" ? "All required services healthy" : report.overall === "degraded" ? "Some services degraded" : "Service errors detected" : "Checking\u2026";
29451
+ return /* @__PURE__ */ jsxs47(Box, { flexDirection: "column", borderStyle: "round", borderColor: report ? OVERALL_COLOR[report.overall] : "gray", paddingX: 1, marginY: 0, flexShrink: 0, children: [
29452
+ /* @__PURE__ */ jsxs47(Box, { justifyContent: "space-between", children: [
29453
+ /* @__PURE__ */ jsxs47(Box, { gap: 1, children: [
29454
+ /* @__PURE__ */ jsx48(Text, { bold: true, children: "Service Connections" }),
29455
+ report ? /* @__PURE__ */ jsxs47(Text, { color: OVERALL_COLOR[report.overall], children: [
29456
+ "\u25CF ",
29457
+ overallLabel
29458
+ ] }) : null
29459
+ ] }),
29460
+ /* @__PURE__ */ jsx48(Text, { dimColor: true, children: "Esc/q close \xB7 r refresh" })
29461
+ ] }),
29462
+ loading && !report ? /* @__PURE__ */ jsx48(Box, { paddingY: 1, children: /* @__PURE__ */ jsx48(Text, { dimColor: true, children: "Checking service health\u2026" }) }) : null,
29463
+ error ? /* @__PURE__ */ jsx48(Box, { paddingY: 1, children: /* @__PURE__ */ jsxs47(Text, { color: "red", children: [
29464
+ "Error: ",
29465
+ error
29466
+ ] }) }) : null,
29467
+ report ? /* @__PURE__ */ jsx48(Box, { flexDirection: "column", marginTop: 0, children: report.services.map((s2) => /* @__PURE__ */ jsx48(ServiceRow, { service: s2 }, s2.id)) }) : null,
29468
+ report ? /* @__PURE__ */ jsx48(Box, { marginTop: 0, children: /* @__PURE__ */ jsxs47(Text, { dimColor: true, children: [
29469
+ "Last checked ",
29470
+ new Date(report.checkedAt).toLocaleTimeString()
29471
+ ] }) }) : null
29472
+ ] });
29473
+ }
29474
+
29151
29475
  // src/components/continue-confirm-panel.tsx
29152
29476
  import React7 from "react";
29153
29477
  import { jsx as jsx49, jsxs as jsxs48 } from "react/jsx-runtime";
@@ -29858,101 +30182,23 @@ function ExitConfirmPanel({
29858
30182
  ] });
29859
30183
  }
29860
30184
 
29861
- // src/components/refine-countdown-panel.tsx
29862
- import { useEffect as useEffect64, useRef as useRef35, useState as useState37 } from "react";
29863
- import { jsx as jsx55, jsxs as jsxs54 } from "react/jsx-runtime";
29864
- function RefineCountdownPanel({
29865
- original,
29866
- seconds,
29867
- onDecision,
29868
- providerId,
29869
- model
29870
- }) {
29871
- const [remaining, setRemaining] = useState37(Math.max(1, seconds));
29872
- const resolvedRef = useRef35(false);
29873
- useEffect64(() => {
29874
- const id = setInterval(() => {
29875
- setRemaining((prev) => {
29876
- if (prev <= 1) {
29877
- clearInterval(id);
29878
- if (!resolvedRef.current) {
29879
- resolvedRef.current = true;
29880
- onDecision("proceed");
29881
- }
29882
- return 0;
29883
- }
29884
- return prev - 1;
29885
- });
29886
- }, 1e3);
29887
- return () => clearInterval(id);
29888
- }, []);
29889
- useInput((_input, key) => {
29890
- if (resolvedRef.current) return;
29891
- resolvedRef.current = true;
29892
- if (key.escape) onDecision("cancel");
29893
- else if (key.return) onDecision("proceed");
29894
- else onDecision("skip");
29895
- });
29896
- const preview = wrapRefinementPreview(original, 60, 2);
29897
- return /* @__PURE__ */ jsxs54(
29898
- Box,
29899
- {
29900
- alignSelf: "stretch",
29901
- width: "100%",
29902
- flexDirection: "column",
29903
- borderStyle: "round",
29904
- borderColor: theme.brand,
29905
- paddingX: 1,
29906
- children: [
29907
- /* @__PURE__ */ jsxs54(Box, { height: 1, children: [
29908
- /* @__PURE__ */ jsxs54(Text, { color: theme.brand, bold: true, children: [
29909
- glyphs.brain,
29910
- " PROMPT REFINER"
29911
- ] }),
29912
- providerId && model ? /* @__PURE__ */ jsxs54(Text, { color: theme.textMuted, children: [
29913
- " ",
29914
- "on ",
29915
- providerId,
29916
- "/",
29917
- model
29918
- ] }) : null,
29919
- /* @__PURE__ */ jsx55(Box, { flexGrow: 1 }),
29920
- /* @__PURE__ */ jsxs54(Text, { color: theme.brand, children: [
29921
- "refining in ",
29922
- remaining,
29923
- "s\u2026"
29924
- ] })
29925
- ] }),
29926
- /* @__PURE__ */ jsx55(Box, { flexDirection: "column", marginTop: 1, children: preview.map((line, index) => /* @__PURE__ */ jsxs54(Box, { height: 1, children: [
29927
- /* @__PURE__ */ jsx55(Text, { color: theme.brand, children: "\u258E" }),
29928
- /* @__PURE__ */ jsxs54(Text, { color: theme.textSecondary, children: [
29929
- " ",
29930
- line
29931
- ] })
29932
- ] }, `refine-countdown-preview-${index}`)) }),
29933
- /* @__PURE__ */ jsx55(Box, { height: 1, marginTop: 1, children: /* @__PURE__ */ jsx55(Text, { color: theme.textMuted, children: "Enter refines now \xB7 any key sends as-is \xB7 Esc cancels" }) })
29934
- ]
29935
- }
29936
- );
29937
- }
29938
-
29939
30185
  // src/components/file-picker.tsx
29940
- import { jsx as jsx56, jsxs as jsxs55 } from "react/jsx-runtime";
30186
+ import { jsx as jsx55, jsxs as jsxs54 } from "react/jsx-runtime";
29941
30187
  function FilePicker({ query, matches, selected }) {
29942
30188
  if (matches.length === 0) {
29943
- return /* @__PURE__ */ jsx56(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: /* @__PURE__ */ jsxs55(Text, { dimColor: true, children: [
30189
+ return /* @__PURE__ */ jsx55(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: /* @__PURE__ */ jsxs54(Text, { dimColor: true, children: [
29944
30190
  "@",
29945
30191
  query,
29946
30192
  " \u2014 no matches"
29947
30193
  ] }) });
29948
30194
  }
29949
- return /* @__PURE__ */ jsxs55(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
29950
- /* @__PURE__ */ jsxs55(Text, { dimColor: true, children: [
30195
+ return /* @__PURE__ */ jsxs54(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
30196
+ /* @__PURE__ */ jsxs54(Text, { dimColor: true, children: [
29951
30197
  "@",
29952
30198
  query || "\u2026",
29953
30199
  " \u2014 \u2191/\u2193 select, Enter attach, Esc cancel"
29954
30200
  ] }),
29955
- matches.map((m, i) => /* @__PURE__ */ jsxs55(Text, { inverse: i === selected, ...i === selected ? { color: "cyan" } : {}, children: [
30201
+ matches.map((m, i) => /* @__PURE__ */ jsxs54(Text, { inverse: i === selected, ...i === selected ? { color: "cyan" } : {}, children: [
29956
30202
  i === selected ? "\u203A " : " ",
29957
30203
  highlight(m, query)
29958
30204
  ] }, m))
@@ -29963,7 +30209,7 @@ function highlight(path11, _query) {
29963
30209
  }
29964
30210
 
29965
30211
  // src/components/help-panel.tsx
29966
- import { Fragment as Fragment18, jsx as jsx57, jsxs as jsxs56 } from "react/jsx-runtime";
30212
+ import { Fragment as Fragment18, jsx as jsx56, jsxs as jsxs55 } from "react/jsx-runtime";
29967
30213
  var CATEGORY_ORDER2 = ["Run", "Session", "Inspect", "Agent", "Config", "App"];
29968
30214
  var CHROME_ROWS3 = 7;
29969
30215
  function normalizeQuery(raw) {
@@ -30036,29 +30282,29 @@ function HelpPanel({
30036
30282
  const hiddenAbove = start;
30037
30283
  const hiddenBelow = rows.length - end;
30038
30284
  const hasFilter = query.length > 0;
30039
- return /* @__PURE__ */ jsxs56(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
30040
- /* @__PURE__ */ jsx57(Text, { bold: true, color: "cyan", children: "\u2501\u2501 Help / Command Palette \u2501\u2501" }),
30041
- /* @__PURE__ */ jsx57(Text, { dimColor: true, children: hasFilter ? `Filter: ${filter} (${filtered.length} match${filtered.length === 1 ? "" : "es"})` : "\u2191/\u2193 navigate \xB7 Enter run \xB7 Tab fill \xB7 type to filter \xB7 Esc close" }),
30042
- /* @__PURE__ */ jsx57(Box, { marginTop: 1, flexDirection: "column", children: filtered.length === 0 ? /* @__PURE__ */ jsxs56(Text, { dimColor: true, children: [
30285
+ return /* @__PURE__ */ jsxs55(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
30286
+ /* @__PURE__ */ jsx56(Text, { bold: true, color: "cyan", children: "\u2501\u2501 Help / Command Palette \u2501\u2501" }),
30287
+ /* @__PURE__ */ jsx56(Text, { dimColor: true, children: hasFilter ? `Filter: ${filter} (${filtered.length} match${filtered.length === 1 ? "" : "es"})` : "\u2191/\u2193 navigate \xB7 Enter run \xB7 Tab fill \xB7 type to filter \xB7 Esc close" }),
30288
+ /* @__PURE__ */ jsx56(Box, { marginTop: 1, flexDirection: "column", children: filtered.length === 0 ? /* @__PURE__ */ jsxs55(Text, { dimColor: true, children: [
30043
30289
  'No commands match "',
30044
30290
  filter,
30045
30291
  '".'
30046
- ] }) : /* @__PURE__ */ jsxs56(Fragment18, { children: [
30047
- hiddenAbove > 0 && !hasFilter ? /* @__PURE__ */ jsx57(Text, { dimColor: true, children: ` \u2191 ${hiddenAbove} more` }) : null,
30048
- contextHeader && !hasFilter ? /* @__PURE__ */ jsxs56(Text, { bold: true, color: "yellow", dimColor: true, children: [
30292
+ ] }) : /* @__PURE__ */ jsxs55(Fragment18, { children: [
30293
+ hiddenAbove > 0 && !hasFilter ? /* @__PURE__ */ jsx56(Text, { dimColor: true, children: ` \u2191 ${hiddenAbove} more` }) : null,
30294
+ contextHeader && !hasFilter ? /* @__PURE__ */ jsxs55(Text, { bold: true, color: "yellow", dimColor: true, children: [
30049
30295
  " ",
30050
30296
  contextHeader
30051
30297
  ] }) : null,
30052
30298
  visible.map((row) => {
30053
30299
  if (row.type === "header") {
30054
- return /* @__PURE__ */ jsxs56(Text, { bold: true, color: "yellow", dimColor: true, children: [
30300
+ return /* @__PURE__ */ jsxs55(Text, { bold: true, color: "yellow", dimColor: true, children: [
30055
30301
  " ",
30056
30302
  row.category
30057
30303
  ] }, `cat-${row.category}`);
30058
30304
  }
30059
30305
  const { entry, index } = row;
30060
30306
  const isSelected = index === focus;
30061
- return /* @__PURE__ */ jsxs56(
30307
+ return /* @__PURE__ */ jsxs55(
30062
30308
  Text,
30063
30309
  {
30064
30310
  inverse: isSelected,
@@ -30066,20 +30312,20 @@ function HelpPanel({
30066
30312
  wrap: "truncate-end",
30067
30313
  children: [
30068
30314
  isSelected ? "\u203A " : " ",
30069
- /* @__PURE__ */ jsxs56(Text, { bold: true, children: [
30315
+ /* @__PURE__ */ jsxs55(Text, { bold: true, children: [
30070
30316
  "/",
30071
30317
  entry.name
30072
30318
  ] }),
30073
- entry.aliases && entry.aliases.length > 0 ? /* @__PURE__ */ jsxs56(Text, { dimColor: true, children: [
30319
+ entry.aliases && entry.aliases.length > 0 ? /* @__PURE__ */ jsxs55(Text, { dimColor: true, children: [
30074
30320
  " (",
30075
30321
  entry.aliases.map((a) => `/${a}`).join(", "),
30076
30322
  ")"
30077
30323
  ] }) : null,
30078
- /* @__PURE__ */ jsxs56(Text, { dimColor: true, children: [
30324
+ /* @__PURE__ */ jsxs55(Text, { dimColor: true, children: [
30079
30325
  " \u2014 ",
30080
30326
  entry.description
30081
30327
  ] }),
30082
- entry.argsHint ? /* @__PURE__ */ jsxs56(Text, { dimColor: true, children: [
30328
+ entry.argsHint ? /* @__PURE__ */ jsxs55(Text, { dimColor: true, children: [
30083
30329
  " ",
30084
30330
  entry.argsHint
30085
30331
  ] }) : null
@@ -30088,30 +30334,30 @@ function HelpPanel({
30088
30334
  entry.name
30089
30335
  );
30090
30336
  }),
30091
- hiddenBelow > 0 && !hasFilter ? /* @__PURE__ */ jsx57(Text, { dimColor: true, children: ` \u2193 ${hiddenBelow} more` }) : null
30337
+ hiddenBelow > 0 && !hasFilter ? /* @__PURE__ */ jsx56(Text, { dimColor: true, children: ` \u2193 ${hiddenBelow} more` }) : null
30092
30338
  ] }) }),
30093
- hint ? /* @__PURE__ */ jsx57(Box, { marginTop: 1, children: /* @__PURE__ */ jsx57(Text, { dimColor: true, children: hint }) }) : null
30339
+ hint ? /* @__PURE__ */ jsx56(Box, { marginTop: 1, children: /* @__PURE__ */ jsx56(Text, { dimColor: true, children: hint }) }) : null
30094
30340
  ] });
30095
30341
  }
30096
30342
 
30097
30343
  // src/components/mcp-picker.tsx
30098
- import { Fragment as Fragment19, jsx as jsx58, jsxs as jsxs57 } from "react/jsx-runtime";
30344
+ import { Fragment as Fragment19, jsx as jsx57, jsxs as jsxs56 } from "react/jsx-runtime";
30099
30345
  var CHROME_ROWS4 = 17;
30100
30346
  function statusBadge(status, enabled) {
30101
- if (!enabled) return /* @__PURE__ */ jsx58(Text, { dimColor: true, children: "\u25CB disabled" });
30347
+ if (!enabled) return /* @__PURE__ */ jsx57(Text, { dimColor: true, children: "\u25CB disabled" });
30102
30348
  switch (status) {
30103
30349
  case "connected":
30104
- return /* @__PURE__ */ jsx58(Text, { color: "green", children: "\u25CF connected" });
30350
+ return /* @__PURE__ */ jsx57(Text, { color: "green", children: "\u25CF connected" });
30105
30351
  case "connecting":
30106
- return /* @__PURE__ */ jsx58(Text, { color: "cyan", children: "\u25D0 connecting" });
30352
+ return /* @__PURE__ */ jsx57(Text, { color: "cyan", children: "\u25D0 connecting" });
30107
30353
  case "reconnecting":
30108
- return /* @__PURE__ */ jsx58(Text, { color: "cyan", children: "\u25D1 reconnecting" });
30354
+ return /* @__PURE__ */ jsx57(Text, { color: "cyan", children: "\u25D1 reconnecting" });
30109
30355
  case "disconnected":
30110
- return /* @__PURE__ */ jsx58(Text, { dimColor: true, children: "\u25CB disconnected" });
30356
+ return /* @__PURE__ */ jsx57(Text, { dimColor: true, children: "\u25CB disconnected" });
30111
30357
  case "failed":
30112
- return /* @__PURE__ */ jsx58(Text, { color: "red", children: "\u2717 failed" });
30358
+ return /* @__PURE__ */ jsx57(Text, { color: "red", children: "\u2717 failed" });
30113
30359
  default:
30114
- return /* @__PURE__ */ jsx58(Text, { dimColor: true, children: status });
30360
+ return /* @__PURE__ */ jsx57(Text, { dimColor: true, children: status });
30115
30361
  }
30116
30362
  }
30117
30363
  function McpPicker({
@@ -30128,36 +30374,36 @@ function McpPicker({
30128
30374
  const windowEnd = Math.min(windowStart + maxVisible, total);
30129
30375
  const above = windowStart;
30130
30376
  const below = total - windowEnd;
30131
- return /* @__PURE__ */ jsxs57(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
30132
- /* @__PURE__ */ jsx58(Text, { bold: true, color: "cyan", children: "MCP Servers" }),
30133
- /* @__PURE__ */ jsx58(Text, { dimColor: true, children: "\u2191/\u2193 select \xB7 Enter/\u2190/\u2192 toggle enable \xB7 r restart \xB7 Esc close" }),
30134
- /* @__PURE__ */ jsx58(Box, { marginTop: 1, flexDirection: "column", children: items.length === 0 ? /* @__PURE__ */ jsx58(Text, { dimColor: true, children: busy ? "Loading servers\u2026" : "No MCP servers configured." }) : /* @__PURE__ */ jsxs57(Fragment19, { children: [
30135
- above > 0 ? /* @__PURE__ */ jsx58(Text, { dimColor: true, children: ` \u2191 ${above} more` }) : null,
30377
+ return /* @__PURE__ */ jsxs56(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
30378
+ /* @__PURE__ */ jsx57(Text, { bold: true, color: "cyan", children: "MCP Servers" }),
30379
+ /* @__PURE__ */ jsx57(Text, { dimColor: true, children: "\u2191/\u2193 select \xB7 Enter/\u2190/\u2192 toggle enable \xB7 r restart \xB7 Esc close" }),
30380
+ /* @__PURE__ */ jsx57(Box, { marginTop: 1, flexDirection: "column", children: items.length === 0 ? /* @__PURE__ */ jsx57(Text, { dimColor: true, children: busy ? "Loading servers\u2026" : "No MCP servers configured." }) : /* @__PURE__ */ jsxs56(Fragment19, { children: [
30381
+ above > 0 ? /* @__PURE__ */ jsx57(Text, { dimColor: true, children: ` \u2191 ${above} more` }) : null,
30136
30382
  items.slice(windowStart, windowEnd).map((item, i) => {
30137
30383
  const index = windowStart + i;
30138
30384
  const focused = index === selected;
30139
30385
  const marker = focused ? "\u203A" : " ";
30140
- return /* @__PURE__ */ jsxs57(Text, { color: focused ? "cyan" : void 0, wrap: "truncate-end", children: [
30386
+ return /* @__PURE__ */ jsxs56(Text, { color: focused ? "cyan" : void 0, wrap: "truncate-end", children: [
30141
30387
  marker,
30142
30388
  " ",
30143
30389
  statusBadge(item.status, item.enabled),
30144
30390
  " ",
30145
- /* @__PURE__ */ jsx58(Text, { bold: true, children: item.name.padEnd(18) }),
30146
- /* @__PURE__ */ jsxs57(Text, { dimColor: true, children: [
30391
+ /* @__PURE__ */ jsx57(Text, { bold: true, children: item.name.padEnd(18) }),
30392
+ /* @__PURE__ */ jsxs56(Text, { dimColor: true, children: [
30147
30393
  item.transport.padEnd(14),
30148
30394
  item.toolCount > 0 ? `${item.toolCount} tools` : "",
30149
30395
  item.lazy ? " lazy" : ""
30150
30396
  ] })
30151
30397
  ] }, item.name);
30152
30398
  }),
30153
- below > 0 ? /* @__PURE__ */ jsx58(Text, { dimColor: true, children: ` \u2193 ${below} more` }) : null
30399
+ below > 0 ? /* @__PURE__ */ jsx57(Text, { dimColor: true, children: ` \u2193 ${below} more` }) : null
30154
30400
  ] }) }),
30155
- hint ? /* @__PURE__ */ jsx58(Box, { marginTop: 1, children: /* @__PURE__ */ jsx58(Text, { dimColor: true, children: hint }) }) : null
30401
+ hint ? /* @__PURE__ */ jsx57(Box, { marginTop: 1, children: /* @__PURE__ */ jsx57(Text, { dimColor: true, children: hint }) }) : null
30156
30402
  ] });
30157
30403
  }
30158
30404
 
30159
30405
  // src/components/model-picker.tsx
30160
- import { jsx as jsx59, jsxs as jsxs58 } from "react/jsx-runtime";
30406
+ import { jsx as jsx58, jsxs as jsxs57 } from "react/jsx-runtime";
30161
30407
  var MAX_VISIBLE2 = 10;
30162
30408
  function getVisibleWindow2(selected, total) {
30163
30409
  const half = Math.floor(MAX_VISIBLE2 / 2);
@@ -30185,27 +30431,27 @@ function ModelPicker({
30185
30431
  }) {
30186
30432
  const title = titleLabel ?? "Switch model";
30187
30433
  if (step === "provider") {
30188
- return /* @__PURE__ */ jsxs58(Box, { flexDirection: "column", borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, children: [
30189
- /* @__PURE__ */ jsx59(Text, { color: UI_COLORS.title, bold: true, children: `\u2501\u2501 ${title} \u2014 Step 1/2: Pick provider \u2501\u2501` }),
30190
- /* @__PURE__ */ jsx59(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel \xB7 Ctrl+C exit" }),
30191
- providerOptions.length === 0 ? /* @__PURE__ */ jsx59(Text, { dimColor: true, children: "(no providers with keys \u2014 add one via `wstack auth`)" }) : /* @__PURE__ */ jsx59(Box, { flexDirection: "column", minHeight: providerOptions.length, children: providerOptions.map((p, i) => {
30434
+ return /* @__PURE__ */ jsxs57(Box, { flexDirection: "column", borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, children: [
30435
+ /* @__PURE__ */ jsx58(Text, { color: UI_COLORS.title, bold: true, children: `\u2501\u2501 ${title} \u2014 Step 1/2: Pick provider \u2501\u2501` }),
30436
+ /* @__PURE__ */ jsx58(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel \xB7 Ctrl+C exit" }),
30437
+ providerOptions.length === 0 ? /* @__PURE__ */ jsx58(Text, { dimColor: true, children: "(no providers with keys \u2014 add one via `wstack auth`)" }) : /* @__PURE__ */ jsx58(Box, { flexDirection: "column", minHeight: providerOptions.length, children: providerOptions.map((p, i) => {
30192
30438
  const isSelected = i === selected;
30193
30439
  const famColor = colorForFamily(p.family);
30194
- return /* @__PURE__ */ jsxs58(
30440
+ return /* @__PURE__ */ jsxs57(
30195
30441
  Text,
30196
30442
  {
30197
30443
  inverse: isSelected,
30198
30444
  ...isSelected ? { color: UI_COLORS.focused } : {},
30199
30445
  children: [
30200
30446
  isSelected ? "\u203A " : " ",
30201
- /* @__PURE__ */ jsx59(Text, { bold: true, color: isSelected ? void 0 : famColor, children: p.id.padEnd(28) }),
30202
- /* @__PURE__ */ jsxs58(Text, { color: isSelected ? void 0 : famColor, dimColor: !isSelected, children: [
30447
+ /* @__PURE__ */ jsx58(Text, { bold: true, color: isSelected ? void 0 : famColor, children: p.id.padEnd(28) }),
30448
+ /* @__PURE__ */ jsxs57(Text, { color: isSelected ? void 0 : famColor, dimColor: !isSelected, children: [
30203
30449
  " ",
30204
30450
  "[",
30205
30451
  p.family,
30206
30452
  "]"
30207
30453
  ] }),
30208
- /* @__PURE__ */ jsxs58(Text, { dimColor: true, children: [
30454
+ /* @__PURE__ */ jsxs57(Text, { dimColor: true, children: [
30209
30455
  " ",
30210
30456
  p.models.length,
30211
30457
  " model",
@@ -30216,15 +30462,15 @@ function ModelPicker({
30216
30462
  p.id
30217
30463
  );
30218
30464
  }) }),
30219
- hint ? /* @__PURE__ */ jsx59(Text, { color: UI_COLORS.hint, children: hint }) : null
30465
+ hint ? /* @__PURE__ */ jsx58(Text, { color: UI_COLORS.hint, children: hint }) : null
30220
30466
  ] });
30221
30467
  }
30222
30468
  const total = filteredOptions.length;
30223
30469
  const { start, end } = getVisibleWindow2(selected, total);
30224
30470
  const visibleItems = filteredOptions.slice(start, end);
30225
30471
  const searchHint = searchQuery ? ` | filter:"${searchQuery}" \u2192 ${total} match${total === 1 ? "" : "es"}` : total > MAX_VISIBLE2 ? ` (${total} models \u2014 type to filter)` : "";
30226
- return /* @__PURE__ */ jsxs58(Box, { flexDirection: "column", borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, children: [
30227
- /* @__PURE__ */ jsxs58(Text, { color: UI_COLORS.title, bold: true, children: [
30472
+ return /* @__PURE__ */ jsxs57(Box, { flexDirection: "column", borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, children: [
30473
+ /* @__PURE__ */ jsxs57(Text, { color: UI_COLORS.title, bold: true, children: [
30228
30474
  `\u2501\u2501 ${title} \u2014 Step 2/2: Pick model `,
30229
30475
  "(",
30230
30476
  pickedProviderId,
@@ -30232,9 +30478,9 @@ function ModelPicker({
30232
30478
  ")",
30233
30479
  " \u2501\u2501"
30234
30480
  ] }),
30235
- /* @__PURE__ */ jsx59(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc back \xB7 Ctrl+C exit \xB7 type to filter" }),
30236
- total === 0 ? /* @__PURE__ */ jsx59(Text, { dimColor: true, children: searchQuery ? `(no models match "${searchQuery}")` : "(no models known for this provider)" }) : /* @__PURE__ */ jsxs58(Box, { flexDirection: "column", minHeight: MAX_VISIBLE2 + 2, children: [
30237
- start > 0 && /* @__PURE__ */ jsxs58(Text, { dimColor: true, children: [
30481
+ /* @__PURE__ */ jsx58(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc back \xB7 Ctrl+C exit \xB7 type to filter" }),
30482
+ total === 0 ? /* @__PURE__ */ jsx58(Text, { dimColor: true, children: searchQuery ? `(no models match "${searchQuery}")` : "(no models known for this provider)" }) : /* @__PURE__ */ jsxs57(Box, { flexDirection: "column", minHeight: MAX_VISIBLE2 + 2, children: [
30483
+ start > 0 && /* @__PURE__ */ jsxs57(Text, { dimColor: true, children: [
30238
30484
  "\u25B2 ",
30239
30485
  start,
30240
30486
  " above"
@@ -30242,7 +30488,7 @@ function ModelPicker({
30242
30488
  visibleItems.map((id, vi) => {
30243
30489
  const absoluteIndex = start + vi;
30244
30490
  const isSelected = absoluteIndex === selected;
30245
- return /* @__PURE__ */ jsxs58(
30491
+ return /* @__PURE__ */ jsxs57(
30246
30492
  Text,
30247
30493
  {
30248
30494
  inverse: isSelected,
@@ -30256,20 +30502,20 @@ function ModelPicker({
30256
30502
  );
30257
30503
  }),
30258
30504
  Array.from({ length: MAX_VISIBLE2 - visibleItems.length }).map(
30259
- (_, i) => /* @__PURE__ */ jsx59(Text, { children: " " }, `pad-${i}`)
30505
+ (_, i) => /* @__PURE__ */ jsx58(Text, { children: " " }, `pad-${i}`)
30260
30506
  ),
30261
- end < total && /* @__PURE__ */ jsxs58(Text, { dimColor: true, children: [
30507
+ end < total && /* @__PURE__ */ jsxs57(Text, { dimColor: true, children: [
30262
30508
  "\u25BC ",
30263
30509
  total - end,
30264
30510
  " below"
30265
30511
  ] })
30266
30512
  ] }),
30267
- hint ? /* @__PURE__ */ jsx59(Text, { color: UI_COLORS.hint, children: hint }) : null
30513
+ hint ? /* @__PURE__ */ jsx58(Text, { color: UI_COLORS.hint, children: hint }) : null
30268
30514
  ] });
30269
30515
  }
30270
30516
 
30271
30517
  // src/components/plugin-picker.tsx
30272
- import { Fragment as Fragment20, jsx as jsx60, jsxs as jsxs59 } from "react/jsx-runtime";
30518
+ import { Fragment as Fragment20, jsx as jsx59, jsxs as jsxs58 } from "react/jsx-runtime";
30273
30519
  var CHROME_ROWS5 = 17;
30274
30520
  var MAX_PICKER_ITEMS = 15;
30275
30521
  function PluginPicker({
@@ -30287,15 +30533,15 @@ function PluginPicker({
30287
30533
  const above = windowStart;
30288
30534
  const below = total - windowEnd;
30289
30535
  const hasLockedRows = items.some((item) => item.lockable === false);
30290
- return /* @__PURE__ */ jsxs59(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
30291
- /* @__PURE__ */ jsx60(Text, { bold: true, color: "cyan", children: "Plugin menu" }),
30292
- /* @__PURE__ */ jsxs59(Text, { dimColor: true, children: [
30536
+ return /* @__PURE__ */ jsxs58(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
30537
+ /* @__PURE__ */ jsx59(Text, { bold: true, color: "cyan", children: "Plugin menu" }),
30538
+ /* @__PURE__ */ jsxs58(Text, { dimColor: true, children: [
30293
30539
  "\u2191/\u2193 select \xB7 Enter/\u2190/\u2192 toggle",
30294
30540
  hasLockedRows ? " \xB7 \u{1F512} = locked" : "",
30295
30541
  " \xB7 Esc close"
30296
30542
  ] }),
30297
- /* @__PURE__ */ jsx60(Box, { marginTop: 1, flexDirection: "column", children: items.length === 0 ? /* @__PURE__ */ jsx60(Text, { dimColor: true, children: busy ? "Loading plugins\u2026" : "No plugins available." }) : /* @__PURE__ */ jsxs59(Fragment20, { children: [
30298
- above > 0 ? /* @__PURE__ */ jsx60(Text, { dimColor: true, children: ` \u2191 ${above} more` }) : null,
30543
+ /* @__PURE__ */ jsx59(Box, { marginTop: 1, flexDirection: "column", children: items.length === 0 ? /* @__PURE__ */ jsx59(Text, { dimColor: true, children: busy ? "Loading plugins\u2026" : "No plugins available." }) : /* @__PURE__ */ jsxs58(Fragment20, { children: [
30544
+ above > 0 ? /* @__PURE__ */ jsx59(Text, { dimColor: true, children: ` \u2191 ${above} more` }) : null,
30299
30545
  items.slice(windowStart, windowEnd).map((item, i) => {
30300
30546
  const index = windowStart + i;
30301
30547
  const focused = index === selected;
@@ -30304,16 +30550,16 @@ function PluginPicker({
30304
30550
  const state = item.enabled ? "\u25CF on " : "\u25CB off";
30305
30551
  const color = item.enabled ? "green" : "gray";
30306
30552
  const lock = isLocked ? "\u{1F512}" : " ";
30307
- return /* @__PURE__ */ jsxs59(Text, { color: focused ? "cyan" : void 0, wrap: "truncate-end", children: [
30553
+ return /* @__PURE__ */ jsxs58(Text, { color: focused ? "cyan" : void 0, wrap: "truncate-end", children: [
30308
30554
  marker,
30309
30555
  " ",
30310
- /* @__PURE__ */ jsx60(Text, { color, children: state }),
30556
+ /* @__PURE__ */ jsx59(Text, { color, children: state }),
30311
30557
  " ",
30312
30558
  item.name.padEnd(18),
30313
30559
  " ",
30314
- /* @__PURE__ */ jsx60(Text, { color: isLocked ? "yellow" : void 0, children: lock }),
30560
+ /* @__PURE__ */ jsx59(Text, { color: isLocked ? "yellow" : void 0, children: lock }),
30315
30561
  " ",
30316
- /* @__PURE__ */ jsxs59(Text, { dimColor: true, children: [
30562
+ /* @__PURE__ */ jsxs58(Text, { dimColor: true, children: [
30317
30563
  "risk=",
30318
30564
  item.risk.padEnd(6)
30319
30565
  ] }),
@@ -30321,14 +30567,14 @@ function PluginPicker({
30321
30567
  item.summary
30322
30568
  ] }, item.name);
30323
30569
  }),
30324
- below > 0 ? /* @__PURE__ */ jsx60(Text, { dimColor: true, children: ` \u2193 ${below} more` }) : null
30570
+ below > 0 ? /* @__PURE__ */ jsx59(Text, { dimColor: true, children: ` \u2193 ${below} more` }) : null
30325
30571
  ] }) }),
30326
- hint ? /* @__PURE__ */ jsx60(Box, { marginTop: 1, children: /* @__PURE__ */ jsx60(Text, { dimColor: true, children: hint }) }) : null
30572
+ hint ? /* @__PURE__ */ jsx59(Box, { marginTop: 1, children: /* @__PURE__ */ jsx59(Text, { dimColor: true, children: hint }) }) : null
30327
30573
  ] });
30328
30574
  }
30329
30575
 
30330
30576
  // src/components/project-picker.tsx
30331
- import { jsx as jsx61, jsxs as jsxs60 } from "react/jsx-runtime";
30577
+ import { jsx as jsx60, jsxs as jsxs59 } from "react/jsx-runtime";
30332
30578
  function actionIcon(key) {
30333
30579
  if (key === "new-session") return glyphs.sessions;
30334
30580
  if (key === "prev-sessions") return glyphs.clock;
@@ -30348,71 +30594,71 @@ function ProjectPicker({
30348
30594
  const window = panelWindow(items.length, selected, limit);
30349
30595
  const visible = items.slice(window.start, window.end);
30350
30596
  const labelWidth = Math.max(12, size.contentWidth - 16);
30351
- return /* @__PURE__ */ jsxs60(
30597
+ return /* @__PURE__ */ jsxs59(
30352
30598
  MonitorShell,
30353
30599
  {
30354
30600
  accent: theme.accent,
30355
30601
  icon: glyphs.folder,
30356
30602
  title: "PROJECTS",
30357
30603
  kicker: size.columns >= 58 ? "workspace switcher" : void 0,
30358
- right: /* @__PURE__ */ jsxs60(Text, { color: theme.textMuted, children: [
30604
+ right: /* @__PURE__ */ jsxs59(Text, { color: theme.textMuted, children: [
30359
30605
  projectCount,
30360
30606
  " project",
30361
30607
  projectCount === 1 ? "" : "s"
30362
30608
  ] }),
30363
- footer: /* @__PURE__ */ jsxs60(Box, { gap: 2, children: [
30364
- /* @__PURE__ */ jsx61(KeyCap, { keyName: "\u2191\u2193", label: "navigate" }),
30365
- /* @__PURE__ */ jsx61(KeyCap, { keyName: "Enter", label: "open", color: theme.success }),
30366
- size.columns >= 64 ? /* @__PURE__ */ jsx61(KeyCap, { keyName: "Esc", label: "close", color: theme.error }) : null
30609
+ footer: /* @__PURE__ */ jsxs59(Box, { gap: 2, children: [
30610
+ /* @__PURE__ */ jsx60(KeyCap, { keyName: "\u2191\u2193", label: "navigate" }),
30611
+ /* @__PURE__ */ jsx60(KeyCap, { keyName: "Enter", label: "open", color: theme.success }),
30612
+ size.columns >= 64 ? /* @__PURE__ */ jsx60(KeyCap, { keyName: "Esc", label: "close", color: theme.error }) : null
30367
30613
  ] }),
30368
30614
  children: [
30369
- /* @__PURE__ */ jsxs60(Box, { height: 1, marginTop: 1, children: [
30370
- /* @__PURE__ */ jsxs60(Text, { color: filter ? theme.warn : theme.textMuted, children: [
30615
+ /* @__PURE__ */ jsxs59(Box, { height: 1, marginTop: 1, children: [
30616
+ /* @__PURE__ */ jsxs59(Text, { color: filter ? theme.warn : theme.textMuted, children: [
30371
30617
  glyphs.prompt,
30372
30618
  " "
30373
30619
  ] }),
30374
- /* @__PURE__ */ jsx61(Text, { color: filter ? theme.textPrimary : theme.textMuted, children: filter || "Type to filter projects and actions" }),
30375
- filter ? /* @__PURE__ */ jsx61(Text, { color: theme.accent, children: "\u2588" }) : null,
30376
- /* @__PURE__ */ jsx61(Box, { flexGrow: 1 }),
30377
- /* @__PURE__ */ jsxs60(Text, { color: theme.textMuted, children: [
30620
+ /* @__PURE__ */ jsx60(Text, { color: filter ? theme.textPrimary : theme.textMuted, children: filter || "Type to filter projects and actions" }),
30621
+ filter ? /* @__PURE__ */ jsx60(Text, { color: theme.accent, children: "\u2588" }) : null,
30622
+ /* @__PURE__ */ jsx60(Box, { flexGrow: 1 }),
30623
+ /* @__PURE__ */ jsxs59(Text, { color: theme.textMuted, children: [
30378
30624
  selectableCount,
30379
30625
  " choices"
30380
30626
  ] })
30381
30627
  ] }),
30382
- window.above > 0 ? /* @__PURE__ */ jsxs60(Text, { color: theme.textMuted, children: [
30628
+ window.above > 0 ? /* @__PURE__ */ jsxs59(Text, { color: theme.textMuted, children: [
30383
30629
  " \u2191 ",
30384
30630
  window.above,
30385
30631
  " hidden"
30386
30632
  ] }) : null,
30387
- visible.length === 0 ? /* @__PURE__ */ jsx61(
30633
+ visible.length === 0 ? /* @__PURE__ */ jsx60(
30388
30634
  EmptyPanelState,
30389
30635
  {
30390
30636
  icon: glyphs.folder,
30391
30637
  title: "No matching projects",
30392
30638
  detail: "Clear the filter or add a project with /project add."
30393
30639
  }
30394
- ) : /* @__PURE__ */ jsx61(Box, { flexDirection: "column", marginTop: 1, children: visible.map((item, offset) => {
30640
+ ) : /* @__PURE__ */ jsx60(Box, { flexDirection: "column", marginTop: 1, children: visible.map((item, offset) => {
30395
30641
  const index = window.start + offset;
30396
30642
  const isSelected = index === selected;
30397
30643
  if (item.key === "__divider__") {
30398
- return /* @__PURE__ */ jsx61(Box, { height: 1, children: /* @__PURE__ */ jsx61(Text, { color: theme.textMuted, children: "\u2500".repeat(Math.min(32, size.contentWidth)) }) }, `${item.key}-${index}`);
30644
+ return /* @__PURE__ */ jsx60(Box, { height: 1, children: /* @__PURE__ */ jsx60(Text, { color: theme.textMuted, children: "\u2500".repeat(Math.min(32, size.contentWidth)) }) }, `${item.key}-${index}`);
30399
30645
  }
30400
30646
  const icon = item.kind === "project" ? glyphs.folder : actionIcon(item.key);
30401
30647
  const accent = item.kind === "project" ? theme.accent : theme.warn;
30402
- return /* @__PURE__ */ jsxs60(
30648
+ return /* @__PURE__ */ jsxs59(
30403
30649
  Box,
30404
30650
  {
30405
30651
  flexDirection: "column",
30406
30652
  ...isSelected && theme.supportsBackground ? { backgroundColor: theme.surfaceRaised } : {},
30407
30653
  children: [
30408
- /* @__PURE__ */ jsxs60(Box, { height: 1, children: [
30409
- /* @__PURE__ */ jsx61(Text, { color: isSelected ? accent : theme.textMuted, children: isSelected ? "\u258E" : " " }),
30410
- /* @__PURE__ */ jsxs60(Text, { color: accent, children: [
30654
+ /* @__PURE__ */ jsxs59(Box, { height: 1, children: [
30655
+ /* @__PURE__ */ jsx60(Text, { color: isSelected ? accent : theme.textMuted, children: isSelected ? "\u258E" : " " }),
30656
+ /* @__PURE__ */ jsxs59(Text, { color: accent, children: [
30411
30657
  " ",
30412
30658
  icon,
30413
30659
  " "
30414
30660
  ] }),
30415
- /* @__PURE__ */ jsx61(
30661
+ /* @__PURE__ */ jsx60(
30416
30662
  Text,
30417
30663
  {
30418
30664
  color: isSelected ? theme.textPrimary : theme.textSecondary,
@@ -30420,24 +30666,24 @@ function ProjectPicker({
30420
30666
  children: truncatePanelText(item.label, labelWidth)
30421
30667
  }
30422
30668
  ),
30423
- /* @__PURE__ */ jsx61(Box, { flexGrow: 1 }),
30424
- item.meta ? /* @__PURE__ */ jsx61(Text, { color: isSelected ? accent : theme.textMuted, children: truncatePanelText(item.meta, 18) }) : null
30669
+ /* @__PURE__ */ jsx60(Box, { flexGrow: 1 }),
30670
+ item.meta ? /* @__PURE__ */ jsx60(Text, { color: isSelected ? accent : theme.textMuted, children: truncatePanelText(item.meta, 18) }) : null
30425
30671
  ] }),
30426
- item.subtitle ? /* @__PURE__ */ jsxs60(Box, { height: 1, children: [
30427
- /* @__PURE__ */ jsx61(Text, { color: isSelected ? accent : theme.textMuted, children: " \u2514\u2500 " }),
30428
- /* @__PURE__ */ jsx61(Text, { color: theme.textMuted, children: truncatePanelText(item.subtitle, labelWidth) })
30672
+ item.subtitle ? /* @__PURE__ */ jsxs59(Box, { height: 1, children: [
30673
+ /* @__PURE__ */ jsx60(Text, { color: isSelected ? accent : theme.textMuted, children: " \u2514\u2500 " }),
30674
+ /* @__PURE__ */ jsx60(Text, { color: theme.textMuted, children: truncatePanelText(item.subtitle, labelWidth) })
30429
30675
  ] }) : null
30430
30676
  ]
30431
30677
  },
30432
30678
  item.key
30433
30679
  );
30434
30680
  }) }),
30435
- window.below > 0 ? /* @__PURE__ */ jsxs60(Text, { color: theme.textMuted, children: [
30681
+ window.below > 0 ? /* @__PURE__ */ jsxs59(Text, { color: theme.textMuted, children: [
30436
30682
  " \u2193 ",
30437
30683
  window.below,
30438
30684
  " hidden"
30439
30685
  ] }) : null,
30440
- hint ? /* @__PURE__ */ jsxs60(Text, { color: theme.warn, children: [
30686
+ hint ? /* @__PURE__ */ jsxs59(Text, { color: theme.warn, children: [
30441
30687
  glyphs.warning,
30442
30688
  " ",
30443
30689
  hint
@@ -30447,9 +30693,94 @@ function ProjectPicker({
30447
30693
  );
30448
30694
  }
30449
30695
 
30696
+ // src/components/refine-countdown-panel.tsx
30697
+ import { useEffect as useEffect64, useRef as useRef35, useState as useState37 } from "react";
30698
+ import { jsx as jsx61, jsxs as jsxs60 } from "react/jsx-runtime";
30699
+ function RefineCountdownPanel({
30700
+ original,
30701
+ seconds,
30702
+ onDecision,
30703
+ providerId,
30704
+ model
30705
+ }) {
30706
+ const [remaining, setRemaining] = useState37(Math.max(1, seconds));
30707
+ const resolvedRef = useRef35(false);
30708
+ useEffect64(() => {
30709
+ const id = setInterval(() => {
30710
+ setRemaining((prev) => {
30711
+ if (prev <= 1) {
30712
+ clearInterval(id);
30713
+ if (!resolvedRef.current) {
30714
+ resolvedRef.current = true;
30715
+ onDecision("proceed");
30716
+ }
30717
+ return 0;
30718
+ }
30719
+ return prev - 1;
30720
+ });
30721
+ }, 1e3);
30722
+ return () => clearInterval(id);
30723
+ }, []);
30724
+ useInput((_input, key) => {
30725
+ if (resolvedRef.current) return;
30726
+ resolvedRef.current = true;
30727
+ if (key.escape) onDecision("cancel");
30728
+ else if (key.return) onDecision("proceed");
30729
+ else onDecision("skip");
30730
+ });
30731
+ const preview = wrapRefinementPreview(original, 60, 2);
30732
+ return /* @__PURE__ */ jsxs60(
30733
+ Box,
30734
+ {
30735
+ alignSelf: "stretch",
30736
+ width: "100%",
30737
+ flexDirection: "column",
30738
+ borderStyle: "round",
30739
+ borderColor: theme.brand,
30740
+ paddingX: 1,
30741
+ children: [
30742
+ /* @__PURE__ */ jsxs60(Box, { height: 1, children: [
30743
+ /* @__PURE__ */ jsxs60(Text, { color: theme.brand, bold: true, children: [
30744
+ glyphs.brain,
30745
+ " PROMPT REFINER"
30746
+ ] }),
30747
+ providerId && model ? /* @__PURE__ */ jsxs60(Text, { color: theme.textMuted, children: [
30748
+ " ",
30749
+ "on ",
30750
+ providerId,
30751
+ "/",
30752
+ model
30753
+ ] }) : null,
30754
+ /* @__PURE__ */ jsx61(Box, { flexGrow: 1 }),
30755
+ /* @__PURE__ */ jsxs60(Text, { color: theme.brand, children: [
30756
+ "refining in ",
30757
+ remaining,
30758
+ "s\u2026"
30759
+ ] })
30760
+ ] }),
30761
+ /* @__PURE__ */ jsx61(Box, { flexDirection: "column", marginTop: 1, children: preview.map((line, index) => /* @__PURE__ */ jsxs60(Box, { height: 1, children: [
30762
+ /* @__PURE__ */ jsx61(Text, { color: theme.brand, children: "\u258E" }),
30763
+ /* @__PURE__ */ jsxs60(Text, { color: theme.textSecondary, children: [
30764
+ " ",
30765
+ line
30766
+ ] })
30767
+ ] }, `refine-countdown-preview-${index}`)) }),
30768
+ /* @__PURE__ */ jsx61(Box, { height: 1, marginTop: 1, children: /* @__PURE__ */ jsx61(Text, { color: theme.textMuted, children: "Enter refines now \xB7 any key sends as-is \xB7 Esc cancels" }) })
30769
+ ]
30770
+ }
30771
+ );
30772
+ }
30773
+
30450
30774
  // src/components/refine-failure-panel.tsx
30451
- import { useState as useState38 } from "react";
30775
+ import { useState as useState38, useMemo as useMemo12 } from "react";
30452
30776
  import { jsx as jsx62, jsxs as jsxs61 } from "react/jsx-runtime";
30777
+ function filterRefineModels(models, query) {
30778
+ const normalized = query.trim().toLowerCase();
30779
+ if (!normalized) return models;
30780
+ return models.filter(
30781
+ (candidate) => candidate.providerId.toLowerCase().includes(normalized) || candidate.model.toLowerCase().includes(normalized) || candidate.label?.toLowerCase().includes(normalized)
30782
+ );
30783
+ }
30453
30784
  var PICK_WINDOW = 8;
30454
30785
  function useColumns() {
30455
30786
  const { stdout } = useStdout();
@@ -30466,17 +30797,26 @@ function RefineFailurePanel({
30466
30797
  const columns = useColumns();
30467
30798
  const [view, setView] = useState38("options");
30468
30799
  const [cursor, setCursor] = useState38(0);
30800
+ const [query, setQuery] = useState38("");
30801
+ const filteredModels = useMemo12(() => filterRefineModels(models, query), [models, query]);
30469
30802
  const width = Math.max(30, columns - 4);
30470
30803
  const canPick = models.length > 0;
30471
30804
  const canFallback = !!fallbackRef;
30472
30805
  useInput((input, key) => {
30473
30806
  if (view === "pick") {
30474
30807
  if (key.escape) {
30808
+ if (query) {
30809
+ setQuery("");
30810
+ return;
30811
+ }
30475
30812
  setView("options");
30813
+ setQuery("");
30814
+ setCursor(0);
30476
30815
  return;
30477
30816
  }
30478
30817
  if (key.downArrow) {
30479
- setCursor((c) => Math.min(c + 1, models.length - 1));
30818
+ const n = filteredModels.length;
30819
+ if (n > 0) setCursor((c) => Math.min(c + 1, n - 1));
30480
30820
  return;
30481
30821
  }
30482
30822
  if (key.upArrow) {
@@ -30484,11 +30824,21 @@ function RefineFailurePanel({
30484
30824
  return;
30485
30825
  }
30486
30826
  if (key.return) {
30487
- const chosen = models[cursor];
30827
+ const chosen = filteredModels[cursor];
30488
30828
  if (chosen)
30489
30829
  onDecision({ kind: "pick", providerId: chosen.providerId, model: chosen.model });
30490
30830
  return;
30491
30831
  }
30832
+ if (key.backspace || key.delete) {
30833
+ setQuery((q) => q.slice(0, -1));
30834
+ setCursor(0);
30835
+ return;
30836
+ }
30837
+ if (input && input.length === 1 && input.charCodeAt(0) >= 32 && input.charCodeAt(0) < 127) {
30838
+ setQuery((q) => q + input);
30839
+ setCursor(0);
30840
+ return;
30841
+ }
30492
30842
  return;
30493
30843
  }
30494
30844
  if (key.return) {
@@ -30503,17 +30853,19 @@ function RefineFailurePanel({
30503
30853
  if (ch === "m" && canFallback) onDecision({ kind: "fallback" });
30504
30854
  else if (ch === "p" && canPick) {
30505
30855
  setCursor(0);
30856
+ setQuery("");
30506
30857
  setView("pick");
30507
30858
  } else if (ch === "s") onDecision({ kind: "original" });
30508
30859
  else if (ch === "e") onDecision({ kind: "edit" });
30509
30860
  });
30510
30861
  if (view === "pick") {
30862
+ const total = filteredModels.length;
30511
30863
  const start = Math.max(
30512
30864
  0,
30513
- Math.min(cursor - Math.floor(PICK_WINDOW / 2), models.length - PICK_WINDOW)
30865
+ Math.min(cursor - Math.floor(PICK_WINDOW / 2), total - PICK_WINDOW)
30514
30866
  );
30515
30867
  const windowStart = Math.max(0, start);
30516
- const visible = models.slice(windowStart, windowStart + PICK_WINDOW);
30868
+ const visible = filteredModels.slice(windowStart, windowStart + PICK_WINDOW);
30517
30869
  return /* @__PURE__ */ jsxs61(
30518
30870
  Box,
30519
30871
  {
@@ -30531,12 +30883,17 @@ function RefineFailurePanel({
30531
30883
  ] }),
30532
30884
  /* @__PURE__ */ jsx62(Box, { flexGrow: 1 }),
30533
30885
  /* @__PURE__ */ jsxs61(Text, { color: theme.textMuted, children: [
30534
- models.length,
30886
+ query ? `${total}/${models.length}` : `${models.length}`,
30535
30887
  " model",
30536
- models.length === 1 ? "" : "s"
30888
+ (query ? total : models.length) === 1 ? "" : "s"
30537
30889
  ] })
30538
30890
  ] }),
30539
- /* @__PURE__ */ jsx62(Box, { flexDirection: "column", marginTop: 1, children: visible.map((m, i) => {
30891
+ /* @__PURE__ */ jsx62(Box, { height: 1, marginTop: 1, children: /* @__PURE__ */ jsx62(Text, { color: theme.textMuted, children: query ? `\u{1F50D} ${query} ` : "\u{1F50D} type to filter" }) }),
30892
+ total === 0 ? /* @__PURE__ */ jsx62(Box, { height: 1, marginTop: 1, children: /* @__PURE__ */ jsxs61(Text, { color: theme.textMuted, children: [
30893
+ '(no models match "',
30894
+ query,
30895
+ '")'
30896
+ ] }) }) : /* @__PURE__ */ jsx62(Box, { flexDirection: "column", marginTop: 1, children: visible.map((m, i) => {
30540
30897
  const idx = windowStart + i;
30541
30898
  const selected = idx === cursor;
30542
30899
  const label = m.label && m.label !== m.model ? ` ${m.label}` : "";
@@ -30546,7 +30903,11 @@ function RefineFailurePanel({
30546
30903
  /* @__PURE__ */ jsx62(Text, { color: theme.textMuted, children: label })
30547
30904
  ] }, `${m.providerId}/${m.model}`);
30548
30905
  }) }),
30549
- /* @__PURE__ */ jsx62(Box, { height: 1, marginTop: 1, children: /* @__PURE__ */ jsx62(Text, { color: theme.textMuted, children: "\u2191/\u2193 choose \xB7 Enter refine on it \xB7 Esc back" }) })
30906
+ /* @__PURE__ */ jsx62(Box, { height: 1, marginTop: 1, children: /* @__PURE__ */ jsxs61(Text, { color: theme.textMuted, children: [
30907
+ "\u2191/\u2193 choose \xB7 Enter refine \xB7 ",
30908
+ query ? "Esc clear \xB7 " : "",
30909
+ "Esc back"
30910
+ ] }) })
30550
30911
  ]
30551
30912
  }
30552
30913
  );
@@ -30712,7 +31073,7 @@ import {
30712
31073
  useCallback as useCallback25,
30713
31074
  useEffect as useEffect67,
30714
31075
  useLayoutEffect as useLayoutEffect2,
30715
- useMemo as useMemo13,
31076
+ useMemo as useMemo14,
30716
31077
  useRef as useRef37,
30717
31078
  useState as useState40
30718
31079
  } from "react";
@@ -31153,7 +31514,7 @@ import { Fragment as Fragment21, jsx as jsx65, jsxs as jsxs63 } from "react/jsx-
31153
31514
  var MAX_TOOL_GROUP_ENTRIES = 12;
31154
31515
  var STRUCTURED_DIFF_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "replace", "diff", "patch"]);
31155
31516
  function hasInjectedMemory(entry) {
31156
- return extractSageBlock(entry.output ?? "").sageLines.length > 0;
31517
+ return resolveEntrySage(entry.output, entry.sageLines).sageLines.length > 0;
31157
31518
  }
31158
31519
  function renderGroupId(group) {
31159
31520
  return group.type === "tool-group" ? group.data.entries[0]?.id ?? 0 : group.entry.id;
@@ -31163,30 +31524,66 @@ function estimateTextRows(text, contentWidth, maxRows) {
31163
31524
  if (!text) return 1;
31164
31525
  const width = Math.max(16, contentWidth);
31165
31526
  let rows = 0;
31166
- let lineStart = 0;
31167
- for (let index = 0; index <= text.length; index++) {
31168
- if (index < text.length && text.charCodeAt(index) !== 10) continue;
31169
- rows += Math.max(1, Math.ceil((index - lineStart) / width));
31170
- if (rows >= maxRows) return maxRows;
31171
- lineStart = index + 1;
31527
+ let lineWidth = 0;
31528
+ for (const char of text) {
31529
+ if (char === "\n") {
31530
+ rows += Math.max(1, Math.ceil(lineWidth / width));
31531
+ if (rows >= maxRows) return maxRows;
31532
+ lineWidth = 0;
31533
+ continue;
31534
+ }
31535
+ lineWidth += displayWidth(char);
31172
31536
  }
31173
- return Math.max(1, rows);
31537
+ rows += Math.max(1, Math.ceil(lineWidth / width));
31538
+ return Math.min(maxRows, Math.max(1, rows));
31539
+ }
31540
+ function estimateSageKvRows(cells, contentWidth) {
31541
+ if (cells.length === 0) return 0;
31542
+ const gap = 2;
31543
+ let rows = 1;
31544
+ let usedColumns = 0;
31545
+ for (const cell of cells) {
31546
+ const cellWidth = displayWidth(cell) + gap;
31547
+ if (usedColumns > 0 && usedColumns + cellWidth > contentWidth) {
31548
+ rows += 1;
31549
+ usedColumns = cellWidth;
31550
+ } else {
31551
+ usedColumns += cellWidth;
31552
+ }
31553
+ }
31554
+ return rows;
31174
31555
  }
31175
31556
  function estimateRenderGroupRows(group, contentWidth) {
31176
31557
  if (group.type === "tool-group") return group.data.entries.length + 2;
31177
31558
  const { entry } = group;
31178
31559
  if (entry.kind === "tool") {
31179
- const { cleanOutput, sageLines } = extractSageBlock(entry.output ?? "");
31560
+ const { cleanOutput, sageLines } = resolveEntrySage(entry.output, entry.sageLines);
31180
31561
  const memoryLines = sageLines.slice(1);
31181
31562
  const panelContentWidth = Math.max(16, contentWidth - 4);
31182
31563
  const sagePanelRows = memoryLines.length > 0 ? 2 + estimateTextRows(
31183
- `\u{1F9E0} SAGE MEMORY INJECTED \xB7 ${entry.name} ${memoryLines.length} ${memoryLines.length === 1 ? "memory" : "memories"}`,
31564
+ `\u{1F9E0} SAGE MEMORY INJECTED \xB7 ${entry.name} ${memoryLines.length} ${memoryLines.length === 1 ? "memory" : "memories"}${entry.sageStats ? ` \xB7 ${entry.sageStats}` : ""}`,
31184
31565
  panelContentWidth,
31185
31566
  MAX_TEXT_ESTIMATE_ROWS
31186
- ) + memoryLines.reduce(
31187
- (rows, line) => rows + estimateTextRows(line, panelContentWidth, MAX_TEXT_ESTIMATE_ROWS),
31188
- 0
31189
- ) : 0;
31567
+ ) + memoryLines.reduce((rows, line) => {
31568
+ const parsed = parseSageMemoryLine(line);
31569
+ if (!parsed) {
31570
+ return rows + estimateTextRows(line, panelContentWidth, MAX_TEXT_ESTIMATE_ROWS);
31571
+ }
31572
+ const labelRow = estimateTextRows(
31573
+ parsed.labels.length > 0 ? parsed.labels.map((l) => `[${l}]`).join("") : "[memory]",
31574
+ panelContentWidth,
31575
+ MAX_TEXT_ESTIMATE_ROWS
31576
+ );
31577
+ const bodyRow = estimateTextRows(parsed.text, panelContentWidth, MAX_TEXT_ESTIMATE_ROWS);
31578
+ const kvCells = [];
31579
+ kvCells.push(`id: ${parsed.id}`);
31580
+ if (parsed.anchor) kvCells.push(`anchor: ${parsed.anchor}`);
31581
+ if (parsed.relation) kvCells.push(`relation: ${parsed.relation}`);
31582
+ if (parsed.tags && parsed.tags.length > 0) kvCells.push(`tags: ${parsed.tags.join(", ")}`);
31583
+ const kvRows = estimateSageKvRows(kvCells, panelContentWidth);
31584
+ const gap = rows > 0 ? 1 : 0;
31585
+ return rows + labelRow + bodyRow + kvRows + gap;
31586
+ }, 0) : 0;
31190
31587
  if (entry.resultRenderMode === "simple") return 3 + sagePanelRows;
31191
31588
  if (STRUCTURED_DIFF_TOOLS.has(entry.name)) {
31192
31589
  const previewCap = entry.name === "replace" || entry.name === "diff" || entry.name === "patch" ? MULTI_DIFF_MAX_ROWS : DIFF_MAX_LINES;
@@ -31371,7 +31768,7 @@ import { memo as memo2, useEffect as useEffect66, useRef as useRef36, useState a
31371
31768
  // src/components/history/entry.tsx
31372
31769
  import { hasOpenTodos } from "@wrongstack/core/utils";
31373
31770
  import { parseNextSteps } from "@wrongstack/tools/next-steps";
31374
- import React8, { useEffect as useEffect65, useMemo as useMemo12 } from "react";
31771
+ import React8, { useEffect as useEffect65, useMemo as useMemo13 } from "react";
31375
31772
 
31376
31773
  // src/markdown-table.ts
31377
31774
  import { expectDefined as expectDefined4 } from "@wrongstack/core/utils";
@@ -32359,8 +32756,28 @@ function ToolCard({
32359
32756
  ] });
32360
32757
  }
32361
32758
 
32759
+ // src/history-entry.ts
32760
+ import {
32761
+ DEFAULT_MIN_IMPORTANCE as DEFAULT_MIN_IMPORTANCE2,
32762
+ DEFAULT_MIN_SCORE as DEFAULT_MIN_SCORE2,
32763
+ MIN_RELATION_STRENGTH as MIN_RELATION_STRENGTH2
32764
+ } from "@wrongstack/sage";
32765
+ var MEMORY_GATE_DEFAULTS = {
32766
+ minScore: DEFAULT_MIN_SCORE2,
32767
+ minImportance: DEFAULT_MIN_IMPORTANCE2,
32768
+ relationFloor: MIN_RELATION_STRENGTH2
32769
+ };
32770
+
32362
32771
  // src/components/history/entry.tsx
32363
32772
  import { Fragment as Fragment24, jsx as jsx70, jsxs as jsxs68 } from "react/jsx-runtime";
32773
+ var MAX_MEMORY_PROOF_ROWS = 4;
32774
+ function formatScoreTerms(terms) {
32775
+ if (terms.length === 0) return "no score breakdown (emitted by an older core build)";
32776
+ return terms.map((term) => {
32777
+ const magnitude = Math.abs(term.value).toFixed(2);
32778
+ return `${term.label} ${term.value < 0 ? "\u2212" : "+"}${magnitude}`;
32779
+ }).join(" \u2502 ");
32780
+ }
32364
32781
  function brainStatusStyle(status) {
32365
32782
  switch (status) {
32366
32783
  case "thinking":
@@ -32428,7 +32845,7 @@ var Entry = React8.memo(function Entry2({
32428
32845
  showModelReasoning
32429
32846
  }) {
32430
32847
  const openTodos = hasOpenTodos(todos);
32431
- const nextSteps = useMemo12(() => {
32848
+ const nextSteps = useMemo13(() => {
32432
32849
  if (entry.kind !== "assistant") return { steps: [], stripped: "" };
32433
32850
  return parseNextSteps(entry.text, true);
32434
32851
  }, [entry.kind, entry.text]);
@@ -32547,7 +32964,7 @@ var Entry = React8.memo(function Entry2({
32547
32964
  }
32548
32965
  case "tool": {
32549
32966
  const { glyph: glyph2, color } = getToolVisual(entry.name);
32550
- const { argSummary, outLines, sageLines, visualLines, diff, multiDiffs, sizeChip, mutation } = useMemo12(() => {
32967
+ const { argSummary, outLines, sageLines, visualLines, diff, multiDiffs, sizeChip, mutation } = useMemo13(() => {
32551
32968
  const argSummary2 = formatToolArgs(entry.name, entry.input);
32552
32969
  const {
32553
32970
  cleanOutput: outputForFormatting,
@@ -32558,7 +32975,8 @@ var Entry = React8.memo(function Entry2({
32558
32975
  entry.output,
32559
32976
  entry.ok,
32560
32977
  entry.outputBytes,
32561
- entry.outputLines
32978
+ entry.outputLines,
32979
+ entry.sageLines
32562
32980
  );
32563
32981
  const visualLines2 = formatToolVisualOutput(
32564
32982
  entry.name,
@@ -32617,6 +33035,7 @@ var Entry = React8.memo(function Entry2({
32617
33035
  }, [
32618
33036
  entry.name,
32619
33037
  entry.output,
33038
+ entry.sageLines,
32620
33039
  entry.input,
32621
33040
  entry.ok,
32622
33041
  entry.outputBytes,
@@ -32684,7 +33103,14 @@ var Entry = React8.memo(function Entry2({
32684
33103
  ]
32685
33104
  }
32686
33105
  ),
32687
- /* @__PURE__ */ jsx70(SageMemoryBlock, { sageLines, toolName: entry.name })
33106
+ /* @__PURE__ */ jsx70(
33107
+ SageMemoryBlock,
33108
+ {
33109
+ sageLines,
33110
+ toolName: entry.name,
33111
+ stats: entry.sageStats
33112
+ }
33113
+ )
32688
33114
  ] });
32689
33115
  }
32690
33116
  const toolContentWidth = Math.max(20, termWidth - 2);
@@ -32764,14 +33190,16 @@ var Entry = React8.memo(function Entry2({
32764
33190
  ]
32765
33191
  }
32766
33192
  ),
32767
- /* @__PURE__ */ jsx70(SageMemoryBlock, { sageLines, toolName: entry.name })
33193
+ /* @__PURE__ */ jsx70(SageMemoryBlock, { sageLines, toolName: entry.name, stats: entry.sageStats })
32768
33194
  ] });
32769
33195
  }
32770
33196
  case "memory-activation": {
33197
+ if (!entry.error) return null;
32771
33198
  const rejected = Object.entries(entry.rejected).filter(([, count]) => count > 0).map(([reason, count]) => `${reason} ${count}`).join(" \xB7 ");
32772
- const injected = entry.activated.filter((memory) => entry.injectedIds.includes(memory.id));
32773
- const injectedSummary = injected.slice(0, 3).map((memory) => memory.id).join(", ");
32774
- const why = [...new Set(injected.flatMap((memory) => memory.activationReasons))].slice(0, 3).join(" \xB7 ");
33199
+ const injected = entry.outcome === "error" ? entry.activated : entry.activated.filter((memory) => entry.injectedIds.includes(memory.id));
33200
+ const shown = injected.slice(0, MAX_MEMORY_PROOF_ROWS);
33201
+ const overflow = injected.length - shown.length;
33202
+ const thresholds = entry.thresholds ?? MEMORY_GATE_DEFAULTS;
32775
33203
  return /* @__PURE__ */ jsxs68(Box, { flexDirection: "column", borderStyle: "single", borderColor: "magenta", paddingX: 1, children: [
32776
33204
  /* @__PURE__ */ jsxs68(Box, { flexDirection: "row", children: [
32777
33205
  /* @__PURE__ */ jsx70(Text, { bold: true, color: "magenta", children: "\u{1F9E0} MEMORY INJECTOR " }),
@@ -32779,17 +33207,23 @@ var Entry = React8.memo(function Entry2({
32779
33207
  /* @__PURE__ */ jsx70(Text, { dimColor: true, children: ` ${entry.activated.length} activated \u2192 ${entry.injectedIds.length} injected / ${entry.candidates} \xB7 ctx ${Math.round(entry.contextPressure * 100)}% \xB7 +${entry.injectedChars} chars` })
32780
33208
  ] }),
32781
33209
  entry.error ? /* @__PURE__ */ jsx70(Text, { color: "red", children: `error: ${entry.error}` }) : null,
32782
- injectedSummary ? /* @__PURE__ */ jsxs68(Text, { children: [
32783
- /* @__PURE__ */ jsx70(Text, { color: "green", children: "injected " }),
32784
- /* @__PURE__ */ jsx70(
32785
- Text,
32786
- {
32787
- dimColor: true,
32788
- children: `${injectedSummary}${injected.length > 3 ? ` +${injected.length - 3}` : ""}`
32789
- }
32790
- ),
32791
- why ? /* @__PURE__ */ jsx70(Text, { dimColor: true, children: ` \xB7 why: ${why}` }) : null
32792
- ] }) : null,
33210
+ entry.queryPreview ? /* @__PURE__ */ jsx70(Text, { dimColor: true, wrap: "truncate-end", children: `query: ${entry.queryPreview}` }) : null,
33211
+ shown.map((memory) => {
33212
+ const scorePass = memory.score >= thresholds.minScore;
33213
+ const relationPass = memory.relationStrength >= thresholds.relationFloor;
33214
+ const reason = memory.activationReasons[0] ?? "no recorded reason";
33215
+ return /* @__PURE__ */ jsxs68(Box, { flexDirection: "column", children: [
33216
+ /* @__PURE__ */ jsxs68(Text, { wrap: "truncate-end", children: [
33217
+ /* @__PURE__ */ jsx70(Text, { color: "green", children: `${memory.id} ` }),
33218
+ /* @__PURE__ */ jsx70(Text, { color: scorePass ? "green" : "red", children: `${memory.score.toFixed(2)} ${scorePass ? "\u2713" : "\u2717"} ${thresholds.minScore.toFixed(2)}` }),
33219
+ /* @__PURE__ */ jsx70(Text, { dimColor: true, children: " \xB7 relation " }),
33220
+ /* @__PURE__ */ jsx70(Text, { color: relationPass ? "green" : "red", children: `${memory.relationStrength.toFixed(2)} ${relationPass ? "\u2713" : "\u2717"} ${thresholds.relationFloor.toFixed(2)}` }),
33221
+ /* @__PURE__ */ jsx70(Text, { dimColor: true, children: ` \xB7 ${reason}` })
33222
+ ] }),
33223
+ /* @__PURE__ */ jsx70(Text, { dimColor: true, wrap: "truncate-end", children: ` ${formatScoreTerms(memory.scoreTerms ?? [])}` })
33224
+ ] }, memory.id);
33225
+ }),
33226
+ overflow > 0 ? /* @__PURE__ */ jsx70(Text, { dimColor: true, children: ` +${overflow} more injected` }) : null,
32793
33227
  rejected ? /* @__PURE__ */ jsx70(Text, { dimColor: true, children: `filtered: ${rejected}` }) : null
32794
33228
  ] });
32795
33229
  }
@@ -32968,8 +33402,10 @@ var Entry = React8.memo(function Entry2({
32968
33402
  });
32969
33403
  function SageMemoryBlock({
32970
33404
  sageLines,
32971
- toolName
33405
+ toolName,
33406
+ stats
32972
33407
  }) {
33408
+ if (!sageLines || sageLines.length < 2) return null;
32973
33409
  const memoryLines = sageLines.slice(1);
32974
33410
  if (memoryLines.length === 0) return null;
32975
33411
  return /* @__PURE__ */ jsxs68(
@@ -32984,19 +33420,42 @@ function SageMemoryBlock({
32984
33420
  children: [
32985
33421
  /* @__PURE__ */ jsxs68(Box, { flexDirection: "row", children: [
32986
33422
  /* @__PURE__ */ jsx70(Text, { bold: true, color: theme.accent, children: `\u{1F9E0} SAGE MEMORY INJECTED \xB7 ${toolName} ` }),
32987
- /* @__PURE__ */ jsx70(
32988
- Text,
32989
- {
32990
- dimColor: true,
32991
- children: `${memoryLines.length} ${memoryLines.length === 1 ? "memory" : "memories"}`
32992
- }
32993
- )
33423
+ /* @__PURE__ */ jsx70(Text, { dimColor: true, children: `${memoryLines.length} ${memoryLines.length === 1 ? "memory" : "memories"}${stats ? ` \xB7 ${stats}` : ""}` })
32994
33424
  ] }),
32995
- memoryLines.map((line, i) => /* @__PURE__ */ jsx70(Text, { color: theme.accent, dimColor: true, children: line }, i))
33425
+ memoryLines.map((line, i) => {
33426
+ const parsed = parseSageMemoryLine(line);
33427
+ if (!parsed) {
33428
+ return /* @__PURE__ */ jsx70(Text, { color: theme.accent, dimColor: true, wrap: "truncate-end", children: line }, i);
33429
+ }
33430
+ return /* @__PURE__ */ jsx70(SageMemoryRow, { parsed, index: i }, i);
33431
+ })
32996
33432
  ]
32997
33433
  }
32998
33434
  );
32999
33435
  }
33436
+ function SageMemoryRow({
33437
+ parsed,
33438
+ index
33439
+ }) {
33440
+ const labelText = parsed.labels.length > 0 ? parsed.labels.map((l) => `[${l}]`).join("") : "[memory]";
33441
+ const tagText = parsed.tags && parsed.tags.length > 0 ? parsed.tags.join(", ") : void 0;
33442
+ return /* @__PURE__ */ jsxs68(Box, { flexDirection: "column", marginTop: index > 0 ? 1 : 0, children: [
33443
+ /* @__PURE__ */ jsx70(Text, { bold: true, color: theme.accent, children: labelText }),
33444
+ /* @__PURE__ */ jsx70(Text, { color: theme.textPrimary, children: parsed.text }),
33445
+ /* @__PURE__ */ jsxs68(Box, { flexDirection: "row", flexWrap: "wrap", children: [
33446
+ /* @__PURE__ */ jsx70(SageKv, { label: "id", value: parsed.id }),
33447
+ parsed.anchor ? /* @__PURE__ */ jsx70(SageKv, { label: "anchor", value: parsed.anchor }) : null,
33448
+ parsed.relation ? /* @__PURE__ */ jsx70(SageKv, { label: "relation", value: parsed.relation }) : null,
33449
+ tagText ? /* @__PURE__ */ jsx70(SageKv, { label: "tags", value: tagText }) : null
33450
+ ] })
33451
+ ] });
33452
+ }
33453
+ function SageKv({ label, value }) {
33454
+ return /* @__PURE__ */ jsxs68(Box, { flexDirection: "row", marginRight: 2, children: [
33455
+ /* @__PURE__ */ jsx70(Text, { dimColor: true, children: `${label}: ` }),
33456
+ /* @__PURE__ */ jsx70(Text, { color: theme.textSecondary, children: value })
33457
+ ] });
33458
+ }
33000
33459
 
33001
33460
  // src/components/history/index.tsx
33002
33461
  import { Fragment as Fragment25, jsx as jsx71, jsxs as jsxs69 } from "react/jsx-runtime";
@@ -33243,7 +33702,7 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
33243
33702
  return maxWidth ? Math.min(raw, maxWidth) : raw;
33244
33703
  }, [stdout, maxWidth]);
33245
33704
  const [viewportWidth, setViewportWidth] = useState40(resolveViewportWidth);
33246
- const termWidth = useMemo13(
33705
+ const termWidth = useMemo14(
33247
33706
  () => Math.max(1, viewportWidth - SCROLLBAR_HIT_WIDTH),
33248
33707
  [viewportWidth]
33249
33708
  );
@@ -33258,8 +33717,8 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
33258
33717
  }, [stdout, resolveViewportWidth]);
33259
33718
  const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
33260
33719
  const toolTailHeight = toolTail && toolStream ? toolStreamBoxHeight(toolStream.name) : 0;
33261
- const groupedEntries = useMemo13(() => groupEntries(entries), [entries]);
33262
- const groupIds = useMemo13(() => groupedEntries.map(renderGroupId), [groupedEntries]);
33720
+ const groupedEntries = useMemo14(() => groupEntries(entries), [entries]);
33721
+ const groupIds = useMemo14(() => groupedEntries.map(renderGroupId), [groupedEntries]);
33263
33722
  const groupIdsKey = groupIds.join(",");
33264
33723
  const heightCacheRef = useRef37(null);
33265
33724
  if (heightCacheRef.current === null) heightCacheRef.current = new EntryHeightCache();
@@ -33351,7 +33810,7 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
33351
33810
  viewportRows: vp,
33352
33811
  tailRows: toolTailHeight
33353
33812
  };
33354
- const groupIndexById = useMemo13(() => {
33813
+ const groupIndexById = useMemo14(() => {
33355
33814
  const indexes = /* @__PURE__ */ new Map();
33356
33815
  groupIds.forEach((id, index) => {
33357
33816
  indexes.set(id, index);
@@ -33383,7 +33842,7 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
33383
33842
  setAnchor(normalized && nextId !== void 0 ? { id: nextId, clip: normalized.clip } : null);
33384
33843
  setMountBump(0);
33385
33844
  }, []);
33386
- const controller = useMemo13(
33845
+ const controller = useMemo14(
33387
33846
  () => ({
33388
33847
  scrollBy: (deltaUp) => {
33389
33848
  applyAnchor(scrollAnchorBy(geometryRef.current, effectiveAnchorRef.current, deltaUp));
@@ -34029,22 +34488,9 @@ function AppView({ host, runtime }) {
34029
34488
  enhanceDelayMs,
34030
34489
  layoutStore
34031
34490
  } = runtime;
34032
- const {
34033
- nowTick,
34034
- workingTimeMs,
34035
- enhanceDots
34036
- } = activity;
34037
- const {
34038
- setYoloLive,
34039
- autonomyLive
34040
- } = environment;
34041
- const {
34042
- inputHint,
34043
- composerStatus,
34044
- composerAnimationStyle,
34045
- inputHeight,
34046
- hideInput
34047
- } = viewState;
34491
+ const { nowTick, workingTimeMs, enhanceDots } = activity;
34492
+ const { setYoloLive, autonomyLive } = environment;
34493
+ const { inputHint, composerStatus, composerAnimationStyle, inputHeight, hideInput } = viewState;
34048
34494
  return (
34049
34495
  /* Hard viewport cap. The managed layout aims for exactly termRows
34050
34496
  (history vp + measured bottom region), but the bottom region grows a
@@ -34230,6 +34676,7 @@ function AppView({ host, runtime }) {
34230
34676
  breakerAutoKillResetMs: state.settingsPicker.breakerAutoKillResetMs,
34231
34677
  showModelReasoning: state.settingsPicker.showModelReasoning,
34232
34678
  showAgentSwarmPanel: state.settingsPicker.showAgentSwarmPanel,
34679
+ readSymbols: state.settingsPicker.readSymbols,
34233
34680
  filter: state.settingsPicker.filter,
34234
34681
  hint: state.settingsPicker.hint
34235
34682
  }
@@ -35827,7 +36274,10 @@ function routeBusyInterruptKey(host, key) {
35827
36274
  const fleetTag = subagentsTerminated > 0 ? ` \xB7 stopped ${subagentsTerminated} subagent${subagentsTerminated === 1 ? "" : "s"}` : "";
35828
36275
  dispatch({
35829
36276
  type: "addEntry",
35830
- entry: { kind: "warn", text: `\u21AF Interrupted${droppedTag}${fleetTag}. Type your new direction.` }
36277
+ entry: {
36278
+ kind: "warn",
36279
+ text: `\u21AF Interrupted${droppedTag}${fleetTag}. Type your new direction.`
36280
+ }
35831
36281
  });
35832
36282
  return true;
35833
36283
  }
@@ -35949,7 +36399,8 @@ function routeSettingsOverlayKey(host, input, key, isEnter) {
35949
36399
  breakerEnabled: config.breakerEnabled ?? false,
35950
36400
  breakerAutoKillResetMs: config.breakerAutoKillResetMs ?? 6e4,
35951
36401
  showModelReasoning: config.showModelReasoning ?? true,
35952
- showAgentSwarmPanel: config.showAgentSwarmPanel ?? true
36402
+ showAgentSwarmPanel: config.showAgentSwarmPanel ?? true,
36403
+ readSymbols: config.readSymbols ?? false
35953
36404
  });
35954
36405
  return true;
35955
36406
  }