@wrongstack/tui 0.296.2 → 0.296.3

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 (44) 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/history-entry.d.ts +50 -0
  23. package/dist/history-entry.d.ts.map +1 -1
  24. package/dist/hooks/use-git-session-status.d.ts.map +1 -1
  25. package/dist/hooks/use-panel-controllers.d.ts +1 -1
  26. package/dist/hooks/use-panel-controllers.d.ts.map +1 -1
  27. package/dist/hooks/use-provider-event-bridge.d.ts.map +1 -1
  28. package/dist/hooks/use-settings-auto-save.d.ts +1 -1
  29. package/dist/hooks/use-settings-auto-save.d.ts.map +1 -1
  30. package/dist/hooks/use-statusbar-view-model.d.ts.map +1 -1
  31. package/dist/hooks/use-subagent-events.d.ts.map +1 -1
  32. package/dist/index.js +1503 -1069
  33. package/dist/index.js.map +4 -4
  34. package/dist/layout-engine.d.ts +4 -0
  35. package/dist/layout-engine.d.ts.map +1 -1
  36. package/dist/memory-context-monitor.d.ts +6 -0
  37. package/dist/memory-context-monitor.d.ts.map +1 -1
  38. package/dist/overlay-key-router.d.ts +1 -1
  39. package/dist/overlay-key-router.d.ts.map +1 -1
  40. package/dist/reducers/settings-panel.d.ts.map +1 -1
  41. package/dist/reducers/settings-values.d.ts.map +1 -1
  42. package/dist/settings-contracts.d.ts +5 -0
  43. package/dist/settings-contracts.d.ts.map +1 -1
  44. 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 || !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,381 @@ 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 { checkCodebaseIndexServerHealth, getIndexState as getIndexState2 } from "@wrongstack/tools";
29096
+ async function collectConnectionsHealth(projectRoot) {
29097
+ const services = await Promise.all([
29098
+ chronicleHealth(projectRoot),
29099
+ codebaseIndexHealth(projectRoot),
29100
+ sageHealth(projectRoot),
29101
+ kanbanHealth(projectRoot),
29102
+ mailboxHealth(projectRoot)
29103
+ ]);
29104
+ const required = services.filter((s2) => s2.required);
29105
+ const overall = required.some((s2) => s2.status === "error") ? "error" : required.some((s2) => s2.status !== "healthy") ? "degraded" : "healthy";
29106
+ return { checkedAt: Date.now(), overall, services };
29107
+ }
29108
+ async function chronicleHealth(projectRoot) {
29109
+ const startedAt = Date.now();
29110
+ let access;
29111
+ try {
29112
+ access = createChronicleProjectAccess({ projectRoot });
29113
+ const health2 = await access.call("ping", {}, { timeoutMs: 5e3 });
29114
+ const quarantined = health2.quarantinedFamilies ?? [];
29115
+ return {
29116
+ id: "chronicle",
29117
+ label: "Chronicle telemetry",
29118
+ status: quarantined.length > 0 ? "degraded" : access.mode === "server" ? "healthy" : "degraded",
29119
+ required: true,
29120
+ mode: access.mode,
29121
+ 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.",
29122
+ ownerPid: health2.pid,
29123
+ endpoint: health2.endpoint,
29124
+ storage: health2.chronicleDirectory,
29125
+ uptimeMs: health2.uptimeMs,
29126
+ latencyMs: Date.now() - startedAt,
29127
+ clients: health2.clients,
29128
+ activeRequests: health2.activeRequests,
29129
+ queuedWork: health2.journal.pendingEvents,
29130
+ watcher: { active: health2.watcher.active, watchedFiles: health2.watcher.watchedFiles },
29131
+ ...quarantined[0] ? { lastError: quarantined[0].reason } : health2.watcher.lastError ? { lastError: health2.watcher.lastError } : {}
29132
+ };
29133
+ } catch (error) {
29134
+ return failureService("chronicle", "Chronicle telemetry", true, access?.mode ?? "unavailable", error, Date.now() - startedAt);
29135
+ } finally {
29136
+ await access?.close();
29137
+ }
29138
+ }
29139
+ async function codebaseIndexHealth(projectRoot) {
29140
+ const startedAt = Date.now();
29141
+ try {
29142
+ const health2 = await checkCodebaseIndexServerHealth(projectRoot, void 0, { timeoutMs: 2e3 });
29143
+ const connection = getIndexState2().server;
29144
+ return {
29145
+ id: "codebase-index",
29146
+ label: "Codebase index",
29147
+ status: health2.status === "unresponsive" ? "error" : health2.status,
29148
+ required: false,
29149
+ mode: "project-server",
29150
+ detail: health2.server?.activity.indexing ? `Indexing ${health2.server.activity.currentFile}/${health2.server.activity.totalFiles}.` : "Shared symbol index is ready for project queries.",
29151
+ ownerPid: connection.pid,
29152
+ endpoint: connection.endpoint,
29153
+ storage: connection.indexDir ?? resolveWstackPaths5({ projectRoot }).projectCodebaseIndex,
29154
+ uptimeMs: health2.server?.uptimeMs,
29155
+ latencyMs: health2.latencyMs ?? Date.now() - startedAt,
29156
+ clients: health2.server?.clients,
29157
+ activeRequests: health2.server?.activeRequests,
29158
+ queuedWork: health2.server?.queuedWrites,
29159
+ watcher: health2.server ? { active: health2.server.watchingExternal, watchedFiles: health2.server.watchingClients } : void 0
29160
+ };
29161
+ } catch (error) {
29162
+ if (isOfflineConnectionError(error)) {
29163
+ return {
29164
+ ...failureService("codebase-index", "Codebase index", false, "on-demand project-server", error, Date.now() - startedAt),
29165
+ status: "offline",
29166
+ detail: "Not running for this project; it starts on demand when indexing is used."
29167
+ };
29168
+ }
29169
+ return failureService("codebase-index", "Codebase index", false, "project-server", error, Date.now() - startedAt);
29170
+ }
29171
+ }
29172
+ async function sageHealth(projectRoot) {
29173
+ const startedAt = Date.now();
29174
+ if (!isSageProjectServerAvailable()) {
29175
+ return {
29176
+ id: "sage",
29177
+ label: "SAGE memory",
29178
+ status: "unavailable",
29179
+ required: false,
29180
+ mode: "unavailable",
29181
+ detail: "The packaged SAGE project server is unavailable in this runtime."
29182
+ };
29183
+ }
29184
+ const connection = new SageProjectServerConnection(projectRoot);
29185
+ try {
29186
+ const status = await connection.status();
29187
+ if (!status) {
29188
+ return {
29189
+ id: "sage",
29190
+ label: "SAGE memory",
29191
+ status: "offline",
29192
+ required: false,
29193
+ mode: "on-demand project-server",
29194
+ detail: "Not running for this project; it starts on demand when memory is used.",
29195
+ endpoint: connection.getState().endpoint,
29196
+ latencyMs: Date.now() - startedAt
29197
+ };
29198
+ }
29199
+ const healthStatus = status.health.status === "ready" ? "healthy" : status.health.status === "degraded" ? "degraded" : "error";
29200
+ return {
29201
+ id: "sage",
29202
+ label: "SAGE memory",
29203
+ status: healthStatus,
29204
+ required: false,
29205
+ mode: "project-server",
29206
+ detail: `Persistent project memory backend: ${status.health.backend}.`,
29207
+ ownerPid: status.pid,
29208
+ endpoint: status.endpoint,
29209
+ storage: status.storageRoot,
29210
+ latencyMs: Date.now() - startedAt,
29211
+ clients: status.clients,
29212
+ activeRequests: status.pendingRequests
29213
+ };
29214
+ } catch (error) {
29215
+ return failureService("sage", "SAGE memory", false, "project-server", error, Date.now() - startedAt);
29216
+ } finally {
29217
+ connection.close();
29218
+ }
29219
+ }
29220
+ async function kanbanHealth(projectRoot) {
29221
+ const startedAt = Date.now();
29222
+ if (process.env["WRONGSTACK_KANBAN_SERVER"] === "0") {
29223
+ return {
29224
+ id: "kanban",
29225
+ label: "Kanban IPC",
29226
+ status: "unavailable",
29227
+ required: false,
29228
+ mode: "disabled",
29229
+ detail: "Kanban IPC daemon is disabled via WRONGSTACK_KANBAN_SERVER=0."
29230
+ };
29231
+ }
29232
+ let connection;
29233
+ try {
29234
+ connection = await getKanbanServerConnection(projectRoot);
29235
+ } catch (error) {
29236
+ return failureService("kanban", "Kanban IPC", false, "project-server", error, Date.now() - startedAt);
29237
+ }
29238
+ if (!connection) {
29239
+ return {
29240
+ id: "kanban",
29241
+ label: "Kanban IPC",
29242
+ status: "unavailable",
29243
+ required: false,
29244
+ mode: "disabled",
29245
+ detail: "Kanban IPC daemon is disabled in this runtime."
29246
+ };
29247
+ }
29248
+ try {
29249
+ const status = await connection.request("ping", {}, { timeoutMs: 5e3 });
29250
+ return {
29251
+ id: "kanban",
29252
+ label: "Kanban IPC",
29253
+ status: "healthy",
29254
+ required: true,
29255
+ mode: "project-server",
29256
+ detail: `Single shared project-server owns kanban state (v${status.protocolVersion}).`,
29257
+ ownerPid: status.pid,
29258
+ endpoint: status.endpoint,
29259
+ storage: status.databasePath,
29260
+ latencyMs: Date.now() - startedAt,
29261
+ clients: status.clients,
29262
+ activeRequests: status.pendingRequests,
29263
+ uptimeMs: Date.now() - new Date(status.startedAt).getTime()
29264
+ };
29265
+ } catch (error) {
29266
+ return failureService("kanban", "Kanban IPC", true, "project-server", error, Date.now() - startedAt);
29267
+ }
29268
+ }
29269
+ async function mailboxHealth(projectRoot) {
29270
+ const startedAt = Date.now();
29271
+ if (!isMailboxProjectServerAvailable()) {
29272
+ return {
29273
+ id: "mailbox",
29274
+ label: "Mailbox IPC",
29275
+ status: "unavailable",
29276
+ required: false,
29277
+ mode: "unavailable",
29278
+ detail: "The packaged mailbox project server is unavailable in this runtime."
29279
+ };
29280
+ }
29281
+ const connection = new MailboxProjectServerConnection(
29282
+ resolveWstackPaths5({ projectRoot }).projectDir
29283
+ );
29284
+ try {
29285
+ const status = await connection.probeStatus();
29286
+ if (!status) {
29287
+ return {
29288
+ id: "mailbox",
29289
+ label: "Mailbox IPC",
29290
+ status: "offline",
29291
+ required: false,
29292
+ mode: "on-demand project-server",
29293
+ detail: "Not running for this project; it starts on demand when mailbox is used.",
29294
+ endpoint: connection.getState().endpoint,
29295
+ latencyMs: Date.now() - startedAt
29296
+ };
29297
+ }
29298
+ return {
29299
+ id: "mailbox",
29300
+ label: "Mailbox IPC",
29301
+ status: "healthy",
29302
+ required: true,
29303
+ mode: "project-server",
29304
+ detail: `Single shared project-server owns inter-agent mailbox state (v${status.protocolVersion}).`,
29305
+ ownerPid: status.pid,
29306
+ endpoint: status.endpoint,
29307
+ storage: status.databasePath,
29308
+ latencyMs: Date.now() - startedAt,
29309
+ clients: status.clients,
29310
+ activeRequests: status.pendingRequests,
29311
+ uptimeMs: Date.now() - new Date(status.startedAt).getTime()
29312
+ };
29313
+ } catch (error) {
29314
+ return failureService("mailbox", "Mailbox IPC", true, "project-server", error, Date.now() - startedAt);
29315
+ } finally {
29316
+ connection.close();
29317
+ }
29318
+ }
29319
+ function isOfflineConnectionError(error) {
29320
+ const message = error instanceof Error ? error.message : String(error);
29321
+ return /(?:ENOENT|ECONNREFUSED|not found|not running|unavailable|connect failed)/iu.test(message);
29322
+ }
29323
+ function failureService(id, label, required, mode, error, latencyMs) {
29324
+ const message = error instanceof Error ? error.message : String(error);
29325
+ return {
29326
+ id,
29327
+ label,
29328
+ status: "error",
29329
+ required,
29330
+ mode,
29331
+ detail: message,
29332
+ lastError: message,
29333
+ ...latencyMs === void 0 ? {} : { latencyMs }
29334
+ };
29335
+ }
29336
+
29337
+ // src/components/connections-panel.tsx
29338
+ import { jsx as jsx48, jsxs as jsxs47 } from "react/jsx-runtime";
29339
+ var REFRESH_MS = 8e3;
29340
+ var STATUS_GLYPH = {
29341
+ healthy: "\u25CF",
29342
+ degraded: "\u25D0",
29343
+ offline: "\u25CB",
29344
+ unavailable: "\u2298",
29345
+ error: "\u2717"
29346
+ };
29347
+ var STATUS_COLOR2 = {
29348
+ healthy: "green",
29349
+ degraded: "yellow",
29350
+ offline: "gray",
29351
+ unavailable: "gray",
29352
+ error: "red"
29353
+ };
29354
+ var OVERALL_COLOR = {
29355
+ healthy: "green",
29356
+ degraded: "yellow",
29357
+ error: "red"
29358
+ };
29359
+ function formatUptime(ms) {
29360
+ if (ms < 1e3) return `${ms}ms`;
29361
+ const s2 = Math.floor(ms / 1e3);
29362
+ if (s2 < 60) return `${s2}s`;
29363
+ const m = Math.floor(s2 / 60);
29364
+ if (m < 60) return `${m}m`;
29365
+ const h = Math.floor(m / 60);
29366
+ return h < 24 ? `${h}h` : `${Math.floor(h / 24)}d`;
29367
+ }
29368
+ function formatLatency(ms) {
29369
+ if (ms === void 0) return null;
29370
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
29371
+ return `${(ms / 1e3).toFixed(1)}s`;
29372
+ }
29373
+ function ServiceRow({ service }) {
29374
+ const glyph2 = STATUS_GLYPH[service.status];
29375
+ const color = STATUS_COLOR2[service.status];
29376
+ const latency = formatLatency(service.latencyMs);
29377
+ const metaParts = [];
29378
+ if (service.ownerPid !== void 0) metaParts.push(`PID ${service.ownerPid}`);
29379
+ if (service.clients !== void 0) metaParts.push(`${service.clients} client${service.clients === 1 ? "" : "s"}`);
29380
+ if (service.activeRequests !== void 0 && service.activeRequests > 0) {
29381
+ metaParts.push(`${service.activeRequests} active`);
29382
+ }
29383
+ if (service.uptimeMs !== void 0) metaParts.push(`up ${formatUptime(service.uptimeMs)}`);
29384
+ if (latency) metaParts.push(`${latency}`);
29385
+ if (service.queuedWork !== void 0 && service.queuedWork > 0) {
29386
+ metaParts.push(`${service.queuedWork} queued`);
29387
+ }
29388
+ return /* @__PURE__ */ jsxs47(Box, { flexDirection: "column", marginTop: 0, children: [
29389
+ /* @__PURE__ */ jsxs47(Box, { gap: 1, children: [
29390
+ /* @__PURE__ */ jsx48(Text, { color, children: glyph2 }),
29391
+ /* @__PURE__ */ jsx48(Text, { bold: true, children: service.label }),
29392
+ service.required ? /* @__PURE__ */ jsx48(Text, { dimColor: true, children: "required" }) : null,
29393
+ /* @__PURE__ */ jsxs47(Text, { dimColor: true, children: [
29394
+ "\xB7 ",
29395
+ service.mode
29396
+ ] })
29397
+ ] }),
29398
+ /* @__PURE__ */ jsx48(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx48(Text, { dimColor: true, children: service.detail }) }),
29399
+ metaParts.length > 0 ? /* @__PURE__ */ jsx48(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx48(Text, { dimColor: true, children: metaParts.join(" \xB7 ") }) }) : null
29400
+ ] });
29401
+ }
29402
+ function ConnectionsPanel({ projectRoot, onClose }) {
29403
+ const [report, setReport] = useState34(null);
29404
+ const [loading, setLoading] = useState34(true);
29405
+ const [error, setError] = useState34(null);
29406
+ const cancelledRef = useRef33(false);
29407
+ const fetch = useCallback24(async () => {
29408
+ try {
29409
+ const r = await collectConnectionsHealth(projectRoot);
29410
+ if (cancelledRef.current) return;
29411
+ setReport(r);
29412
+ setError(null);
29413
+ } catch (e) {
29414
+ if (cancelledRef.current) return;
29415
+ setError(e instanceof Error ? e.message : String(e));
29416
+ } finally {
29417
+ if (!cancelledRef.current) setLoading(false);
29418
+ }
29419
+ }, [projectRoot]);
29420
+ useEffect61(() => {
29421
+ cancelledRef.current = false;
29422
+ void fetch();
29423
+ const timer = setInterval(() => void fetch(), REFRESH_MS);
29424
+ return () => {
29425
+ cancelledRef.current = true;
29426
+ clearInterval(timer);
29427
+ };
29428
+ }, [fetch]);
29429
+ useInput((_input, key) => {
29430
+ if (key.escape || _input === "q") onClose();
29431
+ if (_input === "r") void fetch();
29432
+ });
29433
+ const overallLabel = report ? report.overall === "healthy" ? "All required services healthy" : report.overall === "degraded" ? "Some services degraded" : "Service errors detected" : "Checking\u2026";
29434
+ return /* @__PURE__ */ jsxs47(Box, { flexDirection: "column", borderStyle: "round", borderColor: report ? OVERALL_COLOR[report.overall] : "gray", paddingX: 1, marginY: 0, flexShrink: 0, children: [
29435
+ /* @__PURE__ */ jsxs47(Box, { justifyContent: "space-between", children: [
29436
+ /* @__PURE__ */ jsxs47(Box, { gap: 1, children: [
29437
+ /* @__PURE__ */ jsx48(Text, { bold: true, children: "Service Connections" }),
29438
+ report ? /* @__PURE__ */ jsxs47(Text, { color: OVERALL_COLOR[report.overall], children: [
29439
+ "\u25CF ",
29440
+ overallLabel
29441
+ ] }) : null
29442
+ ] }),
29443
+ /* @__PURE__ */ jsx48(Text, { dimColor: true, children: "Esc/q close \xB7 r refresh" })
29444
+ ] }),
29445
+ loading && !report ? /* @__PURE__ */ jsx48(Box, { paddingY: 1, children: /* @__PURE__ */ jsx48(Text, { dimColor: true, children: "Checking service health\u2026" }) }) : null,
29446
+ error ? /* @__PURE__ */ jsx48(Box, { paddingY: 1, children: /* @__PURE__ */ jsxs47(Text, { color: "red", children: [
29447
+ "Error: ",
29448
+ error
29449
+ ] }) }) : null,
29450
+ report ? /* @__PURE__ */ jsx48(Box, { flexDirection: "column", marginTop: 0, children: report.services.map((s2) => /* @__PURE__ */ jsx48(ServiceRow, { service: s2 }, s2.id)) }) : null,
29451
+ report ? /* @__PURE__ */ jsx48(Box, { marginTop: 0, children: /* @__PURE__ */ jsxs47(Text, { dimColor: true, children: [
29452
+ "Last checked ",
29453
+ new Date(report.checkedAt).toLocaleTimeString()
29454
+ ] }) }) : null
29455
+ ] });
29456
+ }
29457
+
29151
29458
  // src/components/continue-confirm-panel.tsx
29152
29459
  import React7 from "react";
29153
29460
  import { jsx as jsx49, jsxs as jsxs48 } from "react/jsx-runtime";
@@ -29858,101 +30165,23 @@ function ExitConfirmPanel({
29858
30165
  ] });
29859
30166
  }
29860
30167
 
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
30168
  // src/components/file-picker.tsx
29940
- import { jsx as jsx56, jsxs as jsxs55 } from "react/jsx-runtime";
30169
+ import { jsx as jsx55, jsxs as jsxs54 } from "react/jsx-runtime";
29941
30170
  function FilePicker({ query, matches, selected }) {
29942
30171
  if (matches.length === 0) {
29943
- return /* @__PURE__ */ jsx56(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: /* @__PURE__ */ jsxs55(Text, { dimColor: true, children: [
30172
+ return /* @__PURE__ */ jsx55(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: /* @__PURE__ */ jsxs54(Text, { dimColor: true, children: [
29944
30173
  "@",
29945
30174
  query,
29946
30175
  " \u2014 no matches"
29947
30176
  ] }) });
29948
30177
  }
29949
- return /* @__PURE__ */ jsxs55(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
29950
- /* @__PURE__ */ jsxs55(Text, { dimColor: true, children: [
30178
+ return /* @__PURE__ */ jsxs54(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
30179
+ /* @__PURE__ */ jsxs54(Text, { dimColor: true, children: [
29951
30180
  "@",
29952
30181
  query || "\u2026",
29953
30182
  " \u2014 \u2191/\u2193 select, Enter attach, Esc cancel"
29954
30183
  ] }),
29955
- matches.map((m, i) => /* @__PURE__ */ jsxs55(Text, { inverse: i === selected, ...i === selected ? { color: "cyan" } : {}, children: [
30184
+ matches.map((m, i) => /* @__PURE__ */ jsxs54(Text, { inverse: i === selected, ...i === selected ? { color: "cyan" } : {}, children: [
29956
30185
  i === selected ? "\u203A " : " ",
29957
30186
  highlight(m, query)
29958
30187
  ] }, m))
@@ -29963,7 +30192,7 @@ function highlight(path11, _query) {
29963
30192
  }
29964
30193
 
29965
30194
  // src/components/help-panel.tsx
29966
- import { Fragment as Fragment18, jsx as jsx57, jsxs as jsxs56 } from "react/jsx-runtime";
30195
+ import { Fragment as Fragment18, jsx as jsx56, jsxs as jsxs55 } from "react/jsx-runtime";
29967
30196
  var CATEGORY_ORDER2 = ["Run", "Session", "Inspect", "Agent", "Config", "App"];
29968
30197
  var CHROME_ROWS3 = 7;
29969
30198
  function normalizeQuery(raw) {
@@ -30036,29 +30265,29 @@ function HelpPanel({
30036
30265
  const hiddenAbove = start;
30037
30266
  const hiddenBelow = rows.length - end;
30038
30267
  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: [
30268
+ return /* @__PURE__ */ jsxs55(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
30269
+ /* @__PURE__ */ jsx56(Text, { bold: true, color: "cyan", children: "\u2501\u2501 Help / Command Palette \u2501\u2501" }),
30270
+ /* @__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" }),
30271
+ /* @__PURE__ */ jsx56(Box, { marginTop: 1, flexDirection: "column", children: filtered.length === 0 ? /* @__PURE__ */ jsxs55(Text, { dimColor: true, children: [
30043
30272
  'No commands match "',
30044
30273
  filter,
30045
30274
  '".'
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: [
30275
+ ] }) : /* @__PURE__ */ jsxs55(Fragment18, { children: [
30276
+ hiddenAbove > 0 && !hasFilter ? /* @__PURE__ */ jsx56(Text, { dimColor: true, children: ` \u2191 ${hiddenAbove} more` }) : null,
30277
+ contextHeader && !hasFilter ? /* @__PURE__ */ jsxs55(Text, { bold: true, color: "yellow", dimColor: true, children: [
30049
30278
  " ",
30050
30279
  contextHeader
30051
30280
  ] }) : null,
30052
30281
  visible.map((row) => {
30053
30282
  if (row.type === "header") {
30054
- return /* @__PURE__ */ jsxs56(Text, { bold: true, color: "yellow", dimColor: true, children: [
30283
+ return /* @__PURE__ */ jsxs55(Text, { bold: true, color: "yellow", dimColor: true, children: [
30055
30284
  " ",
30056
30285
  row.category
30057
30286
  ] }, `cat-${row.category}`);
30058
30287
  }
30059
30288
  const { entry, index } = row;
30060
30289
  const isSelected = index === focus;
30061
- return /* @__PURE__ */ jsxs56(
30290
+ return /* @__PURE__ */ jsxs55(
30062
30291
  Text,
30063
30292
  {
30064
30293
  inverse: isSelected,
@@ -30066,20 +30295,20 @@ function HelpPanel({
30066
30295
  wrap: "truncate-end",
30067
30296
  children: [
30068
30297
  isSelected ? "\u203A " : " ",
30069
- /* @__PURE__ */ jsxs56(Text, { bold: true, children: [
30298
+ /* @__PURE__ */ jsxs55(Text, { bold: true, children: [
30070
30299
  "/",
30071
30300
  entry.name
30072
30301
  ] }),
30073
- entry.aliases && entry.aliases.length > 0 ? /* @__PURE__ */ jsxs56(Text, { dimColor: true, children: [
30302
+ entry.aliases && entry.aliases.length > 0 ? /* @__PURE__ */ jsxs55(Text, { dimColor: true, children: [
30074
30303
  " (",
30075
30304
  entry.aliases.map((a) => `/${a}`).join(", "),
30076
30305
  ")"
30077
30306
  ] }) : null,
30078
- /* @__PURE__ */ jsxs56(Text, { dimColor: true, children: [
30307
+ /* @__PURE__ */ jsxs55(Text, { dimColor: true, children: [
30079
30308
  " \u2014 ",
30080
30309
  entry.description
30081
30310
  ] }),
30082
- entry.argsHint ? /* @__PURE__ */ jsxs56(Text, { dimColor: true, children: [
30311
+ entry.argsHint ? /* @__PURE__ */ jsxs55(Text, { dimColor: true, children: [
30083
30312
  " ",
30084
30313
  entry.argsHint
30085
30314
  ] }) : null
@@ -30088,30 +30317,30 @@ function HelpPanel({
30088
30317
  entry.name
30089
30318
  );
30090
30319
  }),
30091
- hiddenBelow > 0 && !hasFilter ? /* @__PURE__ */ jsx57(Text, { dimColor: true, children: ` \u2193 ${hiddenBelow} more` }) : null
30320
+ hiddenBelow > 0 && !hasFilter ? /* @__PURE__ */ jsx56(Text, { dimColor: true, children: ` \u2193 ${hiddenBelow} more` }) : null
30092
30321
  ] }) }),
30093
- hint ? /* @__PURE__ */ jsx57(Box, { marginTop: 1, children: /* @__PURE__ */ jsx57(Text, { dimColor: true, children: hint }) }) : null
30322
+ hint ? /* @__PURE__ */ jsx56(Box, { marginTop: 1, children: /* @__PURE__ */ jsx56(Text, { dimColor: true, children: hint }) }) : null
30094
30323
  ] });
30095
30324
  }
30096
30325
 
30097
30326
  // src/components/mcp-picker.tsx
30098
- import { Fragment as Fragment19, jsx as jsx58, jsxs as jsxs57 } from "react/jsx-runtime";
30327
+ import { Fragment as Fragment19, jsx as jsx57, jsxs as jsxs56 } from "react/jsx-runtime";
30099
30328
  var CHROME_ROWS4 = 17;
30100
30329
  function statusBadge(status, enabled) {
30101
- if (!enabled) return /* @__PURE__ */ jsx58(Text, { dimColor: true, children: "\u25CB disabled" });
30330
+ if (!enabled) return /* @__PURE__ */ jsx57(Text, { dimColor: true, children: "\u25CB disabled" });
30102
30331
  switch (status) {
30103
30332
  case "connected":
30104
- return /* @__PURE__ */ jsx58(Text, { color: "green", children: "\u25CF connected" });
30333
+ return /* @__PURE__ */ jsx57(Text, { color: "green", children: "\u25CF connected" });
30105
30334
  case "connecting":
30106
- return /* @__PURE__ */ jsx58(Text, { color: "cyan", children: "\u25D0 connecting" });
30335
+ return /* @__PURE__ */ jsx57(Text, { color: "cyan", children: "\u25D0 connecting" });
30107
30336
  case "reconnecting":
30108
- return /* @__PURE__ */ jsx58(Text, { color: "cyan", children: "\u25D1 reconnecting" });
30337
+ return /* @__PURE__ */ jsx57(Text, { color: "cyan", children: "\u25D1 reconnecting" });
30109
30338
  case "disconnected":
30110
- return /* @__PURE__ */ jsx58(Text, { dimColor: true, children: "\u25CB disconnected" });
30339
+ return /* @__PURE__ */ jsx57(Text, { dimColor: true, children: "\u25CB disconnected" });
30111
30340
  case "failed":
30112
- return /* @__PURE__ */ jsx58(Text, { color: "red", children: "\u2717 failed" });
30341
+ return /* @__PURE__ */ jsx57(Text, { color: "red", children: "\u2717 failed" });
30113
30342
  default:
30114
- return /* @__PURE__ */ jsx58(Text, { dimColor: true, children: status });
30343
+ return /* @__PURE__ */ jsx57(Text, { dimColor: true, children: status });
30115
30344
  }
30116
30345
  }
30117
30346
  function McpPicker({
@@ -30128,36 +30357,36 @@ function McpPicker({
30128
30357
  const windowEnd = Math.min(windowStart + maxVisible, total);
30129
30358
  const above = windowStart;
30130
30359
  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,
30360
+ return /* @__PURE__ */ jsxs56(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
30361
+ /* @__PURE__ */ jsx57(Text, { bold: true, color: "cyan", children: "MCP Servers" }),
30362
+ /* @__PURE__ */ jsx57(Text, { dimColor: true, children: "\u2191/\u2193 select \xB7 Enter/\u2190/\u2192 toggle enable \xB7 r restart \xB7 Esc close" }),
30363
+ /* @__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: [
30364
+ above > 0 ? /* @__PURE__ */ jsx57(Text, { dimColor: true, children: ` \u2191 ${above} more` }) : null,
30136
30365
  items.slice(windowStart, windowEnd).map((item, i) => {
30137
30366
  const index = windowStart + i;
30138
30367
  const focused = index === selected;
30139
30368
  const marker = focused ? "\u203A" : " ";
30140
- return /* @__PURE__ */ jsxs57(Text, { color: focused ? "cyan" : void 0, wrap: "truncate-end", children: [
30369
+ return /* @__PURE__ */ jsxs56(Text, { color: focused ? "cyan" : void 0, wrap: "truncate-end", children: [
30141
30370
  marker,
30142
30371
  " ",
30143
30372
  statusBadge(item.status, item.enabled),
30144
30373
  " ",
30145
- /* @__PURE__ */ jsx58(Text, { bold: true, children: item.name.padEnd(18) }),
30146
- /* @__PURE__ */ jsxs57(Text, { dimColor: true, children: [
30374
+ /* @__PURE__ */ jsx57(Text, { bold: true, children: item.name.padEnd(18) }),
30375
+ /* @__PURE__ */ jsxs56(Text, { dimColor: true, children: [
30147
30376
  item.transport.padEnd(14),
30148
30377
  item.toolCount > 0 ? `${item.toolCount} tools` : "",
30149
30378
  item.lazy ? " lazy" : ""
30150
30379
  ] })
30151
30380
  ] }, item.name);
30152
30381
  }),
30153
- below > 0 ? /* @__PURE__ */ jsx58(Text, { dimColor: true, children: ` \u2193 ${below} more` }) : null
30382
+ below > 0 ? /* @__PURE__ */ jsx57(Text, { dimColor: true, children: ` \u2193 ${below} more` }) : null
30154
30383
  ] }) }),
30155
- hint ? /* @__PURE__ */ jsx58(Box, { marginTop: 1, children: /* @__PURE__ */ jsx58(Text, { dimColor: true, children: hint }) }) : null
30384
+ hint ? /* @__PURE__ */ jsx57(Box, { marginTop: 1, children: /* @__PURE__ */ jsx57(Text, { dimColor: true, children: hint }) }) : null
30156
30385
  ] });
30157
30386
  }
30158
30387
 
30159
30388
  // src/components/model-picker.tsx
30160
- import { jsx as jsx59, jsxs as jsxs58 } from "react/jsx-runtime";
30389
+ import { jsx as jsx58, jsxs as jsxs57 } from "react/jsx-runtime";
30161
30390
  var MAX_VISIBLE2 = 10;
30162
30391
  function getVisibleWindow2(selected, total) {
30163
30392
  const half = Math.floor(MAX_VISIBLE2 / 2);
@@ -30185,27 +30414,27 @@ function ModelPicker({
30185
30414
  }) {
30186
30415
  const title = titleLabel ?? "Switch model";
30187
30416
  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) => {
30417
+ return /* @__PURE__ */ jsxs57(Box, { flexDirection: "column", borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, children: [
30418
+ /* @__PURE__ */ jsx58(Text, { color: UI_COLORS.title, bold: true, children: `\u2501\u2501 ${title} \u2014 Step 1/2: Pick provider \u2501\u2501` }),
30419
+ /* @__PURE__ */ jsx58(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel \xB7 Ctrl+C exit" }),
30420
+ 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
30421
  const isSelected = i === selected;
30193
30422
  const famColor = colorForFamily(p.family);
30194
- return /* @__PURE__ */ jsxs58(
30423
+ return /* @__PURE__ */ jsxs57(
30195
30424
  Text,
30196
30425
  {
30197
30426
  inverse: isSelected,
30198
30427
  ...isSelected ? { color: UI_COLORS.focused } : {},
30199
30428
  children: [
30200
30429
  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: [
30430
+ /* @__PURE__ */ jsx58(Text, { bold: true, color: isSelected ? void 0 : famColor, children: p.id.padEnd(28) }),
30431
+ /* @__PURE__ */ jsxs57(Text, { color: isSelected ? void 0 : famColor, dimColor: !isSelected, children: [
30203
30432
  " ",
30204
30433
  "[",
30205
30434
  p.family,
30206
30435
  "]"
30207
30436
  ] }),
30208
- /* @__PURE__ */ jsxs58(Text, { dimColor: true, children: [
30437
+ /* @__PURE__ */ jsxs57(Text, { dimColor: true, children: [
30209
30438
  " ",
30210
30439
  p.models.length,
30211
30440
  " model",
@@ -30216,15 +30445,15 @@ function ModelPicker({
30216
30445
  p.id
30217
30446
  );
30218
30447
  }) }),
30219
- hint ? /* @__PURE__ */ jsx59(Text, { color: UI_COLORS.hint, children: hint }) : null
30448
+ hint ? /* @__PURE__ */ jsx58(Text, { color: UI_COLORS.hint, children: hint }) : null
30220
30449
  ] });
30221
30450
  }
30222
30451
  const total = filteredOptions.length;
30223
30452
  const { start, end } = getVisibleWindow2(selected, total);
30224
30453
  const visibleItems = filteredOptions.slice(start, end);
30225
30454
  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: [
30455
+ return /* @__PURE__ */ jsxs57(Box, { flexDirection: "column", borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, children: [
30456
+ /* @__PURE__ */ jsxs57(Text, { color: UI_COLORS.title, bold: true, children: [
30228
30457
  `\u2501\u2501 ${title} \u2014 Step 2/2: Pick model `,
30229
30458
  "(",
30230
30459
  pickedProviderId,
@@ -30232,9 +30461,9 @@ function ModelPicker({
30232
30461
  ")",
30233
30462
  " \u2501\u2501"
30234
30463
  ] }),
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: [
30464
+ /* @__PURE__ */ jsx58(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc back \xB7 Ctrl+C exit \xB7 type to filter" }),
30465
+ 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: [
30466
+ start > 0 && /* @__PURE__ */ jsxs57(Text, { dimColor: true, children: [
30238
30467
  "\u25B2 ",
30239
30468
  start,
30240
30469
  " above"
@@ -30242,7 +30471,7 @@ function ModelPicker({
30242
30471
  visibleItems.map((id, vi) => {
30243
30472
  const absoluteIndex = start + vi;
30244
30473
  const isSelected = absoluteIndex === selected;
30245
- return /* @__PURE__ */ jsxs58(
30474
+ return /* @__PURE__ */ jsxs57(
30246
30475
  Text,
30247
30476
  {
30248
30477
  inverse: isSelected,
@@ -30256,20 +30485,20 @@ function ModelPicker({
30256
30485
  );
30257
30486
  }),
30258
30487
  Array.from({ length: MAX_VISIBLE2 - visibleItems.length }).map(
30259
- (_, i) => /* @__PURE__ */ jsx59(Text, { children: " " }, `pad-${i}`)
30488
+ (_, i) => /* @__PURE__ */ jsx58(Text, { children: " " }, `pad-${i}`)
30260
30489
  ),
30261
- end < total && /* @__PURE__ */ jsxs58(Text, { dimColor: true, children: [
30490
+ end < total && /* @__PURE__ */ jsxs57(Text, { dimColor: true, children: [
30262
30491
  "\u25BC ",
30263
30492
  total - end,
30264
30493
  " below"
30265
30494
  ] })
30266
30495
  ] }),
30267
- hint ? /* @__PURE__ */ jsx59(Text, { color: UI_COLORS.hint, children: hint }) : null
30496
+ hint ? /* @__PURE__ */ jsx58(Text, { color: UI_COLORS.hint, children: hint }) : null
30268
30497
  ] });
30269
30498
  }
30270
30499
 
30271
30500
  // src/components/plugin-picker.tsx
30272
- import { Fragment as Fragment20, jsx as jsx60, jsxs as jsxs59 } from "react/jsx-runtime";
30501
+ import { Fragment as Fragment20, jsx as jsx59, jsxs as jsxs58 } from "react/jsx-runtime";
30273
30502
  var CHROME_ROWS5 = 17;
30274
30503
  var MAX_PICKER_ITEMS = 15;
30275
30504
  function PluginPicker({
@@ -30287,15 +30516,15 @@ function PluginPicker({
30287
30516
  const above = windowStart;
30288
30517
  const below = total - windowEnd;
30289
30518
  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: [
30519
+ return /* @__PURE__ */ jsxs58(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
30520
+ /* @__PURE__ */ jsx59(Text, { bold: true, color: "cyan", children: "Plugin menu" }),
30521
+ /* @__PURE__ */ jsxs58(Text, { dimColor: true, children: [
30293
30522
  "\u2191/\u2193 select \xB7 Enter/\u2190/\u2192 toggle",
30294
30523
  hasLockedRows ? " \xB7 \u{1F512} = locked" : "",
30295
30524
  " \xB7 Esc close"
30296
30525
  ] }),
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,
30526
+ /* @__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: [
30527
+ above > 0 ? /* @__PURE__ */ jsx59(Text, { dimColor: true, children: ` \u2191 ${above} more` }) : null,
30299
30528
  items.slice(windowStart, windowEnd).map((item, i) => {
30300
30529
  const index = windowStart + i;
30301
30530
  const focused = index === selected;
@@ -30304,16 +30533,16 @@ function PluginPicker({
30304
30533
  const state = item.enabled ? "\u25CF on " : "\u25CB off";
30305
30534
  const color = item.enabled ? "green" : "gray";
30306
30535
  const lock = isLocked ? "\u{1F512}" : " ";
30307
- return /* @__PURE__ */ jsxs59(Text, { color: focused ? "cyan" : void 0, wrap: "truncate-end", children: [
30536
+ return /* @__PURE__ */ jsxs58(Text, { color: focused ? "cyan" : void 0, wrap: "truncate-end", children: [
30308
30537
  marker,
30309
30538
  " ",
30310
- /* @__PURE__ */ jsx60(Text, { color, children: state }),
30539
+ /* @__PURE__ */ jsx59(Text, { color, children: state }),
30311
30540
  " ",
30312
30541
  item.name.padEnd(18),
30313
30542
  " ",
30314
- /* @__PURE__ */ jsx60(Text, { color: isLocked ? "yellow" : void 0, children: lock }),
30543
+ /* @__PURE__ */ jsx59(Text, { color: isLocked ? "yellow" : void 0, children: lock }),
30315
30544
  " ",
30316
- /* @__PURE__ */ jsxs59(Text, { dimColor: true, children: [
30545
+ /* @__PURE__ */ jsxs58(Text, { dimColor: true, children: [
30317
30546
  "risk=",
30318
30547
  item.risk.padEnd(6)
30319
30548
  ] }),
@@ -30321,14 +30550,14 @@ function PluginPicker({
30321
30550
  item.summary
30322
30551
  ] }, item.name);
30323
30552
  }),
30324
- below > 0 ? /* @__PURE__ */ jsx60(Text, { dimColor: true, children: ` \u2193 ${below} more` }) : null
30553
+ below > 0 ? /* @__PURE__ */ jsx59(Text, { dimColor: true, children: ` \u2193 ${below} more` }) : null
30325
30554
  ] }) }),
30326
- hint ? /* @__PURE__ */ jsx60(Box, { marginTop: 1, children: /* @__PURE__ */ jsx60(Text, { dimColor: true, children: hint }) }) : null
30555
+ hint ? /* @__PURE__ */ jsx59(Box, { marginTop: 1, children: /* @__PURE__ */ jsx59(Text, { dimColor: true, children: hint }) }) : null
30327
30556
  ] });
30328
30557
  }
30329
30558
 
30330
30559
  // src/components/project-picker.tsx
30331
- import { jsx as jsx61, jsxs as jsxs60 } from "react/jsx-runtime";
30560
+ import { jsx as jsx60, jsxs as jsxs59 } from "react/jsx-runtime";
30332
30561
  function actionIcon(key) {
30333
30562
  if (key === "new-session") return glyphs.sessions;
30334
30563
  if (key === "prev-sessions") return glyphs.clock;
@@ -30348,71 +30577,71 @@ function ProjectPicker({
30348
30577
  const window = panelWindow(items.length, selected, limit);
30349
30578
  const visible = items.slice(window.start, window.end);
30350
30579
  const labelWidth = Math.max(12, size.contentWidth - 16);
30351
- return /* @__PURE__ */ jsxs60(
30580
+ return /* @__PURE__ */ jsxs59(
30352
30581
  MonitorShell,
30353
30582
  {
30354
30583
  accent: theme.accent,
30355
30584
  icon: glyphs.folder,
30356
30585
  title: "PROJECTS",
30357
30586
  kicker: size.columns >= 58 ? "workspace switcher" : void 0,
30358
- right: /* @__PURE__ */ jsxs60(Text, { color: theme.textMuted, children: [
30587
+ right: /* @__PURE__ */ jsxs59(Text, { color: theme.textMuted, children: [
30359
30588
  projectCount,
30360
30589
  " project",
30361
30590
  projectCount === 1 ? "" : "s"
30362
30591
  ] }),
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
30592
+ footer: /* @__PURE__ */ jsxs59(Box, { gap: 2, children: [
30593
+ /* @__PURE__ */ jsx60(KeyCap, { keyName: "\u2191\u2193", label: "navigate" }),
30594
+ /* @__PURE__ */ jsx60(KeyCap, { keyName: "Enter", label: "open", color: theme.success }),
30595
+ size.columns >= 64 ? /* @__PURE__ */ jsx60(KeyCap, { keyName: "Esc", label: "close", color: theme.error }) : null
30367
30596
  ] }),
30368
30597
  children: [
30369
- /* @__PURE__ */ jsxs60(Box, { height: 1, marginTop: 1, children: [
30370
- /* @__PURE__ */ jsxs60(Text, { color: filter ? theme.warn : theme.textMuted, children: [
30598
+ /* @__PURE__ */ jsxs59(Box, { height: 1, marginTop: 1, children: [
30599
+ /* @__PURE__ */ jsxs59(Text, { color: filter ? theme.warn : theme.textMuted, children: [
30371
30600
  glyphs.prompt,
30372
30601
  " "
30373
30602
  ] }),
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: [
30603
+ /* @__PURE__ */ jsx60(Text, { color: filter ? theme.textPrimary : theme.textMuted, children: filter || "Type to filter projects and actions" }),
30604
+ filter ? /* @__PURE__ */ jsx60(Text, { color: theme.accent, children: "\u2588" }) : null,
30605
+ /* @__PURE__ */ jsx60(Box, { flexGrow: 1 }),
30606
+ /* @__PURE__ */ jsxs59(Text, { color: theme.textMuted, children: [
30378
30607
  selectableCount,
30379
30608
  " choices"
30380
30609
  ] })
30381
30610
  ] }),
30382
- window.above > 0 ? /* @__PURE__ */ jsxs60(Text, { color: theme.textMuted, children: [
30611
+ window.above > 0 ? /* @__PURE__ */ jsxs59(Text, { color: theme.textMuted, children: [
30383
30612
  " \u2191 ",
30384
30613
  window.above,
30385
30614
  " hidden"
30386
30615
  ] }) : null,
30387
- visible.length === 0 ? /* @__PURE__ */ jsx61(
30616
+ visible.length === 0 ? /* @__PURE__ */ jsx60(
30388
30617
  EmptyPanelState,
30389
30618
  {
30390
30619
  icon: glyphs.folder,
30391
30620
  title: "No matching projects",
30392
30621
  detail: "Clear the filter or add a project with /project add."
30393
30622
  }
30394
- ) : /* @__PURE__ */ jsx61(Box, { flexDirection: "column", marginTop: 1, children: visible.map((item, offset) => {
30623
+ ) : /* @__PURE__ */ jsx60(Box, { flexDirection: "column", marginTop: 1, children: visible.map((item, offset) => {
30395
30624
  const index = window.start + offset;
30396
30625
  const isSelected = index === selected;
30397
30626
  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}`);
30627
+ 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
30628
  }
30400
30629
  const icon = item.kind === "project" ? glyphs.folder : actionIcon(item.key);
30401
30630
  const accent = item.kind === "project" ? theme.accent : theme.warn;
30402
- return /* @__PURE__ */ jsxs60(
30631
+ return /* @__PURE__ */ jsxs59(
30403
30632
  Box,
30404
30633
  {
30405
30634
  flexDirection: "column",
30406
30635
  ...isSelected && theme.supportsBackground ? { backgroundColor: theme.surfaceRaised } : {},
30407
30636
  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: [
30637
+ /* @__PURE__ */ jsxs59(Box, { height: 1, children: [
30638
+ /* @__PURE__ */ jsx60(Text, { color: isSelected ? accent : theme.textMuted, children: isSelected ? "\u258E" : " " }),
30639
+ /* @__PURE__ */ jsxs59(Text, { color: accent, children: [
30411
30640
  " ",
30412
30641
  icon,
30413
30642
  " "
30414
30643
  ] }),
30415
- /* @__PURE__ */ jsx61(
30644
+ /* @__PURE__ */ jsx60(
30416
30645
  Text,
30417
30646
  {
30418
30647
  color: isSelected ? theme.textPrimary : theme.textSecondary,
@@ -30420,24 +30649,24 @@ function ProjectPicker({
30420
30649
  children: truncatePanelText(item.label, labelWidth)
30421
30650
  }
30422
30651
  ),
30423
- /* @__PURE__ */ jsx61(Box, { flexGrow: 1 }),
30424
- item.meta ? /* @__PURE__ */ jsx61(Text, { color: isSelected ? accent : theme.textMuted, children: truncatePanelText(item.meta, 18) }) : null
30652
+ /* @__PURE__ */ jsx60(Box, { flexGrow: 1 }),
30653
+ item.meta ? /* @__PURE__ */ jsx60(Text, { color: isSelected ? accent : theme.textMuted, children: truncatePanelText(item.meta, 18) }) : null
30425
30654
  ] }),
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) })
30655
+ item.subtitle ? /* @__PURE__ */ jsxs59(Box, { height: 1, children: [
30656
+ /* @__PURE__ */ jsx60(Text, { color: isSelected ? accent : theme.textMuted, children: " \u2514\u2500 " }),
30657
+ /* @__PURE__ */ jsx60(Text, { color: theme.textMuted, children: truncatePanelText(item.subtitle, labelWidth) })
30429
30658
  ] }) : null
30430
30659
  ]
30431
30660
  },
30432
30661
  item.key
30433
30662
  );
30434
30663
  }) }),
30435
- window.below > 0 ? /* @__PURE__ */ jsxs60(Text, { color: theme.textMuted, children: [
30664
+ window.below > 0 ? /* @__PURE__ */ jsxs59(Text, { color: theme.textMuted, children: [
30436
30665
  " \u2193 ",
30437
30666
  window.below,
30438
30667
  " hidden"
30439
30668
  ] }) : null,
30440
- hint ? /* @__PURE__ */ jsxs60(Text, { color: theme.warn, children: [
30669
+ hint ? /* @__PURE__ */ jsxs59(Text, { color: theme.warn, children: [
30441
30670
  glyphs.warning,
30442
30671
  " ",
30443
30672
  hint
@@ -30447,9 +30676,94 @@ function ProjectPicker({
30447
30676
  );
30448
30677
  }
30449
30678
 
30679
+ // src/components/refine-countdown-panel.tsx
30680
+ import { useEffect as useEffect64, useRef as useRef35, useState as useState37 } from "react";
30681
+ import { jsx as jsx61, jsxs as jsxs60 } from "react/jsx-runtime";
30682
+ function RefineCountdownPanel({
30683
+ original,
30684
+ seconds,
30685
+ onDecision,
30686
+ providerId,
30687
+ model
30688
+ }) {
30689
+ const [remaining, setRemaining] = useState37(Math.max(1, seconds));
30690
+ const resolvedRef = useRef35(false);
30691
+ useEffect64(() => {
30692
+ const id = setInterval(() => {
30693
+ setRemaining((prev) => {
30694
+ if (prev <= 1) {
30695
+ clearInterval(id);
30696
+ if (!resolvedRef.current) {
30697
+ resolvedRef.current = true;
30698
+ onDecision("proceed");
30699
+ }
30700
+ return 0;
30701
+ }
30702
+ return prev - 1;
30703
+ });
30704
+ }, 1e3);
30705
+ return () => clearInterval(id);
30706
+ }, []);
30707
+ useInput((_input, key) => {
30708
+ if (resolvedRef.current) return;
30709
+ resolvedRef.current = true;
30710
+ if (key.escape) onDecision("cancel");
30711
+ else if (key.return) onDecision("proceed");
30712
+ else onDecision("skip");
30713
+ });
30714
+ const preview = wrapRefinementPreview(original, 60, 2);
30715
+ return /* @__PURE__ */ jsxs60(
30716
+ Box,
30717
+ {
30718
+ alignSelf: "stretch",
30719
+ width: "100%",
30720
+ flexDirection: "column",
30721
+ borderStyle: "round",
30722
+ borderColor: theme.brand,
30723
+ paddingX: 1,
30724
+ children: [
30725
+ /* @__PURE__ */ jsxs60(Box, { height: 1, children: [
30726
+ /* @__PURE__ */ jsxs60(Text, { color: theme.brand, bold: true, children: [
30727
+ glyphs.brain,
30728
+ " PROMPT REFINER"
30729
+ ] }),
30730
+ providerId && model ? /* @__PURE__ */ jsxs60(Text, { color: theme.textMuted, children: [
30731
+ " ",
30732
+ "on ",
30733
+ providerId,
30734
+ "/",
30735
+ model
30736
+ ] }) : null,
30737
+ /* @__PURE__ */ jsx61(Box, { flexGrow: 1 }),
30738
+ /* @__PURE__ */ jsxs60(Text, { color: theme.brand, children: [
30739
+ "refining in ",
30740
+ remaining,
30741
+ "s\u2026"
30742
+ ] })
30743
+ ] }),
30744
+ /* @__PURE__ */ jsx61(Box, { flexDirection: "column", marginTop: 1, children: preview.map((line, index) => /* @__PURE__ */ jsxs60(Box, { height: 1, children: [
30745
+ /* @__PURE__ */ jsx61(Text, { color: theme.brand, children: "\u258E" }),
30746
+ /* @__PURE__ */ jsxs60(Text, { color: theme.textSecondary, children: [
30747
+ " ",
30748
+ line
30749
+ ] })
30750
+ ] }, `refine-countdown-preview-${index}`)) }),
30751
+ /* @__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" }) })
30752
+ ]
30753
+ }
30754
+ );
30755
+ }
30756
+
30450
30757
  // src/components/refine-failure-panel.tsx
30451
- import { useState as useState38 } from "react";
30758
+ import { useState as useState38, useMemo as useMemo12 } from "react";
30452
30759
  import { jsx as jsx62, jsxs as jsxs61 } from "react/jsx-runtime";
30760
+ function filterRefineModels(models, query) {
30761
+ const normalized = query.trim().toLowerCase();
30762
+ if (!normalized) return models;
30763
+ return models.filter(
30764
+ (candidate) => candidate.providerId.toLowerCase().includes(normalized) || candidate.model.toLowerCase().includes(normalized) || candidate.label?.toLowerCase().includes(normalized)
30765
+ );
30766
+ }
30453
30767
  var PICK_WINDOW = 8;
30454
30768
  function useColumns() {
30455
30769
  const { stdout } = useStdout();
@@ -30466,17 +30780,26 @@ function RefineFailurePanel({
30466
30780
  const columns = useColumns();
30467
30781
  const [view, setView] = useState38("options");
30468
30782
  const [cursor, setCursor] = useState38(0);
30783
+ const [query, setQuery] = useState38("");
30784
+ const filteredModels = useMemo12(() => filterRefineModels(models, query), [models, query]);
30469
30785
  const width = Math.max(30, columns - 4);
30470
30786
  const canPick = models.length > 0;
30471
30787
  const canFallback = !!fallbackRef;
30472
30788
  useInput((input, key) => {
30473
30789
  if (view === "pick") {
30474
30790
  if (key.escape) {
30791
+ if (query) {
30792
+ setQuery("");
30793
+ return;
30794
+ }
30475
30795
  setView("options");
30796
+ setQuery("");
30797
+ setCursor(0);
30476
30798
  return;
30477
30799
  }
30478
30800
  if (key.downArrow) {
30479
- setCursor((c) => Math.min(c + 1, models.length - 1));
30801
+ const n = filteredModels.length;
30802
+ if (n > 0) setCursor((c) => Math.min(c + 1, n - 1));
30480
30803
  return;
30481
30804
  }
30482
30805
  if (key.upArrow) {
@@ -30484,11 +30807,21 @@ function RefineFailurePanel({
30484
30807
  return;
30485
30808
  }
30486
30809
  if (key.return) {
30487
- const chosen = models[cursor];
30810
+ const chosen = filteredModels[cursor];
30488
30811
  if (chosen)
30489
30812
  onDecision({ kind: "pick", providerId: chosen.providerId, model: chosen.model });
30490
30813
  return;
30491
30814
  }
30815
+ if (key.backspace || key.delete) {
30816
+ setQuery((q) => q.slice(0, -1));
30817
+ setCursor(0);
30818
+ return;
30819
+ }
30820
+ if (input && input.length === 1 && input.charCodeAt(0) >= 32 && input.charCodeAt(0) < 127) {
30821
+ setQuery((q) => q + input);
30822
+ setCursor(0);
30823
+ return;
30824
+ }
30492
30825
  return;
30493
30826
  }
30494
30827
  if (key.return) {
@@ -30503,17 +30836,19 @@ function RefineFailurePanel({
30503
30836
  if (ch === "m" && canFallback) onDecision({ kind: "fallback" });
30504
30837
  else if (ch === "p" && canPick) {
30505
30838
  setCursor(0);
30839
+ setQuery("");
30506
30840
  setView("pick");
30507
30841
  } else if (ch === "s") onDecision({ kind: "original" });
30508
30842
  else if (ch === "e") onDecision({ kind: "edit" });
30509
30843
  });
30510
30844
  if (view === "pick") {
30845
+ const total = filteredModels.length;
30511
30846
  const start = Math.max(
30512
30847
  0,
30513
- Math.min(cursor - Math.floor(PICK_WINDOW / 2), models.length - PICK_WINDOW)
30848
+ Math.min(cursor - Math.floor(PICK_WINDOW / 2), total - PICK_WINDOW)
30514
30849
  );
30515
30850
  const windowStart = Math.max(0, start);
30516
- const visible = models.slice(windowStart, windowStart + PICK_WINDOW);
30851
+ const visible = filteredModels.slice(windowStart, windowStart + PICK_WINDOW);
30517
30852
  return /* @__PURE__ */ jsxs61(
30518
30853
  Box,
30519
30854
  {
@@ -30531,12 +30866,17 @@ function RefineFailurePanel({
30531
30866
  ] }),
30532
30867
  /* @__PURE__ */ jsx62(Box, { flexGrow: 1 }),
30533
30868
  /* @__PURE__ */ jsxs61(Text, { color: theme.textMuted, children: [
30534
- models.length,
30869
+ query ? `${total}/${models.length}` : `${models.length}`,
30535
30870
  " model",
30536
- models.length === 1 ? "" : "s"
30871
+ (query ? total : models.length) === 1 ? "" : "s"
30537
30872
  ] })
30538
30873
  ] }),
30539
- /* @__PURE__ */ jsx62(Box, { flexDirection: "column", marginTop: 1, children: visible.map((m, i) => {
30874
+ /* @__PURE__ */ jsx62(Box, { height: 1, marginTop: 1, children: /* @__PURE__ */ jsx62(Text, { color: theme.textMuted, children: query ? `\u{1F50D} ${query} ` : "\u{1F50D} type to filter" }) }),
30875
+ total === 0 ? /* @__PURE__ */ jsx62(Box, { height: 1, marginTop: 1, children: /* @__PURE__ */ jsxs61(Text, { color: theme.textMuted, children: [
30876
+ '(no models match "',
30877
+ query,
30878
+ '")'
30879
+ ] }) }) : /* @__PURE__ */ jsx62(Box, { flexDirection: "column", marginTop: 1, children: visible.map((m, i) => {
30540
30880
  const idx = windowStart + i;
30541
30881
  const selected = idx === cursor;
30542
30882
  const label = m.label && m.label !== m.model ? ` ${m.label}` : "";
@@ -30546,7 +30886,11 @@ function RefineFailurePanel({
30546
30886
  /* @__PURE__ */ jsx62(Text, { color: theme.textMuted, children: label })
30547
30887
  ] }, `${m.providerId}/${m.model}`);
30548
30888
  }) }),
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" }) })
30889
+ /* @__PURE__ */ jsx62(Box, { height: 1, marginTop: 1, children: /* @__PURE__ */ jsxs61(Text, { color: theme.textMuted, children: [
30890
+ "\u2191/\u2193 choose \xB7 Enter refine \xB7 ",
30891
+ query ? "Esc clear \xB7 " : "",
30892
+ "Esc back"
30893
+ ] }) })
30550
30894
  ]
30551
30895
  }
30552
30896
  );
@@ -30712,7 +31056,7 @@ import {
30712
31056
  useCallback as useCallback25,
30713
31057
  useEffect as useEffect67,
30714
31058
  useLayoutEffect as useLayoutEffect2,
30715
- useMemo as useMemo13,
31059
+ useMemo as useMemo14,
30716
31060
  useRef as useRef37,
30717
31061
  useState as useState40
30718
31062
  } from "react";
@@ -31153,7 +31497,7 @@ import { Fragment as Fragment21, jsx as jsx65, jsxs as jsxs63 } from "react/jsx-
31153
31497
  var MAX_TOOL_GROUP_ENTRIES = 12;
31154
31498
  var STRUCTURED_DIFF_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "replace", "diff", "patch"]);
31155
31499
  function hasInjectedMemory(entry) {
31156
- return extractSageBlock(entry.output ?? "").sageLines.length > 0;
31500
+ return resolveEntrySage(entry.output, entry.sageLines).sageLines.length > 0;
31157
31501
  }
31158
31502
  function renderGroupId(group) {
31159
31503
  return group.type === "tool-group" ? group.data.entries[0]?.id ?? 0 : group.entry.id;
@@ -31163,30 +31507,66 @@ function estimateTextRows(text, contentWidth, maxRows) {
31163
31507
  if (!text) return 1;
31164
31508
  const width = Math.max(16, contentWidth);
31165
31509
  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;
31510
+ let lineWidth = 0;
31511
+ for (const char of text) {
31512
+ if (char === "\n") {
31513
+ rows += Math.max(1, Math.ceil(lineWidth / width));
31514
+ if (rows >= maxRows) return maxRows;
31515
+ lineWidth = 0;
31516
+ continue;
31517
+ }
31518
+ lineWidth += displayWidth(char);
31172
31519
  }
31173
- return Math.max(1, rows);
31520
+ rows += Math.max(1, Math.ceil(lineWidth / width));
31521
+ return Math.min(maxRows, Math.max(1, rows));
31522
+ }
31523
+ function estimateSageKvRows(cells, contentWidth) {
31524
+ if (cells.length === 0) return 0;
31525
+ const gap = 2;
31526
+ let rows = 1;
31527
+ let usedColumns = 0;
31528
+ for (const cell of cells) {
31529
+ const cellWidth = displayWidth(cell) + gap;
31530
+ if (usedColumns > 0 && usedColumns + cellWidth > contentWidth) {
31531
+ rows += 1;
31532
+ usedColumns = cellWidth;
31533
+ } else {
31534
+ usedColumns += cellWidth;
31535
+ }
31536
+ }
31537
+ return rows;
31174
31538
  }
31175
31539
  function estimateRenderGroupRows(group, contentWidth) {
31176
31540
  if (group.type === "tool-group") return group.data.entries.length + 2;
31177
31541
  const { entry } = group;
31178
31542
  if (entry.kind === "tool") {
31179
- const { cleanOutput, sageLines } = extractSageBlock(entry.output ?? "");
31543
+ const { cleanOutput, sageLines } = resolveEntrySage(entry.output, entry.sageLines);
31180
31544
  const memoryLines = sageLines.slice(1);
31181
31545
  const panelContentWidth = Math.max(16, contentWidth - 4);
31182
31546
  const sagePanelRows = memoryLines.length > 0 ? 2 + estimateTextRows(
31183
- `\u{1F9E0} SAGE MEMORY INJECTED \xB7 ${entry.name} ${memoryLines.length} ${memoryLines.length === 1 ? "memory" : "memories"}`,
31547
+ `\u{1F9E0} SAGE MEMORY INJECTED \xB7 ${entry.name} ${memoryLines.length} ${memoryLines.length === 1 ? "memory" : "memories"}${entry.sageStats ? ` \xB7 ${entry.sageStats}` : ""}`,
31184
31548
  panelContentWidth,
31185
31549
  MAX_TEXT_ESTIMATE_ROWS
31186
- ) + memoryLines.reduce(
31187
- (rows, line) => rows + estimateTextRows(line, panelContentWidth, MAX_TEXT_ESTIMATE_ROWS),
31188
- 0
31189
- ) : 0;
31550
+ ) + memoryLines.reduce((rows, line) => {
31551
+ const parsed = parseSageMemoryLine(line);
31552
+ if (!parsed) {
31553
+ return rows + estimateTextRows(line, panelContentWidth, MAX_TEXT_ESTIMATE_ROWS);
31554
+ }
31555
+ const labelRow = estimateTextRows(
31556
+ parsed.labels.length > 0 ? parsed.labels.map((l) => `[${l}]`).join("") : "[memory]",
31557
+ panelContentWidth,
31558
+ MAX_TEXT_ESTIMATE_ROWS
31559
+ );
31560
+ const bodyRow = estimateTextRows(parsed.text, panelContentWidth, MAX_TEXT_ESTIMATE_ROWS);
31561
+ const kvCells = [];
31562
+ kvCells.push(`id: ${parsed.id}`);
31563
+ if (parsed.anchor) kvCells.push(`anchor: ${parsed.anchor}`);
31564
+ if (parsed.relation) kvCells.push(`relation: ${parsed.relation}`);
31565
+ if (parsed.tags && parsed.tags.length > 0) kvCells.push(`tags: ${parsed.tags.join(", ")}`);
31566
+ const kvRows = estimateSageKvRows(kvCells, panelContentWidth);
31567
+ const gap = rows > 0 ? 1 : 0;
31568
+ return rows + labelRow + bodyRow + kvRows + gap;
31569
+ }, 0) : 0;
31190
31570
  if (entry.resultRenderMode === "simple") return 3 + sagePanelRows;
31191
31571
  if (STRUCTURED_DIFF_TOOLS.has(entry.name)) {
31192
31572
  const previewCap = entry.name === "replace" || entry.name === "diff" || entry.name === "patch" ? MULTI_DIFF_MAX_ROWS : DIFF_MAX_LINES;
@@ -31371,7 +31751,7 @@ import { memo as memo2, useEffect as useEffect66, useRef as useRef36, useState a
31371
31751
  // src/components/history/entry.tsx
31372
31752
  import { hasOpenTodos } from "@wrongstack/core/utils";
31373
31753
  import { parseNextSteps } from "@wrongstack/tools/next-steps";
31374
- import React8, { useEffect as useEffect65, useMemo as useMemo12 } from "react";
31754
+ import React8, { useEffect as useEffect65, useMemo as useMemo13 } from "react";
31375
31755
 
31376
31756
  // src/markdown-table.ts
31377
31757
  import { expectDefined as expectDefined4 } from "@wrongstack/core/utils";
@@ -32359,8 +32739,28 @@ function ToolCard({
32359
32739
  ] });
32360
32740
  }
32361
32741
 
32742
+ // src/history-entry.ts
32743
+ import {
32744
+ DEFAULT_MIN_IMPORTANCE as DEFAULT_MIN_IMPORTANCE2,
32745
+ DEFAULT_MIN_SCORE as DEFAULT_MIN_SCORE2,
32746
+ MIN_RELATION_STRENGTH as MIN_RELATION_STRENGTH2
32747
+ } from "@wrongstack/sage";
32748
+ var MEMORY_GATE_DEFAULTS = {
32749
+ minScore: DEFAULT_MIN_SCORE2,
32750
+ minImportance: DEFAULT_MIN_IMPORTANCE2,
32751
+ relationFloor: MIN_RELATION_STRENGTH2
32752
+ };
32753
+
32362
32754
  // src/components/history/entry.tsx
32363
32755
  import { Fragment as Fragment24, jsx as jsx70, jsxs as jsxs68 } from "react/jsx-runtime";
32756
+ var MAX_MEMORY_PROOF_ROWS = 4;
32757
+ function formatScoreTerms(terms) {
32758
+ if (terms.length === 0) return "no score breakdown (emitted by an older core build)";
32759
+ return terms.map((term) => {
32760
+ const magnitude = Math.abs(term.value).toFixed(2);
32761
+ return `${term.label} ${term.value < 0 ? "\u2212" : "+"}${magnitude}`;
32762
+ }).join(" \u2502 ");
32763
+ }
32364
32764
  function brainStatusStyle(status) {
32365
32765
  switch (status) {
32366
32766
  case "thinking":
@@ -32428,7 +32828,7 @@ var Entry = React8.memo(function Entry2({
32428
32828
  showModelReasoning
32429
32829
  }) {
32430
32830
  const openTodos = hasOpenTodos(todos);
32431
- const nextSteps = useMemo12(() => {
32831
+ const nextSteps = useMemo13(() => {
32432
32832
  if (entry.kind !== "assistant") return { steps: [], stripped: "" };
32433
32833
  return parseNextSteps(entry.text, true);
32434
32834
  }, [entry.kind, entry.text]);
@@ -32547,7 +32947,7 @@ var Entry = React8.memo(function Entry2({
32547
32947
  }
32548
32948
  case "tool": {
32549
32949
  const { glyph: glyph2, color } = getToolVisual(entry.name);
32550
- const { argSummary, outLines, sageLines, visualLines, diff, multiDiffs, sizeChip, mutation } = useMemo12(() => {
32950
+ const { argSummary, outLines, sageLines, visualLines, diff, multiDiffs, sizeChip, mutation } = useMemo13(() => {
32551
32951
  const argSummary2 = formatToolArgs(entry.name, entry.input);
32552
32952
  const {
32553
32953
  cleanOutput: outputForFormatting,
@@ -32558,7 +32958,8 @@ var Entry = React8.memo(function Entry2({
32558
32958
  entry.output,
32559
32959
  entry.ok,
32560
32960
  entry.outputBytes,
32561
- entry.outputLines
32961
+ entry.outputLines,
32962
+ entry.sageLines
32562
32963
  );
32563
32964
  const visualLines2 = formatToolVisualOutput(
32564
32965
  entry.name,
@@ -32617,6 +33018,7 @@ var Entry = React8.memo(function Entry2({
32617
33018
  }, [
32618
33019
  entry.name,
32619
33020
  entry.output,
33021
+ entry.sageLines,
32620
33022
  entry.input,
32621
33023
  entry.ok,
32622
33024
  entry.outputBytes,
@@ -32684,7 +33086,14 @@ var Entry = React8.memo(function Entry2({
32684
33086
  ]
32685
33087
  }
32686
33088
  ),
32687
- /* @__PURE__ */ jsx70(SageMemoryBlock, { sageLines, toolName: entry.name })
33089
+ /* @__PURE__ */ jsx70(
33090
+ SageMemoryBlock,
33091
+ {
33092
+ sageLines,
33093
+ toolName: entry.name,
33094
+ stats: entry.sageStats
33095
+ }
33096
+ )
32688
33097
  ] });
32689
33098
  }
32690
33099
  const toolContentWidth = Math.max(20, termWidth - 2);
@@ -32764,14 +33173,16 @@ var Entry = React8.memo(function Entry2({
32764
33173
  ]
32765
33174
  }
32766
33175
  ),
32767
- /* @__PURE__ */ jsx70(SageMemoryBlock, { sageLines, toolName: entry.name })
33176
+ /* @__PURE__ */ jsx70(SageMemoryBlock, { sageLines, toolName: entry.name, stats: entry.sageStats })
32768
33177
  ] });
32769
33178
  }
32770
33179
  case "memory-activation": {
33180
+ if (!entry.error) return null;
32771
33181
  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 ");
33182
+ const injected = entry.outcome === "error" ? entry.activated : entry.activated.filter((memory) => entry.injectedIds.includes(memory.id));
33183
+ const shown = injected.slice(0, MAX_MEMORY_PROOF_ROWS);
33184
+ const overflow = injected.length - shown.length;
33185
+ const thresholds = entry.thresholds ?? MEMORY_GATE_DEFAULTS;
32775
33186
  return /* @__PURE__ */ jsxs68(Box, { flexDirection: "column", borderStyle: "single", borderColor: "magenta", paddingX: 1, children: [
32776
33187
  /* @__PURE__ */ jsxs68(Box, { flexDirection: "row", children: [
32777
33188
  /* @__PURE__ */ jsx70(Text, { bold: true, color: "magenta", children: "\u{1F9E0} MEMORY INJECTOR " }),
@@ -32779,17 +33190,23 @@ var Entry = React8.memo(function Entry2({
32779
33190
  /* @__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
33191
  ] }),
32781
33192
  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,
33193
+ entry.queryPreview ? /* @__PURE__ */ jsx70(Text, { dimColor: true, wrap: "truncate-end", children: `query: ${entry.queryPreview}` }) : null,
33194
+ shown.map((memory) => {
33195
+ const scorePass = memory.score >= thresholds.minScore;
33196
+ const relationPass = memory.relationStrength >= thresholds.relationFloor;
33197
+ const reason = memory.activationReasons[0] ?? "no recorded reason";
33198
+ return /* @__PURE__ */ jsxs68(Box, { flexDirection: "column", children: [
33199
+ /* @__PURE__ */ jsxs68(Text, { wrap: "truncate-end", children: [
33200
+ /* @__PURE__ */ jsx70(Text, { color: "green", children: `${memory.id} ` }),
33201
+ /* @__PURE__ */ jsx70(Text, { color: scorePass ? "green" : "red", children: `${memory.score.toFixed(2)} ${scorePass ? "\u2713" : "\u2717"} ${thresholds.minScore.toFixed(2)}` }),
33202
+ /* @__PURE__ */ jsx70(Text, { dimColor: true, children: " \xB7 relation " }),
33203
+ /* @__PURE__ */ jsx70(Text, { color: relationPass ? "green" : "red", children: `${memory.relationStrength.toFixed(2)} ${relationPass ? "\u2713" : "\u2717"} ${thresholds.relationFloor.toFixed(2)}` }),
33204
+ /* @__PURE__ */ jsx70(Text, { dimColor: true, children: ` \xB7 ${reason}` })
33205
+ ] }),
33206
+ /* @__PURE__ */ jsx70(Text, { dimColor: true, wrap: "truncate-end", children: ` ${formatScoreTerms(memory.scoreTerms ?? [])}` })
33207
+ ] }, memory.id);
33208
+ }),
33209
+ overflow > 0 ? /* @__PURE__ */ jsx70(Text, { dimColor: true, children: ` +${overflow} more injected` }) : null,
32793
33210
  rejected ? /* @__PURE__ */ jsx70(Text, { dimColor: true, children: `filtered: ${rejected}` }) : null
32794
33211
  ] });
32795
33212
  }
@@ -32968,8 +33385,10 @@ var Entry = React8.memo(function Entry2({
32968
33385
  });
32969
33386
  function SageMemoryBlock({
32970
33387
  sageLines,
32971
- toolName
33388
+ toolName,
33389
+ stats
32972
33390
  }) {
33391
+ if (!sageLines || sageLines.length < 2) return null;
32973
33392
  const memoryLines = sageLines.slice(1);
32974
33393
  if (memoryLines.length === 0) return null;
32975
33394
  return /* @__PURE__ */ jsxs68(
@@ -32984,19 +33403,42 @@ function SageMemoryBlock({
32984
33403
  children: [
32985
33404
  /* @__PURE__ */ jsxs68(Box, { flexDirection: "row", children: [
32986
33405
  /* @__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
- )
33406
+ /* @__PURE__ */ jsx70(Text, { dimColor: true, children: `${memoryLines.length} ${memoryLines.length === 1 ? "memory" : "memories"}${stats ? ` \xB7 ${stats}` : ""}` })
32994
33407
  ] }),
32995
- memoryLines.map((line, i) => /* @__PURE__ */ jsx70(Text, { color: theme.accent, dimColor: true, children: line }, i))
33408
+ memoryLines.map((line, i) => {
33409
+ const parsed = parseSageMemoryLine(line);
33410
+ if (!parsed) {
33411
+ return /* @__PURE__ */ jsx70(Text, { color: theme.accent, dimColor: true, wrap: "truncate-end", children: line }, i);
33412
+ }
33413
+ return /* @__PURE__ */ jsx70(SageMemoryRow, { parsed, index: i }, i);
33414
+ })
32996
33415
  ]
32997
33416
  }
32998
33417
  );
32999
33418
  }
33419
+ function SageMemoryRow({
33420
+ parsed,
33421
+ index
33422
+ }) {
33423
+ const labelText = parsed.labels.length > 0 ? parsed.labels.map((l) => `[${l}]`).join("") : "[memory]";
33424
+ const tagText = parsed.tags && parsed.tags.length > 0 ? parsed.tags.join(", ") : void 0;
33425
+ return /* @__PURE__ */ jsxs68(Box, { flexDirection: "column", marginTop: index > 0 ? 1 : 0, children: [
33426
+ /* @__PURE__ */ jsx70(Text, { bold: true, color: theme.accent, children: labelText }),
33427
+ /* @__PURE__ */ jsx70(Text, { color: theme.textPrimary, children: parsed.text }),
33428
+ /* @__PURE__ */ jsxs68(Box, { flexDirection: "row", flexWrap: "wrap", children: [
33429
+ /* @__PURE__ */ jsx70(SageKv, { label: "id", value: parsed.id }),
33430
+ parsed.anchor ? /* @__PURE__ */ jsx70(SageKv, { label: "anchor", value: parsed.anchor }) : null,
33431
+ parsed.relation ? /* @__PURE__ */ jsx70(SageKv, { label: "relation", value: parsed.relation }) : null,
33432
+ tagText ? /* @__PURE__ */ jsx70(SageKv, { label: "tags", value: tagText }) : null
33433
+ ] })
33434
+ ] });
33435
+ }
33436
+ function SageKv({ label, value }) {
33437
+ return /* @__PURE__ */ jsxs68(Box, { flexDirection: "row", marginRight: 2, children: [
33438
+ /* @__PURE__ */ jsx70(Text, { dimColor: true, children: `${label}: ` }),
33439
+ /* @__PURE__ */ jsx70(Text, { color: theme.textSecondary, children: value })
33440
+ ] });
33441
+ }
33000
33442
 
33001
33443
  // src/components/history/index.tsx
33002
33444
  import { Fragment as Fragment25, jsx as jsx71, jsxs as jsxs69 } from "react/jsx-runtime";
@@ -33243,7 +33685,7 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
33243
33685
  return maxWidth ? Math.min(raw, maxWidth) : raw;
33244
33686
  }, [stdout, maxWidth]);
33245
33687
  const [viewportWidth, setViewportWidth] = useState40(resolveViewportWidth);
33246
- const termWidth = useMemo13(
33688
+ const termWidth = useMemo14(
33247
33689
  () => Math.max(1, viewportWidth - SCROLLBAR_HIT_WIDTH),
33248
33690
  [viewportWidth]
33249
33691
  );
@@ -33258,8 +33700,8 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
33258
33700
  }, [stdout, resolveViewportWidth]);
33259
33701
  const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
33260
33702
  const toolTailHeight = toolTail && toolStream ? toolStreamBoxHeight(toolStream.name) : 0;
33261
- const groupedEntries = useMemo13(() => groupEntries(entries), [entries]);
33262
- const groupIds = useMemo13(() => groupedEntries.map(renderGroupId), [groupedEntries]);
33703
+ const groupedEntries = useMemo14(() => groupEntries(entries), [entries]);
33704
+ const groupIds = useMemo14(() => groupedEntries.map(renderGroupId), [groupedEntries]);
33263
33705
  const groupIdsKey = groupIds.join(",");
33264
33706
  const heightCacheRef = useRef37(null);
33265
33707
  if (heightCacheRef.current === null) heightCacheRef.current = new EntryHeightCache();
@@ -33351,7 +33793,7 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
33351
33793
  viewportRows: vp,
33352
33794
  tailRows: toolTailHeight
33353
33795
  };
33354
- const groupIndexById = useMemo13(() => {
33796
+ const groupIndexById = useMemo14(() => {
33355
33797
  const indexes = /* @__PURE__ */ new Map();
33356
33798
  groupIds.forEach((id, index) => {
33357
33799
  indexes.set(id, index);
@@ -33383,7 +33825,7 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
33383
33825
  setAnchor(normalized && nextId !== void 0 ? { id: nextId, clip: normalized.clip } : null);
33384
33826
  setMountBump(0);
33385
33827
  }, []);
33386
- const controller = useMemo13(
33828
+ const controller = useMemo14(
33387
33829
  () => ({
33388
33830
  scrollBy: (deltaUp) => {
33389
33831
  applyAnchor(scrollAnchorBy(geometryRef.current, effectiveAnchorRef.current, deltaUp));
@@ -34029,22 +34471,9 @@ function AppView({ host, runtime }) {
34029
34471
  enhanceDelayMs,
34030
34472
  layoutStore
34031
34473
  } = 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;
34474
+ const { nowTick, workingTimeMs, enhanceDots } = activity;
34475
+ const { setYoloLive, autonomyLive } = environment;
34476
+ const { inputHint, composerStatus, composerAnimationStyle, inputHeight, hideInput } = viewState;
34048
34477
  return (
34049
34478
  /* Hard viewport cap. The managed layout aims for exactly termRows
34050
34479
  (history vp + measured bottom region), but the bottom region grows a
@@ -34230,6 +34659,7 @@ function AppView({ host, runtime }) {
34230
34659
  breakerAutoKillResetMs: state.settingsPicker.breakerAutoKillResetMs,
34231
34660
  showModelReasoning: state.settingsPicker.showModelReasoning,
34232
34661
  showAgentSwarmPanel: state.settingsPicker.showAgentSwarmPanel,
34662
+ readSymbols: state.settingsPicker.readSymbols,
34233
34663
  filter: state.settingsPicker.filter,
34234
34664
  hint: state.settingsPicker.hint
34235
34665
  }
@@ -35827,7 +36257,10 @@ function routeBusyInterruptKey(host, key) {
35827
36257
  const fleetTag = subagentsTerminated > 0 ? ` \xB7 stopped ${subagentsTerminated} subagent${subagentsTerminated === 1 ? "" : "s"}` : "";
35828
36258
  dispatch({
35829
36259
  type: "addEntry",
35830
- entry: { kind: "warn", text: `\u21AF Interrupted${droppedTag}${fleetTag}. Type your new direction.` }
36260
+ entry: {
36261
+ kind: "warn",
36262
+ text: `\u21AF Interrupted${droppedTag}${fleetTag}. Type your new direction.`
36263
+ }
35831
36264
  });
35832
36265
  return true;
35833
36266
  }
@@ -35949,7 +36382,8 @@ function routeSettingsOverlayKey(host, input, key, isEnter) {
35949
36382
  breakerEnabled: config.breakerEnabled ?? false,
35950
36383
  breakerAutoKillResetMs: config.breakerAutoKillResetMs ?? 6e4,
35951
36384
  showModelReasoning: config.showModelReasoning ?? true,
35952
- showAgentSwarmPanel: config.showAgentSwarmPanel ?? true
36385
+ showAgentSwarmPanel: config.showAgentSwarmPanel ?? true,
36386
+ readSymbols: config.readSymbols ?? false
35953
36387
  });
35954
36388
  return true;
35955
36389
  }