@wrongstack/tui 0.296.3 → 0.297.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/app-action-type.d.ts +2 -0
  2. package/dist/app-action-type.d.ts.map +1 -1
  3. package/dist/app-initial-state.d.ts.map +1 -1
  4. package/dist/app-settings-type.d.ts +4 -0
  5. package/dist/app-settings-type.d.ts.map +1 -1
  6. package/dist/app-state.d.ts +4 -0
  7. package/dist/app-state.d.ts.map +1 -1
  8. package/dist/app-view.d.ts.map +1 -1
  9. package/dist/components/history/entry.d.ts +7 -1
  10. package/dist/components/history/entry.d.ts.map +1 -1
  11. package/dist/components/history/index.d.ts +1 -1
  12. package/dist/components/history/index.d.ts.map +1 -1
  13. package/dist/components/history/replay.d.ts.map +1 -1
  14. package/dist/components/history/tool-group.d.ts +1 -1
  15. package/dist/components/history/tool-group.d.ts.map +1 -1
  16. package/dist/components/history/types.d.ts +6 -0
  17. package/dist/components/history/types.d.ts.map +1 -1
  18. package/dist/components/scrollable-history.d.ts +1 -1
  19. package/dist/components/scrollable-history.d.ts.map +1 -1
  20. package/dist/components/settings-picker-jumps.d.ts.map +1 -1
  21. package/dist/components/settings-picker-model.d.ts +7 -1
  22. package/dist/components/settings-picker-model.d.ts.map +1 -1
  23. package/dist/components/settings-picker.d.ts +5 -1
  24. package/dist/components/settings-picker.d.ts.map +1 -1
  25. package/dist/components/status-bar.d.ts.map +1 -1
  26. package/dist/connections-health.d.ts.map +1 -1
  27. package/dist/heap-watchdog.d.ts +6 -42
  28. package/dist/heap-watchdog.d.ts.map +1 -1
  29. package/dist/history-archive.d.ts +0 -1
  30. package/dist/history-archive.d.ts.map +1 -1
  31. package/dist/history-entry.d.ts +9 -0
  32. package/dist/history-entry.d.ts.map +1 -1
  33. package/dist/hooks/use-panel-controllers.d.ts.map +1 -1
  34. package/dist/hooks/use-provider-event-bridge.d.ts.map +1 -1
  35. package/dist/hooks/use-settings-auto-save.d.ts.map +1 -1
  36. package/dist/hooks/use-tui-activity.d.ts.map +1 -1
  37. package/dist/index.js +354 -262
  38. package/dist/index.js.map +4 -4
  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/rehydrate-history.d.ts.map +1 -1
  43. package/dist/run-blocks-controller.d.ts.map +1 -1
  44. package/dist/settings-contracts.d.ts +4 -0
  45. package/dist/settings-contracts.d.ts.map +1 -1
  46. package/dist/tui-memory-counters.d.ts +38 -0
  47. package/dist/tui-memory-counters.d.ts.map +1 -0
  48. package/package.json +7 -7
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/app.tsx
2
- import * as path8 from "node:path";
2
+ import * as path7 from "node:path";
3
3
  import {
4
4
  applyRewindToConversation,
5
5
  DefaultSessionRewinder
@@ -1565,7 +1565,7 @@ function StatusBar({
1565
1565
  const liveActive = memoryMonitor ? activeMemoryContextCount(memoryMonitor) : 0;
1566
1566
  const reportedActive = memoryMonitor ? liveActive : Sage?.activeInContext ?? 0;
1567
1567
  memoryDetailChips.push(
1568
- /* @__PURE__ */ jsx5(Text, { color: chipColor(theme.accent, isNoColor), children: isNoColor ? "Memory " : `${STATUSLINE_ICONS.memory_context} ` }, "mem-label")
1568
+ /* @__PURE__ */ jsx5(Text, { color: chipColor(theme.accent, isNoColor), children: isNoColor ? "Memory " : `${STATUSLINE_ICONS.memory_context} Memory ` }, "mem-label")
1569
1569
  );
1570
1570
  if (Sage) {
1571
1571
  memoryDetailChips.push(
@@ -1808,7 +1808,15 @@ function StatusBar({
1808
1808
  const detailChips = [];
1809
1809
  if (mailbox && showMailbox) {
1810
1810
  detailChips.push(
1811
- mailbox.unread > 0 ? /* @__PURE__ */ jsx5(Text, { color: isNoColor ? void 0 : theme.warn, bold: true, children: `${STATUSLINE_ICONS.mailbox} ${mailbox.unread} new` }) : /* @__PURE__ */ jsx5(Text, { dimColor: !isNoColor, children: `${STATUSLINE_ICONS.mailbox} 0` }),
1811
+ mailbox.unread > 0 ? /* @__PURE__ */ jsxs4(Text, { color: isNoColor ? void 0 : theme.warn, bold: true, children: [
1812
+ STATUSLINE_ICONS.mailbox,
1813
+ " ",
1814
+ mailbox.unread,
1815
+ " new"
1816
+ ] }) : /* @__PURE__ */ jsxs4(Text, { dimColor: !isNoColor, children: [
1817
+ STATUSLINE_ICONS.mailbox,
1818
+ " 0"
1819
+ ] }),
1812
1820
  /* @__PURE__ */ jsxs4(Text, { color: isNoColor ? void 0 : theme.accent, children: [
1813
1821
  glyphs.peers,
1814
1822
  " ",
@@ -3582,7 +3590,9 @@ var SETTINGS_PICKER_JUMP_CHORDS = Object.freeze([
3582
3590
  { mod: "alt-shift", letter: "g", field: 35, label: "Config scope" },
3583
3591
  { mod: "alt", letter: "n", field: 36, label: "Animation" },
3584
3592
  { mod: "alt", letter: "p", field: 40, label: "Show agent swarm panel" },
3585
- { mod: "alt", letter: "r", field: 41, label: "Pre-refine countdown" }
3593
+ { mod: "alt", letter: "r", field: 41, label: "Pre-refine countdown" },
3594
+ { mod: "alt-shift", letter: "s", field: 43, label: "Show SAGE Memory Inject" },
3595
+ { mod: "alt-shift", letter: "t", field: 44, label: "SAGE Memory Inject threshold" }
3586
3596
  ]);
3587
3597
  function settingsPickerJumpField(mod, letter) {
3588
3598
  const lower = letter.toLowerCase();
@@ -3708,7 +3718,7 @@ var MODE_DESC = {
3708
3718
  suggest: "Shows next-step suggestions after each turn",
3709
3719
  auto: "Self-driving \u2014 agent continues automatically"
3710
3720
  };
3711
- var SETTINGS_FIELD_COUNT = 43;
3721
+ var SETTINGS_FIELD_COUNT = 45;
3712
3722
  var THINKING_WORD_FIELD = 22;
3713
3723
  var THINKING_WORD_PRESETS = [
3714
3724
  "thinking",
@@ -3811,8 +3821,12 @@ var SETTINGS_FIELD_LABELS = [
3811
3821
  // 40
3812
3822
  "Pre-refine countdown",
3813
3823
  // 41
3814
- "Read symbols"
3824
+ "Read symbols",
3815
3825
  // 42
3826
+ "Show SAGE Memory Inject",
3827
+ // 43
3828
+ "SAGE Memory Inject threshold"
3829
+ // 44
3816
3830
  ];
3817
3831
  function resolveSettingsFieldValue(field, input) {
3818
3832
  const raw = input.trim().toLowerCase();
@@ -3837,7 +3851,8 @@ function resolveSettingsFieldValue(field, input) {
3837
3851
  [37, "breakerEnabled"],
3838
3852
  [39, "showModelReasoning"],
3839
3853
  [40, "showAgentSwarmPanel"],
3840
- [42, "readSymbols"]
3854
+ [42, "readSymbols"],
3855
+ [43, "showSageMemoryInject"]
3841
3856
  ]);
3842
3857
  const boolKey = BOOL_FIELDS.get(field);
3843
3858
  if (boolKey) {
@@ -3906,6 +3921,26 @@ function resolveSettingsFieldValue(field, input) {
3906
3921
  error: `Invalid value "${input}" for ${label}. Valid: ${values.join(", ")}.`
3907
3922
  };
3908
3923
  }
3924
+ if (field === 44) {
3925
+ const presets = SAGE_THRESHOLD_PRESETS;
3926
+ const rawMatch = raw.match(/^(\d+(?:\.\d+)?)$/);
3927
+ if (rawMatch) {
3928
+ const asFloat = Number.parseFloat(rawMatch[1]);
3929
+ if (presets.includes(asFloat)) {
3930
+ return {
3931
+ ok: true,
3932
+ patch: { sageMemoryInjectThreshold: asFloat },
3933
+ label,
3934
+ displayValue: formatSageThreshold(asFloat)
3935
+ };
3936
+ }
3937
+ }
3938
+ const options = presets.map((p) => formatSageThreshold(p)).join(", ");
3939
+ return {
3940
+ ok: false,
3941
+ error: `Invalid value "${input}" for ${label}. Available: ${options}.`
3942
+ };
3943
+ }
3909
3944
  const presetLabel = (n, zeroLabel) => n === 0 ? zeroLabel : String(n);
3910
3945
  const PRESET_FIELDS = [
3911
3946
  [1, "delayMs", DELAY_PRESETS_MS, (n) => formatSettingsDelay(n)],
@@ -3988,7 +4023,8 @@ function getSettingsFieldValue(values, field) {
3988
4023
  [37, "breakerEnabled"],
3989
4024
  [39, "showModelReasoning"],
3990
4025
  [40, "showAgentSwarmPanel"],
3991
- [42, "readSymbols"]
4026
+ [42, "readSymbols"],
4027
+ [43, "showSageMemoryInject"]
3992
4028
  ];
3993
4029
  for (const [f, key] of BOOL_KEYS) {
3994
4030
  if (field !== f) continue;
@@ -4023,7 +4059,8 @@ function getSettingsFieldValue(values, field) {
4023
4059
  [41, "preRefineSeconds", formatPreRefineSeconds],
4024
4060
  [21, "multiDiffSummaryThreshold", formatMultiDiffSummaryThreshold],
4025
4061
  [30, "maxConcurrent", (n) => presetLabel(n, "runtime default")],
4026
- [38, "breakerAutoKillResetMs", formatBreakerTimeout]
4062
+ [38, "breakerAutoKillResetMs", formatBreakerTimeout],
4063
+ [44, "sageMemoryInjectThreshold", formatSageThreshold]
4027
4064
  ];
4028
4065
  for (const [f, key, fmt2] of PRESET_KEYS) {
4029
4066
  if (field !== f) continue;
@@ -4077,7 +4114,7 @@ var SETTINGS_SECTIONS = [
4077
4114
  },
4078
4115
  {
4079
4116
  name: "Display",
4080
- fields: [39, 40, 41]
4117
+ fields: [39, 40, 41, 43, 44]
4081
4118
  }
4082
4119
  ];
4083
4120
  function formatAllSettingsSummary(values) {
@@ -4093,6 +4130,10 @@ function formatAllSettingsSummary(values) {
4093
4130
  }
4094
4131
  return lines.join("\n");
4095
4132
  }
4133
+ var SAGE_THRESHOLD_PRESETS = [0.72, 0.75, 0.85, 0.9, 0.95];
4134
+ function formatSageThreshold(t) {
4135
+ return t.toFixed(2);
4136
+ }
4096
4137
  var SETTINGS_DEFAULTS = Object.freeze({
4097
4138
  // Factory defaults mirror the core config defaults: autonomy 'auto'
4098
4139
  // (self-driving) and yolo on (auto-approve). Live values still come from
@@ -4139,7 +4180,9 @@ var SETTINGS_DEFAULTS = Object.freeze({
4139
4180
  breakerAutoKillResetMs: 6e4,
4140
4181
  showModelReasoning: true,
4141
4182
  showAgentSwarmPanel: true,
4142
- readSymbols: false
4183
+ readSymbols: false,
4184
+ showSageMemoryInject: false,
4185
+ sageMemoryInjectThreshold: 0.85
4143
4186
  });
4144
4187
  function resetSettingsFieldValue(field) {
4145
4188
  const result = getSettingsFieldValue(SETTINGS_DEFAULTS, field);
@@ -4192,7 +4235,9 @@ function buildResetPatch(field) {
4192
4235
  [39, "showModelReasoning"],
4193
4236
  [40, "showAgentSwarmPanel"],
4194
4237
  [41, "preRefineSeconds"],
4195
- [42, "readSymbols"]
4238
+ [42, "readSymbols"],
4239
+ [43, "showSageMemoryInject"],
4240
+ [44, "sageMemoryInjectThreshold"]
4196
4241
  ];
4197
4242
  for (const [f, key] of KEY_MAP) {
4198
4243
  if (f === field) {
@@ -4323,6 +4368,8 @@ function SettingsPicker({
4323
4368
  breakerAutoKillResetMs,
4324
4369
  showModelReasoning,
4325
4370
  showAgentSwarmPanel,
4371
+ showSageMemoryInject,
4372
+ sageMemoryInjectThreshold,
4326
4373
  readSymbols,
4327
4374
  hint
4328
4375
  }) {
@@ -4562,6 +4609,16 @@ function SettingsPicker({
4562
4609
  label: "Read symbols",
4563
4610
  value: boolVal(readSymbols),
4564
4611
  detail: "Include codebase-index symbols in read tool results"
4612
+ },
4613
+ {
4614
+ label: "Show SAGE Memory Inject",
4615
+ value: boolVal(showSageMemoryInject),
4616
+ detail: "Show SAGE memory injection blocks in tool results"
4617
+ },
4618
+ {
4619
+ label: "SAGE Memory Inject threshold",
4620
+ value: formatSageThreshold(sageMemoryInjectThreshold),
4621
+ detail: "Min relation strength for injection (0.72\u20130.95)"
4565
4622
  }
4566
4623
  ];
4567
4624
  const fieldRowIndex = [];
@@ -10427,6 +10484,7 @@ function retainCheckpoints(checkpoints, budget = {}) {
10427
10484
 
10428
10485
  // src/components/history/replay.ts
10429
10486
  import { SESSION_MARKER_EVENT_TYPES, sessionEventToMarker } from "@wrongstack/core/types";
10487
+ import { isFinalTurnStopReason } from "@wrongstack/tools/next-steps";
10430
10488
  var MARKER_EVENT_TYPES = SESSION_MARKER_EVENT_TYPES;
10431
10489
  var SUBAGENT_MARKER_STYLE = {
10432
10490
  agent_spawned: { color: "magenta", icon: "\u26A1" },
@@ -10464,18 +10522,18 @@ function replaySessionMessages(messages, events, startId) {
10464
10522
  backbone.push({ ts, entry });
10465
10523
  return entry;
10466
10524
  };
10467
- const appendText = (role, text, ts) => {
10525
+ const appendText = (role, text, ts, final) => {
10468
10526
  if (!text.trim()) return;
10469
10527
  push(
10470
10528
  ts,
10471
- role === "assistant" ? { kind: "assistant", text } : role === "system" ? { kind: "info", text } : { kind: "user", text }
10529
+ role === "assistant" ? { kind: "assistant", text, final } : role === "system" ? { kind: "info", text } : { kind: "user", text }
10472
10530
  );
10473
10531
  };
10474
10532
  for (const message of messages) {
10475
10533
  const ts = message.ts ?? lastTs;
10476
10534
  if (message.ts) lastTs = message.ts;
10477
10535
  if (typeof message.content === "string") {
10478
- appendText(message.role, message.content, ts);
10536
+ appendText(message.role, message.content, ts, true);
10479
10537
  continue;
10480
10538
  }
10481
10539
  for (const block of message.content) {
@@ -10486,7 +10544,8 @@ function replaySessionMessages(messages, events, startId) {
10486
10544
  appendText(
10487
10545
  message.role,
10488
10546
  message.content.filter((block) => block.type === "text").map((block) => block.text).join(""),
10489
- ts
10547
+ ts,
10548
+ !message.content.some((block) => block.type === "tool_use")
10490
10549
  );
10491
10550
  for (const block of message.content) {
10492
10551
  if (block.type === "tool_use") {
@@ -10567,7 +10626,7 @@ function eventToEntry(ev, pendingTools, completedTools) {
10567
10626
  }
10568
10627
  const text = ev.content.filter((b) => b.type === "text").map((b) => b.text).join("");
10569
10628
  if (!text.trim()) return null;
10570
- return { kind: "assistant", text };
10629
+ return { kind: "assistant", text, final: isFinalTurnStopReason(ev.stopReason) };
10571
10630
  }
10572
10631
  case "tool_use": {
10573
10632
  pendingTools.set(ev.id, { name: ev.name, input: ev.input, ts: ev.ts });
@@ -10824,9 +10883,9 @@ function rehydrateHistory(messages, startId, toolCalls) {
10824
10883
  outputTokens: tc.outputTokens,
10825
10884
  outputLines: tc.outputLines
10826
10885
  });
10827
- const pushAssistantText = (parts) => {
10886
+ const pushAssistantText = (parts, final) => {
10828
10887
  const text = parts.join("").trim();
10829
- if (text) entries.push({ id: nextId++, kind: "assistant", text });
10888
+ if (text) entries.push({ id: nextId++, kind: "assistant", text, final });
10830
10889
  parts.length = 0;
10831
10890
  };
10832
10891
  for (const msg of messages) {
@@ -10839,23 +10898,25 @@ function rehydrateHistory(messages, startId, toolCalls) {
10839
10898
  if (msg.role === "assistant") {
10840
10899
  if (!Array.isArray(msg.content)) {
10841
10900
  const text = textOf(msg).trim();
10842
- if (text) entries.push({ id: nextId++, kind: "assistant", text });
10901
+ if (text) entries.push({ id: nextId++, kind: "assistant", text, final: true });
10843
10902
  continue;
10844
10903
  }
10904
+ const blocks = msg.content;
10905
+ const final = !blocks.some((block) => block.type === "tool_use");
10845
10906
  const textParts = [];
10846
- for (const block of msg.content) {
10907
+ for (const block of blocks) {
10847
10908
  if (block.type === "text") {
10848
10909
  textParts.push(block.text);
10849
10910
  continue;
10850
10911
  }
10851
10912
  if (block.type !== "tool_use") continue;
10852
- pushAssistantText(textParts);
10913
+ pushAssistantText(textParts, final);
10853
10914
  const tc = toolCallsById.get(block.id);
10854
10915
  if (!tc) continue;
10855
10916
  entries.push(toolEntryFor(tc));
10856
10917
  consumed.add(block.id);
10857
10918
  }
10858
- pushAssistantText(textParts);
10919
+ pushAssistantText(textParts, final);
10859
10920
  }
10860
10921
  }
10861
10922
  if (toolCalls) {
@@ -11049,6 +11110,8 @@ function createInitialState(options) {
11049
11110
  breakerAutoKillResetMs: 6e4,
11050
11111
  showModelReasoning: true,
11051
11112
  showAgentSwarmPanel: true,
11113
+ showSageMemoryInject: false,
11114
+ sageMemoryInjectThreshold: 0.85,
11052
11115
  readSymbols: false
11053
11116
  },
11054
11117
  statuslinePicker: {
@@ -13343,6 +13406,8 @@ function reduceSettingsPanel(state, action) {
13343
13406
  breakerAutoKillResetMs: action.breakerAutoKillResetMs,
13344
13407
  showModelReasoning: action.showModelReasoning,
13345
13408
  showAgentSwarmPanel: action.showAgentSwarmPanel,
13409
+ showSageMemoryInject: action.showSageMemoryInject,
13410
+ sageMemoryInjectThreshold: action.sageMemoryInjectThreshold,
13346
13411
  readSymbols: action.readSymbols,
13347
13412
  hint: void 0
13348
13413
  }
@@ -13791,6 +13856,24 @@ function reduceSettingsValues(state, action) {
13791
13856
  ...state,
13792
13857
  settingsPicker: { ...sp, readSymbols: !sp.readSymbols, hint: void 0 }
13793
13858
  };
13859
+ if (f === 43)
13860
+ return {
13861
+ ...state,
13862
+ settingsPicker: { ...sp, showSageMemoryInject: !sp.showSageMemoryInject, hint: void 0 }
13863
+ };
13864
+ if (f === 44) {
13865
+ const j = SAGE_THRESHOLD_PRESETS.indexOf(sp.sageMemoryInjectThreshold);
13866
+ const base = j < 0 ? 0 : j;
13867
+ const next = (base + action.delta + SAGE_THRESHOLD_PRESETS.length) % SAGE_THRESHOLD_PRESETS.length;
13868
+ return {
13869
+ ...state,
13870
+ settingsPicker: {
13871
+ ...sp,
13872
+ sageMemoryInjectThreshold: expectDefined2(SAGE_THRESHOLD_PRESETS[next]),
13873
+ hint: void 0
13874
+ }
13875
+ };
13876
+ }
13794
13877
  return state;
13795
13878
  }
13796
13879
  case "settingsValueSet": {
@@ -15856,6 +15939,8 @@ function usePanelControllers({
15856
15939
  breakerAutoKillResetMs: s2.breakerAutoKillResetMs ?? 6e4,
15857
15940
  showModelReasoning: s2.showModelReasoning ?? true,
15858
15941
  showAgentSwarmPanel: s2.showAgentSwarmPanel ?? true,
15942
+ showSageMemoryInject: s2.showSageMemoryInject ?? false,
15943
+ sageMemoryInjectThreshold: s2.sageMemoryInjectThreshold ?? 0.85,
15859
15944
  readSymbols: s2.readSymbols ?? false
15860
15945
  });
15861
15946
  }, [getSettings]);
@@ -16318,6 +16403,7 @@ function usePromptPicker({
16318
16403
 
16319
16404
  // src/hooks/use-provider-event-bridge.ts
16320
16405
  import { tuiStreamFlushMs } from "@wrongstack/core/utils";
16406
+ import { isFinalTurnStopReason as isFinalTurnStopReason2 } from "@wrongstack/tools/next-steps";
16321
16407
  import {
16322
16408
  DEFAULT_MIN_IMPORTANCE,
16323
16409
  DEFAULT_MIN_SCORE,
@@ -16433,6 +16519,65 @@ function shortId(node) {
16433
16519
  return value.length > 22 ? `${value.slice(0, 19)}\u2026` : value;
16434
16520
  }
16435
16521
 
16522
+ // src/tui-memory-counters.ts
16523
+ var counters = {
16524
+ appRenders: 0,
16525
+ providerTextDeltaEvents: 0,
16526
+ providerTextDeltaChars: 0,
16527
+ providerThinkingDeltaEvents: 0,
16528
+ providerThinkingDeltaChars: 0,
16529
+ providerResponses: 0,
16530
+ toolProgressEvents: 0,
16531
+ toolProgressChars: 0,
16532
+ toolExecutedEvents: 0,
16533
+ toolOutputBytes: 0,
16534
+ streamFlushes: 0,
16535
+ historyRetainedEntries: 0,
16536
+ historyGroupedEntries: 0,
16537
+ historyMountedGroups: 0,
16538
+ historyMountedEntries: 0,
16539
+ historyCachedGroups: 0,
16540
+ historyMeasuredGroups: 0,
16541
+ historyViewportRows: 0,
16542
+ historyTotalRows: 0
16543
+ };
16544
+ function add(key, amount = 1) {
16545
+ counters[key] += Number.isFinite(amount) ? Math.max(0, amount) : 0;
16546
+ }
16547
+ function recordTuiAppRender() {
16548
+ add("appRenders");
16549
+ }
16550
+ function recordProviderTextDelta(chars) {
16551
+ add("providerTextDeltaEvents");
16552
+ add("providerTextDeltaChars", chars);
16553
+ }
16554
+ function recordProviderThinkingDelta(chars) {
16555
+ add("providerThinkingDeltaEvents");
16556
+ add("providerThinkingDeltaChars", chars);
16557
+ }
16558
+ function recordProviderResponse() {
16559
+ add("providerResponses");
16560
+ }
16561
+ function recordToolProgress(chars) {
16562
+ add("toolProgressEvents");
16563
+ add("toolProgressChars", chars);
16564
+ }
16565
+ function recordToolExecuted(outputBytes) {
16566
+ add("toolExecutedEvents");
16567
+ add("toolOutputBytes", outputBytes ?? 0);
16568
+ }
16569
+ function recordStreamFlush() {
16570
+ add("streamFlushes");
16571
+ }
16572
+ function setTuiHistoryMemoryGauges(gauges) {
16573
+ for (const [key, value] of Object.entries(gauges)) {
16574
+ counters[key] = Number.isFinite(value) ? Math.max(0, value) : 0;
16575
+ }
16576
+ }
16577
+ function snapshotTuiMemoryCounters() {
16578
+ return { ...counters };
16579
+ }
16580
+
16436
16581
  // src/hooks/subagent-history-format.ts
16437
16582
  function formatHistoryDuration(ms) {
16438
16583
  if (!Number.isFinite(ms) || ms < 0) return "0s";
@@ -16544,14 +16689,11 @@ function useProviderEventBridge({
16544
16689
  }
16545
16690
  };
16546
16691
  const flush = () => {
16547
- if (pendingDeltaRef.current) {
16548
- dispatch({ type: "streamDelta", delta: pendingDeltaRef.current });
16549
- pendingDeltaRef.current = "";
16550
- }
16551
16692
  if (pendingToolStream) {
16552
16693
  dispatch({ type: "toolStreamAppend", ...pendingToolStream });
16553
16694
  pendingToolStream = null;
16554
16695
  }
16696
+ recordStreamFlush();
16555
16697
  flushTimerRef.current = null;
16556
16698
  };
16557
16699
  const scheduleFlush = () => {
@@ -16560,29 +16702,19 @@ function useProviderEventBridge({
16560
16702
  const offDelta = events.on("provider.text_delta", (e) => {
16561
16703
  if (activeRunGenerationRef.current !== sessionGenerationRef.current) return;
16562
16704
  const text = e.text.replace(/\x1b?\[200~|\x1b?\[201~/g, "");
16705
+ recordProviderTextDelta(text.length);
16563
16706
  streamingTextRef.current = retainStreamTail(
16564
16707
  streamingTextRef.current,
16565
16708
  text,
16566
16709
  MAX_ASSISTANT_STREAM_RETAINED_CHARS
16567
16710
  );
16568
- pendingDeltaRef.current = retainStreamTail(
16569
- pendingDeltaRef.current,
16570
- text,
16571
- MAX_ASSISTANT_STREAM_RETAINED_CHARS
16572
- );
16573
16711
  appendStreamSegment("assistant", text);
16574
- scheduleFlush();
16575
16712
  });
16576
16713
  const offThinking = events.on("provider.thinking_delta", (e) => {
16577
16714
  if (activeRunGenerationRef.current !== sessionGenerationRef.current) return;
16578
16715
  const text = e.text.replace(/\x1b?\[200~|\x1b?\[201~/g, "");
16716
+ recordProviderThinkingDelta(text.length);
16579
16717
  appendStreamSegment("thinking", text);
16580
- pendingDeltaRef.current = retainStreamTail(
16581
- pendingDeltaRef.current,
16582
- text,
16583
- MAX_ASSISTANT_STREAM_RETAINED_CHARS
16584
- );
16585
- scheduleFlush();
16586
16718
  });
16587
16719
  const offToolStart = events.on("tool.started", (e) => {
16588
16720
  dispatch({ type: "toolStarted", id: e.id, name: e.name });
@@ -16596,11 +16728,16 @@ function useProviderEventBridge({
16596
16728
  });
16597
16729
  const offToolProgress = events.on("tool.progress", (e) => {
16598
16730
  if (e.event.type !== "partial_output" || !e.event.text) return;
16731
+ recordToolProgress(e.event.text.length);
16599
16732
  const prev = pendingToolStream;
16600
16733
  if (prev && prev.toolUseId === e.id) {
16601
16734
  pendingToolStream = {
16602
16735
  ...prev,
16603
- text: prev.text + e.event.text,
16736
+ text: retainStreamTail(
16737
+ prev.text,
16738
+ e.event.text,
16739
+ MAX_TOOL_STREAM_RETAINED_CHARS
16740
+ ),
16604
16741
  startedAt: prev.startedAt
16605
16742
  };
16606
16743
  } else {
@@ -16615,6 +16752,7 @@ function useProviderEventBridge({
16615
16752
  scheduleFlush();
16616
16753
  });
16617
16754
  const offTool = events.on("tool.executed", (e) => {
16755
+ recordToolExecuted(e.outputBytes);
16618
16756
  if (pendingToolStream) {
16619
16757
  dispatch({ type: "toolStreamAppend", ...pendingToolStream });
16620
16758
  pendingToolStream = null;
@@ -16674,6 +16812,7 @@ function useProviderEventBridge({
16674
16812
  });
16675
16813
  const offProvResp = events.on("provider.response", (e) => {
16676
16814
  if (activeRunGenerationRef.current !== sessionGenerationRef.current) return;
16815
+ recordProviderResponse();
16677
16816
  const segments = canonicalStreamSegments(e.content ?? []);
16678
16817
  const fallbackText = contentBlocksText(e.content);
16679
16818
  streamingTextRef.current = "";
@@ -16684,14 +16823,18 @@ function useProviderEventBridge({
16684
16823
  flushTimerRef.current = null;
16685
16824
  }
16686
16825
  dispatch({ type: "streamReset" });
16826
+ const final = isFinalTurnStopReason2(e.stopReason);
16687
16827
  for (const seg of segments) {
16688
16828
  if (seg.text.trim()) {
16689
- dispatch({ type: "addEntry", entry: { kind: seg.kind, text: seg.text } });
16829
+ dispatch({
16830
+ type: "addEntry",
16831
+ entry: seg.kind === "assistant" ? { kind: "assistant", text: seg.text, final } : { kind: "thinking", text: seg.text }
16832
+ });
16690
16833
  if (seg.kind === "assistant") assistantCommittedThisRunRef.current = true;
16691
16834
  }
16692
16835
  }
16693
16836
  if (segments.length === 0 && fallbackText.trim()) {
16694
- dispatch({ type: "addEntry", entry: { kind: "assistant", text: fallbackText } });
16837
+ dispatch({ type: "addEntry", entry: { kind: "assistant", text: fallbackText, final } });
16695
16838
  assistantCommittedThisRunRef.current = true;
16696
16839
  }
16697
16840
  });
@@ -17266,6 +17409,8 @@ function useSettingsAutoSave(state, saveSettings, dispatch) {
17266
17409
  breakerAutoKillResetMs: sp.breakerAutoKillResetMs,
17267
17410
  showModelReasoning: sp.showModelReasoning,
17268
17411
  showAgentSwarmPanel: sp.showAgentSwarmPanel,
17412
+ showSageMemoryInject: sp.showSageMemoryInject,
17413
+ sageMemoryInjectThreshold: sp.sageMemoryInjectThreshold,
17269
17414
  readSymbols: sp.readSymbols
17270
17415
  })
17271
17416
  ).then((err) => {
@@ -17315,6 +17460,8 @@ function useSettingsAutoSave(state, saveSettings, dispatch) {
17315
17460
  state.settingsPicker.breakerAutoKillResetMs,
17316
17461
  state.settingsPicker.showModelReasoning,
17317
17462
  state.settingsPicker.showAgentSwarmPanel,
17463
+ state.settingsPicker.showSageMemoryInject,
17464
+ state.settingsPicker.sageMemoryInjectThreshold,
17318
17465
  state.settingsPicker.readSymbols,
17319
17466
  saveSettings
17320
17467
  ]);
@@ -17949,131 +18096,12 @@ import { useCallback as useCallback18, useEffect as useEffect39, useMemo as useM
17949
18096
  import * as os from "node:os";
17950
18097
 
17951
18098
  // src/heap-watchdog.ts
17952
- import * as fsp from "node:fs/promises";
17953
- import * as path5 from "node:path";
17954
- import * as v8 from "node:v8";
17955
- import { wstackGlobalRoot } from "@wrongstack/core/utils";
17956
- var MB = 1024 * 1024;
17957
- function defaultHeapLogPath() {
17958
- return path5.join(wstackGlobalRoot(), "logs", "heap.jsonl");
17959
- }
17960
- function takeHeapSample() {
17961
- const m = process.memoryUsage();
17962
- const limit = v8.getHeapStatistics().heap_size_limit || 0;
17963
- return {
17964
- ts: (/* @__PURE__ */ new Date()).toISOString(),
17965
- rss: m.rss,
17966
- heapUsed: m.heapUsed,
17967
- heapTotal: m.heapTotal,
17968
- external: m.external,
17969
- heapLimit: limit,
17970
- load: limit > 0 ? m.heapUsed / limit : 0
17971
- };
17972
- }
17973
- function startHeapWatchdog(opts = {}) {
17974
- const sampleEveryMs = opts.sampleEveryMs ?? 6e4;
17975
- const logEveryMs = opts.logEveryMs ?? 3e5;
17976
- const logPath = opts.logPath ?? defaultHeapLogPath();
17977
- const warnAt = opts.warnAt ?? 0.6;
17978
- const criticalAt = opts.criticalAt ?? 0.85;
17979
- const REARM_MARGIN = 0.05;
17980
- let warnArmed = true;
17981
- let criticalArmed = true;
17982
- let lastLogAt = 0;
17983
- let writeInFlight = false;
17984
- let pendingLine;
17985
- let drainPromise;
17986
- let stopped = false;
17987
- let dirReady = false;
17988
- const writeDiagnosticLine = opts.writeDiagnosticLine ?? (async (targetPath, line) => {
17989
- if (!dirReady) {
17990
- await fsp.mkdir(path5.dirname(targetPath), { recursive: true });
17991
- dirReady = true;
17992
- }
17993
- await fsp.appendFile(targetPath, `${line}
17994
- `, "utf8");
17995
- });
17996
- const drainWrites = async (firstLine) => {
17997
- let line = firstLine;
17998
- while (line !== void 0) {
17999
- try {
18000
- await writeDiagnosticLine(logPath, line);
18001
- } catch {
18002
- }
18003
- line = pendingLine;
18004
- pendingLine = void 0;
18005
- }
18006
- writeInFlight = false;
18007
- };
18008
- const append = (line) => {
18009
- if (stopped) return;
18010
- if (writeInFlight) {
18011
- pendingLine = line;
18012
- return;
18013
- }
18014
- writeInFlight = true;
18015
- drainPromise = drainWrites(line);
18016
- };
18017
- const tick = () => {
18018
- let userTimings = 0;
18019
- try {
18020
- userTimings = performance.getEntriesByType("mark").length + performance.getEntriesByType("measure").length;
18021
- if (userTimings > 0) {
18022
- performance.clearMarks();
18023
- performance.clearMeasures();
18024
- }
18025
- } catch {
18026
- }
18027
- const s2 = takeHeapSample();
18028
- if (s2.load >= criticalAt && criticalArmed) {
18029
- criticalArmed = false;
18030
- warnArmed = false;
18031
- opts.onWarn?.(
18032
- "critical",
18033
- `Heap critical: ${Math.round(s2.heapUsed / MB)} MB of ~${Math.round(s2.heapLimit / MB)} MB V8 limit (${Math.round(s2.load * 100)}%). An out-of-memory crash is likely soon \u2014 finish/checkpoint work and restart the session. Diagnostics: ${logPath}`,
18034
- s2
18035
- );
18036
- } else if (s2.load >= warnAt && warnArmed) {
18037
- warnArmed = false;
18038
- opts.onWarn?.(
18039
- "warn",
18040
- `Heap high: ${Math.round(s2.heapUsed / MB)} MB of ~${Math.round(s2.heapLimit / MB)} MB V8 limit (${Math.round(s2.load * 100)}%). Memory diagnostics are being recorded to ${logPath}`,
18041
- s2
18042
- );
18043
- }
18044
- if (!warnArmed && s2.load < warnAt - REARM_MARGIN) warnArmed = true;
18045
- if (!criticalArmed && s2.load < criticalAt - REARM_MARGIN) criticalArmed = true;
18046
- const due = Date.now() - lastLogAt >= logEveryMs;
18047
- const crossed = s2.load >= warnAt;
18048
- if (due || crossed) {
18049
- lastLogAt = Date.now();
18050
- let extras = {};
18051
- try {
18052
- extras = opts.collectStats?.() ?? {};
18053
- } catch {
18054
- }
18055
- append(JSON.stringify({ pid: process.pid, ...s2, userTimings, ...extras }));
18056
- }
18057
- };
18058
- const timer = setInterval(tick, sampleEveryMs);
18059
- timer.unref?.();
18060
- tick();
18061
- if (process.env["NODE_ENV"] !== "production") {
18062
- append(
18063
- JSON.stringify({
18064
- pid: process.pid,
18065
- warn: "dev_reconciler",
18066
- nodeEnv: process.env["NODE_ENV"] ?? "",
18067
- ts: (/* @__PURE__ */ new Date()).toISOString()
18068
- })
18069
- );
18070
- }
18071
- return async () => {
18072
- stopped = true;
18073
- clearInterval(timer);
18074
- await drainPromise;
18075
- };
18076
- }
18099
+ import {
18100
+ defaultHeapLogPath,
18101
+ startHeapWatchdog,
18102
+ startSharedHeapWatchdog,
18103
+ takeHeapSample
18104
+ } from "@wrongstack/core/utils";
18077
18105
 
18078
18106
  // src/hooks/use-tui-activity.ts
18079
18107
  function attachmentsSize(store) {
@@ -18102,6 +18130,7 @@ function useTuiActivity({
18102
18130
  attachments,
18103
18131
  builderRef
18104
18132
  }) {
18133
+ recordTuiAppRender();
18105
18134
  const [rolledThinkingWord, setRolledThinkingWord] = useState17(() => pickRandomTuiThinkingWord());
18106
18135
  const thinkingWorking = status === "running" || status === "streaming";
18107
18136
  const prevThinkingWorkingRef = useRef28(false);
@@ -18178,30 +18207,38 @@ function useTuiActivity({
18178
18207
  return Math.min(100, Math.round(cpuDeltaUsec / 1e3 / wallMs / CPU_CORES * 100));
18179
18208
  }, [nowTick]);
18180
18209
  useEffect39(() => {
18181
- const stopHeapWatchdog = startHeapWatchdog({
18182
- collectStats: () => ({
18183
- // Keep this slice to shallow cardinalities: serializing the full
18184
- // retained graphs created a second allocation spike precisely when
18185
- // the heap was already high.
18186
- historyEntries: stateRef.current.entries.length,
18187
- messages: agentContext.state.messages.length,
18188
- runningTools: stateRef.current.runningTools.size,
18189
- stdoutQueued: process.stdout.writableLength ?? 0,
18190
- fleetSize: Object.keys(stateRef.current.fleet ?? {}).length,
18191
- queued: stateRef.current.queue?.length ?? 0,
18192
- inputHistory: stateRef.current.inputHistory?.length ?? 0,
18193
- // RAM retention sentinels — distinguish which layer holds old graphs
18194
- // after /clear so we know where to add cleanup wiring.
18195
- // attachments: DefaultAttachmentStore.items.size (cumulative
18196
- // pastes/files/images for the lifetime of this process).
18197
- // builderRefs: InputBuilder.refs.length (refs retained by the
18198
- // mounted builder until reset() is called).
18199
- // directorInFlight: coordinator in-flight task count, used to
18200
- // detect that `/clear` did not actually drain subagent work.
18201
- attachments: attachmentsSize(attachments),
18202
- builderRefs: builderRefsLength(builderRef.current),
18203
- directorInFlight: directorInFlight(agentContext)
18204
- }),
18210
+ const stopHeapWatchdog = startSharedHeapWatchdog({
18211
+ collectStats: () => {
18212
+ const currentState = stateRef.current;
18213
+ const messages = agentContext.state.messages;
18214
+ return {
18215
+ surface: "tui",
18216
+ sessionId: agentContext.session.id,
18217
+ // Keep this slice to shallow cardinalities: serializing the full
18218
+ // retained graphs created a second allocation spike precisely when
18219
+ // the heap was already high.
18220
+ historyEntries: currentState.entries.length,
18221
+ messages: messages.length,
18222
+ runningTools: currentState.runningTools.size,
18223
+ stdoutQueued: process.stdout.writableLength ?? 0,
18224
+ fleetSize: Object.keys(currentState.fleet ?? {}).length,
18225
+ queued: currentState.queue?.length ?? 0,
18226
+ inputHistory: currentState.inputHistory?.length ?? 0,
18227
+ toolStreamChars: currentState.toolStream?.text.length ?? 0,
18228
+ ...snapshotTuiMemoryCounters(),
18229
+ // RAM retention sentinels — distinguish which layer holds old graphs
18230
+ // after /clear so we know where to add cleanup wiring.
18231
+ // attachments: DefaultAttachmentStore.items.size (cumulative
18232
+ // pastes/files/images for the lifetime of this process).
18233
+ // builderRefs: InputBuilder.refs.length (refs retained by the
18234
+ // mounted builder until reset() is called).
18235
+ // directorInFlight: coordinator in-flight task count, used to
18236
+ // detect that `/clear` did not actually drain subagent work.
18237
+ attachments: attachmentsSize(attachments),
18238
+ builderRefs: builderRefsLength(builderRef.current),
18239
+ directorInFlight: directorInFlight(agentContext)
18240
+ };
18241
+ },
18205
18242
  onWarn: (level, message) => {
18206
18243
  dispatch({
18207
18244
  type: "addEntry",
@@ -19283,12 +19320,12 @@ ${menu}` };
19283
19320
  kitId: active.kit,
19284
19321
  outPath
19285
19322
  });
19286
- const fsp2 = await import("node:fs/promises");
19323
+ const fsp = await import("node:fs/promises");
19287
19324
  const nodePath = await import("node:path");
19288
19325
  const abs = nodePath.join(projectRoot, result.path);
19289
19326
  try {
19290
- await fsp2.mkdir(nodePath.dirname(abs), { recursive: true });
19291
- await fsp2.writeFile(abs, result.content);
19327
+ await fsp.mkdir(nodePath.dirname(abs), { recursive: true });
19328
+ await fsp.writeFile(abs, result.content);
19292
19329
  } catch (e) {
19293
19330
  return { message: `Failed to write ${result.path}: ${e.message}` };
19294
19331
  }
@@ -19562,11 +19599,11 @@ Available chords:
19562
19599
  }
19563
19600
 
19564
19601
  // src/hooks/use-working-dir-chip.ts
19565
- import * as path6 from "node:path";
19602
+ import * as path5 from "node:path";
19566
19603
  import { useEffect as useEffect45, useState as useState18 } from "react";
19567
19604
  function formatWorkingDirChip(workingDir, projectRoot) {
19568
19605
  if (!workingDir || workingDir === projectRoot) return void 0;
19569
- const impl = workingDir.includes("\\") || projectRoot.includes("\\") ? path6.win32 : path6;
19606
+ const impl = workingDir.includes("\\") || projectRoot.includes("\\") ? path5.win32 : path5;
19570
19607
  const rel = impl.relative(projectRoot, workingDir) || ".";
19571
19608
  if (rel === ".") return void 0;
19572
19609
  return rel.replaceAll(impl.sep, "/");
@@ -23452,7 +23489,7 @@ function PhasePanel({ phases, nowTick }) {
23452
23489
  // src/components/plan-panel.tsx
23453
23490
  import { useCallback as useCallback22, useEffect as useEffect56, useMemo as useMemo11, useState as useState29 } from "react";
23454
23491
  import * as fs4 from "node:fs/promises";
23455
- import * as path7 from "node:path";
23492
+ import * as path6 from "node:path";
23456
23493
  import { resolveWstackPaths as resolveWstackPaths4, sessionScopedPath } from "@wrongstack/core/utils";
23457
23494
  import { jsx as jsx29, jsxs as jsxs28 } from "react/jsx-runtime";
23458
23495
  var STATUS_COLOR = {
@@ -23468,12 +23505,12 @@ var STATUS_ICON = {
23468
23505
  function planFilePath(projectRoot, sessionId, scope) {
23469
23506
  const base = resolveWstackPaths4({ projectRoot }).projectSessions;
23470
23507
  if (scope === "project") {
23471
- return path7.join(base, "backlog.plan.json");
23508
+ return path6.join(base, "backlog.plan.json");
23472
23509
  }
23473
23510
  if (sessionId) {
23474
23511
  return sessionScopedPath(base, sessionId, ".plan.json");
23475
23512
  }
23476
- return path7.join(base, "backlog.plan.json");
23513
+ return path6.join(base, "backlog.plan.json");
23477
23514
  }
23478
23515
  function PlanPanel({
23479
23516
  projectRoot,
@@ -26413,8 +26450,8 @@ function detectLang(fenceInfo) {
26413
26450
  const tag = fenceInfo.trim().toLowerCase().split(/\s+/)[0] ?? "";
26414
26451
  return LANG_ALIASES[tag] ?? "plain";
26415
26452
  }
26416
- function langFromPath(path11) {
26417
- const m = /\.([A-Za-z0-9]+)$/.exec(path11.trim());
26453
+ function langFromPath(path10) {
26454
+ const m = /\.([A-Za-z0-9]+)$/.exec(path10.trim());
26418
26455
  const ext = m?.[1]?.toLowerCase() ?? "";
26419
26456
  return LANG_ALIASES[ext] ?? "plain";
26420
26457
  }
@@ -27463,7 +27500,7 @@ function parseMeta(meta) {
27463
27500
  }
27464
27501
  function parseSageMemoryLine(line) {
27465
27502
  const match = SAGE_MEMORY_DETAIL.exec(line);
27466
- if (!match || !match.groups) return null;
27503
+ if (!match?.groups) return null;
27467
27504
  const rawLabels = match.groups["labels"] ?? "";
27468
27505
  const labels = Array.from(rawLabels.matchAll(/\[([^\]]+)\]/g)).map((m) => m[1] ?? "").filter(Boolean);
27469
27506
  return {
@@ -27726,19 +27763,19 @@ function formatToolArgs(toolName, input) {
27726
27763
  case "codebase-stats":
27727
27764
  return "index health";
27728
27765
  case "lsp_diagnostics": {
27729
- const path11 = stringOf(obj["path"]);
27766
+ const path10 = stringOf(obj["path"]);
27730
27767
  const severity = stringArrayOf(obj["severity"]) ?? stringArrayOf(obj["severities"]);
27731
- return [path11 ? shortenPath(path11, 40) : "workspace", severity?.join("+")].filter(Boolean).join(" \xB7 ");
27768
+ return [path10 ? shortenPath(path10, 40) : "workspace", severity?.join("+")].filter(Boolean).join(" \xB7 ");
27732
27769
  }
27733
27770
  case "lsp_definition":
27734
27771
  case "lsp_completion":
27735
27772
  case "lsp_rename": {
27736
- const path11 = stringOf(obj["path"]) ?? stringOf(obj["file"]);
27773
+ const path10 = stringOf(obj["path"]) ?? stringOf(obj["file"]);
27737
27774
  const line = numOf(obj["line"]);
27738
27775
  const character = numOf(obj["character"]);
27739
27776
  const position = line !== void 0 ? `L${line}:${character ?? 1}` : "";
27740
27777
  const rename = toolName === "lsp_rename" ? stringOf(obj["new_name"]) : void 0;
27741
- return [path11 ? shortenPath(path11, 38) : "", position, rename ? `\u2192 ${rename}` : ""].filter(Boolean).join(" \xB7 ");
27778
+ return [path10 ? shortenPath(path10, 38) : "", position, rename ? `\u2192 ${rename}` : ""].filter(Boolean).join(" \xB7 ");
27742
27779
  }
27743
27780
  case "set_working_dir": {
27744
27781
  const p = stringOf(obj["path"]);
@@ -28181,19 +28218,19 @@ function visualEdit(toolName, text, ok) {
28181
28218
  const parsed = tryParseJson(text);
28182
28219
  if (parsed && typeof parsed === "object") {
28183
28220
  const obj = parsed;
28184
- const path11 = typeof obj["path"] === "string" ? obj["path"] : void 0;
28221
+ const path10 = typeof obj["path"] === "string" ? obj["path"] : void 0;
28185
28222
  const replacements = typeof obj["replacements"] === "number" ? obj["replacements"] : void 0;
28186
28223
  const bytes = typeof obj["bytes"] === "number" ? obj["bytes"] : void 0;
28187
28224
  const created = obj["created"] === true;
28188
28225
  const files = Array.isArray(obj["files"]) ? obj["files"] : void 0;
28189
28226
  const results = Array.isArray(obj["results"]) ? obj["results"] : void 0;
28190
- if (toolName === "edit" && path11 !== void 0) {
28227
+ if (toolName === "edit" && path10 !== void 0) {
28191
28228
  const repText = replacements !== void 0 ? `${replacements} replacement${replacements === 1 ? "" : "s"}` : void 0;
28192
- rows.push({ kind: "ok", text: "", marker: "edit ", path: path11 });
28229
+ rows.push({ kind: "ok", text: "", marker: "edit ", path: path10 });
28193
28230
  if (repText) rows.push({ kind: "meta", text: repText });
28194
- } else if (toolName === "write" && path11 !== void 0) {
28231
+ } else if (toolName === "write" && path10 !== void 0) {
28195
28232
  const sizeText = bytes !== void 0 ? `${bytes} bytes` : created ? "new file" : "updated";
28196
- rows.push({ kind: "ok", text: "", marker: "write ", path: path11 });
28233
+ rows.push({ kind: "ok", text: "", marker: "write ", path: path10 });
28197
28234
  rows.push({ kind: "meta", text: sizeText });
28198
28235
  } else if ((toolName === "diff" || toolName === "patch") && files && files.length > 0) {
28199
28236
  rows.push({
@@ -28216,8 +28253,8 @@ function visualEdit(toolName, text, ok) {
28216
28253
  marker: "replace ",
28217
28254
  text: `${results.length} replacement${results.length === 1 ? "" : "s"} across ${fileCount} file${fileCount === 1 ? "" : "s"}`
28218
28255
  });
28219
- } else if (path11 !== void 0) {
28220
- rows.push({ kind: "ok", text: "", marker: `${toolName} `, path: path11 });
28256
+ } else if (path10 !== void 0) {
28257
+ rows.push({ kind: "ok", text: "", marker: `${toolName} `, path: path10 });
28221
28258
  } else {
28222
28259
  return void 0;
28223
28260
  }
@@ -28311,12 +28348,12 @@ function parseMatchHit(hit) {
28311
28348
  }
28312
28349
  if (hit && typeof hit === "object") {
28313
28350
  const o = hit;
28314
- const path11 = stringOf(o["file"]) ?? stringOf(o["path"]) ?? stringOf(o["url"]);
28351
+ const path10 = stringOf(o["file"]) ?? stringOf(o["path"]) ?? stringOf(o["url"]);
28315
28352
  const line = numOf(o["line"]) ?? numOf(o["lineNumber"]);
28316
28353
  const title = stringOf(o["title"]);
28317
28354
  const snippet2 = stringOf(o["snippet"]);
28318
28355
  const text = stringOf(o["text"]) ?? stringOf(o["match"]) ?? stringOf(o["preview"]) ?? [title, snippet2].filter(Boolean).join(" \u2014 ");
28319
- return { path: path11, line: line === void 0 ? void 0 : String(line), text };
28356
+ return { path: path10, line: line === void 0 ? void 0 : String(line), text };
28320
28357
  }
28321
28358
  return void 0;
28322
28359
  }
@@ -28414,8 +28451,8 @@ function splitReplaceDiffs(obj) {
28414
28451
  const record = result;
28415
28452
  const diff = stringOf(record["diff"]);
28416
28453
  if (!diff?.trim()) continue;
28417
- const path11 = stringOf(record["path"]);
28418
- items.push(path11 ? { path: path11, diff } : { diff });
28454
+ const path10 = stringOf(record["path"]);
28455
+ items.push(path10 ? { path: path10, diff } : { diff });
28419
28456
  }
28420
28457
  return items;
28421
28458
  }
@@ -28490,8 +28527,8 @@ function collectMultiFileDiffItems(toolName, output, _input) {
28490
28527
  if (!diffText?.trim()) return void 0;
28491
28528
  const blocks = splitGitStyleDiff(diffText);
28492
28529
  if (blocks.length === 0) {
28493
- const path11 = explicitFiles[0] ?? extractPatchHeaderPath(diffText);
28494
- return [{ path: path11, diff: diffText }];
28530
+ const path10 = explicitFiles[0] ?? extractPatchHeaderPath(diffText);
28531
+ return [{ path: path10, diff: diffText }];
28495
28532
  }
28496
28533
  return blocks.map((block, idx) => ({
28497
28534
  path: explicitFiles[idx] ?? block.path,
@@ -28525,9 +28562,9 @@ function newFileDiffFromWriteInput(output, input) {
28525
28562
  const obj = input;
28526
28563
  const content = stringOf(obj["content"]);
28527
28564
  if (content === void 0) return void 0;
28528
- const path11 = stringOf(output["path"]) ?? stringOf(obj["path"]) ?? "new file";
28565
+ const path10 = stringOf(output["path"]) ?? stringOf(obj["path"]) ?? "new file";
28529
28566
  const lines = content === "" ? [] : content.replace(/\n$/, "").split("\n");
28530
- const header = [`+++ ${path11}`, `@@ -0,0 +1,${lines.length} @@`];
28567
+ const header = [`+++ ${path10}`, `@@ -0,0 +1,${lines.length} @@`];
28531
28568
  return [...header, ...lines.map((line) => `+${line}`)].join("\n");
28532
28569
  }
28533
28570
  function splitGitStyleDiff(diff) {
@@ -28567,8 +28604,8 @@ function splitGitStyleDiff(diff) {
28567
28604
  }
28568
28605
  flush();
28569
28606
  if (blocks.length === 1 && !blocks[0].path) {
28570
- const path11 = extractPatchHeaderPath(blocks[0].diff);
28571
- if (path11) blocks[0] = { path: path11, diff: blocks[0].diff };
28607
+ const path10 = extractPatchHeaderPath(blocks[0].diff);
28608
+ if (path10) blocks[0] = { path: path10, diff: blocks[0].diff };
28572
28609
  }
28573
28610
  return blocks;
28574
28611
  }
@@ -28589,8 +28626,8 @@ function parseDiffGitPath(line) {
28589
28626
  function extractPatchHeaderPath(diffText) {
28590
28627
  for (const line of diffText.split("\n")) {
28591
28628
  if (line.startsWith("+++ ")) {
28592
- const path11 = line.slice(4).trim();
28593
- const cleaned = path11.replace(/^b\//, "").split(" ")[0].trim();
28629
+ const path10 = line.slice(4).trim();
28630
+ const cleaned = path10.replace(/^b\//, "").split(" ")[0].trim();
28594
28631
  return cleaned || void 0;
28595
28632
  }
28596
28633
  }
@@ -28722,13 +28759,13 @@ function formatMultiDiffSummary(summary, threshold = MULTI_DIFF_SUMMARY_THRESHOL
28722
28759
  return parts.join(" \xB7 ");
28723
28760
  }
28724
28761
  function DiffFileBlock({
28725
- path: path11,
28762
+ path: path10,
28726
28763
  preview,
28727
28764
  useColor = true,
28728
28765
  contentWidth
28729
28766
  }) {
28730
28767
  return /* @__PURE__ */ jsxs45(Box, { flexDirection: "column", marginTop: 1, children: [
28731
- /* @__PURE__ */ jsx46(Text, { dimColor: true, italic: true, children: path11 }),
28768
+ /* @__PURE__ */ jsx46(Text, { dimColor: true, italic: true, children: path10 }),
28732
28769
  /* @__PURE__ */ jsx46(
28733
28770
  DiffBlock,
28734
28771
  {
@@ -28739,7 +28776,7 @@ function DiffFileBlock({
28739
28776
  hiddenAdded: preview.hiddenAdded,
28740
28777
  hiddenRemoved: preview.hiddenRemoved,
28741
28778
  useColor,
28742
- lang: langFromPath(path11),
28779
+ lang: langFromPath(path10),
28743
28780
  contentWidth
28744
28781
  }
28745
28782
  )
@@ -28996,7 +29033,7 @@ function renderDiffLine(line) {
28996
29033
  "\n"
28997
29034
  ] }, line);
28998
29035
  }
28999
- function renderDiff(diff, path11) {
29036
+ function renderDiff(diff, path10) {
29000
29037
  const preview = parseUnifiedDiff(diff, CONFIRM_DIFF_MAX_LINES);
29001
29038
  if (preview.rows.length > 0) {
29002
29039
  return /* @__PURE__ */ jsx47(
@@ -29009,7 +29046,7 @@ function renderDiff(diff, path11) {
29009
29046
  hiddenAdded: preview.hiddenAdded,
29010
29047
  hiddenRemoved: preview.hiddenRemoved,
29011
29048
  useColor: theme.supportsBackground,
29012
- lang: langFromPath(path11 ?? "")
29049
+ lang: langFromPath(path10 ?? "")
29013
29050
  }
29014
29051
  );
29015
29052
  }
@@ -29092,7 +29129,11 @@ import {
29092
29129
  import { resolveWstackPaths as resolveWstackPaths5 } from "@wrongstack/core/utils";
29093
29130
  import { getKanbanServerConnection } from "@wrongstack/kanban";
29094
29131
  import { isSageProjectServerAvailable, SageProjectServerConnection } from "@wrongstack/sage";
29095
- import { checkCodebaseIndexServerHealth, getIndexState as getIndexState2 } from "@wrongstack/tools";
29132
+ import {
29133
+ checkCodebaseIndexServerHealth,
29134
+ getIndexState as getIndexState2,
29135
+ resolveProjectIndexDaemonAvailability
29136
+ } from "@wrongstack/tools";
29096
29137
  async function collectConnectionsHealth(projectRoot) {
29097
29138
  const services = await Promise.all([
29098
29139
  chronicleHealth(projectRoot),
@@ -29138,6 +29179,19 @@ async function chronicleHealth(projectRoot) {
29138
29179
  }
29139
29180
  async function codebaseIndexHealth(projectRoot) {
29140
29181
  const startedAt = Date.now();
29182
+ const availability = resolveProjectIndexDaemonAvailability(projectRoot);
29183
+ if (availability.kind === "endpoint-invalid") {
29184
+ return {
29185
+ id: "codebase-index",
29186
+ label: "Codebase index",
29187
+ status: "unavailable",
29188
+ required: false,
29189
+ mode: "endpoint-invalid",
29190
+ detail: `Socket path is ${availability.byteLength} bytes \u2014 over this platform's ${availability.maxBytes}-byte sun_path limit. Queries fall back to a process-local index. Set a shorter TMPDIR to restore the shared daemon.`,
29191
+ endpoint: availability.endpoint,
29192
+ latencyMs: Date.now() - startedAt
29193
+ };
29194
+ }
29141
29195
  try {
29142
29196
  const health2 = await checkCodebaseIndexServerHealth(projectRoot, void 0, { timeoutMs: 2e3 });
29143
29197
  const connection = getIndexState2().server;
@@ -30187,8 +30241,8 @@ function FilePicker({ query, matches, selected }) {
30187
30241
  ] }, m))
30188
30242
  ] });
30189
30243
  }
30190
- function highlight(path11, _query) {
30191
- return path11;
30244
+ function highlight(path10, _query) {
30245
+ return path10;
30192
30246
  }
30193
30247
 
30194
30248
  // src/components/help-panel.tsx
@@ -31536,14 +31590,14 @@ function estimateSageKvRows(cells, contentWidth) {
31536
31590
  }
31537
31591
  return rows;
31538
31592
  }
31539
- function estimateRenderGroupRows(group, contentWidth) {
31593
+ function estimateRenderGroupRows(group, contentWidth, showSageMemoryInject) {
31540
31594
  if (group.type === "tool-group") return group.data.entries.length + 2;
31541
31595
  const { entry } = group;
31542
31596
  if (entry.kind === "tool") {
31543
31597
  const { cleanOutput, sageLines } = resolveEntrySage(entry.output, entry.sageLines);
31544
31598
  const memoryLines = sageLines.slice(1);
31545
31599
  const panelContentWidth = Math.max(16, contentWidth - 4);
31546
- const sagePanelRows = memoryLines.length > 0 ? 2 + estimateTextRows(
31600
+ const sagePanelRows = showSageMemoryInject === false || memoryLines.length === 0 ? 0 : 2 + estimateTextRows(
31547
31601
  `\u{1F9E0} SAGE MEMORY INJECTED \xB7 ${entry.name} ${memoryLines.length} ${memoryLines.length === 1 ? "memory" : "memories"}${entry.sageStats ? ` \xB7 ${entry.sageStats}` : ""}`,
31548
31602
  panelContentWidth,
31549
31603
  MAX_TEXT_ESTIMATE_ROWS
@@ -31566,7 +31620,7 @@ function estimateRenderGroupRows(group, contentWidth) {
31566
31620
  const kvRows = estimateSageKvRows(kvCells, panelContentWidth);
31567
31621
  const gap = rows > 0 ? 1 : 0;
31568
31622
  return rows + labelRow + bodyRow + kvRows + gap;
31569
- }, 0) : 0;
31623
+ }, 0);
31570
31624
  if (entry.resultRenderMode === "simple") return 3 + sagePanelRows;
31571
31625
  if (STRUCTURED_DIFF_TOOLS.has(entry.name)) {
31572
31626
  const previewCap = entry.name === "replace" || entry.name === "diff" || entry.name === "patch" ? MULTI_DIFF_MAX_ROWS : DIFF_MAX_LINES;
@@ -32825,9 +32879,11 @@ var Entry = React8.memo(function Entry2({
32825
32879
  autonomyMode,
32826
32880
  multiDiffSummaryThreshold,
32827
32881
  todos,
32828
- showModelReasoning
32882
+ showModelReasoning,
32883
+ showSageMemoryInject
32829
32884
  }) {
32830
32885
  const openTodos = hasOpenTodos(todos);
32886
+ const isFinalTurnEntry = entry.kind === "assistant" && entry.final === true;
32831
32887
  const nextSteps = useMemo13(() => {
32832
32888
  if (entry.kind !== "assistant") return { steps: [], stripped: "" };
32833
32889
  return parseNextSteps(entry.text, true);
@@ -32836,10 +32892,17 @@ var Entry = React8.memo(function Entry2({
32836
32892
  if (!setSuggestions) return;
32837
32893
  if (entry.kind !== "assistant") return;
32838
32894
  if (openTodos) return;
32895
+ if (!isFinalTurnEntry) return;
32839
32896
  const text = entry.text ?? "";
32840
32897
  const { texts } = parseNextSteps(text, true);
32841
32898
  if (texts.length > 0) setSuggestions(texts);
32842
- }, [entry.kind, entry.text, openTodos, setSuggestions]);
32899
+ }, [
32900
+ entry.kind,
32901
+ entry.text,
32902
+ openTodos,
32903
+ isFinalTurnEntry,
32904
+ setSuggestions
32905
+ ]);
32843
32906
  switch (entry.kind) {
32844
32907
  case "user":
32845
32908
  return /* @__PURE__ */ jsx70(
@@ -32894,7 +32957,7 @@ var Entry = React8.memo(function Entry2({
32894
32957
  case "assistant": {
32895
32958
  const contentWidth = assistantContentWidth(termWidth);
32896
32959
  const { steps, stripped } = nextSteps;
32897
- const hasNext = steps.length > 0 && !openTodos;
32960
+ const hasNext = steps.length > 0 && !openTodos && isFinalTurnEntry;
32898
32961
  return /* @__PURE__ */ jsxs68(Box, { flexDirection: "column", children: [
32899
32962
  /* @__PURE__ */ jsxs68(
32900
32963
  Box,
@@ -32994,15 +33057,15 @@ var Entry = React8.memo(function Entry2({
32994
33057
  const outObj = outJson && typeof outJson === "object" ? outJson : void 0;
32995
33058
  const created = name === "write" && (outObj?.["created"] === true || /^[^\n]*\bcreated=true\b/.test(outputForFormatting));
32996
33059
  const verb = created ? "Write" : "Update";
32997
- const path11 = stringOf(inputObj?.["path"]) ?? stringOf(outObj?.["path"]);
33060
+ const path10 = stringOf(inputObj?.["path"]) ?? stringOf(outObj?.["path"]);
32998
33061
  const agg = multiDiffs2 ? summarizeMultiFileDiffs(multiDiffs2) : void 0;
32999
- const target = multiDiffs2 ? agg?.fileCount === 1 ? multiDiffs2[0].path : `${agg?.fileCount ?? multiDiffs2.length} files` : path11 ?? "file";
33062
+ const target = multiDiffs2 ? agg?.fileCount === 1 ? multiDiffs2[0].path : `${agg?.fileCount ?? multiDiffs2.length} files` : path10 ?? "file";
33000
33063
  return {
33001
33064
  verb,
33002
33065
  target,
33003
33066
  added: agg ? agg.added : diff2?.added ?? 0,
33004
33067
  removed: agg ? agg.removed : diff2?.removed ?? 0,
33005
- lang: langFromPath(multiDiffs2 ? "" : path11 ?? "")
33068
+ lang: langFromPath(multiDiffs2 ? "" : path10 ?? "")
33006
33069
  };
33007
33070
  })();
33008
33071
  return {
@@ -33086,14 +33149,14 @@ var Entry = React8.memo(function Entry2({
33086
33149
  ]
33087
33150
  }
33088
33151
  ),
33089
- /* @__PURE__ */ jsx70(
33152
+ showSageMemoryInject !== false ? /* @__PURE__ */ jsx70(
33090
33153
  SageMemoryBlock,
33091
33154
  {
33092
33155
  sageLines,
33093
33156
  toolName: entry.name,
33094
33157
  stats: entry.sageStats
33095
33158
  }
33096
- )
33159
+ ) : null
33097
33160
  ] });
33098
33161
  }
33099
33162
  const toolContentWidth = Math.max(20, termWidth - 2);
@@ -33173,7 +33236,7 @@ var Entry = React8.memo(function Entry2({
33173
33236
  ]
33174
33237
  }
33175
33238
  ),
33176
- /* @__PURE__ */ jsx70(SageMemoryBlock, { sageLines, toolName: entry.name, stats: entry.sageStats })
33239
+ showSageMemoryInject !== false ? /* @__PURE__ */ jsx70(SageMemoryBlock, { sageLines, toolName: entry.name, stats: entry.sageStats }) : null
33177
33240
  ] });
33178
33241
  }
33179
33242
  case "memory-activation": {
@@ -33450,7 +33513,8 @@ var History = memo2(function History2({
33450
33513
  autonomyMode,
33451
33514
  multiDiffSummaryThreshold,
33452
33515
  todos,
33453
- showModelReasoning
33516
+ showModelReasoning,
33517
+ showSageMemoryInject
33454
33518
  }) {
33455
33519
  const { stdout } = useStdout();
33456
33520
  const [termSize, setTermSize] = useState39({
@@ -33525,7 +33589,8 @@ var History = memo2(function History2({
33525
33589
  autonomyMode,
33526
33590
  multiDiffSummaryThreshold,
33527
33591
  todos,
33528
- showModelReasoning
33592
+ showModelReasoning,
33593
+ showSageMemoryInject
33529
33594
  }
33530
33595
  ) }, entry.id) }, staticKey),
33531
33596
  /* @__PURE__ */ jsx71(Box, { flexGrow: 1 })
@@ -33675,6 +33740,7 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
33675
33740
  multiDiffSummaryThreshold,
33676
33741
  todos,
33677
33742
  showModelReasoning,
33743
+ showSageMemoryInject,
33678
33744
  layoutStore,
33679
33745
  copiedEntryId,
33680
33746
  onRequestOlderEntries
@@ -33717,7 +33783,8 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
33717
33783
  const preparedGroupIdsRef = useRef37(null);
33718
33784
  const preparedEstimateWidthRef = useRef37(null);
33719
33785
  const reasoningKey = showModelReasoning === false ? "|r0" : "";
33720
- const cacheKey = (layoutStore ? `${groupIdsKey}|w${termWidth}` : groupIdsKey) + reasoningKey;
33786
+ const sageKey = showSageMemoryInject === false ? "|s0" : "";
33787
+ const cacheKey = (layoutStore ? `${groupIdsKey}|w${termWidth}` : groupIdsKey) + reasoningKey + sageKey;
33721
33788
  if (preparedGroupIdsRef.current !== cacheKey || preparedEstimateWidthRef.current !== termWidth) {
33722
33789
  const widthChanged = preparedEstimateWidthRef.current !== null && preparedEstimateWidthRef.current !== termWidth;
33723
33790
  if (widthChanged) {
@@ -33744,7 +33811,7 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
33744
33811
  }
33745
33812
  if (stored.kind === "measured") measuredGroupIdsRef.current.add(id);
33746
33813
  } else {
33747
- const estimatedRows = estimateRenderGroupRows(group, termWidth);
33814
+ const estimatedRows = estimateRenderGroupRows(group, termWidth, showSageMemoryInject);
33748
33815
  heightCache.record(id, estimatedRows);
33749
33816
  const kind = group.type === "tool-group" ? "tool-group" : group.entry.kind;
33750
33817
  const text = group.type === "tool-group" ? group.data.name : group.entry.text ?? "";
@@ -33763,7 +33830,10 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
33763
33830
  const id = renderGroupId(group);
33764
33831
  const hidden = group.type !== "tool-group" && group.entry.kind === "thinking" && showModelReasoning === false;
33765
33832
  if (hidden) hiddenIds.add(id);
33766
- return [id, hidden ? 0 : estimateRenderGroupRows(group, termWidth)];
33833
+ return [
33834
+ id,
33835
+ hidden ? 0 : estimateRenderGroupRows(group, termWidth, showSageMemoryInject)
33836
+ ];
33767
33837
  })
33768
33838
  );
33769
33839
  heightCache.recordMany(
@@ -33895,6 +33965,19 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
33895
33965
  const clip2 = effectiveAnchor?.clip ?? 0;
33896
33966
  planStartIdxRef.current = plan.startIdx;
33897
33967
  const totalRows = contentRows(geometry);
33968
+ setTuiHistoryMemoryGauges({
33969
+ historyRetainedEntries: entries.length,
33970
+ historyGroupedEntries: groupedEntries.length,
33971
+ historyMountedGroups: renderGroups.length,
33972
+ historyMountedEntries: renderGroups.reduce(
33973
+ (count, group) => count + (group.type === "tool-group" ? group.data.entries.length : 1),
33974
+ 0
33975
+ ),
33976
+ historyCachedGroups: heightCache.size,
33977
+ historyMeasuredGroups: measuredGroupIdsRef.current.size,
33978
+ historyViewportRows: vp,
33979
+ historyTotalRows: totalRows
33980
+ });
33898
33981
  const offsetFromBottom = scrolled ? Math.max(0, maxTopRow(geometry) - anchorTopRow(geometry, effectiveAnchor)) : 0;
33899
33982
  const copyRegistry = buildCopyRegistry({
33900
33983
  renderGroups,
@@ -34012,7 +34095,8 @@ var ScrollableHistory = memo3(function ScrollableHistory2({
34012
34095
  autonomyMode,
34013
34096
  multiDiffSummaryThreshold,
34014
34097
  todos,
34015
- showModelReasoning
34098
+ showModelReasoning,
34099
+ showSageMemoryInject
34016
34100
  }
34017
34101
  )
34018
34102
  }
@@ -34508,6 +34592,7 @@ function AppView({ host, runtime }) {
34508
34592
  multiDiffSummaryThreshold: state.settingsPicker.multiDiffSummaryThreshold,
34509
34593
  todos: liveTodos,
34510
34594
  showModelReasoning: state.settingsPicker.open ? state.settingsPicker.showModelReasoning : liveSettings?.showModelReasoning ?? true,
34595
+ showSageMemoryInject: state.settingsPicker.open ? state.settingsPicker.showSageMemoryInject : liveSettings?.showSageMemoryInject ?? false,
34511
34596
  layoutStore,
34512
34597
  copiedEntryId: state.copiedEntryId,
34513
34598
  onRequestOlderEntries: runtime.onRequestOlderEntries
@@ -34659,6 +34744,8 @@ function AppView({ host, runtime }) {
34659
34744
  breakerAutoKillResetMs: state.settingsPicker.breakerAutoKillResetMs,
34660
34745
  showModelReasoning: state.settingsPicker.showModelReasoning,
34661
34746
  showAgentSwarmPanel: state.settingsPicker.showAgentSwarmPanel,
34747
+ showSageMemoryInject: state.settingsPicker.showSageMemoryInject,
34748
+ sageMemoryInjectThreshold: state.settingsPicker.sageMemoryInjectThreshold,
34662
34749
  readSymbols: state.settingsPicker.readSymbols,
34663
34750
  filter: state.settingsPicker.filter,
34664
34751
  hint: state.settingsPicker.hint
@@ -35052,7 +35139,10 @@ function createRunBlocksController(host) {
35052
35139
  }
35053
35140
  dispatch({ type: "streamReset" });
35054
35141
  if (result.status === "done" && result.finalText?.trim() && !refs.assistantCommitted.current) {
35055
- dispatch({ type: "addEntry", entry: { kind: "assistant", text: result.finalText } });
35142
+ dispatch({
35143
+ type: "addEntry",
35144
+ entry: { kind: "assistant", text: result.finalText, final: true }
35145
+ });
35056
35146
  }
35057
35147
  if (result.status === "aborted") {
35058
35148
  const reason = result.abortReason ? `Aborted (${result.abortReason}).` : "Aborted.";
@@ -36383,6 +36473,8 @@ function routeSettingsOverlayKey(host, input, key, isEnter) {
36383
36473
  breakerAutoKillResetMs: config.breakerAutoKillResetMs ?? 6e4,
36384
36474
  showModelReasoning: config.showModelReasoning ?? true,
36385
36475
  showAgentSwarmPanel: config.showAgentSwarmPanel ?? true,
36476
+ showSageMemoryInject: config.showSageMemoryInject ?? false,
36477
+ sageMemoryInjectThreshold: config.sageMemoryInjectThreshold ?? 0.85,
36386
36478
  readSymbols: config.readSymbols ?? false
36387
36479
  });
36388
36480
  return true;
@@ -37159,8 +37251,8 @@ function App(props) {
37159
37251
  submitRef
37160
37252
  } = useAppRuntimeRefs(attachments, state);
37161
37253
  const projectName = React10.useMemo(() => {
37162
- const base = path8.basename(projectRoot);
37163
- return base && base !== path8.sep ? base : void 0;
37254
+ const base = path7.basename(projectRoot);
37255
+ return base && base !== path7.sep ? base : void 0;
37164
37256
  }, [projectRoot]);
37165
37257
  const workingDirChip = useWorkingDirChip(agent.ctx, projectRoot);
37166
37258
  const {
@@ -37896,9 +37988,9 @@ var BRACKETED_PASTE_OFF = "\x1B[?2004l";
37896
37988
 
37897
37989
  // src/run-tui-client-registration.ts
37898
37990
  import { randomUUID } from "node:crypto";
37899
- import * as path9 from "node:path";
37991
+ import * as path8 from "node:path";
37900
37992
  import { RemoteMailbox, resolveProjectDir } from "@wrongstack/core/coordination";
37901
- import { wstackGlobalRoot as wstackGlobalRoot2 } from "@wrongstack/core/utils";
37993
+ import { wstackGlobalRoot } from "@wrongstack/core/utils";
37902
37994
  import {
37903
37995
  createHqPublisherFromEnv,
37904
37996
  startBrainTelemetryBridge,
@@ -37924,7 +38016,7 @@ function createRunTuiClientRegistration(opts) {
37924
38016
  if (!opts.projectRoot) return null;
37925
38017
  const generation = ++registrationGeneration;
37926
38018
  try {
37927
- const projectDir = resolveProjectDir(opts.projectRoot, wstackGlobalRoot2());
38019
+ const projectDir = resolveProjectDir(opts.projectRoot, wstackGlobalRoot());
37928
38020
  const mailbox = new RemoteMailbox({ projectDir, events: opts.events });
37929
38021
  const clientId = `tui@${randomUUID().slice(0, 8)}`;
37930
38022
  await mailbox.initialize();
@@ -37983,7 +38075,7 @@ function createRunTuiClientRegistration(opts) {
37983
38075
  await mailbox.registerClient({
37984
38076
  clientId,
37985
38077
  sessionId: opts.getSessionId?.() ?? opts.projectRoot,
37986
- name: `TUI [${path9.basename(opts.projectRoot)}]`,
38078
+ name: `TUI [${path8.basename(opts.projectRoot)}]`,
37987
38079
  source: "tui",
37988
38080
  pid: process.pid
37989
38081
  });
@@ -37998,7 +38090,7 @@ function createRunTuiClientRegistration(opts) {
37998
38090
  tuiHqPublisher = createHqPublisherFromEnv({
37999
38091
  clientKind: "tui",
38000
38092
  projectRoot: opts.projectRoot,
38001
- projectName: path9.basename(opts.projectRoot),
38093
+ projectName: path8.basename(opts.projectRoot),
38002
38094
  appConfig: opts.appConfig
38003
38095
  });
38004
38096
  tuiHqPublisher?.connect();
@@ -38011,7 +38103,7 @@ function createRunTuiClientRegistration(opts) {
38011
38103
  events: opts.events,
38012
38104
  sessionId: tuiSessionId,
38013
38105
  projectRoot: opts.projectRoot,
38014
- projectName: path9.basename(opts.projectRoot)
38106
+ projectName: path8.basename(opts.projectRoot)
38015
38107
  })
38016
38108
  );
38017
38109
  } catch {
@@ -38136,7 +38228,7 @@ function createRunTuiClientRegistration(opts) {
38136
38228
  }
38137
38229
 
38138
38230
  // src/run-tui-title-controller.ts
38139
- import * as path10 from "node:path";
38231
+ import * as path9 from "node:path";
38140
38232
 
38141
38233
  // src/terminal-title.ts
38142
38234
  var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
@@ -38216,7 +38308,7 @@ function createRunTuiTitleController(opts) {
38216
38308
  stdout: opts.stdout,
38217
38309
  events: opts.events,
38218
38310
  model: currentModel,
38219
- appName: opts.projectRoot ? path10.basename(opts.projectRoot) : void 0
38311
+ appName: opts.projectRoot ? path9.basename(opts.projectRoot) : void 0
38220
38312
  });
38221
38313
  };
38222
38314
  const stop = () => {