@wrongstack/tui 0.285.0 → 0.286.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 (43) hide show
  1. package/dist/app-initial-state.d.ts +3 -1
  2. package/dist/app-initial-state.d.ts.map +1 -1
  3. package/dist/app-reducer.d.ts.map +1 -1
  4. package/dist/app-state.d.ts +51 -11
  5. package/dist/app-state.d.ts.map +1 -1
  6. package/dist/app.d.ts +19 -5
  7. package/dist/app.d.ts.map +1 -1
  8. package/dist/components/agents-monitor.d.ts +66 -1
  9. package/dist/components/agents-monitor.d.ts.map +1 -1
  10. package/dist/components/brain-panel-model.d.ts +107 -0
  11. package/dist/components/brain-panel-model.d.ts.map +1 -0
  12. package/dist/components/brain-panel.d.ts +7 -1
  13. package/dist/components/brain-panel.d.ts.map +1 -1
  14. package/dist/components/model-picker.d.ts +6 -1
  15. package/dist/components/model-picker.d.ts.map +1 -1
  16. package/dist/components/settings-picker.d.ts +24 -8
  17. package/dist/components/settings-picker.d.ts.map +1 -1
  18. package/dist/hooks/fleet-chat-coalescer.d.ts +32 -0
  19. package/dist/hooks/fleet-chat-coalescer.d.ts.map +1 -0
  20. package/dist/hooks/use-brain-panel.d.ts +43 -0
  21. package/dist/hooks/use-brain-panel.d.ts.map +1 -0
  22. package/dist/hooks/use-director-fleet-bridge.d.ts +17 -3
  23. package/dist/hooks/use-director-fleet-bridge.d.ts.map +1 -1
  24. package/dist/hooks/use-model-pick.d.ts +34 -0
  25. package/dist/hooks/use-model-pick.d.ts.map +1 -0
  26. package/dist/hooks/use-picker-keys.d.ts +14 -0
  27. package/dist/hooks/use-picker-keys.d.ts.map +1 -1
  28. package/dist/hooks/use-subagent-events.d.ts +10 -2
  29. package/dist/hooks/use-subagent-events.d.ts.map +1 -1
  30. package/dist/hooks/use-tui-controllers.d.ts +8 -2
  31. package/dist/hooks/use-tui-controllers.d.ts.map +1 -1
  32. package/dist/hooks/use-tui-event-bridge.d.ts.map +1 -1
  33. package/dist/hooks/use-working-dir-chip.d.ts.map +1 -1
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +1803 -874
  37. package/dist/index.js.map +4 -4
  38. package/dist/reducers/fleet.d.ts +1 -1
  39. package/dist/run-tui.d.ts +16 -1
  40. package/dist/run-tui.d.ts.map +1 -1
  41. package/dist/sdd-lifecycle-entry.d.ts +18 -0
  42. package/dist/sdd-lifecycle-entry.d.ts.map +1 -0
  43. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  writeErr,
4
4
  GlobalMailbox,
5
5
  createHqPublisherFromEnv,
6
+ startSessionTelemetryBridge,
6
7
  startFleetTelemetryBridge,
7
8
  startBrainTelemetryBridge,
8
9
  startWorktreeTelemetryBridge,
@@ -15,7 +16,7 @@ import { getProcessRegistry as getProcessRegistry5 } from "@wrongstack/tools";
15
16
  import { render } from "ink";
16
17
  import { randomUUID } from "node:crypto";
17
18
  import * as path9 from "node:path";
18
- import React8 from "react";
19
+ import React10 from "react";
19
20
 
20
21
  // src/app.tsx
21
22
  import * as fs4 from "node:fs/promises";
@@ -51,8 +52,8 @@ import {
51
52
  import { toErrorMessage as toErrorMessage3 } from "@wrongstack/core/utils";
52
53
  import { routeImagesForModel } from "@wrongstack/runtime/vision";
53
54
  import { getIndexState, getProcessRegistry as getProcessRegistry4, onIndexStateChange } from "@wrongstack/tools";
54
- import React7, {
55
- useCallback as useCallback8,
55
+ import React9, {
56
+ useCallback as useCallback9,
56
57
  useEffect as useEffect26,
57
58
  useLayoutEffect as useLayoutEffect2,
58
59
  useMemo as useMemo6,
@@ -141,6 +142,46 @@ function authMoveSelected(state, delta) {
141
142
  return ((state.selected + delta) % count + count) % count;
142
143
  }
143
144
 
145
+ // src/components/brain-panel-model.ts
146
+ function brainPanelRows(settings) {
147
+ const rows = [{ kind: "mode" }, { kind: "risk" }];
148
+ for (let i = 0; i < settings.pool.length; i += 1) rows.push({ kind: "poolModel", index: i });
149
+ rows.push({ kind: "poolAdd" });
150
+ if (settings.pool.length > 1) rows.push({ kind: "strategy" });
151
+ rows.push({ kind: "timeout" }, { kind: "humanTimeout" });
152
+ rows.push({ kind: "councilToggle" });
153
+ if (settings.councilEnabled || settings.voters.length > 0) {
154
+ rows.push({ kind: "councilMinRisk" });
155
+ for (let i = 0; i < settings.voters.length; i += 1) rows.push({ kind: "voter", index: i });
156
+ rows.push({ kind: "voterAdd" }, { kind: "judge" });
157
+ }
158
+ rows.push({ kind: "ledgerToggle" });
159
+ if (settings.ledgerEnabled) rows.push({ kind: "autoDeny" });
160
+ return rows;
161
+ }
162
+ var DECISION_TIMEOUT_PRESETS = [
163
+ void 0,
164
+ 5e3,
165
+ 1e4,
166
+ 2e4,
167
+ 3e4,
168
+ 6e4
169
+ ];
170
+ var HUMAN_TIMEOUT_PRESETS = [
171
+ void 0,
172
+ 3e4,
173
+ 6e4,
174
+ 12e4,
175
+ 3e5
176
+ ];
177
+ var AUTO_DENY_PRESETS = [void 0, 0, 2, 3, 5];
178
+ function cyclePreset(presets, current, delta) {
179
+ const idx = presets.indexOf(current);
180
+ const from = idx >= 0 ? idx : 0;
181
+ const next = (from + delta + presets.length) % presets.length;
182
+ return presets[next];
183
+ }
184
+
144
185
  // src/ink.tsx
145
186
  import { Box as InkBox, Text as InkText } from "ink";
146
187
  import {
@@ -547,6 +588,11 @@ var MAX_ITERATIONS_PRESETS = [100, 200, 500, 1e3, 0];
547
588
  var MAX_CONCURRENT_PRESETS = [1, 3, 4, 5, 10, 25, 50, 0];
548
589
  var AUTO_PROCEED_MAX_PRESETS = [10, 25, 50, 100, 250, 0];
549
590
  var ENHANCE_DELAY_PRESETS = [15e3, 3e4, 45e3, 6e4, 9e4, 12e4];
591
+ var BREAKER_TIMEOUT_PRESETS = [0, 3e4, 6e4, 12e4, 3e5];
592
+ function formatBreakerTimeout(ms) {
593
+ if (ms === 0) return "manual";
594
+ return formatSettingsDelay(ms);
595
+ }
550
596
  var MULTI_DIFF_SUMMARY_THRESHOLD_PRESETS = [3, 5, 8, 10, 15, 0];
551
597
  var ENHANCE_LANGUAGES = ["original", "english"];
552
598
  var TOKEN_SAVING_TIERS = ["off", "minimal", "light", "medium", "aggressive"];
@@ -557,6 +603,12 @@ var TOKEN_SAVING_TIER_DESCS = {
557
603
  medium: "~1.4\u20131.5k tokens \u2014 most tools enabled",
558
604
  aggressive: "~1k tokens \u2014 many tools, trimmed prompt"
559
605
  };
606
+ var FLEET_CHAT_MODES = ["off", "compact", "full"];
607
+ var FLEET_CHAT_MODE_DESCS = {
608
+ off: "No subagent lines in chat (F2/F3 monitors stay live)",
609
+ compact: "Spawn, one summary per agent turn, completion",
610
+ full: "Every subagent tool call and message"
611
+ };
560
612
  function formatSettingsDelay(ms) {
561
613
  if (ms === 0) return "disabled";
562
614
  if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
@@ -583,7 +635,7 @@ var MODE_DESC = {
583
635
  suggest: "Shows next-step suggestions after each turn",
584
636
  auto: "Self-driving \u2014 agent continues automatically"
585
637
  };
586
- var SETTINGS_FIELD_COUNT = 37;
638
+ var SETTINGS_FIELD_COUNT = 39;
587
639
  var THINKING_WORD_FIELD = 22;
588
640
  var SETTINGS_PICKER_JUMP_CHORDS = Object.freeze([
589
641
  // ── Ctrl chords (Tools / Reasoning / Fleet / Debug sections) ──
@@ -684,7 +736,7 @@ var SETTINGS_FIELD_LABELS = [
684
736
  // 2
685
737
  "YOLO mode",
686
738
  // 3
687
- "Stream fleet",
739
+ "Fleet chat",
688
740
  // 4
689
741
  "Completion chime",
690
742
  // 5
@@ -748,8 +800,12 @@ var SETTINGS_FIELD_LABELS = [
748
800
  // 34
749
801
  "Config scope",
750
802
  // 35
751
- "Animation"
803
+ "Animation",
752
804
  // 36
805
+ "Circuit breaker",
806
+ // 37
807
+ "Breaker timeout"
808
+ // 38
753
809
  ];
754
810
  function resolveSettingsFieldValue(field, input) {
755
811
  const raw = input.trim().toLowerCase();
@@ -757,7 +813,6 @@ function resolveSettingsFieldValue(field, input) {
757
813
  const BOOL_FIELDS = /* @__PURE__ */ new Map([
758
814
  [2, "titleAnimation"],
759
815
  [3, "yolo"],
760
- [4, "streamFleet"],
761
816
  [5, "chime"],
762
817
  [6, "confirmExit"],
763
818
  [7, "nextPrediction"],
@@ -771,7 +826,8 @@ function resolveSettingsFieldValue(field, input) {
771
826
  [20, "indexOnStart"],
772
827
  [25, "reasoningPreserve"],
773
828
  [27, "contextAutoCompact"],
774
- [33, "debugStream"]
829
+ [33, "debugStream"],
830
+ [37, "breakerEnabled"]
775
831
  ]);
776
832
  const boolKey = BOOL_FIELDS.get(field);
777
833
  if (boolKey) {
@@ -783,6 +839,17 @@ function resolveSettingsFieldValue(field, input) {
783
839
  }
784
840
  return { ok: false, error: `Invalid value "${input}" for ${label}. Use on or off.` };
785
841
  }
842
+ if (field === 4) {
843
+ const legacy = ["on", "true", "yes", "1"].includes(raw) ? "full" : ["false", "no", "0"].includes(raw) ? "off" : void 0;
844
+ const match = FLEET_CHAT_MODES.includes(raw) ? raw : legacy;
845
+ if (match) {
846
+ return { ok: true, patch: { fleetChat: match }, label, displayValue: match };
847
+ }
848
+ return {
849
+ ok: false,
850
+ error: `Invalid value "${input}" for ${label}. Valid: ${FLEET_CHAT_MODES.join(", ")}.`
851
+ };
852
+ }
786
853
  const ENUM_FIELDS = [
787
854
  [0, "mode", SETTINGS_MODES],
788
855
  [13, "tokenSavingTier", TOKEN_SAVING_TIERS],
@@ -816,7 +883,8 @@ function resolveSettingsFieldValue(field, input) {
816
883
  [16, "autoProceedMaxIterations", AUTO_PROCEED_MAX_PRESETS, (n) => formatMaxIterations(n)],
817
884
  [17, "enhanceDelayMs", ENHANCE_DELAY_PRESETS, (n) => formatEnhanceDelay(n)],
818
885
  [21, "multiDiffSummaryThreshold", MULTI_DIFF_SUMMARY_THRESHOLD_PRESETS, (n) => formatMultiDiffSummaryThreshold(n)],
819
- [30, "maxConcurrent", MAX_CONCURRENT_PRESETS, (n) => presetLabel(n, "runtime default")]
886
+ [30, "maxConcurrent", MAX_CONCURRENT_PRESETS, (n) => presetLabel(n, "runtime default")],
887
+ [38, "breakerAutoKillResetMs", BREAKER_TIMEOUT_PRESETS, (n) => formatBreakerTimeout(n)]
820
888
  ];
821
889
  for (const [f, key, presets, fmt2] of PRESET_FIELDS) {
822
890
  if (field !== f) continue;
@@ -857,7 +925,6 @@ function getSettingsFieldValue(values, field) {
857
925
  const BOOL_KEYS = [
858
926
  [2, "titleAnimation"],
859
927
  [3, "yolo"],
860
- [4, "streamFleet"],
861
928
  [5, "chime"],
862
929
  [6, "confirmExit"],
863
930
  [7, "nextPrediction"],
@@ -871,7 +938,8 @@ function getSettingsFieldValue(values, field) {
871
938
  [20, "indexOnStart"],
872
939
  [25, "reasoningPreserve"],
873
940
  [27, "contextAutoCompact"],
874
- [33, "debugStream"]
941
+ [33, "debugStream"],
942
+ [37, "breakerEnabled"]
875
943
  ];
876
944
  for (const [f, key] of BOOL_KEYS) {
877
945
  if (field !== f) continue;
@@ -879,6 +947,7 @@ function getSettingsFieldValue(values, field) {
879
947
  }
880
948
  const ENUM_KEYS = [
881
949
  [0, "mode"],
950
+ [4, "fleetChat"],
882
951
  [13, "tokenSavingTier"],
883
952
  [19, "enhanceLanguage"],
884
953
  [23, "reasoningMode"],
@@ -903,7 +972,8 @@ function getSettingsFieldValue(values, field) {
903
972
  [16, "autoProceedMaxIterations", formatMaxIterations],
904
973
  [17, "enhanceDelayMs", formatEnhanceDelay],
905
974
  [21, "multiDiffSummaryThreshold", formatMultiDiffSummaryThreshold],
906
- [30, "maxConcurrent", (n) => presetLabel(n, "runtime default")]
975
+ [30, "maxConcurrent", (n) => presetLabel(n, "runtime default")],
976
+ [38, "breakerAutoKillResetMs", formatBreakerTimeout]
907
977
  ];
908
978
  for (const [f, key, fmt2] of PRESET_KEYS) {
909
979
  if (field !== f) continue;
@@ -950,6 +1020,10 @@ var SETTINGS_SECTIONS = [
950
1020
  {
951
1021
  name: "Debug",
952
1022
  fields: [33, 34, 35, 36]
1023
+ },
1024
+ {
1025
+ name: "Safety",
1026
+ fields: [37, 38]
953
1027
  }
954
1028
  ];
955
1029
  function formatAllSettingsSummary(values) {
@@ -973,7 +1047,7 @@ var SETTINGS_DEFAULTS = Object.freeze({
973
1047
  delayMs: 0,
974
1048
  titleAnimation: true,
975
1049
  yolo: true,
976
- streamFleet: true,
1050
+ fleetChat: "compact",
977
1051
  chime: false,
978
1052
  confirmExit: true,
979
1053
  nextPrediction: false,
@@ -1005,7 +1079,9 @@ var SETTINGS_DEFAULTS = Object.freeze({
1005
1079
  thinkingWord: "thinking",
1006
1080
  cacheTtl: "default",
1007
1081
  configScope: "global",
1008
- animationStyle: "rainbow"
1082
+ animationStyle: "rainbow",
1083
+ breakerEnabled: false,
1084
+ breakerAutoKillResetMs: 6e4
1009
1085
  });
1010
1086
  function resetSettingsFieldValue(field) {
1011
1087
  const result = getSettingsFieldValue(SETTINGS_DEFAULTS, field);
@@ -1020,7 +1096,7 @@ function buildResetPatch(field) {
1020
1096
  [1, "delayMs"],
1021
1097
  [2, "titleAnimation"],
1022
1098
  [3, "yolo"],
1023
- [4, "streamFleet"],
1099
+ [4, "fleetChat"],
1024
1100
  [5, "chime"],
1025
1101
  [6, "confirmExit"],
1026
1102
  [7, "nextPrediction"],
@@ -1052,7 +1128,9 @@ function buildResetPatch(field) {
1052
1128
  [33, "debugStream"],
1053
1129
  [34, "statuslineMode"],
1054
1130
  [35, "configScope"],
1055
- [36, "animationStyle"]
1131
+ [36, "animationStyle"],
1132
+ [37, "breakerEnabled"],
1133
+ [38, "breakerAutoKillResetMs"]
1056
1134
  ];
1057
1135
  for (const [f, key] of KEY_MAP) {
1058
1136
  if (f === field) {
@@ -1068,7 +1146,7 @@ function SettingsPicker({
1068
1146
  delayMs,
1069
1147
  titleAnimation,
1070
1148
  yolo,
1071
- streamFleet,
1149
+ fleetChat,
1072
1150
  chime,
1073
1151
  confirmExit,
1074
1152
  nextPrediction,
@@ -1103,6 +1181,8 @@ function SettingsPicker({
1103
1181
  statuslineMode,
1104
1182
  configScope,
1105
1183
  animationStyle,
1184
+ breakerEnabled,
1185
+ breakerAutoKillResetMs,
1106
1186
  hint
1107
1187
  }) {
1108
1188
  const boolVal = (v) => v ? "on" : "off";
@@ -1128,9 +1208,9 @@ function SettingsPicker({
1128
1208
  detail: "Skip all confirmation prompts"
1129
1209
  },
1130
1210
  {
1131
- label: "Stream fleet to chat",
1132
- value: boolVal(streamFleet),
1133
- detail: "Show subagent messages in main chat"
1211
+ label: "Fleet chat",
1212
+ value: fleetChat,
1213
+ detail: FLEET_CHAT_MODE_DESCS[fleetChat]
1134
1214
  },
1135
1215
  {
1136
1216
  label: "Completion chime",
@@ -1305,6 +1385,18 @@ function SettingsPicker({
1305
1385
  label: "Animation",
1306
1386
  value: animationStyle,
1307
1387
  detail: formatAnimationStyle(animationStyle)
1388
+ },
1389
+ // ── Safety ──
1390
+ { section: "Safety" },
1391
+ {
1392
+ label: "Circuit breaker",
1393
+ value: boolVal(breakerEnabled),
1394
+ detail: "Gate bash/exec after repeated failures"
1395
+ },
1396
+ {
1397
+ label: "Breaker timeout",
1398
+ value: formatBreakerTimeout(breakerAutoKillResetMs),
1399
+ detail: "Auto kill/reset delay when tripped (manual = /kill reset)"
1308
1400
  }
1309
1401
  ];
1310
1402
  const fieldRowIndex = [];
@@ -2129,8 +2221,8 @@ function reduceFleetState(state, action) {
2129
2221
  }
2130
2222
  };
2131
2223
  }
2132
- case "setStreamFleet":
2133
- return { ...state, streamFleet: action.enabled };
2224
+ case "setFleetChat":
2225
+ return { ...state, fleetChat: action.mode };
2134
2226
  default:
2135
2227
  return null;
2136
2228
  }
@@ -2385,10 +2477,14 @@ function reducer(state, action) {
2385
2477
  case "clearInputHistory": {
2386
2478
  return { ...state, inputHistory: [], historyIndex: 0, historyDraft: "" };
2387
2479
  }
2388
- case "modelPickerOpen":
2480
+ case "modelPickerOpen": {
2481
+ const purpose = action.purpose ?? "switch";
2389
2482
  return {
2390
2483
  ...state,
2391
- ...closePanels(state),
2484
+ // Generic 'pick' invocations are transient overlays ON TOP of the
2485
+ // calling panel (e.g. the Brain panel) — leave other panels open so
2486
+ // the caller is still there when the promise resolves.
2487
+ ...purpose === "pick" ? {} : closePanels(state),
2392
2488
  modelPicker: {
2393
2489
  open: true,
2394
2490
  step: "provider",
@@ -2397,9 +2493,12 @@ function reducer(state, action) {
2397
2493
  filteredOptions: [],
2398
2494
  selected: 0,
2399
2495
  hint: void 0,
2400
- searchQuery: ""
2496
+ searchQuery: "",
2497
+ purpose,
2498
+ title: action.title
2401
2499
  }
2402
2500
  };
2501
+ }
2403
2502
  case "modelPickerClose":
2404
2503
  return {
2405
2504
  ...state,
@@ -2410,7 +2509,9 @@ function reducer(state, action) {
2410
2509
  modelOptions: [],
2411
2510
  filteredOptions: [],
2412
2511
  selected: 0,
2413
- searchQuery: ""
2512
+ searchQuery: "",
2513
+ purpose: "switch",
2514
+ title: void 0
2414
2515
  }
2415
2516
  };
2416
2517
  case "modelPickerMove": {
@@ -2663,7 +2764,7 @@ function reducer(state, action) {
2663
2764
  delayMs: action.delayMs,
2664
2765
  titleAnimation: action.titleAnimation,
2665
2766
  yolo: action.yolo,
2666
- streamFleet: action.streamFleet,
2767
+ fleetChat: action.fleetChat,
2667
2768
  chime: action.chime,
2668
2769
  confirmExit: action.confirmExit,
2669
2770
  nextPrediction: action.nextPrediction,
@@ -2701,6 +2802,8 @@ function reducer(state, action) {
2701
2802
  cacheTtl: action.cacheTtl,
2702
2803
  configScope: action.configScope,
2703
2804
  animationStyle: action.animationStyle,
2805
+ breakerEnabled: action.breakerEnabled,
2806
+ breakerAutoKillResetMs: action.breakerAutoKillResetMs,
2704
2807
  hint: void 0
2705
2808
  }
2706
2809
  };
@@ -2772,11 +2875,19 @@ function reducer(state, action) {
2772
2875
  settingsPicker: { ...sp, titleAnimation: !sp.titleAnimation, hint: void 0 }
2773
2876
  };
2774
2877
  if (f === 3) return { ...state, settingsPicker: { ...sp, yolo: !sp.yolo, hint: void 0 } };
2775
- if (f === 4)
2878
+ if (f === 4) {
2879
+ const i = FLEET_CHAT_MODES.indexOf(sp.fleetChat);
2880
+ const base = i < 0 ? 0 : i;
2881
+ const next = (base + action.delta + FLEET_CHAT_MODES.length) % FLEET_CHAT_MODES.length;
2776
2882
  return {
2777
2883
  ...state,
2778
- settingsPicker: { ...sp, streamFleet: !sp.streamFleet, hint: void 0 }
2884
+ settingsPicker: {
2885
+ ...sp,
2886
+ fleetChat: expectDefined(FLEET_CHAT_MODES[next]),
2887
+ hint: void 0
2888
+ }
2779
2889
  };
2890
+ }
2780
2891
  if (f === 5)
2781
2892
  return { ...state, settingsPicker: { ...sp, chime: !sp.chime, hint: void 0 } };
2782
2893
  if (f === 6)
@@ -3071,6 +3182,24 @@ function reducer(state, action) {
3071
3182
  }
3072
3183
  };
3073
3184
  }
3185
+ if (f === 37)
3186
+ return {
3187
+ ...state,
3188
+ settingsPicker: { ...sp, breakerEnabled: !sp.breakerEnabled, hint: void 0 }
3189
+ };
3190
+ if (f === 38) {
3191
+ const j = BREAKER_TIMEOUT_PRESETS.indexOf(sp.breakerAutoKillResetMs);
3192
+ const base = j < 0 ? 0 : j;
3193
+ const next = (base + action.delta + BREAKER_TIMEOUT_PRESETS.length) % BREAKER_TIMEOUT_PRESETS.length;
3194
+ return {
3195
+ ...state,
3196
+ settingsPicker: {
3197
+ ...sp,
3198
+ breakerAutoKillResetMs: expectDefined(BREAKER_TIMEOUT_PRESETS[next]),
3199
+ hint: void 0
3200
+ }
3201
+ };
3202
+ }
3074
3203
  return state;
3075
3204
  }
3076
3205
  case "settingsValueSet": {
@@ -3351,12 +3480,18 @@ function reducer(state, action) {
3351
3480
  riskLevel: action.riskLevel,
3352
3481
  log: action.log,
3353
3482
  selected: 0,
3354
- hint: void 0
3483
+ hint: void 0,
3484
+ // With a settings snapshot the panel opens in editor view;
3485
+ // without one it falls back to the legacy risk+log view.
3486
+ view: action.settings ? "settings" : "log",
3487
+ settings: action.settings,
3488
+ row: 0,
3489
+ busy: false
3355
3490
  }
3356
3491
  };
3357
3492
  }
3358
3493
  case "brainClose":
3359
- return { ...state, brainPanel: { ...state.brainPanel, open: false } };
3494
+ return { ...state, brainPanel: { ...state.brainPanel, open: false, busy: false } };
3360
3495
  case "brainMove": {
3361
3496
  const count = state.brainPanel.log.length;
3362
3497
  if (count === 0) return state;
@@ -3392,6 +3527,40 @@ function reducer(state, action) {
3392
3527
  };
3393
3528
  case "brainHint":
3394
3529
  return { ...state, brainPanel: { ...state.brainPanel, hint: action.text } };
3530
+ case "brainSettingsLoaded": {
3531
+ const rows = brainPanelRows(action.settings);
3532
+ return {
3533
+ ...state,
3534
+ brainPanel: {
3535
+ ...state.brainPanel,
3536
+ settings: action.settings,
3537
+ riskLevel: action.settings.riskLevel,
3538
+ row: Math.min(state.brainPanel.row, Math.max(0, rows.length - 1)),
3539
+ busy: false
3540
+ }
3541
+ };
3542
+ }
3543
+ case "brainView":
3544
+ return {
3545
+ ...state,
3546
+ brainPanel: { ...state.brainPanel, view: action.view, hint: void 0 }
3547
+ };
3548
+ case "brainRowMove": {
3549
+ const settings = state.brainPanel.settings;
3550
+ if (!settings) return state;
3551
+ const count = brainPanelRows(settings).length;
3552
+ if (count === 0) return state;
3553
+ return {
3554
+ ...state,
3555
+ brainPanel: {
3556
+ ...state.brainPanel,
3557
+ row: (state.brainPanel.row + action.delta + count) % count,
3558
+ hint: void 0
3559
+ }
3560
+ };
3561
+ }
3562
+ case "brainBusy":
3563
+ return { ...state, brainPanel: { ...state.brainPanel, busy: action.busy } };
3395
3564
  case "helpOpen":
3396
3565
  return {
3397
3566
  ...state,
@@ -3664,7 +3833,7 @@ function reducer(state, action) {
3664
3833
  case "leaderToolStart":
3665
3834
  case "leaderToolEnd":
3666
3835
  case "leaderCtxPct":
3667
- case "setStreamFleet": {
3836
+ case "setFleetChat": {
3668
3837
  const fleetResult = reduceFleetState(state, action);
3669
3838
  if (fleetResult) return fleetResult;
3670
3839
  return state;
@@ -5567,10 +5736,164 @@ function AgentRow({
5567
5736
  ] }) : null
5568
5737
  ] });
5569
5738
  }
5739
+ var TRANSCRIPT_ROWS = 10;
5740
+ var TRANSCRIPT_FETCH_LIMIT = 500;
5741
+ function transcriptRowsForTerminal(termRows, rosterCount, fullscreen = false) {
5742
+ const chrome = 4 + Math.max(0, rosterCount - 1) + 3 + 3 + 8;
5743
+ const available = (termRows ?? 30) - chrome;
5744
+ return Math.max(6, fullscreen ? available : Math.min(24, available));
5745
+ }
5746
+ var TRANSCRIPT_GLYPHS = {
5747
+ text: "\u{1F4AC}",
5748
+ thinking: "\u2234",
5749
+ tool_use: "\u{1F527}",
5750
+ tool_result: "\u{1F4CE}",
5751
+ status: "\xB7",
5752
+ error: "\u274C",
5753
+ system: "\xB7"
5754
+ };
5755
+ function formatTranscriptLine(e, maxWidth = 110) {
5756
+ const time = e.ts ? (() => {
5757
+ const d = new Date(e.ts);
5758
+ return `${Number.isNaN(d.getTime()) ? "??:??:??" : d.toLocaleTimeString("en-US", { hour12: false })} `;
5759
+ })() : "";
5760
+ const iter = e.iteration > 0 ? `L${e.iteration} ` : "";
5761
+ const glyph2 = TRANSCRIPT_GLYPHS[e.kind] ?? "\xB7";
5762
+ const lines = e.content.split("\n");
5763
+ const firstLine = (lines[0] ?? "").trim();
5764
+ const tool = e.toolName && !firstLine.includes(e.toolName) ? `${e.toolName}${e.toolOk === false ? " \u2717" : ""} ` : e.toolName && e.toolOk === false && !firstLine.includes("\u2717") && !firstLine.startsWith("Failed") ? "\u2717 " : "";
5765
+ const extraLines = lines.length - 1;
5766
+ const tail = extraLines > 0 ? ` (+${extraLines})` : "";
5767
+ const head = `${time}${iter}${glyph2} ${tool}`;
5768
+ return `${head}${snippet(firstLine, Math.max(16, maxWidth - head.length - tail.length))}${tail}`;
5769
+ }
5770
+ function leaderTimelineFromEntries(entries) {
5771
+ const out = [];
5772
+ for (const e of entries) {
5773
+ const base = { id: `h${e.id}`, subagentId: "leader", agentName: "LEADER", ts: "", iteration: 0 };
5774
+ switch (e.kind) {
5775
+ case "user":
5776
+ out.push({ ...base, kind: "status", content: `\u276F ${e.text}` });
5777
+ break;
5778
+ case "assistant":
5779
+ out.push({ ...base, kind: "text", content: e.text });
5780
+ break;
5781
+ case "thinking":
5782
+ out.push({ ...base, kind: "thinking", content: e.text });
5783
+ break;
5784
+ case "tool": {
5785
+ const meta = [
5786
+ e.durationMs > 0 ? `${e.durationMs}ms` : "",
5787
+ typeof e.outputLines === "number" && e.outputLines > 0 ? `${e.outputLines}L` : ""
5788
+ ].filter(Boolean).join(" \xB7 ");
5789
+ out.push({ ...base, kind: "tool_use", toolName: e.name, toolOk: e.ok, content: meta });
5790
+ break;
5791
+ }
5792
+ case "error":
5793
+ out.push({ ...base, kind: "error", content: e.text });
5794
+ break;
5795
+ case "warn":
5796
+ out.push({ ...base, kind: "system", content: `\u26A0 ${e.text}` });
5797
+ break;
5798
+ case "info":
5799
+ case "turn-summary":
5800
+ out.push({ ...base, kind: "system", content: e.text });
5801
+ break;
5802
+ case "brain":
5803
+ out.push({ ...base, kind: "system", content: `\u{1F9E0} ${e.question}${e.decision ? ` \u2192 ${e.decision}` : ""}` });
5804
+ break;
5805
+ // banner / confirm / subagent: intentionally skipped.
5806
+ default:
5807
+ break;
5808
+ }
5809
+ }
5810
+ return out;
5811
+ }
5812
+ function selectTranscriptWindow(entries, scrollOffset, rows = TRANSCRIPT_ROWS) {
5813
+ const total = entries.length;
5814
+ const maxOffset = Math.max(0, total - rows);
5815
+ const offset = Math.max(0, Math.min(scrollOffset, maxOffset));
5816
+ const end = total - offset;
5817
+ const start = Math.max(0, end - rows);
5818
+ return { slice: entries.slice(start, end), above: start, below: offset };
5819
+ }
5820
+ function TranscriptPane({
5821
+ entries,
5822
+ scrollOffset,
5823
+ rows = TRANSCRIPT_ROWS
5824
+ }) {
5825
+ const { slice, above, below } = selectTranscriptWindow(entries, scrollOffset, rows);
5826
+ const padding = Math.max(0, rows - slice.length);
5827
+ return /* @__PURE__ */ jsxs7(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, children: [
5828
+ /* @__PURE__ */ jsxs7(Box, { flexDirection: "row", gap: 1, children: [
5829
+ /* @__PURE__ */ jsx8(Text, { dimColor: true, bold: true, children: "transcript" }),
5830
+ /* @__PURE__ */ jsxs7(Text, { dimColor: true, children: [
5831
+ entries.length,
5832
+ " entries \xB7 PgUp/PgDn scroll",
5833
+ above > 0 ? ` \xB7 \u2191 ${above} earlier` : "",
5834
+ below > 0 ? ` \xB7 \u2193 ${below} newer` : ""
5835
+ ] })
5836
+ ] }),
5837
+ slice.map((e) => /* @__PURE__ */ jsx8(Text, { dimColor: e.kind === "thinking" || e.kind === "system", color: e.kind === "error" ? "red" : void 0, children: formatTranscriptLine(e) }, e.id)),
5838
+ Array.from({ length: padding }, (_, i) => /* @__PURE__ */ jsx8(Text, { children: " " }, `pad-${i}`))
5839
+ ] });
5840
+ }
5570
5841
  function AgentDetail({
5571
5842
  entry,
5572
- now
5843
+ now,
5844
+ transcript,
5845
+ transcriptScroll = 0,
5846
+ transcriptRows = TRANSCRIPT_ROWS
5573
5847
  }) {
5848
+ if (transcript) {
5849
+ const modelLbl = fmtModelLabel(entry.provider, entry.model) || [entry.provider, entry.model].filter(Boolean).join("/");
5850
+ const statusMeta = STATUS2[entry.status];
5851
+ const liveTail = entry.status === "running" && entry.streamingText ? snippet(entry.streamingText.slice(-160), 110) : "";
5852
+ const alert = entry.failureReason && entry.status !== "success" ? { color: "red", text: `\u2717 ${snippet(entry.failureReason, 90)}` } : entry.budgetWarning ? { color: "yellow", text: `\u26A1 ${entry.budgetWarning.kind} ${entry.budgetWarning.used}/${entry.budgetWarning.limit}${entry.extensions ? ` \xD7${entry.extensions}` : ""}` } : liveTail ? { color: "gray", text: `\u2234 \u2026${liveTail}` } : { color: entry.currentTool ? "cyan" : "gray", text: currentAction(entry, now) };
5853
+ return /* @__PURE__ */ jsx8(Box, { alignSelf: "stretch", flexDirection: "column", width: "100%", flexGrow: 1, children: /* @__PURE__ */ jsxs7(
5854
+ Box,
5855
+ {
5856
+ alignSelf: "stretch",
5857
+ flexDirection: "column",
5858
+ width: "100%",
5859
+ flexGrow: 1,
5860
+ paddingX: 1,
5861
+ borderStyle: "single",
5862
+ borderColor: "magenta",
5863
+ children: [
5864
+ /* @__PURE__ */ jsxs7(Box, { flexDirection: "row", gap: 1, children: [
5865
+ /* @__PURE__ */ jsx8(Text, { color: "magenta", bold: true, children: formatAgentDetailHeader(entry) }),
5866
+ /* @__PURE__ */ jsx8(Text, { dimColor: true, children: entry.id }),
5867
+ modelLbl ? /* @__PURE__ */ jsx8(Text, { color: "cyan", children: modelLbl }) : /* @__PURE__ */ jsx8(Text, { dimColor: true, children: "\xB7" }),
5868
+ /* @__PURE__ */ jsxs7(Text, { color: statusMeta.color, children: [
5869
+ statusMeta.icon,
5870
+ " ",
5871
+ entry.status
5872
+ ] })
5873
+ ] }),
5874
+ /* @__PURE__ */ jsxs7(Box, { flexDirection: "row", gap: 1, children: [
5875
+ /* @__PURE__ */ jsx8(Text, { dimColor: true, children: "runtime" }),
5876
+ /* @__PURE__ */ jsx8(Text, { children: fmtElapsed(Math.max(0, now - entry.startedAt)) }),
5877
+ /* @__PURE__ */ jsxs7(Text, { color: "cyan", children: [
5878
+ "L",
5879
+ entry.iterations,
5880
+ " ",
5881
+ entry.toolCalls,
5882
+ "t"
5883
+ ] }),
5884
+ /* @__PURE__ */ jsx8(Text, { dimColor: true, children: "ctx" }),
5885
+ /* @__PURE__ */ jsx8(Text, { children: formatContextRunway(entry.ctxTokens, entry.ctxMaxTokens) }),
5886
+ /* @__PURE__ */ jsxs7(Text, { color: "green", children: [
5887
+ "$",
5888
+ entry.cost.toFixed(4)
5889
+ ] })
5890
+ ] }),
5891
+ /* @__PURE__ */ jsx8(Box, { flexDirection: "row", gap: 1, children: /* @__PURE__ */ jsx8(Text, { color: alert.color, children: alert.text }) }),
5892
+ /* @__PURE__ */ jsx8(TranscriptPane, { entries: transcript, scrollOffset: transcriptScroll, rows: transcriptRows })
5893
+ ]
5894
+ }
5895
+ ) });
5896
+ }
5574
5897
  const spark = sparkline(bucketActivity(entry.recentTools, now));
5575
5898
  const lastTool = entry.recentTools[entry.recentTools.length - 1];
5576
5899
  const lastMessage = entry.recentMessages[entry.recentMessages.length - 1];
@@ -5712,7 +6035,10 @@ function AgentsMonitor({
5712
6035
  leaderCost = 0,
5713
6036
  totalTokens,
5714
6037
  nowTick,
5715
- onClose
6038
+ onClose,
6039
+ transcripts,
6040
+ leaderTranscript,
6041
+ fullscreen = false
5716
6042
  }) {
5717
6043
  const all = Object.values(entries);
5718
6044
  const grandCost = leaderCost + totalCost;
@@ -5726,6 +6052,20 @@ function AgentsMonitor({
5726
6052
  }, [emptyAgentsCloseStartedAt, live.length, nowTick, onClose]);
5727
6053
  const selected = selectAgentDetail(live, selectedId);
5728
6054
  const selectedIndex = selected ? live.findIndex((entry) => entry.id === selected.id) : -1;
6055
+ const [transcriptScroll, setTranscriptScroll] = useState3(0);
6056
+ const selectedTranscriptId = selected?.id;
6057
+ const transcript = useMemo2(() => {
6058
+ if (!selected) return void 0;
6059
+ if (isLeaderEntry(selected)) {
6060
+ return leaderTranscript ? leaderTranscript().slice(-TRANSCRIPT_FETCH_LIMIT) : void 0;
6061
+ }
6062
+ return transcripts ? transcripts.getTranscript(selected.id, TRANSCRIPT_FETCH_LIMIT).slice().reverse() : void 0;
6063
+ }, [transcripts, leaderTranscript, selectedTranscriptId, nowTick]);
6064
+ useEffect3(() => {
6065
+ setTranscriptScroll(0);
6066
+ }, [selectedTranscriptId]);
6067
+ const { stdout } = useStdout();
6068
+ const paneRows = transcriptRowsForTerminal(stdout?.rows, live.length, fullscreen);
5729
6069
  useInput((_input, key) => {
5730
6070
  if (live.length === 0) return;
5731
6071
  if (key.upArrow) {
@@ -5734,6 +6074,11 @@ function AgentsMonitor({
5734
6074
  } else if (key.downArrow) {
5735
6075
  const next = Math.min(live.length - 1, selectedIndex + 1);
5736
6076
  setSelectedId(live[next]?.id);
6077
+ } else if (key.pageUp && transcript) {
6078
+ const maxOffset = Math.max(0, transcript.length - paneRows);
6079
+ setTranscriptScroll((s2) => Math.min(maxOffset, s2 + paneRows));
6080
+ } else if (key.pageDown && transcript) {
6081
+ setTranscriptScroll((s2) => Math.max(0, s2 - paneRows));
5737
6082
  }
5738
6083
  });
5739
6084
  const running = live.filter((e) => e.status === "running").length;
@@ -5773,7 +6118,11 @@ function AgentsMonitor({
5773
6118
  "\u2717",
5774
6119
  totalFailed
5775
6120
  ] }) : null,
5776
- /* @__PURE__ */ jsx8(Text, { dimColor: true, children: "\xB7 \u2191\u2193 nav \xB7 Ctrl+G / F3 close" })
6121
+ /* @__PURE__ */ jsxs7(Text, { dimColor: true, children: [
6122
+ "\xB7 \u2191\u2193 nav",
6123
+ transcripts ? " \xB7 PgUp/PgDn transcript" : "",
6124
+ " \xB7 Esc / Ctrl+G / F3 close"
6125
+ ] })
5777
6126
  ] }),
5778
6127
  /* @__PURE__ */ jsxs7(Box, { flexDirection: "row", gap: 1, children: [
5779
6128
  /* @__PURE__ */ jsx8(Text, { dimColor: true, children: "pulse" }),
@@ -5833,7 +6182,17 @@ function AgentsMonitor({
5833
6182
  ] })
5834
6183
  ] }),
5835
6184
  live.length === 0 ? /* @__PURE__ */ jsx8(Text, { dimColor: true, children: "No live agents \u2014 spawn with /spawn or /fleet dispatch." }) : null,
5836
- live.map((e) => e.id === selected?.id ? /* @__PURE__ */ jsx8(AgentDetail, { entry: e, now: nowTick }, e.id) : /* @__PURE__ */ jsx8(AgentRow, { entry: e, now: nowTick, selected: false }, e.id))
6185
+ live.map((e) => e.id === selected?.id ? /* @__PURE__ */ jsx8(
6186
+ AgentDetail,
6187
+ {
6188
+ entry: e,
6189
+ now: nowTick,
6190
+ transcript,
6191
+ transcriptScroll,
6192
+ transcriptRows: paneRows
6193
+ },
6194
+ e.id
6195
+ ) : /* @__PURE__ */ jsx8(AgentRow, { entry: e, now: nowTick, selected: false }, e.id))
5837
6196
  ]
5838
6197
  }
5839
6198
  );
@@ -9033,9 +9392,9 @@ function splitGitStyleDiff(diff) {
9033
9392
  function parseDiffGitPath(line) {
9034
9393
  const rest = line.slice("diff --git ".length).trim();
9035
9394
  if (!rest) return void 0;
9036
- const sep3 = rest.lastIndexOf(" b/");
9037
- if (sep3 > 0 && sep3 > rest.length - sep3 - 3) {
9038
- return rest.slice(sep3 + 3);
9395
+ const sep2 = rest.lastIndexOf(" b/");
9396
+ if (sep2 > 0 && sep2 > rest.length - sep2 - 3) {
9397
+ return rest.slice(sep2 + 3);
9039
9398
  }
9040
9399
  const spaceIdx = rest.lastIndexOf(" ");
9041
9400
  if (spaceIdx > 0) {
@@ -10338,8 +10697,8 @@ var SEP_RE = /^\s*\|[\s\-:|]+\|\s*$/;
10338
10697
  function detectTable(lines, start) {
10339
10698
  if (start + 1 >= lines.length) return start;
10340
10699
  if (!ROW_RE.test(lines[start] ?? "")) return start;
10341
- const sep3 = lines[start + 1] ?? "";
10342
- if (!SEP_RE.test(sep3) || !/-/.test(sep3)) return start;
10700
+ const sep2 = lines[start + 1] ?? "";
10701
+ if (!SEP_RE.test(sep2) || !/-/.test(sep2)) return start;
10343
10702
  let end = start + 2;
10344
10703
  while (end < lines.length && ROW_RE.test(lines[end] ?? "")) end++;
10345
10704
  return end;
@@ -10365,8 +10724,8 @@ function parseCells(line) {
10365
10724
  parts.push(buf);
10366
10725
  return parts.map((c) => c.trim());
10367
10726
  }
10368
- function parseAlign(sep3) {
10369
- const t = sep3.trim();
10727
+ function parseAlign(sep2) {
10728
+ const t = sep2.trim();
10370
10729
  const left = t.startsWith(":");
10371
10730
  const right = t.endsWith(":");
10372
10731
  if (left && right) return "center";
@@ -12509,11 +12868,13 @@ function ModelPicker({
12509
12868
  selected,
12510
12869
  pickedProviderId,
12511
12870
  searchQuery,
12512
- hint
12871
+ hint,
12872
+ titleLabel
12513
12873
  }) {
12874
+ const title = titleLabel ?? "Switch model";
12514
12875
  if (step === "provider") {
12515
12876
  return /* @__PURE__ */ jsxs37(Box, { flexDirection: "column", borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, children: [
12516
- /* @__PURE__ */ jsx38(Text, { color: UI_COLORS.title, bold: true, children: "\u2501\u2501 Switch model \u2014 Step 1/2: Pick provider \u2501\u2501" }),
12877
+ /* @__PURE__ */ jsx38(Text, { color: UI_COLORS.title, bold: true, children: `\u2501\u2501 ${title} \u2014 Step 1/2: Pick provider \u2501\u2501` }),
12517
12878
  /* @__PURE__ */ jsx38(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel \xB7 Ctrl+C exit" }),
12518
12879
  providerOptions.length === 0 ? /* @__PURE__ */ jsx38(Text, { dimColor: true, children: "(no providers with keys \u2014 add one via `wstack auth`)" }) : /* @__PURE__ */ jsx38(Box, { flexDirection: "column", minHeight: providerOptions.length, children: providerOptions.map((p, i) => {
12519
12880
  const isSelected = i === selected;
@@ -12552,7 +12913,7 @@ function ModelPicker({
12552
12913
  const searchHint = searchQuery ? ` | filter:"${searchQuery}" \u2192 ${total} match${total === 1 ? "" : "es"}` : total > MAX_VISIBLE2 ? ` (${total} models \u2014 type to filter)` : "";
12553
12914
  return /* @__PURE__ */ jsxs37(Box, { flexDirection: "column", borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, children: [
12554
12915
  /* @__PURE__ */ jsxs37(Text, { color: UI_COLORS.title, bold: true, children: [
12555
- "\u2501\u2501 Switch model \u2014 Step 2/2: Pick model ",
12916
+ `\u2501\u2501 ${title} \u2014 Step 2/2: Pick model `,
12556
12917
  "(",
12557
12918
  pickedProviderId,
12558
12919
  searchHint,
@@ -12951,24 +13312,126 @@ var RISK_COLORS2 = {
12951
13312
  high: "red",
12952
13313
  all: "magenta"
12953
13314
  };
13315
+ function fmtMs(ms, fallback) {
13316
+ if (ms === void 0) return fallback;
13317
+ return ms % 6e4 === 0 ? `${ms / 6e4}m` : `${ms / 1e3}s`;
13318
+ }
13319
+ function rowText(row, s2) {
13320
+ switch (row.kind) {
13321
+ case "mode":
13322
+ return {
13323
+ label: "Mode",
13324
+ value: s2.mode.toUpperCase(),
13325
+ dim: s2.mode === "headless" ? "never blocks on you (terminal policy)" : "escalations prompt you"
13326
+ };
13327
+ case "risk":
13328
+ return { label: "Risk ceiling", value: s2.riskLevel.toUpperCase(), dim: RISK_DESCS[s2.riskLevel] };
13329
+ case "strategy":
13330
+ return { label: "Pool strategy", value: s2.strategy, dim: "fallback = ordered, round-robin = rotate" };
13331
+ case "timeout":
13332
+ return { label: "Decision timeout", value: fmtMs(s2.decisionTimeoutMs, "default (15s)") };
13333
+ case "humanTimeout":
13334
+ return {
13335
+ label: "Human timeout",
13336
+ value: fmtMs(s2.humanTimeoutMs, "off"),
13337
+ dim: s2.humanTimeoutMs ? "then terminal policy" : "wait indefinitely"
13338
+ };
13339
+ case "poolModel": {
13340
+ const label = s2.pool[row.index] ?? "";
13341
+ const resolved = s2.poolResolved.some((r) => r === label || r.endsWith(`/${label}`));
13342
+ return {
13343
+ label: ` ${row.index + 1}.`,
13344
+ value: label,
13345
+ dim: resolved ? "d = remove" : "UNRESOLVED \u2014 falls back to session model"
13346
+ };
13347
+ }
13348
+ case "poolAdd":
13349
+ return {
13350
+ label: s2.pool.length === 0 ? "Decision model" : " +",
13351
+ value: s2.pool.length === 0 ? "session model" : "add model\u2026",
13352
+ dim: s2.pool.length === 0 ? "Enter = pick a dedicated model" : "Enter"
13353
+ };
13354
+ case "councilToggle":
13355
+ return {
13356
+ label: "Council",
13357
+ value: s2.councilEnabled ? "ON" : "off",
13358
+ dim: s2.councilEnabled ? s2.voters.length === 0 && s2.councilSeats.length > 0 ? `seats from pool: ${s2.councilSeats.join(", ")} \xB7 Enter = pick voters` : "multi-LLM vote on high-stakes questions \xB7 Enter = add voter" : "Enter = enable + pick voters \xB7 \u2190/\u2192 toggle"
13359
+ };
13360
+ case "councilMinRisk":
13361
+ return { label: " risk floor", value: s2.councilMinRisk, dim: "council convenes at/above this risk" };
13362
+ case "voter": {
13363
+ const v = s2.voters[row.index];
13364
+ return {
13365
+ label: ` ${row.index + 1}.`,
13366
+ value: v?.label ?? "",
13367
+ dim: `${v?.persona ?? "voter"}${v?.veto ? " \xB7 VETO" : ""}${v?.weight ? ` \xB7 w=${v.weight}` : ""} (p persona \xB7 v veto \xB7 d remove)`
13368
+ };
13369
+ }
13370
+ case "voterAdd":
13371
+ return { label: " +", value: "add voter\u2026", dim: "Enter" };
13372
+ case "judge":
13373
+ return { label: " judge", value: s2.judgeLabel ?? "auto", dim: "Enter = pick \xB7 d = auto" };
13374
+ case "ledgerToggle":
13375
+ return {
13376
+ label: "Ledger",
13377
+ value: s2.ledgerEnabled ? "ON" : "off",
13378
+ dim: "records decisions + outcomes, feeds them back into prompts"
13379
+ };
13380
+ case "autoDeny":
13381
+ return {
13382
+ label: " auto-deny",
13383
+ value: s2.autoDenyAfterFailures === void 0 ? "default (3)" : s2.autoDenyAfterFailures === 0 ? "off" : String(s2.autoDenyAfterFailures),
13384
+ dim: "deny after N observed consecutive failures"
13385
+ };
13386
+ default:
13387
+ return { label: "", value: "" };
13388
+ }
13389
+ }
12954
13390
  function BrainPanel({
12955
13391
  riskLevel,
12956
13392
  log,
12957
13393
  selected,
12958
- hint
13394
+ hint,
13395
+ view,
13396
+ settings,
13397
+ row,
13398
+ busy
12959
13399
  }) {
12960
13400
  const { stdout } = useStdout();
12961
13401
  const termRows = stdout?.rows ?? 24;
13402
+ const editable = settings !== void 0;
13403
+ const activeView = editable ? view ?? "settings" : "log";
12962
13404
  const maxVisible = Math.max(4, termRows - CHROME_ROWS2);
12963
13405
  const total = log.length;
12964
13406
  const windowStart = total <= maxVisible ? 0 : Math.max(0, Math.min(selected - Math.floor(maxVisible / 2), total - maxVisible));
12965
13407
  const windowEnd = Math.min(windowStart + maxVisible, total);
12966
13408
  const above = windowStart;
12967
13409
  const below = total - windowEnd;
13410
+ 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";
12968
13411
  return /* @__PURE__ */ jsxs41(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [
12969
- /* @__PURE__ */ jsx42(Text, { bold: true, color: "magenta", children: "\u2501\u2501 Brain \u2501\u2501" }),
12970
- /* @__PURE__ */ jsx42(Text, { dimColor: true, children: "\u2191/\u2193 navigate log \xB7 \u2190/\u2192 change risk \xB7 Esc close" }),
12971
- /* @__PURE__ */ jsxs41(Box, { marginTop: 1, flexDirection: "column", children: [
13412
+ /* @__PURE__ */ jsx42(Text, { bold: true, color: "magenta", children: `\u2501\u2501 Brain \u2501\u2501${busy ? " \u2026" : ""}` }),
13413
+ /* @__PURE__ */ jsx42(Text, { dimColor: true, children: headerHint }),
13414
+ activeView === "settings" && settings ? /* @__PURE__ */ jsxs41(Box, { marginTop: 1, flexDirection: "column", children: [
13415
+ brainPanelRows(settings).map((r, i) => {
13416
+ const focused = i === (row ?? 0);
13417
+ const { label, value, dim } = rowText(r, settings);
13418
+ return /* @__PURE__ */ jsxs41(Text, { wrap: "truncate-end", children: [
13419
+ focused ? /* @__PURE__ */ jsx42(Text, { color: "magenta", children: "\u203A " }) : " ",
13420
+ /* @__PURE__ */ jsx42(Text, { bold: focused, children: label.padEnd(18) }),
13421
+ /* @__PURE__ */ jsx42(
13422
+ Text,
13423
+ {
13424
+ color: r.kind === "risk" ? RISK_COLORS2[settings.riskLevel] : focused ? "cyan" : void 0,
13425
+ bold: r.kind === "risk" || r.kind === "mode",
13426
+ inverse: focused,
13427
+ children: value
13428
+ }
13429
+ ),
13430
+ dim ? /* @__PURE__ */ jsx42(Text, { dimColor: true, children: ` ${dim}` }) : null
13431
+ ] }, `${r.kind}-${"index" in r ? r.index : 0}`);
13432
+ }),
13433
+ /* @__PURE__ */ jsx42(Box, { marginTop: 1, children: /* @__PURE__ */ jsx42(Text, { dimColor: true, children: "changes apply live and persist to ~/.wrongstack/config.json" }) })
13434
+ ] }) : /* @__PURE__ */ jsxs41(Box, { marginTop: 1, flexDirection: "column", children: [
12972
13435
  /* @__PURE__ */ jsxs41(Box, { children: [
12973
13436
  /* @__PURE__ */ jsx42(Text, { bold: true, children: "Risk ceiling: " }),
12974
13437
  /* @__PURE__ */ jsx42(Text, { color: RISK_COLORS2[riskLevel], bold: true, children: riskLevel.toUpperCase() }),
@@ -13006,6 +13469,243 @@ function BrainPanel({
13006
13469
  ] });
13007
13470
  }
13008
13471
 
13472
+ // src/hooks/use-brain-panel.ts
13473
+ import React7 from "react";
13474
+ function useBrainPanel(opts) {
13475
+ const { dispatch, getBrainData, brainPanelHost, requestModelPick } = opts;
13476
+ const openBrainPanel = React7.useCallback(() => {
13477
+ if (!getBrainData) return;
13478
+ const data = getBrainData();
13479
+ dispatch({
13480
+ type: "brainOpen",
13481
+ riskLevel: data.riskLevel,
13482
+ log: data.log,
13483
+ settings: brainPanelHost?.getSettings()
13484
+ });
13485
+ }, [dispatch, getBrainData, brainPanelHost]);
13486
+ const runBrainMutation = React7.useCallback(
13487
+ (op, successHint, onSuccess) => {
13488
+ if (!brainPanelHost) return;
13489
+ dispatch({ type: "brainBusy", busy: true });
13490
+ void op().then((err) => {
13491
+ dispatch({ type: "brainSettingsLoaded", settings: brainPanelHost.getSettings() });
13492
+ dispatch({
13493
+ type: "brainHint",
13494
+ text: err ?? successHint ?? "Saved to ~/.wrongstack/config.json"
13495
+ });
13496
+ if (!err) onSuccess?.();
13497
+ }).catch((e) => {
13498
+ dispatch({ type: "brainBusy", busy: false });
13499
+ dispatch({ type: "brainHint", text: e instanceof Error ? e.message : String(e) });
13500
+ });
13501
+ },
13502
+ [dispatch, brainPanelHost]
13503
+ );
13504
+ const reportError = React7.useCallback(
13505
+ (e) => {
13506
+ dispatch({ type: "brainBusy", busy: false });
13507
+ dispatch({ type: "brainHint", text: e instanceof Error ? e.message : String(e) });
13508
+ },
13509
+ [dispatch]
13510
+ );
13511
+ const pickThenApply = React7.useCallback(
13512
+ (title, apply) => {
13513
+ if (!requestModelPick) return;
13514
+ void requestModelPick(title).then((sel) => {
13515
+ if (sel) runBrainMutation(() => apply(sel.providerId, sel.model));
13516
+ }).catch(reportError);
13517
+ },
13518
+ [requestModelPick, runBrainMutation, reportError]
13519
+ );
13520
+ const addVotersViaPicker = React7.useCallback(() => {
13521
+ const host = brainPanelHost;
13522
+ if (!host || !requestModelPick) return;
13523
+ const loop = async () => {
13524
+ const sel = await requestModelPick("Add council voter (Esc = done)");
13525
+ if (!sel) return;
13526
+ const err = await host.addVoter(sel.providerId, sel.model);
13527
+ dispatch({ type: "brainSettingsLoaded", settings: host.getSettings() });
13528
+ dispatch({
13529
+ type: "brainHint",
13530
+ text: err ?? `Voter added: ${sel.providerId}/${sel.model} \u2014 pick another or Esc`
13531
+ });
13532
+ if (!err) await loop();
13533
+ };
13534
+ void loop().catch(reportError);
13535
+ }, [dispatch, brainPanelHost, requestModelPick, reportError]);
13536
+ const handleBrainAdjust = React7.useCallback(
13537
+ (row, delta) => {
13538
+ const host = brainPanelHost;
13539
+ const settings = host?.getSettings();
13540
+ if (!host || !settings) return;
13541
+ switch (row.kind) {
13542
+ case "mode":
13543
+ runBrainMutation(
13544
+ () => host.setMode(settings.mode === "headless" ? "interactive" : "headless")
13545
+ );
13546
+ return;
13547
+ case "risk": {
13548
+ const levels = ["off", "low", "medium", "high", "all"];
13549
+ const next = levels[(levels.indexOf(settings.riskLevel) + delta + levels.length) % levels.length];
13550
+ runBrainMutation(() => host.setRisk(next), `Risk ceiling \u2192 ${next.toUpperCase()}`);
13551
+ return;
13552
+ }
13553
+ case "strategy":
13554
+ runBrainMutation(
13555
+ () => host.setStrategy(settings.strategy === "fallback" ? "round-robin" : "fallback")
13556
+ );
13557
+ return;
13558
+ case "timeout":
13559
+ runBrainMutation(
13560
+ () => host.setDecisionTimeout(
13561
+ cyclePreset(DECISION_TIMEOUT_PRESETS, settings.decisionTimeoutMs, delta)
13562
+ )
13563
+ );
13564
+ return;
13565
+ case "humanTimeout":
13566
+ runBrainMutation(
13567
+ () => host.setHumanTimeout(
13568
+ cyclePreset(HUMAN_TIMEOUT_PRESETS, settings.humanTimeoutMs, delta)
13569
+ )
13570
+ );
13571
+ return;
13572
+ case "councilToggle":
13573
+ runBrainMutation(() => host.setCouncilEnabled(!settings.councilEnabled));
13574
+ return;
13575
+ case "councilMinRisk": {
13576
+ const risks = ["medium", "high", "critical"];
13577
+ const next = risks[(risks.indexOf(settings.councilMinRisk) + delta + risks.length) % risks.length];
13578
+ runBrainMutation(() => host.setCouncilMinRisk(next));
13579
+ return;
13580
+ }
13581
+ case "ledgerToggle":
13582
+ runBrainMutation(() => host.setLedgerEnabled(!settings.ledgerEnabled));
13583
+ return;
13584
+ case "autoDeny":
13585
+ runBrainMutation(
13586
+ () => host.setAutoDeny(cyclePreset(AUTO_DENY_PRESETS, settings.autoDenyAfterFailures, delta))
13587
+ );
13588
+ return;
13589
+ case "voter":
13590
+ runBrainMutation(() => host.cycleVoterPersona(row.index));
13591
+ return;
13592
+ default:
13593
+ return;
13594
+ }
13595
+ },
13596
+ [brainPanelHost, runBrainMutation]
13597
+ );
13598
+ const handleBrainEnter = React7.useCallback(
13599
+ (row) => {
13600
+ switch (row.kind) {
13601
+ case "mode":
13602
+ case "strategy":
13603
+ case "ledgerToggle":
13604
+ handleBrainAdjust(row, 1);
13605
+ return;
13606
+ case "councilToggle": {
13607
+ const settings = brainPanelHost?.getSettings();
13608
+ if (!brainPanelHost || !settings) return;
13609
+ if (settings.councilEnabled) {
13610
+ addVotersViaPicker();
13611
+ return;
13612
+ }
13613
+ const host = brainPanelHost;
13614
+ runBrainMutation(
13615
+ () => host.setCouncilEnabled(true),
13616
+ "Council enabled \u2014 pick voters (Esc when done)",
13617
+ addVotersViaPicker
13618
+ );
13619
+ return;
13620
+ }
13621
+ case "poolAdd": {
13622
+ const host = brainPanelHost;
13623
+ if (!host) return;
13624
+ pickThenApply(
13625
+ "Add Brain pool model",
13626
+ (providerId, model) => host.addPoolModel(providerId, model)
13627
+ );
13628
+ return;
13629
+ }
13630
+ case "voterAdd":
13631
+ addVotersViaPicker();
13632
+ return;
13633
+ case "judge": {
13634
+ const host = brainPanelHost;
13635
+ if (!host) return;
13636
+ pickThenApply(
13637
+ "Pick council judge",
13638
+ (providerId, model) => host.setJudge(providerId, model)
13639
+ );
13640
+ return;
13641
+ }
13642
+ default:
13643
+ return;
13644
+ }
13645
+ },
13646
+ [brainPanelHost, handleBrainAdjust, addVotersViaPicker, pickThenApply, runBrainMutation]
13647
+ );
13648
+ const handleBrainDelete = React7.useCallback(
13649
+ (row) => {
13650
+ const host = brainPanelHost;
13651
+ if (!host) return;
13652
+ if (row.kind === "poolModel") runBrainMutation(() => host.removePoolModel(row.index));
13653
+ else if (row.kind === "voter") runBrainMutation(() => host.removeVoter(row.index));
13654
+ else if (row.kind === "judge") runBrainMutation(() => host.clearJudge(), "Judge \u2192 auto");
13655
+ },
13656
+ [brainPanelHost, runBrainMutation]
13657
+ );
13658
+ const handleBrainVoterMod = React7.useCallback(
13659
+ (index, mod) => {
13660
+ const host = brainPanelHost;
13661
+ if (!host) return;
13662
+ runBrainMutation(
13663
+ () => mod === "persona" ? host.cycleVoterPersona(index) : host.toggleVoterVeto(index)
13664
+ );
13665
+ },
13666
+ [brainPanelHost, runBrainMutation]
13667
+ );
13668
+ return {
13669
+ openBrainPanel,
13670
+ handleBrainAdjust,
13671
+ handleBrainEnter,
13672
+ handleBrainDelete,
13673
+ handleBrainVoterMod
13674
+ };
13675
+ }
13676
+
13677
+ // src/hooks/use-model-pick.ts
13678
+ import React8 from "react";
13679
+ function useModelPickRequest(opts) {
13680
+ const { dispatch, getPickableProviders, pickerOpen } = opts;
13681
+ const resolverRef = React8.useRef(null);
13682
+ const requestModelPick = React8.useCallback(
13683
+ async (title) => {
13684
+ if (!getPickableProviders) return null;
13685
+ resolverRef.current?.(null);
13686
+ const providers = await getPickableProviders();
13687
+ return new Promise((resolve) => {
13688
+ resolverRef.current = resolve;
13689
+ dispatch({ type: "modelPickerOpen", providers, purpose: "pick", title });
13690
+ });
13691
+ },
13692
+ [dispatch, getPickableProviders]
13693
+ );
13694
+ const handleModelPicked = React8.useCallback((providerId, modelId) => {
13695
+ const resolve = resolverRef.current;
13696
+ resolverRef.current = null;
13697
+ resolve?.({ providerId, model: modelId });
13698
+ }, []);
13699
+ React8.useEffect(() => {
13700
+ if (!pickerOpen && resolverRef.current) {
13701
+ const resolve = resolverRef.current;
13702
+ resolverRef.current = null;
13703
+ resolve(null);
13704
+ }
13705
+ }, [pickerOpen]);
13706
+ return { requestModelPick, handleModelPicked };
13707
+ }
13708
+
13009
13709
  // src/components/mcp-picker.tsx
13010
13710
  import { Fragment as Fragment15, jsx as jsx43, jsxs as jsxs42 } from "react/jsx-runtime";
13011
13711
  var CHROME_ROWS3 = 17;
@@ -14726,9 +15426,11 @@ function createInitialState(options) {
14726
15426
  keyTail,
14727
15427
  restoredEntries,
14728
15428
  enhanceEnabled,
14729
- initialAgentsMonitorOpen
15429
+ initialAgentsMonitorOpen,
15430
+ initialFleetChat
14730
15431
  } = options;
14731
15432
  const initialNextId = 1 + restoredEntries.length;
15433
+ const fleetChat = initialFleetChat ?? "compact";
14732
15434
  return {
14733
15435
  entries: [
14734
15436
  ...banner ? [
@@ -14773,7 +15475,8 @@ function createInitialState(options) {
14773
15475
  modelOptions: [],
14774
15476
  filteredOptions: [],
14775
15477
  selected: 0,
14776
- searchQuery: ""
15478
+ searchQuery: "",
15479
+ purpose: "switch"
14777
15480
  },
14778
15481
  autonomyPicker: { open: false, options: [], selected: 0 },
14779
15482
  modePicker: { open: false, modes: [], selected: 0 },
@@ -14803,7 +15506,7 @@ function createInitialState(options) {
14803
15506
  delayMs: 0,
14804
15507
  titleAnimation: true,
14805
15508
  yolo: false,
14806
- streamFleet: true,
15509
+ fleetChat,
14807
15510
  chime: false,
14808
15511
  confirmExit: true,
14809
15512
  nextPrediction: false,
@@ -14837,7 +15540,9 @@ function createInitialState(options) {
14837
15540
  thinkingWordDraft: "",
14838
15541
  cacheTtl: "default",
14839
15542
  configScope: "global",
14840
- animationStyle: "rainbow"
15543
+ animationStyle: "rainbow",
15544
+ breakerEnabled: false,
15545
+ breakerAutoKillResetMs: 6e4
14841
15546
  },
14842
15547
  statuslinePicker: {
14843
15548
  open: false,
@@ -14849,7 +15554,7 @@ function createInitialState(options) {
14849
15554
  pluginPicker: { open: false, items: [], selected: 0, busy: false, hint: void 0 },
14850
15555
  mcpPicker: { open: false, items: [], selected: 0, busy: false, hint: void 0 },
14851
15556
  toolsPicker: { open: false, items: [], selected: 0, busy: false, hint: void 0, filter: void 0 },
14852
- brainPanel: { open: false, riskLevel: "medium", log: [], selected: 0, hint: void 0 },
15557
+ brainPanel: { open: false, riskLevel: "medium", log: [], selected: 0, hint: void 0, view: "settings", settings: void 0, row: 0, busy: false },
14853
15558
  helpPanel: { open: false, entries: [], selected: 0, filter: "", hint: void 0 },
14854
15559
  shadowPanel: { open: false, shadow: { activeId: null, running: false, model: "", intervalMs: 3e4 }, hint: void 0 },
14855
15560
  authPanel: AUTH_PANEL_INITIAL,
@@ -14884,7 +15589,7 @@ function createInitialState(options) {
14884
15589
  fleetCost: 0,
14885
15590
  fleetTokens: { input: 0, output: 0 },
14886
15591
  fleetConcurrency: 4,
14887
- streamFleet: true,
15592
+ fleetChat,
14888
15593
  monitorOpen: false,
14889
15594
  agentsMonitorOpen: initialAgentsMonitorOpen ?? false,
14890
15595
  helpOpen: false,
@@ -15328,6 +16033,38 @@ function useAutonomousCoordinator(subscribeCoordinatorEvents, dispatch) {
15328
16033
  // src/hooks/use-director-fleet-bridge.ts
15329
16034
  import { useEffect as useEffect15, useRef as useRef6 } from "react";
15330
16035
  import { stripNextStepsBlock } from "@wrongstack/tools/next-steps";
16036
+
16037
+ // src/hooks/fleet-chat-coalescer.ts
16038
+ function newToolAgg() {
16039
+ return { total: 0, fails: 0, totalMs: 0, byTool: /* @__PURE__ */ new Map() };
16040
+ }
16041
+ function addTool(agg, name, ok, durationMs) {
16042
+ agg.total += 1;
16043
+ if (ok === false) agg.fails += 1;
16044
+ if (typeof durationMs === "number" && Number.isFinite(durationMs) && durationMs > 0) {
16045
+ agg.totalMs += durationMs;
16046
+ }
16047
+ agg.byTool.set(name, (agg.byTool.get(name) ?? 0) + 1);
16048
+ }
16049
+ function formatDuration(ms) {
16050
+ if (ms >= 6e4) return `${(ms / 6e4).toFixed(1)}m`;
16051
+ if (ms >= 1e3) return `${(ms / 1e3).toFixed(1)}s`;
16052
+ return `${Math.round(ms)}ms`;
16053
+ }
16054
+ function formatToolSummary(agg, maxTools = 4) {
16055
+ if (agg.total === 0) return "";
16056
+ const parts = [`${agg.total} tool${agg.total === 1 ? "" : "s"}`];
16057
+ const sorted = [...agg.byTool.entries()].sort((a, b) => b[1] - a[1]);
16058
+ const top = sorted.slice(0, maxTools).map(([name, count]) => count > 1 ? `${name}\xD7${count}` : name);
16059
+ const restCount = sorted.length - maxTools;
16060
+ if (restCount > 0) top.push(`+${restCount} more`);
16061
+ if (top.length > 0) parts.push(top.join(" "));
16062
+ if (agg.totalMs > 0) parts.push(formatDuration(agg.totalMs));
16063
+ if (agg.fails > 0) parts.push(`${agg.fails} \u2717`);
16064
+ return parts.join(" \xB7 ");
16065
+ }
16066
+
16067
+ // src/hooks/use-director-fleet-bridge.ts
15331
16068
  var FLUSH_MS = 150;
15332
16069
  var STREAM_COLORS = ["cyan", "magenta", "yellow", "green", "blue"];
15333
16070
  function labelFor(labelsRef, id, name) {
@@ -15345,13 +16082,13 @@ function useDirectorFleetBridge({
15345
16082
  director,
15346
16083
  dispatch,
15347
16084
  stateRef,
15348
- streamFleet
16085
+ chatMode
15349
16086
  }) {
15350
16087
  const labelsRef = useRef6(/* @__PURE__ */ new Map());
15351
- const streamFleetRef = useRef6(streamFleet);
16088
+ const chatModeRef = useRef6(chatMode);
15352
16089
  useEffect15(() => {
15353
- streamFleetRef.current = streamFleet;
15354
- }, [streamFleet]);
16090
+ chatModeRef.current = chatMode;
16091
+ }, [chatMode]);
15355
16092
  useEffect15(() => {
15356
16093
  const d = director;
15357
16094
  if (!d) return;
@@ -15383,23 +16120,33 @@ function useDirectorFleetBridge({
15383
16120
  streamFlushTimer = null;
15384
16121
  };
15385
16122
  const historyBuf = /* @__PURE__ */ new Map();
15386
- const finalizeHistory = (id) => {
16123
+ const toolAgg = /* @__PURE__ */ new Map();
16124
+ const finalizeTurn = (id) => {
15387
16125
  const text = historyBuf.get(id);
15388
16126
  historyBuf.delete(id);
15389
- if (!text || !streamFleetRef.current) return;
15390
- const cleaned = stripNextStepsBlock(text);
15391
- if (!cleaned.trim()) return;
16127
+ const agg = toolAgg.get(id);
16128
+ toolAgg.delete(id);
16129
+ const mode = chatModeRef.current;
16130
+ if (mode === "off") return;
16131
+ const cleaned = text ? stripNextStepsBlock(text).trim() : "";
16132
+ const summary = agg ? formatToolSummary(agg) : "";
16133
+ if (!cleaned && !summary) return;
15392
16134
  const label = labelFor(labelsRef, id);
15393
- enq({
15394
- type: "addEntry",
15395
- entry: {
15396
- kind: "subagent",
15397
- agentLabel: label.label,
15398
- agentColor: label.color,
15399
- icon: "\u{1F4AC}",
15400
- text: cleaned
15401
- }
15402
- });
16135
+ const base = {
16136
+ kind: "subagent",
16137
+ agentLabel: label.label,
16138
+ agentColor: label.color
16139
+ };
16140
+ if (cleaned) {
16141
+ const multiline = cleaned.includes("\n");
16142
+ enq({
16143
+ type: "addEntry",
16144
+ entry: !summary ? { ...base, icon: "\u{1F4AC}", text: cleaned } : multiline ? { ...base, icon: "\u{1F4AC}", text: `${cleaned}
16145
+ \u{1F527} ${summary}` } : { ...base, icon: "\u{1F4AC}", text: cleaned, detail: `\u{1F527} ${summary}` }
16146
+ });
16147
+ } else {
16148
+ enq({ type: "addEntry", entry: { ...base, icon: "\u{1F527}", text: summary } });
16149
+ }
15403
16150
  };
15404
16151
  const status = d.status();
15405
16152
  for (const subagent of status.subagents) {
@@ -15444,7 +16191,7 @@ function useDirectorFleetBridge({
15444
16191
  model: meta?.model
15445
16192
  });
15446
16193
  const label = labelFor(labelsRef, event.subagentId, meta?.name);
15447
- if (streamFleetRef.current) {
16194
+ if (chatModeRef.current !== "off") {
15448
16195
  const where = meta?.provider && meta?.model ? `${meta.provider}/${meta.model}` : "spawned";
15449
16196
  enqueue({
15450
16197
  type: "addEntry",
@@ -15523,7 +16270,7 @@ function useDirectorFleetBridge({
15523
16270
  case "tool.started": {
15524
16271
  const payload = event.payload;
15525
16272
  if (payload?.name) {
15526
- finalizeHistory(event.subagentId);
16273
+ if (chatModeRef.current === "full") finalizeTurn(event.subagentId);
15527
16274
  enqueue({ type: "fleetToolStart", id: event.subagentId, name: payload.name });
15528
16275
  }
15529
16276
  break;
@@ -15540,18 +16287,28 @@ function useDirectorFleetBridge({
15540
16287
  outputLines: payload?.outputLines
15541
16288
  });
15542
16289
  enqueue({ type: "fleetToolEnd", id: event.subagentId });
15543
- if (streamFleetRef.current && payload?.name) {
15544
- const label = labelFor(labelsRef, event.subagentId);
15545
- enqueue({
15546
- type: "addEntry",
15547
- entry: {
15548
- kind: "subagent",
15549
- agentLabel: label.label,
15550
- agentColor: label.color,
15551
- icon: "\u{1F527}",
15552
- text: `\u2192 ${payload.name} ${payload.ok === false ? "\u2717" : "\u2713"}${payload.durationMs != null ? ` (${payload.durationMs}ms)` : ""}`
16290
+ if (payload?.name) {
16291
+ const mode = chatModeRef.current;
16292
+ if (mode === "full") {
16293
+ const label = labelFor(labelsRef, event.subagentId);
16294
+ enqueue({
16295
+ type: "addEntry",
16296
+ entry: {
16297
+ kind: "subagent",
16298
+ agentLabel: label.label,
16299
+ agentColor: label.color,
16300
+ icon: "\u{1F527}",
16301
+ text: `\u2192 ${payload.name} ${payload.ok === false ? "\u2717" : "\u2713"}${payload.durationMs != null ? ` (${payload.durationMs}ms)` : ""}`
16302
+ }
16303
+ });
16304
+ } else if (mode === "compact") {
16305
+ let agg = toolAgg.get(event.subagentId);
16306
+ if (!agg) {
16307
+ agg = newToolAgg();
16308
+ toolAgg.set(event.subagentId, agg);
15553
16309
  }
15554
- });
16310
+ addTool(agg, payload.name, payload.ok, payload.durationMs);
16311
+ }
15555
16312
  }
15556
16313
  break;
15557
16314
  }
@@ -15565,13 +16322,15 @@ function useDirectorFleetBridge({
15565
16322
  });
15566
16323
  break;
15567
16324
  case "session.ended":
15568
- finalizeHistory(event.subagentId);
16325
+ finalizeTurn(event.subagentId);
15569
16326
  break;
15570
16327
  case "compaction.fired":
15571
- enqueue({
15572
- type: "addEntry",
15573
- entry: { kind: "info", text: "subagent compaction triggered" }
15574
- });
16328
+ if (chatModeRef.current !== "off") {
16329
+ enqueue({
16330
+ type: "addEntry",
16331
+ entry: { kind: "info", text: "subagent compaction triggered" }
16332
+ });
16333
+ }
15575
16334
  break;
15576
16335
  case "compaction.failed":
15577
16336
  enqueue({
@@ -15580,10 +16339,12 @@ function useDirectorFleetBridge({
15580
16339
  });
15581
16340
  break;
15582
16341
  case "token.threshold":
15583
- enqueue({
15584
- type: "addEntry",
15585
- entry: { kind: "info", text: "subagent token threshold reached" }
15586
- });
16342
+ if (chatModeRef.current === "full") {
16343
+ enqueue({
16344
+ type: "addEntry",
16345
+ entry: { kind: "info", text: "subagent token threshold reached" }
16346
+ });
16347
+ }
15587
16348
  break;
15588
16349
  case "budget.threshold_reached": {
15589
16350
  const payload = event.payload;
@@ -15650,7 +16411,7 @@ function useDirectorFleetBridge({
15650
16411
  output: d.snapshot().total.output,
15651
16412
  perAgent: d.snapshot().perSubagent
15652
16413
  });
15653
- finalizeHistory(payload.result.subagentId);
16414
+ finalizeTurn(payload.result.subagentId);
15654
16415
  if (streamFlushTimer) {
15655
16416
  clearTimeout(streamFlushTimer);
15656
16417
  flushStreamBufs();
@@ -15665,7 +16426,7 @@ function useDirectorFleetBridge({
15665
16426
  doFlush();
15666
16427
  if (streamFlushTimer) clearTimeout(streamFlushTimer);
15667
16428
  flushStreamBufs();
15668
- for (const id of [...historyBuf.keys()]) finalizeHistory(id);
16429
+ for (const id of /* @__PURE__ */ new Set([...historyBuf.keys(), ...toolAgg.keys()])) finalizeTurn(id);
15669
16430
  if (batchTimer) clearTimeout(batchTimer);
15670
16431
  flushBatch();
15671
16432
  };
@@ -16160,6 +16921,11 @@ function usePickerKeys(host) {
16160
16921
  const providerId = state.modelPicker.pickedProviderId;
16161
16922
  const modelId = state.modelPicker.filteredOptions[state.modelPicker.selected];
16162
16923
  if (!providerId || !modelId) return true;
16924
+ if (state.modelPicker.purpose === "pick") {
16925
+ host.onModelPicked?.(providerId, modelId);
16926
+ dispatch({ type: "modelPickerClose" });
16927
+ return true;
16928
+ }
16163
16929
  const complete = (err) => {
16164
16930
  if (err) {
16165
16931
  dispatch({ type: "modelPickerHint", text: err });
@@ -16554,10 +17320,66 @@ function usePickerKeys(host) {
16554
17320
  return true;
16555
17321
  }
16556
17322
  if (state.brainPanel.open) {
17323
+ const panel = state.brainPanel;
17324
+ if (panel.view === "settings" && panel.settings) {
17325
+ const rows = brainPanelRows(panel.settings);
17326
+ const row = rows[Math.min(panel.row, Math.max(0, rows.length - 1))];
17327
+ if (key.escape) {
17328
+ dispatch({ type: "brainClose" });
17329
+ return true;
17330
+ }
17331
+ if (key.tab) {
17332
+ dispatch({ type: "brainView", view: "log" });
17333
+ return true;
17334
+ }
17335
+ if (key.mouse?.kind === "wheel") {
17336
+ dispatch({ type: "brainRowMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
17337
+ return true;
17338
+ }
17339
+ if (key.upArrow) {
17340
+ dispatch({ type: "brainRowMove", delta: -1 });
17341
+ return true;
17342
+ }
17343
+ if (key.downArrow) {
17344
+ dispatch({ type: "brainRowMove", delta: 1 });
17345
+ return true;
17346
+ }
17347
+ if (!row || panel.busy) return true;
17348
+ if (key.leftArrow) {
17349
+ host.onBrainAdjust?.(row, -1);
17350
+ return true;
17351
+ }
17352
+ if (key.rightArrow) {
17353
+ host.onBrainAdjust?.(row, 1);
17354
+ return true;
17355
+ }
17356
+ if (isEnter) {
17357
+ if (debouncedEnter(host)) return true;
17358
+ host.onBrainEnter?.(row);
17359
+ return true;
17360
+ }
17361
+ if ((input === "d" || input === "D" || key.delete) && (row.kind === "poolModel" || row.kind === "voter" || row.kind === "judge")) {
17362
+ host.onBrainDelete?.(row);
17363
+ return true;
17364
+ }
17365
+ if ((input === "p" || input === "P") && row.kind === "voter") {
17366
+ host.onBrainVoterMod?.(row.index, "persona");
17367
+ return true;
17368
+ }
17369
+ if ((input === "v" || input === "V") && row.kind === "voter") {
17370
+ host.onBrainVoterMod?.(row.index, "veto");
17371
+ return true;
17372
+ }
17373
+ return true;
17374
+ }
16557
17375
  if (key.escape) {
16558
17376
  dispatch({ type: "brainClose" });
16559
17377
  return true;
16560
17378
  }
17379
+ if (key.tab && panel.settings) {
17380
+ dispatch({ type: "brainView", view: "settings" });
17381
+ return true;
17382
+ }
16561
17383
  if (key.mouse?.kind === "wheel") {
16562
17384
  dispatch({ type: "brainMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
16563
17385
  return true;
@@ -17153,9 +17975,10 @@ import * as path7 from "node:path";
17153
17975
  import { useEffect as useEffect21, useState as useState12 } from "react";
17154
17976
  function formatWorkingDirChip(workingDir, projectRoot) {
17155
17977
  if (!workingDir || workingDir === projectRoot) return void 0;
17156
- const rel = path7.relative(projectRoot, workingDir) || ".";
17978
+ const impl = workingDir.includes("\\") || projectRoot.includes("\\") ? path7.win32 : path7;
17979
+ const rel = impl.relative(projectRoot, workingDir) || ".";
17157
17980
  if (rel === ".") return void 0;
17158
- return rel.replaceAll(path7.sep, "/");
17981
+ return rel.replaceAll(impl.sep, "/");
17159
17982
  }
17160
17983
  function useWorkingDirChip(ctx, projectRoot) {
17161
17984
  const [workingDirChip, setWorkingDirChip] = useState12(
@@ -17207,7 +18030,7 @@ function useStatuslineState(opts) {
17207
18030
  import { useEffect as useEffect22 } from "react";
17208
18031
  function useTuiControllers({
17209
18032
  dispatch,
17210
- streamFleet,
18033
+ fleetChat,
17211
18034
  enhanceEnabled,
17212
18035
  agentsMonitorOpen,
17213
18036
  fleetStreamController,
@@ -17216,19 +18039,30 @@ function useTuiControllers({
17216
18039
  }) {
17217
18040
  useEffect22(() => {
17218
18041
  if (!fleetStreamController) return;
17219
- fleetStreamController.enabled = streamFleet;
18042
+ fleetStreamController.mode = fleetChat;
18043
+ fleetStreamController.enabled = fleetChat !== "off";
18044
+ fleetStreamController.setMode = (mode) => {
18045
+ dispatch({ type: "setFleetChat", mode });
18046
+ };
17220
18047
  fleetStreamController.setEnabled = (enabled) => {
17221
- dispatch({ type: "setStreamFleet", enabled });
18048
+ dispatch({ type: "setFleetChat", mode: enabled ? "full" : "off" });
17222
18049
  };
17223
18050
  return () => {
18051
+ fleetStreamController.setMode = (mode) => {
18052
+ fleetStreamController.mode = mode;
18053
+ fleetStreamController.enabled = mode !== "off";
18054
+ };
17224
18055
  fleetStreamController.setEnabled = (enabled) => {
17225
- fleetStreamController.enabled = enabled;
18056
+ fleetStreamController.setMode(enabled ? "full" : "off");
17226
18057
  };
17227
18058
  };
17228
- }, [dispatch, fleetStreamController, streamFleet]);
18059
+ }, [dispatch, fleetStreamController, fleetChat]);
17229
18060
  useEffect22(() => {
17230
- if (fleetStreamController) fleetStreamController.enabled = streamFleet;
17231
- }, [fleetStreamController, streamFleet]);
18061
+ if (fleetStreamController) {
18062
+ fleetStreamController.mode = fleetChat;
18063
+ fleetStreamController.enabled = fleetChat !== "off";
18064
+ }
18065
+ }, [fleetStreamController, fleetChat]);
17232
18066
  useEffect22(() => {
17233
18067
  if (!enhanceController) return;
17234
18068
  enhanceController.enabled = enhanceEnabled;
@@ -17261,7 +18095,7 @@ function useTuiControllers({
17261
18095
  }
17262
18096
 
17263
18097
  // src/hooks/use-tui-event-bridge.ts
17264
- import { useEffect as useEffect25 } from "react";
18098
+ import { useCallback as useCallback8, useEffect as useEffect25 } from "react";
17265
18099
 
17266
18100
  // src/hooks/use-brain-events.ts
17267
18101
  import { useEffect as useEffect23 } from "react";
@@ -17347,7 +18181,7 @@ function labelFor2(labelsRef, id, name) {
17347
18181
  m.set(id, v);
17348
18182
  return v;
17349
18183
  }
17350
- function useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId) {
18184
+ function useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId, getChatMode) {
17351
18185
  const labelsRef = useRef11(/* @__PURE__ */ new Map());
17352
18186
  const lbl = useCallback7(
17353
18187
  (id, name) => labelFor2(labelsRef, id, name),
@@ -17359,20 +18193,26 @@ function useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId)
17359
18193
  const current = getSessionId?.();
17360
18194
  return !sessionId || !current || sessionId === current;
17361
18195
  };
18196
+ const mode = () => getChatMode?.() ?? "full";
18197
+ const warnedBudget = /* @__PURE__ */ new Set();
17362
18198
  const offSpawned = events.on("subagent.spawned", (e) => {
17363
18199
  if (!isCurrentSession(e.sessionId)) return;
17364
18200
  const l = lbl(e.subagentId, e.name);
17365
18201
  dispatch({ type: "fleetSpawn", id: e.subagentId, name: e.name, provider: e.provider, model: e.model, transcriptPath: e.transcriptPath });
17366
- const where = e.provider && e.model ? `${e.provider}/${e.model}` : "spawned";
17367
- const desc = e.description ? ` \u2014 ${e.description.slice(0, 80)}` : "";
17368
- dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon: "\u25B6", text: `${where}${desc}` } });
18202
+ if (mode() !== "off") {
18203
+ const where = e.provider && e.model ? `${e.provider}/${e.model}` : "spawned";
18204
+ const desc = e.description ? ` \u2014 ${e.description.slice(0, 80)}` : "";
18205
+ dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon: "\u25B6", text: `${where}${desc}` } });
18206
+ }
17369
18207
  });
17370
18208
  const offStarted = events.on("subagent.task_started", (e) => {
17371
18209
  if (!isCurrentSession(e.sessionId)) return;
17372
18210
  const l = lbl(e.subagentId);
17373
18211
  dispatch({ type: "fleetStart", id: e.subagentId, taskId: e.taskId });
17374
- const desc = e.description ? ` \u2014 ${e.description.slice(0, 80)}` : "";
17375
- dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon: "\u25CF", text: `task started${desc}` } });
18212
+ if (mode() === "full") {
18213
+ const desc = e.description ? ` \u2014 ${e.description.slice(0, 80)}` : "";
18214
+ dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon: "\u25CF", text: `task started${desc}` } });
18215
+ }
17376
18216
  });
17377
18217
  const offCompleted = events.on("subagent.task_completed", (e) => {
17378
18218
  if (!isCurrentSession(e.sessionId)) return;
@@ -17380,32 +18220,46 @@ function useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId)
17380
18220
  const errKind = e.error?.kind;
17381
18221
  dispatch({ type: "fleetDone", id: e.subagentId, status: e.status, iterations: e.iterations, toolCalls: e.toolCalls, failureReason: errKind });
17382
18222
  const icon = e.status === "success" ? "\u2713" : e.status === "timeout" ? "\u23F1" : e.status === "stopped" ? "\u2298" : "\u2717";
17383
- const errMsg = e.error?.message;
17384
- const errMsgTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 100)}${errMsg.length > 100 ? "\u2026" : ""}` : "";
17385
- const errChip = errKind ? ` [${errKind}]` : "";
17386
- const secs = (e.durationMs / 1e3).toFixed(e.durationMs < 1e4 ? 1 : 0);
17387
- dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon, text: `${e.status} (${e.iterations} iter \xB7 ${e.toolCalls} tools \xB7 ${secs}s)${errChip}${errMsgTail}` } });
18223
+ if (mode() !== "off" || e.status !== "success") {
18224
+ const errMsg = e.error?.message;
18225
+ const errMsgTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 100)}${errMsg.length > 100 ? "\u2026" : ""}` : "";
18226
+ const errChip = errKind ? ` [${errKind}]` : "";
18227
+ const secs = (e.durationMs / 1e3).toFixed(e.durationMs < 1e4 ? 1 : 0);
18228
+ dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon, text: `${e.status} (${e.iterations} iter \xB7 ${e.toolCalls} tools \xB7 ${secs}s)${errChip}${errMsgTail}` } });
18229
+ }
17388
18230
  });
17389
18231
  const offRemoved = events.on("subagent.removed", (e) => {
17390
18232
  if (!isCurrentSession(e.sessionId)) return;
17391
18233
  labelsRef.current.delete(e.subagentId);
18234
+ for (const key of warnedBudget) {
18235
+ if (key.startsWith(`${e.subagentId}\0`)) warnedBudget.delete(key);
18236
+ }
17392
18237
  dispatch({ type: "fleetRemove", id: e.subagentId });
17393
18238
  });
17394
18239
  const offBudgetWarning = events.on("subagent.budget_warning", (e) => {
17395
18240
  if (!isCurrentSession(e.sessionId)) return;
17396
18241
  const l = lbl(e.subagentId);
17397
18242
  dispatch({ type: "fleetBudgetWarning", id: e.subagentId, kind: e.kind, used: e.used, limit: e.limit });
17398
- const timeoutSuffix = e.kind === "timeout" ? " (subagent continues running)" : " \u2014 extending";
17399
- dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon: "\u26A1", text: `hitting ${e.kind} limit (${e.used}/${e.limit})${timeoutSuffix}` } });
18243
+ const m = mode();
18244
+ const budgetKey = `${e.subagentId}\0${e.kind}`;
18245
+ const show = m === "full" || m === "compact" && !warnedBudget.has(budgetKey);
18246
+ if (show) {
18247
+ warnedBudget.add(budgetKey);
18248
+ const timeoutSuffix = e.kind === "timeout" ? " (subagent continues running)" : " \u2014 extending";
18249
+ dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon: "\u26A1", text: `hitting ${e.kind} limit (${e.used}/${e.limit})${timeoutSuffix}` } });
18250
+ }
17400
18251
  });
17401
18252
  const offBudgetExtended = events.on("subagent.budget_extended", (e) => {
17402
18253
  if (!isCurrentSession(e.sessionId)) return;
17403
18254
  const l = lbl(e.subagentId);
17404
18255
  dispatch({ type: "fleetBudgetExtended", id: e.subagentId, totalExtensions: e.totalExtensions });
17405
- dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon: "\u26A1", text: `extended ${e.kind} \u2192 ${e.newLimit} (\xD7${e.totalExtensions})` } });
18256
+ if (mode() === "full") {
18257
+ dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon: "\u26A1", text: `extended ${e.kind} \u2192 ${e.newLimit} (\xD7${e.totalExtensions})` } });
18258
+ }
17406
18259
  });
17407
18260
  const offIterationSummary = events.on("subagent.iteration_summary", (e) => {
17408
18261
  if (!isCurrentSession(e.sessionId)) return;
18262
+ if (mode() !== "full") return;
17409
18263
  const l = lbl(e.subagentId);
17410
18264
  const costStr = e.costUsd > 0 ? ` \xB7 ${e.costUsd.toFixed(4)}` : "";
17411
18265
  const toolStr = e.currentTool ? ` \xB7 doing ${e.currentTool}` : "";
@@ -17464,7 +18318,7 @@ function useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId)
17464
18318
  offCompactionFired();
17465
18319
  offTool();
17466
18320
  };
17467
- }, [events, dispatch, setActiveMaxContext, getSessionId, lbl]);
18321
+ }, [events, dispatch, setActiveMaxContext, getSessionId, getChatMode, lbl]);
17468
18322
  }
17469
18323
 
17470
18324
  // src/hooks/use-tui-event-bridge.ts
@@ -17477,7 +18331,8 @@ function useTuiEventBridge({
17477
18331
  subscribeAutoPhase,
17478
18332
  onClearHistory
17479
18333
  }) {
17480
- useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId);
18334
+ const getChatMode = useCallback8(() => stateRef.current.fleetChat, [stateRef]);
18335
+ useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId, getChatMode);
17481
18336
  useSessionEvents(events, dispatch, onClearHistory, getSessionId);
17482
18337
  useBrainEvents(events, dispatch, getSessionId);
17483
18338
  useAutoPhaseEvents(subscribeAutoPhase, dispatch, stateRef, getSessionId);
@@ -18032,6 +18887,49 @@ function aliasCandidates({ cmd, owner, fullName }) {
18032
18887
  return out;
18033
18888
  }
18034
18889
 
18890
+ // src/sdd-lifecycle-entry.ts
18891
+ function sddLifecycleEntry(op, result) {
18892
+ if (typeof result === "number") {
18893
+ return {
18894
+ kind: result > 0 ? "info" : "warn",
18895
+ text: result > 0 ? `Cleaned ${result} SDD worktree${result === 1 ? "" : "s"}.` : "No SDD worktrees to clean (stop the run first if it is live)."
18896
+ };
18897
+ }
18898
+ if (!("op" in result)) {
18899
+ return {
18900
+ kind: result.ok ? "info" : "warn",
18901
+ text: result.ok ? result.reverted > 0 ? `Rolled back ${result.reverted} run commit${result.reverted === 1 ? "" : "s"} (revert commits added).` : "Nothing to roll back." : `Rollback failed: ${result.reason ?? "unknown error"}`
18902
+ };
18903
+ }
18904
+ if (!result.ok) {
18905
+ const label = op === "cleanup_worktrees" ? "Clean" : op === "rollback" ? "Rollback" : "Destroy";
18906
+ return { kind: "warn", text: `${label} failed: ${result.reason ?? "unknown error"}` };
18907
+ }
18908
+ if (op === "cleanup_worktrees") {
18909
+ const removed = result.removed ?? 0;
18910
+ return {
18911
+ kind: removed > 0 ? "info" : "warn",
18912
+ text: removed > 0 ? `Cleaned ${removed} SDD worktree${removed === 1 ? "" : "s"}.` : "No SDD worktrees to clean."
18913
+ };
18914
+ }
18915
+ if (op === "rollback") {
18916
+ const reverted = result.reverted ?? 0;
18917
+ return {
18918
+ kind: "info",
18919
+ text: reverted > 0 ? `Rolled back ${reverted} run commit${reverted === 1 ? "" : "s"}.` : "Nothing to roll back."
18920
+ };
18921
+ }
18922
+ const parts = [
18923
+ `${result.removed ?? 0} worktree${(result.removed ?? 0) === 1 ? "" : "s"} removed`,
18924
+ result.deleted?.length ? `deleted ${result.deleted.join(", ")}` : "",
18925
+ (result.reverted ?? 0) > 0 ? `${result.reverted} commit${result.reverted === 1 ? "" : "s"} reverted` : ""
18926
+ ].filter(Boolean);
18927
+ return {
18928
+ kind: "info",
18929
+ text: `SDD project destroyed \u2014 ${parts.join(" \xB7 ")}.${result.reason ? ` (${result.reason})` : ""}`
18930
+ };
18931
+ }
18932
+
18035
18933
  // src/steering-preamble.ts
18036
18934
  function buildSteeringPreamble(snapshot, newDirection) {
18037
18935
  const lines = ["[STEERING \u2014 I pressed Esc to interrupt you mid-task on purpose.", ""];
@@ -18146,47 +19044,6 @@ function selectedSlashCommandLine(picker) {
18146
19044
  var PASTE_THRESHOLD_CHARS = 200;
18147
19045
  var CONTINUE_CONFIRM_DELAY_MS = 4e3;
18148
19046
  var SB_PADX2 = 2;
18149
- function sddLifecycleEntry(op, r) {
18150
- if (typeof r === "number") {
18151
- return {
18152
- kind: r > 0 ? "info" : "warn",
18153
- text: r > 0 ? `Cleaned ${r} SDD worktree${r === 1 ? "" : "s"}.` : "No SDD worktrees to clean (stop the run first if it is live)."
18154
- };
18155
- }
18156
- if (!("op" in r)) {
18157
- return {
18158
- kind: r.ok ? "info" : "warn",
18159
- text: r.ok ? r.reverted > 0 ? `Rolled back ${r.reverted} run commit${r.reverted === 1 ? "" : "s"} (revert commits added).` : "Nothing to roll back." : `Rollback failed: ${r.reason ?? "unknown error"}`
18160
- };
18161
- }
18162
- if (!r.ok) {
18163
- const label = op === "cleanup_worktrees" ? "Clean" : op === "rollback" ? "Rollback" : "Destroy";
18164
- return { kind: "warn", text: `${label} failed: ${r.reason ?? "unknown error"}` };
18165
- }
18166
- if (op === "cleanup_worktrees") {
18167
- const n = r.removed ?? 0;
18168
- return {
18169
- kind: n > 0 ? "info" : "warn",
18170
- text: n > 0 ? `Cleaned ${n} SDD worktree${n === 1 ? "" : "s"}.` : "No SDD worktrees to clean."
18171
- };
18172
- }
18173
- if (op === "rollback") {
18174
- const n = r.reverted ?? 0;
18175
- return {
18176
- kind: "info",
18177
- text: n > 0 ? `Rolled back ${n} run commit${n === 1 ? "" : "s"}.` : "Nothing to roll back."
18178
- };
18179
- }
18180
- const parts = [
18181
- `${r.removed ?? 0} worktree${(r.removed ?? 0) === 1 ? "" : "s"} removed`,
18182
- r.deleted?.length ? `deleted ${r.deleted.join(", ")}` : "",
18183
- (r.reverted ?? 0) > 0 ? `${r.reverted} commit${r.reverted === 1 ? "" : "s"} reverted` : ""
18184
- ].filter(Boolean);
18185
- return {
18186
- kind: "info",
18187
- text: `SDD project destroyed \u2014 ${parts.join(" \xB7 ")}.${r.reason ? ` (${r.reason})` : ""}`
18188
- };
18189
- }
18190
19047
  function App({
18191
19048
  agent,
18192
19049
  slashRegistry,
@@ -18240,6 +19097,7 @@ function App({
18240
19097
  onToolToggle,
18241
19098
  getBrainData,
18242
19099
  onBrainRiskLevel,
19100
+ brainPanelHost,
18243
19101
  getShadowData,
18244
19102
  onShadowStart,
18245
19103
  onShadowStop,
@@ -18261,6 +19119,7 @@ function App({
18261
19119
  listSessions,
18262
19120
  onResumeSession,
18263
19121
  fleetStreamController,
19122
+ agentTranscripts,
18264
19123
  interruptController,
18265
19124
  statuslineHiddenItems,
18266
19125
  setStatuslineHiddenItems,
@@ -18371,7 +19230,7 @@ function App({
18371
19230
  }, [setStatuslineHiddenItems, saveStatuslineHiddenItems, hiddenItems]);
18372
19231
  const projectRoot = agent.ctx.projectRoot;
18373
19232
  const promptUsageRef = useRef12(null);
18374
- const refreshGoalSummary = useCallback8(() => {
19233
+ const refreshGoalSummary = useCallback9(() => {
18375
19234
  if (!projectRoot) return;
18376
19235
  const goalPath = resolveWstackPaths4({ projectRoot }).projectGoal;
18377
19236
  loadGoal(goalPath).then((goal) => {
@@ -18414,7 +19273,8 @@ function App({
18414
19273
  keyTail,
18415
19274
  restoredEntries,
18416
19275
  enhanceEnabled,
18417
- initialAgentsMonitorOpen
19276
+ initialAgentsMonitorOpen,
19277
+ initialFleetChat: fleetStreamController?.mode
18418
19278
  })
18419
19279
  );
18420
19280
  useInputHistoryPersistence({
@@ -18424,19 +19284,17 @@ function App({
18424
19284
  });
18425
19285
  const { openPromptPicker } = usePromptPicker({ projectRoot, dispatch });
18426
19286
  const { openModePicker } = useModePicker({ dispatch, getModes });
18427
- const openBrainPanel = React7.useCallback(() => {
18428
- if (!getBrainData) return;
18429
- const data = getBrainData();
18430
- dispatch({ type: "brainOpen", riskLevel: data.riskLevel, log: data.log });
18431
- }, [getBrainData]);
18432
- const changeBrainRisk = React7.useCallback(
19287
+ const { requestModelPick, handleModelPicked } = useModelPickRequest({ dispatch, getPickableProviders, pickerOpen: state.modelPicker.open });
19288
+ const brainCtl = useBrainPanel({ dispatch, getBrainData, brainPanelHost, requestModelPick });
19289
+ const openBrainPanel = brainCtl.openBrainPanel;
19290
+ const changeBrainRisk = React9.useCallback(
18433
19291
  (delta) => {
18434
19292
  dispatch({ type: "brainRiskChange", delta });
18435
19293
  },
18436
19294
  []
18437
19295
  );
18438
- const prevRiskRef = React7.useRef(state.brainPanel.riskLevel);
18439
- React7.useEffect(() => {
19296
+ const prevRiskRef = React9.useRef(state.brainPanel.riskLevel);
19297
+ React9.useEffect(() => {
18440
19298
  if (!state.brainPanel.open) return;
18441
19299
  const current = state.brainPanel.riskLevel;
18442
19300
  if (current !== prevRiskRef.current && onBrainRiskLevel) {
@@ -18446,12 +19304,12 @@ function App({
18446
19304
  else dispatch({ type: "brainHint", text: `Risk ceiling \u2192 ${current.toUpperCase()}` });
18447
19305
  }
18448
19306
  }, [state.brainPanel.riskLevel, state.brainPanel.open, onBrainRiskLevel]);
18449
- const openShadowPanel = React7.useCallback(() => {
19307
+ const openShadowPanel = React9.useCallback(() => {
18450
19308
  if (!getShadowData) return;
18451
19309
  const data = getShadowData();
18452
19310
  dispatch({ type: "shadowOpen", shadow: data });
18453
19311
  }, [getShadowData]);
18454
- const handleShadowStart = React7.useCallback(async () => {
19312
+ const handleShadowStart = React9.useCallback(async () => {
18455
19313
  if (!onShadowStart) return;
18456
19314
  dispatch({ type: "shadowHint", text: "Starting Shadow Agent\u2026" });
18457
19315
  try {
@@ -18465,7 +19323,7 @@ function App({
18465
19323
  dispatch({ type: "shadowHint", text: `Failed to start: ${toErrorMessage3(e)}` });
18466
19324
  }
18467
19325
  }, [onShadowStart, getShadowData]);
18468
- const handleShadowStop = React7.useCallback(async () => {
19326
+ const handleShadowStop = React9.useCallback(async () => {
18469
19327
  if (!onShadowStop) return;
18470
19328
  dispatch({ type: "shadowHint", text: "Stopping Shadow Agent\u2026" });
18471
19329
  try {
@@ -18479,7 +19337,7 @@ function App({
18479
19337
  dispatch({ type: "shadowHint", text: `Failed to stop: ${toErrorMessage3(e)}` });
18480
19338
  }
18481
19339
  }, [onShadowStop, getShadowData]);
18482
- const openHelpPanel = React7.useCallback(() => {
19340
+ const openHelpPanel = React9.useCallback(() => {
18483
19341
  const entries = Array.from(slashRegistry.listWithOwner()).map(({ cmd, owner }) => ({
18484
19342
  name: owner === "core" ? cmd.name : `${owner}:${cmd.name}`,
18485
19343
  description: cmd.description,
@@ -18511,7 +19369,7 @@ function App({
18511
19369
  const inputGateRef = useRef12(false);
18512
19370
  const lastEnterAtRef = useRef12(0);
18513
19371
  const tokenPreviewsRef = useRef12(/* @__PURE__ */ new Map());
18514
- const projectName = React7.useMemo(() => {
19372
+ const projectName = React9.useMemo(() => {
18515
19373
  const base = path8.basename(projectRoot);
18516
19374
  return base && base !== path8.sep ? base : void 0;
18517
19375
  }, [projectRoot]);
@@ -18550,11 +19408,11 @@ function App({
18550
19408
  stateRef.current = state;
18551
19409
  const draftRef = useRef12({ buffer: state.buffer, cursor: state.cursor });
18552
19410
  draftRef.current = { buffer: state.buffer, cursor: state.cursor };
18553
- const liveDirector = useCallback8(
19411
+ const liveDirector = useCallback9(
18554
19412
  () => getDirector?.() ?? director,
18555
19413
  [getDirector, director]
18556
19414
  );
18557
- const clearPendingConfirms = useCallback8(() => {
19415
+ const clearPendingConfirms = useCallback9(() => {
18558
19416
  const queue = stateRef.current.confirmQueue;
18559
19417
  if (queue.length === 0) return;
18560
19418
  for (const c of queue) {
@@ -18571,7 +19429,7 @@ function App({
18571
19429
  dispatch,
18572
19430
  open: state.authPanel.open
18573
19431
  });
18574
- const statuslineHiddenForPicker = useCallback8(() => {
19432
+ const statuslineHiddenForPicker = useCallback9(() => {
18575
19433
  const hookHidden = hiddenItemsRef.current;
18576
19434
  const hookHiddenSet = new Set(hookHidden);
18577
19435
  const reducerOnlyHidden = stateRef.current.statuslinePicker.hiddenItems.filter(
@@ -18579,7 +19437,7 @@ function App({
18579
19437
  );
18580
19438
  return [...hookHidden, ...reducerOnlyHidden];
18581
19439
  }, []);
18582
- const openStatuslinePicker = useCallback8(
19440
+ const openStatuslinePicker = useCallback9(
18583
19441
  (field) => {
18584
19442
  if (field !== void 0) {
18585
19443
  dispatch({ type: "statuslineFieldSet", field });
@@ -18631,7 +19489,7 @@ function App({
18631
19489
  }
18632
19490
  });
18633
19491
  const handleKeyRef = useRef12(null);
18634
- const handleRewindTo = React7.useCallback(
19492
+ const handleRewindTo = React9.useCallback(
18635
19493
  async (checkpointIndex) => {
18636
19494
  const sessionId = agent.ctx.session.id;
18637
19495
  if (!sessionId) return;
@@ -18653,7 +19511,7 @@ function App({
18653
19511
  dispatch({ type: "clearInput" });
18654
19512
  };
18655
19513
  const startedAtRef = useRef12(Date.now());
18656
- const [nowTick, setNowTick] = React7.useState(Date.now());
19514
+ const [nowTick, setNowTick] = React9.useState(Date.now());
18657
19515
  useEffect26(() => {
18658
19516
  const t = setInterval(() => setNowTick(Date.now()), 1e4);
18659
19517
  return () => clearInterval(t);
@@ -18688,8 +19546,8 @@ function App({
18688
19546
  });
18689
19547
  }, [agent.ctx]);
18690
19548
  useEffect26(() => {
18691
- if (state.goalPanelOpen) refreshGoalSummary();
18692
- }, [state.goalPanelOpen, nowTick, refreshGoalSummary]);
19549
+ refreshGoalSummary();
19550
+ }, [nowTick, refreshGoalSummary]);
18693
19551
  const [enhanceDots, setEnhanceDots] = useState14(0);
18694
19552
  useEffect26(() => {
18695
19553
  if (!state.enhanceBusy) return;
@@ -18739,7 +19597,7 @@ function App({
18739
19597
  agent.ctx.provider,
18740
19598
  agent.ctx.todos
18741
19599
  ]);
18742
- const [gitInfo, setGitInfo] = React7.useState(null);
19600
+ const [gitInfo, setGitInfo] = React9.useState(null);
18743
19601
  useEffect26(() => {
18744
19602
  let cancelled = false;
18745
19603
  const refresh = () => {
@@ -19066,13 +19924,13 @@ function App({
19066
19924
  const prevAnyOverlayOpen = useRef12(false);
19067
19925
  const prevEntriesCount = useRef12(0);
19068
19926
  const prevToolStreamLen = useRef12(0);
19069
- const eraseLiveRegion = useCallback8(() => {
19927
+ const eraseLiveRegion = useCallback9(() => {
19070
19928
  try {
19071
19929
  writeOut3("\x1B[J");
19072
19930
  } catch {
19073
19931
  }
19074
19932
  }, []);
19075
- React7.useLayoutEffect(() => {
19933
+ React9.useLayoutEffect(() => {
19076
19934
  const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.designPicker.open || state.resumePicker.open || state.settingsPicker.open || state.enhanceBusy || state.enhance != null || state.continueConfirm != null || state.coordinator.monitorOpen || state.escConfirm != null || state.sendModePicker != null || state.confirmQueue.length > 0;
19077
19935
  const overlayClosed = prevAnyOverlayOpen.current && !anyOpenNow;
19078
19936
  const newEntryCommitted = state.entries.length > prevEntriesCount.current;
@@ -19160,7 +20018,7 @@ function App({
19160
20018
  delayMs: sp.delayMs,
19161
20019
  titleAnimation: sp.titleAnimation,
19162
20020
  yolo: sp.yolo,
19163
- streamFleet: sp.streamFleet,
20021
+ fleetChat: sp.fleetChat,
19164
20022
  chime: sp.chime,
19165
20023
  confirmExit: sp.confirmExit,
19166
20024
  nextPrediction: sp.nextPrediction,
@@ -19193,7 +20051,9 @@ function App({
19193
20051
  thinkingWord: sp.thinkingWord,
19194
20052
  cacheTtl: sp.cacheTtl,
19195
20053
  configScope: sp.configScope,
19196
- animationStyle: sp.animationStyle
20054
+ animationStyle: sp.animationStyle,
20055
+ breakerEnabled: sp.breakerEnabled,
20056
+ breakerAutoKillResetMs: sp.breakerAutoKillResetMs
19197
20057
  });
19198
20058
  }
19199
20059
  if (prev.projectPicker) {
@@ -19226,7 +20086,7 @@ function App({
19226
20086
  process.stdout.off("resize", handleResize);
19227
20087
  };
19228
20088
  }, [eraseLiveRegion]);
19229
- React7.useLayoutEffect(() => {
20089
+ React9.useLayoutEffect(() => {
19230
20090
  if (state.enhanceBusy || state.enhance != null || state.continueConfirm != null)
19231
20091
  eraseLiveRegion();
19232
20092
  });
@@ -19270,7 +20130,7 @@ function App({
19270
20130
  getProcessRegistry4().killAll();
19271
20131
  };
19272
20132
  }, []);
19273
- const runSteerSequence = useCallback8(
20133
+ const runSteerSequence = useCallback9(
19274
20134
  (text) => {
19275
20135
  const s2 = stateRef.current;
19276
20136
  const runningTools = Array.from(s2.runningTools.values()).map((t) => t.name);
@@ -19380,20 +20240,20 @@ function App({
19380
20240
  slashRegistry.unregister("agents");
19381
20241
  };
19382
20242
  }, [slashRegistry]);
19383
- const openModelPicker = React7.useCallback(async () => {
20243
+ const openModelPicker = React9.useCallback(async () => {
19384
20244
  if (!getPickableProviders) return;
19385
20245
  const providers = await getPickableProviders();
19386
20246
  dispatch({ type: "modelPickerOpen", providers });
19387
20247
  }, [getPickableProviders]);
19388
- const openProjectPicker = React7.useCallback(async () => {
20248
+ const openProjectPicker = React9.useCallback(async () => {
19389
20249
  if (!getProjectPickerItems) return;
19390
20250
  const items = await getProjectPickerItems();
19391
20251
  dispatch({ type: "projectPickerOpen", items });
19392
20252
  }, [getProjectPickerItems]);
19393
- const openFKeyPicker = React7.useCallback(() => {
20253
+ const openFKeyPicker = React9.useCallback(() => {
19394
20254
  dispatch({ type: "fKeyPickerOpen" });
19395
20255
  }, [dispatch]);
19396
- const loadLiveSessions = React7.useCallback(async () => {
20256
+ const loadLiveSessions = React9.useCallback(async () => {
19397
20257
  if (!getLiveSessions) {
19398
20258
  dispatch({ type: "sessionsPanelSet", sessions: [] });
19399
20259
  return;
@@ -19440,7 +20300,7 @@ function App({
19440
20300
  }, 5e3);
19441
20301
  return () => clearInterval(t);
19442
20302
  }, [state.sessionsPanelOpen, getLiveSessions, loadLiveSessions]);
19443
- const openSettings = React7.useCallback(() => {
20303
+ const openSettings = React9.useCallback(() => {
19444
20304
  if (!getSettings) return;
19445
20305
  const s2 = getSettings();
19446
20306
  dispatch({
@@ -19449,7 +20309,7 @@ function App({
19449
20309
  delayMs: s2.delayMs,
19450
20310
  titleAnimation: s2.titleAnimation ?? true,
19451
20311
  yolo: s2.yolo ?? false,
19452
- streamFleet: s2.streamFleet ?? true,
20312
+ fleetChat: s2.fleetChatVerbosity ?? "compact",
19453
20313
  chime: s2.chime ?? false,
19454
20314
  confirmExit: s2.confirmExit ?? true,
19455
20315
  nextPrediction: s2.nextPrediction ?? false,
@@ -19482,10 +20342,12 @@ function App({
19482
20342
  thinkingWord: s2.thinkingWord ?? "thinking",
19483
20343
  cacheTtl: s2.cacheTtl ?? "default",
19484
20344
  configScope: s2.configScope ?? "global",
19485
- animationStyle: s2.animationStyle ?? "rainbow"
20345
+ animationStyle: s2.animationStyle ?? "rainbow",
20346
+ breakerEnabled: s2.breakerEnabled ?? false,
20347
+ breakerAutoKillResetMs: s2.breakerAutoKillResetMs ?? 6e4
19486
20348
  });
19487
20349
  }, [getSettings]);
19488
- const refreshPluginPicker = React7.useCallback(() => {
20350
+ const refreshPluginPicker = React9.useCallback(() => {
19489
20351
  if (!getPluginItems) return;
19490
20352
  dispatch({ type: "pluginPickerSetItems", items: getPluginItems() });
19491
20353
  }, [getPluginItems]);
@@ -19493,7 +20355,7 @@ function App({
19493
20355
  if (!state.pluginPicker.open) return;
19494
20356
  refreshPluginPicker();
19495
20357
  }, [state.pluginPicker.open, refreshPluginPicker]);
19496
- const toggleSelectedPlugin = React7.useCallback(async () => {
20358
+ const toggleSelectedPlugin = React9.useCallback(async () => {
19497
20359
  if (!onPluginToggle) return;
19498
20360
  const selected = stateRef.current.pluginPicker.items[stateRef.current.pluginPicker.selected];
19499
20361
  if (!selected) return;
@@ -19517,7 +20379,7 @@ function App({
19517
20379
  });
19518
20380
  }
19519
20381
  }, [onPluginToggle]);
19520
- const refreshMcpPicker = React7.useCallback(() => {
20382
+ const refreshMcpPicker = React9.useCallback(() => {
19521
20383
  if (!getMcpServers) return;
19522
20384
  dispatch({ type: "mcpPickerSetItems", items: getMcpServers() });
19523
20385
  }, [getMcpServers]);
@@ -19525,7 +20387,7 @@ function App({
19525
20387
  if (!state.mcpPicker.open) return;
19526
20388
  refreshMcpPicker();
19527
20389
  }, [state.mcpPicker.open, refreshMcpPicker]);
19528
- const toggleSelectedMcpServer = React7.useCallback(async () => {
20390
+ const toggleSelectedMcpServer = React9.useCallback(async () => {
19529
20391
  if (!onMcpToggle) return;
19530
20392
  const selected = stateRef.current.mcpPicker.items[stateRef.current.mcpPicker.selected];
19531
20393
  if (!selected) return;
@@ -19542,7 +20404,7 @@ function App({
19542
20404
  });
19543
20405
  }
19544
20406
  }, [onMcpToggle]);
19545
- const restartSelectedMcpServer = React7.useCallback(async () => {
20407
+ const restartSelectedMcpServer = React9.useCallback(async () => {
19546
20408
  if (!onMcpRestart) return;
19547
20409
  const selected = stateRef.current.mcpPicker.items[stateRef.current.mcpPicker.selected];
19548
20410
  if (!selected) return;
@@ -19559,7 +20421,7 @@ function App({
19559
20421
  });
19560
20422
  }
19561
20423
  }, [onMcpRestart]);
19562
- const refreshToolsPicker = React7.useCallback(() => {
20424
+ const refreshToolsPicker = React9.useCallback(() => {
19563
20425
  if (!getToolsItems) return;
19564
20426
  const items = getToolsItems();
19565
20427
  dispatch({ type: "toolsPickerSetItems", items });
@@ -19569,7 +20431,7 @@ function App({
19569
20431
  if (!state.toolsPicker.open) return;
19570
20432
  refreshToolsPicker();
19571
20433
  }, [state.toolsPicker.open, refreshToolsPicker]);
19572
- const toggleSelectedTool = React7.useCallback(async () => {
20434
+ const toggleSelectedTool = React9.useCallback(async () => {
19573
20435
  if (!onToolToggle) return;
19574
20436
  const selected = stateRef.current.toolsPicker.items[stateRef.current.toolsPicker.selected];
19575
20437
  if (!selected) return;
@@ -19719,7 +20581,7 @@ function App({
19719
20581
  delayMs: sp.delayMs,
19720
20582
  titleAnimation: sp.titleAnimation,
19721
20583
  yolo: sp.yolo,
19722
- streamFleet: sp.streamFleet,
20584
+ fleetChatVerbosity: sp.fleetChat,
19723
20585
  chime: sp.chime,
19724
20586
  confirmExit: sp.confirmExit,
19725
20587
  nextPrediction: sp.nextPrediction,
@@ -19752,7 +20614,9 @@ function App({
19752
20614
  thinkingWord: sp.thinkingWord,
19753
20615
  cacheTtl: sp.cacheTtl,
19754
20616
  configScope: sp.configScope,
19755
- animationStyle: sp.animationStyle
20617
+ animationStyle: sp.animationStyle,
20618
+ breakerEnabled: sp.breakerEnabled,
20619
+ breakerAutoKillResetMs: sp.breakerAutoKillResetMs
19756
20620
  })
19757
20621
  ).then((err) => {
19758
20622
  if (err) dispatch({ type: "settingsHint", text: err });
@@ -19763,7 +20627,7 @@ function App({
19763
20627
  state.settingsPicker.delayMs,
19764
20628
  state.settingsPicker.titleAnimation,
19765
20629
  state.settingsPicker.yolo,
19766
- state.settingsPicker.streamFleet,
20630
+ state.settingsPicker.fleetChat,
19767
20631
  state.settingsPicker.chime,
19768
20632
  state.settingsPicker.confirmExit,
19769
20633
  state.settingsPicker.nextPrediction,
@@ -19795,6 +20659,8 @@ function App({
19795
20659
  state.settingsPicker.cacheTtl,
19796
20660
  state.settingsPicker.configScope,
19797
20661
  state.settingsPicker.animationStyle,
20662
+ state.settingsPicker.breakerEnabled,
20663
+ state.settingsPicker.breakerAutoKillResetMs,
19798
20664
  saveSettings
19799
20665
  ]);
19800
20666
  useEffect26(() => {
@@ -20479,7 +21345,7 @@ Available chords:
20479
21345
  const [nextStepsAutoSubmitLabel, setNextStepsAutoSubmitLabel] = useState14(null);
20480
21346
  const nextStepsAutoSubmitSuggestionRef = useRef12(null);
20481
21347
  const nextStepsAutoSubmitTimerRef = useRef12(void 0);
20482
- const cancelNextStepsCountdown = useCallback8(() => {
21348
+ const cancelNextStepsCountdown = useCallback9(() => {
20483
21349
  if (nextStepsAutoSubmitTimerRef.current == null) return;
20484
21350
  clearInterval(nextStepsAutoSubmitTimerRef.current);
20485
21351
  nextStepsAutoSubmitTimerRef.current = void 0;
@@ -20487,7 +21353,11 @@ Available chords:
20487
21353
  setNextStepsAutoSubmitLabel(null);
20488
21354
  nextStepsAutoSubmitSuggestionRef.current = null;
20489
21355
  }, []);
20490
- const getActiveSessionId = useCallback8(() => agent.ctx.session.id, [agent]);
21356
+ const getActiveSessionId = useCallback9(() => agent.ctx.session.id, [agent]);
21357
+ const getLeaderTranscript = useCallback9(
21358
+ () => leaderTimelineFromEntries(stateRef.current.entries),
21359
+ [stateRef]
21360
+ );
20491
21361
  useTuiEventBridge({
20492
21362
  events,
20493
21363
  dispatch,
@@ -20499,7 +21369,7 @@ Available chords:
20499
21369
  });
20500
21370
  useTuiControllers({
20501
21371
  dispatch,
20502
- streamFleet: state.streamFleet,
21372
+ fleetChat: state.fleetChat,
20503
21373
  enhanceEnabled: state.enhanceEnabled,
20504
21374
  agentsMonitorOpen: state.agentsMonitorOpen,
20505
21375
  fleetStreamController,
@@ -20548,7 +21418,7 @@ Available chords:
20548
21418
  director,
20549
21419
  dispatch,
20550
21420
  stateRef,
20551
- streamFleet: state.streamFleet
21421
+ chatMode: state.fleetChat
20552
21422
  });
20553
21423
  const { onPickerEnter } = useFileSearch({
20554
21424
  state,
@@ -20749,6 +21619,11 @@ Available chords:
20749
21619
  submitRef.current(`/${entry.name}`);
20750
21620
  },
20751
21621
  onBrainRiskChange: changeBrainRisk,
21622
+ onBrainAdjust: brainCtl.handleBrainAdjust,
21623
+ onBrainEnter: brainCtl.handleBrainEnter,
21624
+ onBrainDelete: brainCtl.handleBrainDelete,
21625
+ onBrainVoterMod: brainCtl.handleBrainVoterMod,
21626
+ onModelPicked: handleModelPicked,
20752
21627
  onShadowStart: handleShadowStart,
20753
21628
  onShadowStop: handleShadowStop,
20754
21629
  onAuthEnter: authPanelController.onAuthEnter,
@@ -20774,7 +21649,7 @@ Available chords:
20774
21649
  onPickerEnter
20775
21650
  });
20776
21651
  const interruptsSyncRef = useRef12(0);
20777
- const runInterruptLadder = useCallback8(() => {
21652
+ const runInterruptLadder = useCallback9(() => {
20778
21653
  const current = stateRef.current;
20779
21654
  const pressCount = interruptsSyncRef.current + 1;
20780
21655
  interruptsSyncRef.current = pressCount;
@@ -21009,6 +21884,10 @@ Available chords:
21009
21884
  }
21010
21885
  if (pasteAccumRef.current !== null && isEnter) return;
21011
21886
  if (tryPickerKey(input, key, isEnter)) return;
21887
+ if (key.escape && state.agentsMonitorOpen) {
21888
+ dispatch({ type: "toggleAgentsMonitor" });
21889
+ return;
21890
+ }
21012
21891
  if (key.escape && state.status !== "idle" && state.confirmQueue.length === 0) {
21013
21892
  const runningTools = Array.from(state.runningTools.values()).map((t) => t.name);
21014
21893
  const subagents = Object.values(state.fleet).filter((e) => e.status === "running").map((e) => ({
@@ -21323,7 +22202,7 @@ Available chords:
21323
22202
  delayMs: cfg.delayMs,
21324
22203
  titleAnimation: cfg.titleAnimation ?? true,
21325
22204
  yolo: cfg.yolo ?? false,
21326
- streamFleet: cfg.streamFleet ?? true,
22205
+ fleetChat: cfg.fleetChatVerbosity ?? "compact",
21327
22206
  chime: cfg.chime ?? false,
21328
22207
  confirmExit: cfg.confirmExit ?? true,
21329
22208
  nextPrediction: cfg.nextPrediction ?? false,
@@ -21356,7 +22235,9 @@ Available chords:
21356
22235
  thinkingWord: cfg.thinkingWord ?? "thinking",
21357
22236
  cacheTtl: cfg.cacheTtl ?? "default",
21358
22237
  configScope: cfg.configScope ?? "global",
21359
- animationStyle: cfg.animationStyle ?? "rainbow"
22238
+ animationStyle: cfg.animationStyle ?? "rainbow",
22239
+ breakerEnabled: cfg.breakerEnabled ?? false,
22240
+ breakerAutoKillResetMs: cfg.breakerAutoKillResetMs ?? 6e4
21360
22241
  });
21361
22242
  }
21362
22243
  return;
@@ -22385,7 +23266,7 @@ ${content}
22385
23266
  })();
22386
23267
  }, [initialAsk, initialGoal]);
22387
23268
  handleKeyRef.current = handleKey;
22388
- const stableOnKey = useCallback8((input, key) => {
23269
+ const stableOnKey = useCallback9((input, key) => {
22389
23270
  handleKeyRef.current?.(input, key);
22390
23271
  }, []);
22391
23272
  const inputHint = useMemo6(() => {
@@ -22404,620 +23285,628 @@ ${content}
22404
23285
  const inputHeight = Math.max(1, inputCellRows.length);
22405
23286
  const hideInput = enhanceActive || state.continueConfirm != null || state.helpOpen || state.processListOpen || state.authPanel.open;
22406
23287
  const lowerFunctionPanelOpen = state.monitorOpen || state.agentsMonitorOpen || (state.autoPhase?.monitorOpen ?? false) || state.worktreeMonitorOpen || state.planPanelOpen || state.kanbanPanelOpen || state.todosMonitorOpen || state.queuePanelOpen || state.processListOpen || state.goalPanelOpen;
22407
- return /* @__PURE__ */ jsx59(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs58(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 0, children: [
22408
- mouseMode ? /* @__PURE__ */ jsx59(
22409
- ScrollableHistory,
22410
- {
22411
- entries: state.entries,
22412
- streamingText: state.streamingText,
22413
- toolStream: state.toolStream,
22414
- scrollOffset: state.scrollOffset,
22415
- viewportRows: state.viewportRows,
22416
- totalLines: state.totalLines,
22417
- onMeasure: (totalLines) => dispatch({ type: "setMeasuredLines", totalLines }),
22418
- setSuggestions,
22419
- autonomyMode: autonomyLive,
22420
- multiDiffSummaryThreshold: state.settingsPicker.multiDiffSummaryThreshold,
22421
- todos: agent.ctx.todos
22422
- }
22423
- ) : /* @__PURE__ */ jsx59(
22424
- History,
22425
- {
22426
- entries: state.entries,
22427
- generation: state.historyGen,
22428
- streamingText: state.streamingText,
22429
- toolStream: state.toolStream,
22430
- setSuggestions,
22431
- autonomyMode: autonomyLive,
22432
- multiDiffSummaryThreshold: state.settingsPicker.multiDiffSummaryThreshold,
22433
- todos: agent.ctx.todos
22434
- }
22435
- ),
22436
- /* @__PURE__ */ jsxs58(Box, { flexDirection: "column", flexShrink: 0, ref: bottomRegionRef, children: [
22437
- /* @__PURE__ */ jsx59(
22438
- Input,
22439
- {
22440
- prompt: INPUT_PROMPT,
22441
- value: state.buffer,
22442
- cursor: state.cursor,
22443
- hidden: hideInput,
22444
- placeholderHeight: inputHeight,
22445
- disabled: state.status === "aborting" && !state.steeringPending || state.confirmQueue.length > 0,
22446
- hint: inputHint,
22447
- onKey: stableOnKey
22448
- }
22449
- ),
22450
- state.picker.open ? /* @__PURE__ */ jsx59(
22451
- FilePicker,
22452
- {
22453
- query: state.picker.query,
22454
- matches: state.picker.matches,
22455
- selected: state.picker.selected
22456
- }
22457
- ) : null,
22458
- state.slashPicker.open ? /* @__PURE__ */ jsx59(
22459
- SlashMenu,
22460
- {
22461
- query: state.slashPicker.query,
22462
- matches: state.slashPicker.matches,
22463
- selected: state.slashPicker.selected
22464
- }
22465
- ) : null,
22466
- state.modelPicker.open ? /* @__PURE__ */ jsx59(
22467
- ModelPicker,
22468
- {
22469
- step: state.modelPicker.step,
22470
- providerOptions: state.modelPicker.providerOptions,
22471
- modelOptions: state.modelPicker.modelOptions,
22472
- filteredOptions: state.modelPicker.filteredOptions,
22473
- selected: state.modelPicker.selected,
22474
- pickedProviderId: state.modelPicker.pickedProviderId,
22475
- searchQuery: state.modelPicker.searchQuery,
22476
- hint: state.modelPicker.hint
22477
- }
22478
- ) : null,
22479
- state.autonomyPicker.open ? /* @__PURE__ */ jsx59(
22480
- AutonomyPicker,
22481
- {
22482
- options: state.autonomyPicker.options,
22483
- selected: state.autonomyPicker.selected,
22484
- hint: state.autonomyPicker.hint
22485
- }
22486
- ) : null,
22487
- state.modePicker.open ? /* @__PURE__ */ jsx59(
22488
- ModePicker,
22489
- {
22490
- modes: state.modePicker.modes,
22491
- selected: state.modePicker.selected,
22492
- hint: state.modePicker.hint
22493
- }
22494
- ) : null,
22495
- state.designPicker.open ? /* @__PURE__ */ jsx59(
22496
- DesignPicker,
22497
- {
22498
- kits: state.designPicker.kits,
22499
- selected: state.designPicker.selected,
22500
- stack: state.designPicker.stack
22501
- }
22502
- ) : null,
22503
- state.promptPicker.open ? /* @__PURE__ */ jsx59(
22504
- PromptPicker,
22505
- {
22506
- entries: filterPromptPicker(
22507
- state.promptPicker.all,
22508
- state.promptPicker.categories,
22509
- state.promptPicker.catIndex,
22510
- state.promptPicker.recentSlugs
22511
- ),
22512
- selected: state.promptPicker.selected,
22513
- category: state.promptPicker.categories[state.promptPicker.catIndex] ?? "all",
22514
- total: state.promptPicker.all.length
22515
- }
22516
- ) : null,
22517
- state.resumePicker.open ? /* @__PURE__ */ jsx59(
22518
- ResumePicker,
22519
- {
22520
- sessions: state.resumePicker.sessions,
22521
- selected: state.resumePicker.selected,
22522
- busy: state.resumePicker.busy,
22523
- error: state.resumePicker.error,
22524
- hint: state.resumePicker.hint
22525
- }
22526
- ) : null,
22527
- state.settingsPicker.open ? /* @__PURE__ */ jsx59(
22528
- SettingsPicker,
22529
- {
22530
- field: state.settingsPicker.field,
22531
- mode: state.settingsPicker.mode,
22532
- delayMs: state.settingsPicker.delayMs,
22533
- titleAnimation: state.settingsPicker.titleAnimation,
22534
- yolo: state.settingsPicker.yolo,
22535
- streamFleet: state.settingsPicker.streamFleet,
22536
- chime: state.settingsPicker.chime,
22537
- confirmExit: state.settingsPicker.confirmExit,
22538
- nextPrediction: state.settingsPicker.nextPrediction,
22539
- featureMcp: state.settingsPicker.featureMcp,
22540
- featurePlugins: state.settingsPicker.featurePlugins,
22541
- featureMemory: state.settingsPicker.featureMemory,
22542
- featureSkills: state.settingsPicker.featureSkills,
22543
- featureModelsRegistry: state.settingsPicker.featureModelsRegistry,
22544
- tokenSavingTier: state.settingsPicker.tokenSavingTier,
22545
- allowOutsideProjectRoot: state.settingsPicker.allowOutsideProjectRoot,
22546
- contextAutoCompact: state.settingsPicker.contextAutoCompact,
22547
- contextStrategy: state.settingsPicker.contextStrategy,
22548
- contextMode: state.settingsPicker.contextMode,
22549
- maxConcurrent: state.settingsPicker.maxConcurrent,
22550
- logLevel: state.settingsPicker.logLevel,
22551
- auditLevel: state.settingsPicker.auditLevel,
22552
- indexOnStart: state.settingsPicker.indexOnStart,
22553
- multiDiffSummaryThreshold: state.settingsPicker.multiDiffSummaryThreshold,
22554
- thinkingWord: state.settingsPicker.thinkingWord,
22555
- thinkingWordEditing: state.settingsPicker.thinkingWordEditing,
22556
- thinkingWordDraft: state.settingsPicker.thinkingWordDraft,
22557
- maxIterations: state.settingsPicker.maxIterations,
22558
- autoProceedMaxIterations: state.settingsPicker.autoProceedMaxIterations,
22559
- enhanceDelayMs: state.settingsPicker.enhanceDelayMs,
22560
- enhanceEnabled: state.settingsPicker.enhanceEnabled,
22561
- enhanceLanguage: state.settingsPicker.enhanceLanguage,
22562
- debugStream: state.settingsPicker.debugStream,
22563
- statuslineMode: state.settingsPicker.statuslineMode,
22564
- reasoningMode: state.settingsPicker.reasoningMode,
22565
- reasoningEffort: state.settingsPicker.reasoningEffort,
22566
- reasoningPreserve: state.settingsPicker.reasoningPreserve,
22567
- cacheTtl: state.settingsPicker.cacheTtl,
22568
- configScope: state.settingsPicker.configScope,
22569
- animationStyle: state.settingsPicker.animationStyle,
22570
- filter: state.settingsPicker.filter,
22571
- hint: state.settingsPicker.hint
22572
- }
22573
- ) : null,
22574
- state.statuslinePicker.open ? /* @__PURE__ */ jsx59(
22575
- StatuslinePicker,
22576
- {
22577
- field: state.statuslinePicker.field,
22578
- hiddenItems: state.statuslinePicker.hiddenItems,
22579
- visibleChips: state.statuslinePicker.visibleChips,
22580
- hint: state.statuslinePicker.hint
22581
- }
22582
- ) : null,
22583
- state.pluginPicker.open ? /* @__PURE__ */ jsx59(
22584
- PluginPicker,
22585
- {
22586
- items: state.pluginPicker.items,
22587
- selected: state.pluginPicker.selected,
22588
- busy: state.pluginPicker.busy,
22589
- hint: state.pluginPicker.hint
22590
- }
22591
- ) : null,
22592
- state.mcpPicker.open ? /* @__PURE__ */ jsx59(
22593
- McpPicker,
22594
- {
22595
- items: state.mcpPicker.items,
22596
- selected: state.mcpPicker.selected,
22597
- busy: state.mcpPicker.busy,
22598
- hint: state.mcpPicker.hint
22599
- }
22600
- ) : null,
22601
- state.toolsPicker.open ? /* @__PURE__ */ jsx59(
22602
- ToolsPicker,
22603
- {
22604
- items: state.toolsPicker.items,
22605
- selected: state.toolsPicker.selected,
22606
- busy: state.toolsPicker.busy,
22607
- hint: state.toolsPicker.hint,
22608
- filter: state.toolsPicker.filter
22609
- }
22610
- ) : null,
22611
- state.brainPanel.open ? /* @__PURE__ */ jsx59(
22612
- BrainPanel,
22613
- {
22614
- riskLevel: state.brainPanel.riskLevel,
22615
- log: state.brainPanel.log,
22616
- selected: state.brainPanel.selected,
22617
- hint: state.brainPanel.hint
22618
- }
22619
- ) : null,
22620
- state.helpPanel.open ? /* @__PURE__ */ jsx59(
22621
- HelpPanel,
22622
- {
22623
- entries: state.helpPanel.entries,
22624
- filter: state.helpPanel.filter,
22625
- selected: state.helpPanel.selected,
22626
- hint: state.helpPanel.hint
22627
- }
22628
- ) : null,
22629
- state.shadowPanel.open ? /* @__PURE__ */ jsx59(
22630
- ShadowPanel,
22631
- {
22632
- shadow: state.shadowPanel.shadow,
22633
- hint: state.shadowPanel.hint
22634
- }
22635
- ) : null,
22636
- state.authPanel.open ? /* @__PURE__ */ jsx59(AuthPanel, { panel: state.authPanel }) : null,
22637
- state.projectPicker.open ? /* @__PURE__ */ jsx59(
22638
- ProjectPicker,
22639
- {
22640
- items: state.projectPicker.items,
22641
- selected: state.projectPicker.selected,
22642
- filter: state.projectPicker.filter,
22643
- hint: state.projectPicker.hint
22644
- }
22645
- ) : null,
22646
- state.fKeyPicker.open ? /* @__PURE__ */ jsx59(FKeyPicker, { selected: state.fKeyPicker.selected }) : null,
22647
- state.sessionsPanelOpen ? /* @__PURE__ */ jsx59(
22648
- SessionsPanel,
22649
- {
22650
- sessions: state.sessionsPanel.sessions,
22651
- busy: state.sessionsPanel.busy,
22652
- selected: state.sessionsPanel.selected,
22653
- resumeConfirm: state.sessionResumeConfirm ? { sessionName: state.sessionResumeConfirm.sessionName } : void 0,
22654
- currentSessionId: agent.ctx.session?.id
22655
- }
22656
- ) : null,
22657
- state.coordinator.monitorOpen ? /* @__PURE__ */ jsx59(
22658
- CoordinatorPanel,
22659
- {
22660
- coordinator: state.coordinator,
22661
- nowTick,
22662
- onClose: () => dispatch({ type: "toggleCoordinatorMonitor" })
22663
- }
22664
- ) : null,
22665
- state.auditPanelOpen ? /* @__PURE__ */ jsx59(
22666
- AuditPanel,
22667
- {
22668
- sideEffects: agent.ctx.sideEffects ?? [],
22669
- onClose: () => dispatch({ type: "toggleAuditPanel" })
22670
- }
22671
- ) : null,
22672
- state.rewindOverlay ? (() => {
22673
- const overlay = state.rewindOverlay;
22674
- return /* @__PURE__ */ jsx59(
22675
- CheckpointTimeline,
23288
+ const agentsFullscreen = state.agentsMonitorOpen;
23289
+ return /* @__PURE__ */ jsx59(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs58(
23290
+ Box,
23291
+ {
23292
+ flexDirection: "column",
23293
+ flexGrow: 1,
23294
+ flexShrink: 0,
23295
+ ...agentsFullscreen ? { minHeight: termRows } : {},
23296
+ children: [
23297
+ mouseMode ? agentsFullscreen ? /* @__PURE__ */ jsx59(Box, { flexGrow: 1 }) : /* @__PURE__ */ jsx59(
23298
+ ScrollableHistory,
22676
23299
  {
22677
- checkpoints: overlay.checkpoints,
22678
- selected: overlay.selected,
22679
- onSelect: (i) => dispatch({ type: "rewindOverlayMove", delta: i - overlay.selected }),
22680
- onConfirm: (i) => {
22681
- const checkpoint = overlay.checkpoints[i];
22682
- if (checkpoint) handleRewindTo(checkpoint.promptIndex);
22683
- },
22684
- onClose: () => dispatch({ type: "rewindOverlayClose" })
22685
- }
22686
- );
22687
- })() : null,
22688
- state.brainPrompt ? /* @__PURE__ */ jsx59(Box, { flexDirection: "column", marginY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx59(
22689
- BrainDecisionPrompt,
22690
- {
22691
- ...state.brainPrompt,
22692
- onAnswer: (answer) => {
22693
- events.emit("brain.human_answered", { ...answer, at: Date.now() });
22694
- dispatch({ type: "brainPromptClear" });
22695
- }
22696
- }
22697
- ) }) : null,
22698
- state.shellCommandWarning ? (() => {
22699
- const info = state.shellCommandWarning;
22700
- let resolved = false;
22701
- const onDecision = (decision) => {
22702
- if (resolved) return;
22703
- resolved = true;
22704
- info.resolve(decision);
22705
- dispatch({ type: "shellCommandWarningClose" });
22706
- };
22707
- return /* @__PURE__ */ jsx59(ShellCommandWarning, { command: info.command, onDecision });
22708
- })() : null,
22709
- state.confirmQueue.length > 0 && (() => {
22710
- const head = expectDefined5(state.confirmQueue[0]);
22711
- let resolved = false;
22712
- const onDecision = (decision) => {
22713
- if (resolved) return;
22714
- resolved = true;
22715
- head.resolve(decision);
22716
- dispatch({ type: "confirmClose" });
22717
- };
22718
- const onEnableYolo = () => {
22719
- if (resolved) return;
22720
- onYolo?.(true);
22721
- setYoloLive(true);
22722
- const cur = getSettings?.();
22723
- if (cur && saveSettings) {
22724
- Promise.resolve(saveSettings({ ...cur, yolo: true })).catch(() => {
22725
- });
23300
+ entries: state.entries,
23301
+ streamingText: state.streamingText,
23302
+ toolStream: state.toolStream,
23303
+ scrollOffset: state.scrollOffset,
23304
+ viewportRows: state.viewportRows,
23305
+ totalLines: state.totalLines,
23306
+ onMeasure: (totalLines) => dispatch({ type: "setMeasuredLines", totalLines }),
23307
+ setSuggestions,
23308
+ autonomyMode: autonomyLive,
23309
+ multiDiffSummaryThreshold: state.settingsPicker.multiDiffSummaryThreshold,
23310
+ todos: agent.ctx.todos
22726
23311
  }
22727
- resolved = true;
22728
- head.resolve("yes");
22729
- dispatch({ type: "confirmClose" });
22730
- };
22731
- return /* @__PURE__ */ jsx59(
22732
- ConfirmPrompt,
23312
+ ) : /* @__PURE__ */ jsx59(
23313
+ History,
22733
23314
  {
22734
- toolName: head.toolName,
22735
- input: head.input,
22736
- suggestedPattern: head.suggestedPattern,
22737
- onDecision,
22738
- onEnableYolo,
22739
- destructive: head.destructive
23315
+ entries: state.entries,
23316
+ generation: state.historyGen,
23317
+ streamingText: agentsFullscreen ? "" : state.streamingText,
23318
+ toolStream: state.toolStream,
23319
+ setSuggestions,
23320
+ autonomyMode: autonomyLive,
23321
+ multiDiffSummaryThreshold: state.settingsPicker.multiDiffSummaryThreshold,
23322
+ todos: agent.ctx.todos
22740
23323
  }
22741
- );
22742
- })(),
22743
- state.escConfirm ? /* @__PURE__ */ jsx59(Box, { flexDirection: "column", marginY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx59(
22744
- EscConfirmPrompt,
22745
- {
22746
- runningTools: state.escConfirm.snapshot.runningTools,
22747
- subagentCount: state.escConfirm.snapshot.subagentsTerminated,
22748
- onConfirm: () => {
22749
- const escConfirm = state.escConfirm;
22750
- if (!escConfirm) return;
22751
- const { snapshot } = escConfirm;
22752
- activeCtrlRef.current?.abort("user interrupt (Esc)");
22753
- clearPendingConfirms();
22754
- dispatch({ type: "status", status: "aborting" });
22755
- dispatch({ type: "steerStart", snapshot });
22756
- const escConfirmDir = liveDirector();
22757
- if (escConfirmDir && snapshot.subagentsTerminated > 0) {
22758
- const cap = new Promise((resolve) => {
22759
- const t = setTimeout(resolve, 1500);
22760
- t.unref?.();
22761
- });
22762
- void Promise.race([escConfirmDir.terminateAll().catch(() => void 0), cap]);
23324
+ ),
23325
+ /* @__PURE__ */ jsxs58(Box, { flexDirection: "column", flexShrink: 0, ref: bottomRegionRef, children: [
23326
+ /* @__PURE__ */ jsx59(
23327
+ Input,
23328
+ {
23329
+ prompt: INPUT_PROMPT,
23330
+ value: state.buffer,
23331
+ cursor: state.cursor,
23332
+ hidden: hideInput,
23333
+ placeholderHeight: inputHeight,
23334
+ disabled: state.status === "aborting" && !state.steeringPending || state.confirmQueue.length > 0,
23335
+ hint: inputHint,
23336
+ onKey: stableOnKey
22763
23337
  }
22764
- const droppedCount = state.queue.length;
22765
- if (droppedCount > 0) dispatch({ type: "queueClear" });
22766
- const droppedTag = droppedCount > 0 ? ` \xB7 dropped ${droppedCount} queued` : "";
22767
- const fleetTag = snapshot.subagentsTerminated > 0 ? ` \xB7 stopped ${snapshot.subagentsTerminated} subagent${snapshot.subagentsTerminated === 1 ? "" : "s"}` : "";
22768
- dispatch({
22769
- type: "addEntry",
22770
- entry: {
22771
- kind: "warn",
22772
- text: `\u21AF Interrupted${droppedTag}${fleetTag}. Type your new direction.`
23338
+ ),
23339
+ state.picker.open ? /* @__PURE__ */ jsx59(
23340
+ FilePicker,
23341
+ {
23342
+ query: state.picker.query,
23343
+ matches: state.picker.matches,
23344
+ selected: state.picker.selected
23345
+ }
23346
+ ) : null,
23347
+ state.slashPicker.open ? /* @__PURE__ */ jsx59(
23348
+ SlashMenu,
23349
+ {
23350
+ query: state.slashPicker.query,
23351
+ matches: state.slashPicker.matches,
23352
+ selected: state.slashPicker.selected
23353
+ }
23354
+ ) : null,
23355
+ state.modelPicker.open ? /* @__PURE__ */ jsx59(
23356
+ ModelPicker,
23357
+ {
23358
+ step: state.modelPicker.step,
23359
+ providerOptions: state.modelPicker.providerOptions,
23360
+ modelOptions: state.modelPicker.modelOptions,
23361
+ filteredOptions: state.modelPicker.filteredOptions,
23362
+ selected: state.modelPicker.selected,
23363
+ pickedProviderId: state.modelPicker.pickedProviderId,
23364
+ searchQuery: state.modelPicker.searchQuery,
23365
+ hint: state.modelPicker.hint,
23366
+ titleLabel: state.modelPicker.title
23367
+ }
23368
+ ) : null,
23369
+ state.autonomyPicker.open ? /* @__PURE__ */ jsx59(
23370
+ AutonomyPicker,
23371
+ {
23372
+ options: state.autonomyPicker.options,
23373
+ selected: state.autonomyPicker.selected,
23374
+ hint: state.autonomyPicker.hint
23375
+ }
23376
+ ) : null,
23377
+ state.modePicker.open ? /* @__PURE__ */ jsx59(
23378
+ ModePicker,
23379
+ {
23380
+ modes: state.modePicker.modes,
23381
+ selected: state.modePicker.selected,
23382
+ hint: state.modePicker.hint
23383
+ }
23384
+ ) : null,
23385
+ state.designPicker.open ? /* @__PURE__ */ jsx59(
23386
+ DesignPicker,
23387
+ {
23388
+ kits: state.designPicker.kits,
23389
+ selected: state.designPicker.selected,
23390
+ stack: state.designPicker.stack
23391
+ }
23392
+ ) : null,
23393
+ state.promptPicker.open ? /* @__PURE__ */ jsx59(
23394
+ PromptPicker,
23395
+ {
23396
+ entries: filterPromptPicker(
23397
+ state.promptPicker.all,
23398
+ state.promptPicker.categories,
23399
+ state.promptPicker.catIndex,
23400
+ state.promptPicker.recentSlugs
23401
+ ),
23402
+ selected: state.promptPicker.selected,
23403
+ category: state.promptPicker.categories[state.promptPicker.catIndex] ?? "all",
23404
+ total: state.promptPicker.all.length
23405
+ }
23406
+ ) : null,
23407
+ state.resumePicker.open ? /* @__PURE__ */ jsx59(
23408
+ ResumePicker,
23409
+ {
23410
+ sessions: state.resumePicker.sessions,
23411
+ selected: state.resumePicker.selected,
23412
+ busy: state.resumePicker.busy,
23413
+ error: state.resumePicker.error,
23414
+ hint: state.resumePicker.hint
23415
+ }
23416
+ ) : null,
23417
+ state.settingsPicker.open ? /* @__PURE__ */ jsx59(
23418
+ SettingsPicker,
23419
+ {
23420
+ field: state.settingsPicker.field,
23421
+ mode: state.settingsPicker.mode,
23422
+ delayMs: state.settingsPicker.delayMs,
23423
+ titleAnimation: state.settingsPicker.titleAnimation,
23424
+ yolo: state.settingsPicker.yolo,
23425
+ fleetChat: state.settingsPicker.fleetChat,
23426
+ chime: state.settingsPicker.chime,
23427
+ confirmExit: state.settingsPicker.confirmExit,
23428
+ nextPrediction: state.settingsPicker.nextPrediction,
23429
+ featureMcp: state.settingsPicker.featureMcp,
23430
+ featurePlugins: state.settingsPicker.featurePlugins,
23431
+ featureMemory: state.settingsPicker.featureMemory,
23432
+ featureSkills: state.settingsPicker.featureSkills,
23433
+ featureModelsRegistry: state.settingsPicker.featureModelsRegistry,
23434
+ tokenSavingTier: state.settingsPicker.tokenSavingTier,
23435
+ allowOutsideProjectRoot: state.settingsPicker.allowOutsideProjectRoot,
23436
+ contextAutoCompact: state.settingsPicker.contextAutoCompact,
23437
+ contextStrategy: state.settingsPicker.contextStrategy,
23438
+ contextMode: state.settingsPicker.contextMode,
23439
+ maxConcurrent: state.settingsPicker.maxConcurrent,
23440
+ logLevel: state.settingsPicker.logLevel,
23441
+ auditLevel: state.settingsPicker.auditLevel,
23442
+ indexOnStart: state.settingsPicker.indexOnStart,
23443
+ multiDiffSummaryThreshold: state.settingsPicker.multiDiffSummaryThreshold,
23444
+ thinkingWord: state.settingsPicker.thinkingWord,
23445
+ thinkingWordEditing: state.settingsPicker.thinkingWordEditing,
23446
+ thinkingWordDraft: state.settingsPicker.thinkingWordDraft,
23447
+ maxIterations: state.settingsPicker.maxIterations,
23448
+ autoProceedMaxIterations: state.settingsPicker.autoProceedMaxIterations,
23449
+ enhanceDelayMs: state.settingsPicker.enhanceDelayMs,
23450
+ enhanceEnabled: state.settingsPicker.enhanceEnabled,
23451
+ enhanceLanguage: state.settingsPicker.enhanceLanguage,
23452
+ debugStream: state.settingsPicker.debugStream,
23453
+ statuslineMode: state.settingsPicker.statuslineMode,
23454
+ reasoningMode: state.settingsPicker.reasoningMode,
23455
+ reasoningEffort: state.settingsPicker.reasoningEffort,
23456
+ reasoningPreserve: state.settingsPicker.reasoningPreserve,
23457
+ cacheTtl: state.settingsPicker.cacheTtl,
23458
+ configScope: state.settingsPicker.configScope,
23459
+ animationStyle: state.settingsPicker.animationStyle,
23460
+ breakerEnabled: state.settingsPicker.breakerEnabled,
23461
+ breakerAutoKillResetMs: state.settingsPicker.breakerAutoKillResetMs,
23462
+ filter: state.settingsPicker.filter,
23463
+ hint: state.settingsPicker.hint
23464
+ }
23465
+ ) : null,
23466
+ state.statuslinePicker.open ? /* @__PURE__ */ jsx59(
23467
+ StatuslinePicker,
23468
+ {
23469
+ field: state.statuslinePicker.field,
23470
+ hiddenItems: state.statuslinePicker.hiddenItems,
23471
+ visibleChips: state.statuslinePicker.visibleChips,
23472
+ hint: state.statuslinePicker.hint
23473
+ }
23474
+ ) : null,
23475
+ state.pluginPicker.open ? /* @__PURE__ */ jsx59(
23476
+ PluginPicker,
23477
+ {
23478
+ items: state.pluginPicker.items,
23479
+ selected: state.pluginPicker.selected,
23480
+ busy: state.pluginPicker.busy,
23481
+ hint: state.pluginPicker.hint
23482
+ }
23483
+ ) : null,
23484
+ state.mcpPicker.open ? /* @__PURE__ */ jsx59(
23485
+ McpPicker,
23486
+ {
23487
+ items: state.mcpPicker.items,
23488
+ selected: state.mcpPicker.selected,
23489
+ busy: state.mcpPicker.busy,
23490
+ hint: state.mcpPicker.hint
23491
+ }
23492
+ ) : null,
23493
+ state.toolsPicker.open ? /* @__PURE__ */ jsx59(
23494
+ ToolsPicker,
23495
+ {
23496
+ items: state.toolsPicker.items,
23497
+ selected: state.toolsPicker.selected,
23498
+ busy: state.toolsPicker.busy,
23499
+ hint: state.toolsPicker.hint,
23500
+ filter: state.toolsPicker.filter
23501
+ }
23502
+ ) : null,
23503
+ state.brainPanel.open && !state.modelPicker.open ? /* @__PURE__ */ jsx59(BrainPanel, { ...state.brainPanel }) : null,
23504
+ state.helpPanel.open ? /* @__PURE__ */ jsx59(
23505
+ HelpPanel,
23506
+ {
23507
+ entries: state.helpPanel.entries,
23508
+ filter: state.helpPanel.filter,
23509
+ selected: state.helpPanel.selected,
23510
+ hint: state.helpPanel.hint
23511
+ }
23512
+ ) : null,
23513
+ state.shadowPanel.open ? /* @__PURE__ */ jsx59(
23514
+ ShadowPanel,
23515
+ {
23516
+ shadow: state.shadowPanel.shadow,
23517
+ hint: state.shadowPanel.hint
23518
+ }
23519
+ ) : null,
23520
+ state.authPanel.open ? /* @__PURE__ */ jsx59(AuthPanel, { panel: state.authPanel }) : null,
23521
+ state.projectPicker.open ? /* @__PURE__ */ jsx59(
23522
+ ProjectPicker,
23523
+ {
23524
+ items: state.projectPicker.items,
23525
+ selected: state.projectPicker.selected,
23526
+ filter: state.projectPicker.filter,
23527
+ hint: state.projectPicker.hint
23528
+ }
23529
+ ) : null,
23530
+ state.fKeyPicker.open ? /* @__PURE__ */ jsx59(FKeyPicker, { selected: state.fKeyPicker.selected }) : null,
23531
+ state.sessionsPanelOpen ? /* @__PURE__ */ jsx59(
23532
+ SessionsPanel,
23533
+ {
23534
+ sessions: state.sessionsPanel.sessions,
23535
+ busy: state.sessionsPanel.busy,
23536
+ selected: state.sessionsPanel.selected,
23537
+ resumeConfirm: state.sessionResumeConfirm ? { sessionName: state.sessionResumeConfirm.sessionName } : void 0,
23538
+ currentSessionId: agent.ctx.session?.id
23539
+ }
23540
+ ) : null,
23541
+ state.coordinator.monitorOpen ? /* @__PURE__ */ jsx59(
23542
+ CoordinatorPanel,
23543
+ {
23544
+ coordinator: state.coordinator,
23545
+ nowTick,
23546
+ onClose: () => dispatch({ type: "toggleCoordinatorMonitor" })
23547
+ }
23548
+ ) : null,
23549
+ state.auditPanelOpen ? /* @__PURE__ */ jsx59(
23550
+ AuditPanel,
23551
+ {
23552
+ sideEffects: agent.ctx.sideEffects ?? [],
23553
+ onClose: () => dispatch({ type: "toggleAuditPanel" })
23554
+ }
23555
+ ) : null,
23556
+ state.rewindOverlay ? (() => {
23557
+ const overlay = state.rewindOverlay;
23558
+ return /* @__PURE__ */ jsx59(
23559
+ CheckpointTimeline,
23560
+ {
23561
+ checkpoints: overlay.checkpoints,
23562
+ selected: overlay.selected,
23563
+ onSelect: (i) => dispatch({ type: "rewindOverlayMove", delta: i - overlay.selected }),
23564
+ onConfirm: (i) => {
23565
+ const checkpoint = overlay.checkpoints[i];
23566
+ if (checkpoint) handleRewindTo(checkpoint.promptIndex);
23567
+ },
23568
+ onClose: () => dispatch({ type: "rewindOverlayClose" })
22773
23569
  }
22774
- });
22775
- dispatch({ type: "escConfirmClose" });
22776
- },
22777
- onCancel: () => {
22778
- dispatch({ type: "escConfirmClose" });
22779
- }
22780
- }
22781
- ) }) : null,
22782
- state.sendModePicker ? (() => {
22783
- const info = state.sendModePicker;
22784
- let resolved = false;
22785
- const finish = (decision) => {
22786
- if (resolved) return;
22787
- resolved = true;
22788
- info.resolve(decision);
22789
- };
22790
- return /* @__PURE__ */ jsx59(
22791
- SendModePicker,
22792
- {
22793
- selected: info.selected,
22794
- messagePreview: info.displayText,
22795
- onMove: (delta) => dispatch({ type: "sendModePickerMove", delta }),
22796
- onSelect: finish
22797
- }
22798
- );
22799
- })() : null,
22800
- state.enhanceBusy && !state.enhance ? /* @__PURE__ */ jsx59(Box, { paddingX: 1, children: /* @__PURE__ */ jsxs58(Text, { dimColor: true, children: [
22801
- "\u2728 refining",
22802
- " ",
22803
- /* @__PURE__ */ jsx59(Text, { color: "cyan", children: state.buffer.length > 100 ? `${state.buffer.slice(0, 97)}\u2026` : state.buffer }),
22804
- /* @__PURE__ */ jsxs58(Text, { color: "cyan", children: [
22805
- " ",
22806
- ".".repeat(enhanceDots) || "\xA0"
22807
- ] })
22808
- ] }) }) : null,
22809
- state.enhance ? (() => {
22810
- const info = state.enhance;
22811
- let resolved = false;
22812
- const onDecision = (decision) => {
22813
- if (resolved) return;
22814
- resolved = true;
22815
- setEnhanceCountdown(null);
22816
- info.resolve(decision);
22817
- };
22818
- return /* @__PURE__ */ jsx59(
22819
- EnhancePanel,
22820
- {
22821
- original: info.original,
22822
- refined: info.refined,
22823
- english: info.english,
22824
- delayMs: enhanceDelayMs,
22825
- onDecision,
22826
- onTick: (r) => setEnhanceCountdown(r > 0 ? r : null)
22827
- }
22828
- );
22829
- })() : null,
22830
- state.continueConfirm ? (() => {
22831
- const info = state.continueConfirm;
22832
- let resolved = false;
22833
- const onDecision = (decision) => {
22834
- if (resolved) return;
22835
- resolved = true;
22836
- info.resolve(decision);
22837
- };
22838
- return /* @__PURE__ */ jsx59(
22839
- ContinueConfirmPanel,
22840
- {
22841
- label: info.label,
22842
- instruction: info.instruction,
22843
- source: info.source,
22844
- grounded: info.grounded,
22845
- delayMs: CONTINUE_CONFIRM_DELAY_MS,
22846
- onDecision
22847
- }
22848
- );
22849
- })() : null,
22850
- /* @__PURE__ */ jsx59(Box, { ref: statusBarWrapRef, flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx59(
22851
- StatusBar,
22852
- {
22853
- model: `${liveProvider}/${liveModel}`,
22854
- version: appVersion,
22855
- state: state.status,
22856
- thinkingWord: displayThinkingWord,
22857
- thinkingAnimationStyle: (
22858
- // While the settings picker is open, preview the picker's live
22859
- // ←/→ selection synchronously (saveSettings → configStore is
22860
- // async, so liveAnimationStyle lags a keystroke). When closed,
22861
- // use the persisted value from getSettings().
22862
- state.settingsPicker.open ? state.settingsPicker.animationStyle : liveAnimationStyle
23570
+ );
23571
+ })() : null,
23572
+ state.brainPrompt ? /* @__PURE__ */ jsx59(Box, { flexDirection: "column", marginY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx59(
23573
+ BrainDecisionPrompt,
23574
+ {
23575
+ ...state.brainPrompt,
23576
+ onAnswer: (answer) => {
23577
+ events.emit("brain.human_answered", { ...answer, at: Date.now() });
23578
+ dispatch({ type: "brainPromptClear" });
23579
+ }
23580
+ }
23581
+ ) }) : null,
23582
+ state.shellCommandWarning ? (() => {
23583
+ const info = state.shellCommandWarning;
23584
+ let resolved = false;
23585
+ const onDecision = (decision) => {
23586
+ if (resolved) return;
23587
+ resolved = true;
23588
+ info.resolve(decision);
23589
+ dispatch({ type: "shellCommandWarningClose" });
23590
+ };
23591
+ return /* @__PURE__ */ jsx59(ShellCommandWarning, { command: info.command, onDecision });
23592
+ })() : null,
23593
+ state.confirmQueue.length > 0 && (() => {
23594
+ const head = expectDefined5(state.confirmQueue[0]);
23595
+ let resolved = false;
23596
+ const onDecision = (decision) => {
23597
+ if (resolved) return;
23598
+ resolved = true;
23599
+ head.resolve(decision);
23600
+ dispatch({ type: "confirmClose" });
23601
+ };
23602
+ const onEnableYolo = () => {
23603
+ if (resolved) return;
23604
+ onYolo?.(true);
23605
+ setYoloLive(true);
23606
+ const cur = getSettings?.();
23607
+ if (cur && saveSettings) {
23608
+ Promise.resolve(saveSettings({ ...cur, yolo: true })).catch(() => {
23609
+ });
23610
+ }
23611
+ resolved = true;
23612
+ head.resolve("yes");
23613
+ dispatch({ type: "confirmClose" });
23614
+ };
23615
+ return /* @__PURE__ */ jsx59(
23616
+ ConfirmPrompt,
23617
+ {
23618
+ toolName: head.toolName,
23619
+ input: head.input,
23620
+ suggestedPattern: head.suggestedPattern,
23621
+ onDecision,
23622
+ onEnableYolo,
23623
+ destructive: head.destructive
23624
+ }
23625
+ );
23626
+ })(),
23627
+ state.escConfirm ? /* @__PURE__ */ jsx59(Box, { flexDirection: "column", marginY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx59(
23628
+ EscConfirmPrompt,
23629
+ {
23630
+ runningTools: state.escConfirm.snapshot.runningTools,
23631
+ subagentCount: state.escConfirm.snapshot.subagentsTerminated,
23632
+ onConfirm: () => {
23633
+ const escConfirm = state.escConfirm;
23634
+ if (!escConfirm) return;
23635
+ const { snapshot } = escConfirm;
23636
+ activeCtrlRef.current?.abort("user interrupt (Esc)");
23637
+ clearPendingConfirms();
23638
+ dispatch({ type: "status", status: "aborting" });
23639
+ dispatch({ type: "steerStart", snapshot });
23640
+ const escConfirmDir = liveDirector();
23641
+ if (escConfirmDir && snapshot.subagentsTerminated > 0) {
23642
+ const cap = new Promise((resolve) => {
23643
+ const t = setTimeout(resolve, 1500);
23644
+ t.unref?.();
23645
+ });
23646
+ void Promise.race([escConfirmDir.terminateAll().catch(() => void 0), cap]);
23647
+ }
23648
+ const droppedCount = state.queue.length;
23649
+ if (droppedCount > 0) dispatch({ type: "queueClear" });
23650
+ const droppedTag = droppedCount > 0 ? ` \xB7 dropped ${droppedCount} queued` : "";
23651
+ const fleetTag = snapshot.subagentsTerminated > 0 ? ` \xB7 stopped ${snapshot.subagentsTerminated} subagent${snapshot.subagentsTerminated === 1 ? "" : "s"}` : "";
23652
+ dispatch({
23653
+ type: "addEntry",
23654
+ entry: {
23655
+ kind: "warn",
23656
+ text: `\u21AF Interrupted${droppedTag}${fleetTag}. Type your new direction.`
23657
+ }
23658
+ });
23659
+ dispatch({ type: "escConfirmClose" });
23660
+ },
23661
+ onCancel: () => {
23662
+ dispatch({ type: "escConfirmClose" });
23663
+ }
23664
+ }
23665
+ ) }) : null,
23666
+ state.sendModePicker ? (() => {
23667
+ const info = state.sendModePicker;
23668
+ let resolved = false;
23669
+ const finish = (decision) => {
23670
+ if (resolved) return;
23671
+ resolved = true;
23672
+ info.resolve(decision);
23673
+ };
23674
+ return /* @__PURE__ */ jsx59(
23675
+ SendModePicker,
23676
+ {
23677
+ selected: info.selected,
23678
+ messagePreview: info.displayText,
23679
+ onMove: (delta) => dispatch({ type: "sendModePickerMove", delta }),
23680
+ onSelect: finish
23681
+ }
23682
+ );
23683
+ })() : null,
23684
+ state.enhanceBusy && !state.enhance ? /* @__PURE__ */ jsx59(Box, { paddingX: 1, children: /* @__PURE__ */ jsxs58(Text, { dimColor: true, children: [
23685
+ "\u2728 refining",
23686
+ " ",
23687
+ /* @__PURE__ */ jsx59(Text, { color: "cyan", children: state.buffer.length > 100 ? `${state.buffer.slice(0, 97)}\u2026` : state.buffer }),
23688
+ /* @__PURE__ */ jsxs58(Text, { color: "cyan", children: [
23689
+ " ",
23690
+ ".".repeat(enhanceDots) || "\xA0"
23691
+ ] })
23692
+ ] }) }) : null,
23693
+ state.enhance ? (() => {
23694
+ const info = state.enhance;
23695
+ let resolved = false;
23696
+ const onDecision = (decision) => {
23697
+ if (resolved) return;
23698
+ resolved = true;
23699
+ setEnhanceCountdown(null);
23700
+ info.resolve(decision);
23701
+ };
23702
+ return /* @__PURE__ */ jsx59(
23703
+ EnhancePanel,
23704
+ {
23705
+ original: info.original,
23706
+ refined: info.refined,
23707
+ english: info.english,
23708
+ delayMs: enhanceDelayMs,
23709
+ onDecision,
23710
+ onTick: (r) => setEnhanceCountdown(r > 0 ? r : null)
23711
+ }
23712
+ );
23713
+ })() : null,
23714
+ state.continueConfirm ? (() => {
23715
+ const info = state.continueConfirm;
23716
+ let resolved = false;
23717
+ const onDecision = (decision) => {
23718
+ if (resolved) return;
23719
+ resolved = true;
23720
+ info.resolve(decision);
23721
+ };
23722
+ return /* @__PURE__ */ jsx59(
23723
+ ContinueConfirmPanel,
23724
+ {
23725
+ label: info.label,
23726
+ instruction: info.instruction,
23727
+ source: info.source,
23728
+ grounded: info.grounded,
23729
+ delayMs: CONTINUE_CONFIRM_DELAY_MS,
23730
+ onDecision
23731
+ }
23732
+ );
23733
+ })() : null,
23734
+ /* @__PURE__ */ jsx59(Box, { ref: statusBarWrapRef, flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx59(
23735
+ StatusBar,
23736
+ {
23737
+ model: `${liveProvider}/${liveModel}`,
23738
+ version: appVersion,
23739
+ state: state.status,
23740
+ thinkingWord: displayThinkingWord,
23741
+ thinkingAnimationStyle: (
23742
+ // While the settings picker is open, preview the picker's live
23743
+ // ←/→ selection synchronously (saveSettings → configStore is
23744
+ // async, so liveAnimationStyle lags a keystroke). When closed,
23745
+ // use the persisted value from getSettings().
23746
+ state.settingsPicker.open ? state.settingsPicker.animationStyle : liveAnimationStyle
23747
+ ),
23748
+ tokenCounter,
23749
+ hint: renderRunningTools(state.runningTools) || state.hint,
23750
+ queueCount: state.queue.length,
23751
+ yolo: yoloLive,
23752
+ autonomy: autonomyLive,
23753
+ startedAt: startedAtRef.current,
23754
+ todos,
23755
+ plan: planCounts ?? void 0,
23756
+ tasks: taskCounts ?? void 0,
23757
+ fleet: fleetCounts,
23758
+ git: gitInfo,
23759
+ context: contextWindow,
23760
+ contextStrategy: getSettings ? getSettings().contextStrategy : void 0,
23761
+ brain: state.brain,
23762
+ projectName,
23763
+ workingDir: workingDirChip,
23764
+ subagentCount: visibleSubagentCount,
23765
+ processCount: getProcessRegistry4().activeCount,
23766
+ hiddenItems,
23767
+ mode: liveStatuslineMode,
23768
+ visibleChips: state.statuslinePicker.visibleChips,
23769
+ events,
23770
+ sessionId: agent.ctx.session.id,
23771
+ eternalStage: state.eternalStage,
23772
+ goalSummary: state.goalSummary,
23773
+ indexState,
23774
+ breakerCountdown,
23775
+ modeLabel: liveModeLabel || void 0,
23776
+ debugStreamStats: state.debugStreamStats,
23777
+ enhanceCountdown,
23778
+ nextStepsAutoSubmitCountdown,
23779
+ nextStepsAutoSubmitLabel,
23780
+ autoProceedCountdown: state.countdown?.remainingSeconds ?? null,
23781
+ sessionCount,
23782
+ mailbox: mailboxStatus,
23783
+ tokenSavingMode: liveSettings?.featureTokenSaving ?? tokenSavingMode,
23784
+ toolCount: liveToolCount,
23785
+ sideEffectCount: agent.ctx.sideEffects?.length ?? 0
23786
+ }
23787
+ ) }),
23788
+ /* @__PURE__ */ jsx59(
23789
+ MailboxPanel,
23790
+ {
23791
+ messages: mailboxMessages,
23792
+ agents: mailboxAgents,
23793
+ unreadCount: mailboxStatus.unread,
23794
+ open: mailboxPanelOpen
23795
+ }
22863
23796
  ),
22864
- tokenCounter,
22865
- hint: renderRunningTools(state.runningTools) || state.hint,
22866
- queueCount: state.queue.length,
22867
- yolo: yoloLive,
22868
- autonomy: autonomyLive,
22869
- startedAt: startedAtRef.current,
22870
- todos,
22871
- plan: planCounts ?? void 0,
22872
- tasks: taskCounts ?? void 0,
22873
- fleet: fleetCounts,
22874
- git: gitInfo,
22875
- context: contextWindow,
22876
- contextStrategy: getSettings ? getSettings().contextStrategy : void 0,
22877
- brain: state.brain,
22878
- projectName,
22879
- workingDir: workingDirChip,
22880
- subagentCount: visibleSubagentCount,
22881
- processCount: getProcessRegistry4().activeCount,
22882
- hiddenItems,
22883
- mode: liveStatuslineMode,
22884
- visibleChips: state.statuslinePicker.visibleChips,
22885
- events,
22886
- sessionId: agent.ctx.session.id,
22887
- eternalStage: state.eternalStage,
22888
- goalSummary: state.goalSummary,
22889
- indexState,
22890
- breakerCountdown,
22891
- modeLabel: liveModeLabel || void 0,
22892
- debugStreamStats: state.debugStreamStats,
22893
- enhanceCountdown,
22894
- nextStepsAutoSubmitCountdown,
22895
- nextStepsAutoSubmitLabel,
22896
- autoProceedCountdown: state.countdown?.remainingSeconds ?? null,
22897
- sessionCount,
22898
- mailbox: mailboxStatus,
22899
- tokenSavingMode: liveSettings?.featureTokenSaving ?? tokenSavingMode,
22900
- toolCount: liveToolCount,
22901
- sideEffectCount: agent.ctx.sideEffects?.length ?? 0
22902
- }
22903
- ) }),
22904
- /* @__PURE__ */ jsx59(
22905
- MailboxPanel,
22906
- {
22907
- messages: mailboxMessages,
22908
- agents: mailboxAgents,
22909
- unreadCount: mailboxStatus.unread,
22910
- open: mailboxPanelOpen
22911
- }
22912
- ),
22913
- /* @__PURE__ */ jsxs58(Box, { ref: belowStatusBarRef, flexDirection: "column", flexShrink: 0, children: [
22914
- state.helpOpen ? /* @__PURE__ */ jsx59(HelpOverlay, {}) : null,
22915
- state.agentsMonitorOpen ? /* @__PURE__ */ jsx59(
22916
- AgentsMonitor,
22917
- {
22918
- entries: entriesWithLeader,
22919
- totalCost: state.fleetCost,
22920
- leaderCost: tokenCounter?.estimateCost().total ?? 0,
22921
- totalTokens: state.fleetTokens,
22922
- nowTick,
22923
- onClose: () => dispatch({ type: "toggleAgentsMonitor" })
22924
- }
22925
- ) : state.autoPhase?.monitorOpen ? /* @__PURE__ */ jsx59(
22926
- PhaseMonitor,
22927
- {
22928
- phases: state.autoPhase.phases,
22929
- runningPhaseIds: state.autoPhase.runningPhaseIds,
22930
- elapsedMs: state.autoPhase.elapsedMs,
22931
- nowTick
22932
- }
22933
- ) : state.sddBoard?.monitorOpen ? /* @__PURE__ */ jsx59(
22934
- SddBoardOverlay,
22935
- {
22936
- snapshot: state.sddBoard.snapshot,
22937
- focusColumn: state.sddBoard.focusColumn ?? null
22938
- }
22939
- ) : state.worktreeMonitorOpen ? /* @__PURE__ */ jsx59(
22940
- WorktreeMonitor,
22941
- {
22942
- worktrees: state.worktrees,
22943
- baseBranch: state.worktreeBase,
22944
- nowTick,
22945
- onClose: () => dispatch({ type: "worktreeMonitorToggle" })
22946
- }
22947
- ) : state.todosMonitorOpen ? /* @__PURE__ */ jsx59(TodosMonitor, { todos: agent.ctx.todos }) : state.monitorOpen ? /* @__PURE__ */ jsx59(
22948
- FleetMonitor,
22949
- {
22950
- entries: state.fleet,
22951
- totalCost: state.fleetCost,
22952
- totalTokens: state.fleetTokens,
22953
- maxConcurrent: state.fleetConcurrency,
22954
- nowTick,
22955
- collabSession: state.collabSession
22956
- }
22957
- ) : state.planPanelOpen ? /* @__PURE__ */ jsx59(
22958
- PlanPanel,
22959
- {
22960
- projectRoot: agent.ctx.projectRoot,
22961
- sessionId: agent.ctx.session?.id ?? null,
22962
- onClose: () => dispatch({ type: "togglePlanPanel" })
22963
- }
22964
- ) : state.kanbanPanelOpen ? /* @__PURE__ */ jsx59(
22965
- KanbanPanel,
22966
- {
22967
- projectRoot: agent.ctx.projectRoot,
22968
- onClose: () => dispatch({ type: "toggleKanbanPanel" })
22969
- }
22970
- ) : state.queuePanelOpen ? /* @__PURE__ */ jsx59(QueuePanel, { items: state.queue }) : state.processListOpen ? /* @__PURE__ */ jsx59(ProcessListMonitor, {}) : state.goalPanelOpen ? /* @__PURE__ */ jsx59(
22971
- GoalPanel,
22972
- {
22973
- goal: state.goalSummary,
22974
- onCoordinatorStart: onCoordinatorStart ?? void 0,
22975
- onCoordinatorStop: onCoordinatorStop ?? void 0,
22976
- coordinatorRunning
22977
- }
22978
- ) : director || hasVisibleFleetPanel || state.collabSession ? /* @__PURE__ */ jsx59(
22979
- FleetPanel,
22980
- {
22981
- entries: entriesWithLeader,
22982
- totalCost: state.fleetCost,
22983
- roster: fleetRoster,
22984
- todos: agent.ctx.todos,
22985
- nowTick,
22986
- collabSession: state.collabSession
22987
- }
22988
- ) : null,
22989
- state.autoPhase && !lowerFunctionPanelOpen ? /* @__PURE__ */ jsx59(
22990
- PhasePanel,
22991
- {
22992
- phases: state.autoPhase.phases,
22993
- runningPhaseIds: state.autoPhase.runningPhaseIds,
22994
- nowTick
22995
- }
22996
- ) : null,
22997
- Object.keys(state.worktrees).length > 0 && !lowerFunctionPanelOpen ? /* @__PURE__ */ jsx59(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null,
22998
- (() => {
22999
- const anyMonitorOpen = state.agentsMonitorOpen || (state.autoPhase?.monitorOpen ?? false) || state.worktreeMonitorOpen || state.todosMonitorOpen || state.monitorOpen || state.processListOpen || state.queuePanelOpen || state.goalPanelOpen;
23000
- let nextPanelHint;
23001
- if (state.agentsMonitorOpen) {
23002
- nextPanelHint = { key: "F6", label: "todos" };
23003
- } else if (state.autoPhase?.monitorOpen || state.worktreeMonitorOpen || state.todosMonitorOpen) {
23004
- nextPanelHint = { key: "F9", label: "goal" };
23005
- } else if (state.queuePanelOpen || state.processListOpen || state.goalPanelOpen) {
23006
- nextPanelHint = { key: "F3", label: "agents" };
23007
- } else if (anyMonitorOpen) {
23008
- nextPanelHint = { key: "F3", label: "agents" };
23009
- }
23010
- const ctx = {
23011
- monitor: anyMonitorOpen,
23012
- managed: state.scrollOffset > 0,
23013
- picker: state.settingsPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.designPicker.open,
23014
- nextPanelHint
23015
- };
23016
- return /* @__PURE__ */ jsx59(KeyHintBar, { context: ctx });
23017
- })()
23018
- ] })
23019
- ] })
23020
- ] }) });
23797
+ /* @__PURE__ */ jsxs58(Box, { ref: belowStatusBarRef, flexDirection: "column", flexShrink: 0, children: [
23798
+ state.helpOpen ? /* @__PURE__ */ jsx59(HelpOverlay, {}) : null,
23799
+ state.agentsMonitorOpen ? /* @__PURE__ */ jsx59(
23800
+ AgentsMonitor,
23801
+ {
23802
+ entries: entriesWithLeader,
23803
+ totalCost: state.fleetCost,
23804
+ leaderCost: tokenCounter?.estimateCost().total ?? 0,
23805
+ totalTokens: state.fleetTokens,
23806
+ nowTick,
23807
+ onClose: () => dispatch({ type: "toggleAgentsMonitor" }),
23808
+ transcripts: agentTranscripts,
23809
+ leaderTranscript: getLeaderTranscript,
23810
+ fullscreen: true
23811
+ }
23812
+ ) : state.autoPhase?.monitorOpen ? /* @__PURE__ */ jsx59(
23813
+ PhaseMonitor,
23814
+ {
23815
+ phases: state.autoPhase.phases,
23816
+ runningPhaseIds: state.autoPhase.runningPhaseIds,
23817
+ elapsedMs: state.autoPhase.elapsedMs,
23818
+ nowTick
23819
+ }
23820
+ ) : state.sddBoard?.monitorOpen ? /* @__PURE__ */ jsx59(
23821
+ SddBoardOverlay,
23822
+ {
23823
+ snapshot: state.sddBoard.snapshot,
23824
+ focusColumn: state.sddBoard.focusColumn ?? null
23825
+ }
23826
+ ) : state.worktreeMonitorOpen ? /* @__PURE__ */ jsx59(
23827
+ WorktreeMonitor,
23828
+ {
23829
+ worktrees: state.worktrees,
23830
+ baseBranch: state.worktreeBase,
23831
+ nowTick,
23832
+ onClose: () => dispatch({ type: "worktreeMonitorToggle" })
23833
+ }
23834
+ ) : state.todosMonitorOpen ? /* @__PURE__ */ jsx59(TodosMonitor, { todos: agent.ctx.todos }) : state.monitorOpen ? /* @__PURE__ */ jsx59(
23835
+ FleetMonitor,
23836
+ {
23837
+ entries: state.fleet,
23838
+ totalCost: state.fleetCost,
23839
+ totalTokens: state.fleetTokens,
23840
+ maxConcurrent: state.fleetConcurrency,
23841
+ nowTick,
23842
+ collabSession: state.collabSession
23843
+ }
23844
+ ) : state.planPanelOpen ? /* @__PURE__ */ jsx59(
23845
+ PlanPanel,
23846
+ {
23847
+ projectRoot: agent.ctx.projectRoot,
23848
+ sessionId: agent.ctx.session?.id ?? null,
23849
+ onClose: () => dispatch({ type: "togglePlanPanel" })
23850
+ }
23851
+ ) : state.kanbanPanelOpen ? /* @__PURE__ */ jsx59(
23852
+ KanbanPanel,
23853
+ {
23854
+ projectRoot: agent.ctx.projectRoot,
23855
+ onClose: () => dispatch({ type: "toggleKanbanPanel" })
23856
+ }
23857
+ ) : state.queuePanelOpen ? /* @__PURE__ */ jsx59(QueuePanel, { items: state.queue }) : state.processListOpen ? /* @__PURE__ */ jsx59(ProcessListMonitor, {}) : state.goalPanelOpen ? /* @__PURE__ */ jsx59(
23858
+ GoalPanel,
23859
+ {
23860
+ goal: state.goalSummary,
23861
+ onCoordinatorStart: onCoordinatorStart ?? void 0,
23862
+ onCoordinatorStop: onCoordinatorStop ?? void 0,
23863
+ coordinatorRunning
23864
+ }
23865
+ ) : director || hasVisibleFleetPanel || state.collabSession ? /* @__PURE__ */ jsx59(
23866
+ FleetPanel,
23867
+ {
23868
+ entries: entriesWithLeader,
23869
+ totalCost: state.fleetCost,
23870
+ roster: fleetRoster,
23871
+ todos: agent.ctx.todos,
23872
+ nowTick,
23873
+ collabSession: state.collabSession
23874
+ }
23875
+ ) : null,
23876
+ state.autoPhase && !lowerFunctionPanelOpen ? /* @__PURE__ */ jsx59(
23877
+ PhasePanel,
23878
+ {
23879
+ phases: state.autoPhase.phases,
23880
+ runningPhaseIds: state.autoPhase.runningPhaseIds,
23881
+ nowTick
23882
+ }
23883
+ ) : null,
23884
+ Object.keys(state.worktrees).length > 0 && !lowerFunctionPanelOpen ? /* @__PURE__ */ jsx59(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null,
23885
+ (() => {
23886
+ const anyMonitorOpen = state.agentsMonitorOpen || (state.autoPhase?.monitorOpen ?? false) || state.worktreeMonitorOpen || state.todosMonitorOpen || state.monitorOpen || state.processListOpen || state.queuePanelOpen || state.goalPanelOpen;
23887
+ let nextPanelHint;
23888
+ if (state.agentsMonitorOpen) {
23889
+ nextPanelHint = { key: "F6", label: "todos" };
23890
+ } else if (state.autoPhase?.monitorOpen || state.worktreeMonitorOpen || state.todosMonitorOpen) {
23891
+ nextPanelHint = { key: "F9", label: "goal" };
23892
+ } else if (state.queuePanelOpen || state.processListOpen || state.goalPanelOpen) {
23893
+ nextPanelHint = { key: "F3", label: "agents" };
23894
+ } else if (anyMonitorOpen) {
23895
+ nextPanelHint = { key: "F3", label: "agents" };
23896
+ }
23897
+ const ctx = {
23898
+ monitor: anyMonitorOpen,
23899
+ managed: state.scrollOffset > 0,
23900
+ picker: state.settingsPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.designPicker.open,
23901
+ nextPanelHint
23902
+ };
23903
+ return /* @__PURE__ */ jsx59(KeyHintBar, { context: ctx });
23904
+ })()
23905
+ ] })
23906
+ ] })
23907
+ ]
23908
+ }
23909
+ ) });
23021
23910
  }
23022
23911
  function renderRunningTools(running) {
23023
23912
  if (running.size === 0) return "";
@@ -23282,52 +24171,20 @@ async function runTui(opts) {
23282
24171
  };
23283
24172
  let clientHeartbeatTimer = null;
23284
24173
  let clientSyncTimer = null;
24174
+ let initialClientSyncTimer = null;
24175
+ let registeredMailbox = null;
24176
+ let registeredClientId = null;
24177
+ let tuiHqPublisher;
24178
+ let registrationGeneration = 0;
23285
24179
  const stopHqAuxBridges = [];
23286
24180
  const CLIENT_HEARTBEAT_MS = 15e3;
23287
24181
  const CLIENT_SYNC_MS = 3e4;
23288
24182
  const registerTuiClient = async () => {
23289
24183
  if (!opts.projectRoot) return null;
24184
+ const generation = ++registrationGeneration;
23290
24185
  try {
23291
24186
  const projectDir = resolveProjectDir(opts.projectRoot, wstackGlobalRoot2());
23292
- const hqPublisher = createHqPublisherFromEnv({
23293
- clientKind: "tui",
23294
- projectRoot: opts.projectRoot,
23295
- projectName: path9.basename(opts.projectRoot),
23296
- appConfig: opts.appConfig
23297
- });
23298
- hqPublisher?.connect();
23299
- const tuiSessionId = opts.getSessionId?.() ?? opts.projectRoot;
23300
- try {
23301
- stopHqAuxBridges.push(
23302
- startFleetTelemetryBridge({ events: opts.events, publisher: hqPublisher, runId: tuiSessionId, sessionId: tuiSessionId })
23303
- );
23304
- } catch {
23305
- }
23306
- try {
23307
- stopHqAuxBridges.push(
23308
- startBrainTelemetryBridge({ events: opts.events, publisher: hqPublisher, sessionId: tuiSessionId })
23309
- );
23310
- } catch {
23311
- }
23312
- try {
23313
- stopHqAuxBridges.push(
23314
- startWorktreeTelemetryBridge({ events: opts.events, publisher: hqPublisher, sessionId: tuiSessionId })
23315
- );
23316
- } catch {
23317
- }
23318
- try {
23319
- stopHqAuxBridges.push(
23320
- startToolTelemetryBridge({ events: opts.events, publisher: hqPublisher, projectRoot: opts.projectRoot, sessionId: tuiSessionId })
23321
- );
23322
- } catch {
23323
- }
23324
- try {
23325
- stopHqAuxBridges.push(
23326
- startCostTelemetryBridge({ events: opts.events, publisher: hqPublisher, sessionId: tuiSessionId })
23327
- );
23328
- } catch {
23329
- }
23330
- const mailbox = new GlobalMailbox(projectDir, opts.events, hqPublisher);
24187
+ const mailbox = new GlobalMailbox(projectDir, opts.events);
23331
24188
  const clientId = `tui@${randomUUID().slice(0, 8)}`;
23332
24189
  await mailbox.registerClient({
23333
24190
  clientId,
@@ -23336,6 +24193,66 @@ async function runTui(opts) {
23336
24193
  source: "tui",
23337
24194
  pid: process.pid
23338
24195
  });
24196
+ if (cleaned || generation !== registrationGeneration) {
24197
+ await mailbox.deregisterClient(clientId);
24198
+ return null;
24199
+ }
24200
+ registeredMailbox = mailbox;
24201
+ registeredClientId = clientId;
24202
+ if (!opts.hqTelemetryOwnedExternally) {
24203
+ tuiHqPublisher = createHqPublisherFromEnv({
24204
+ clientKind: "tui",
24205
+ projectRoot: opts.projectRoot,
24206
+ projectName: path9.basename(opts.projectRoot),
24207
+ appConfig: opts.appConfig
24208
+ });
24209
+ tuiHqPublisher?.connect();
24210
+ const tuiSessionId = opts.getSessionId?.() ?? opts.projectRoot;
24211
+ if (tuiHqPublisher) {
24212
+ try {
24213
+ stopHqAuxBridges.push(
24214
+ startSessionTelemetryBridge({
24215
+ publisher: tuiHqPublisher,
24216
+ events: opts.events,
24217
+ sessionId: tuiSessionId,
24218
+ projectRoot: opts.projectRoot,
24219
+ projectName: path9.basename(opts.projectRoot)
24220
+ })
24221
+ );
24222
+ } catch {
24223
+ }
24224
+ try {
24225
+ stopHqAuxBridges.push(
24226
+ startFleetTelemetryBridge({ events: opts.events, publisher: tuiHqPublisher, runId: tuiSessionId, sessionId: tuiSessionId })
24227
+ );
24228
+ } catch {
24229
+ }
24230
+ try {
24231
+ stopHqAuxBridges.push(
24232
+ startBrainTelemetryBridge({ events: opts.events, publisher: tuiHqPublisher, sessionId: tuiSessionId })
24233
+ );
24234
+ } catch {
24235
+ }
24236
+ try {
24237
+ stopHqAuxBridges.push(
24238
+ startWorktreeTelemetryBridge({ events: opts.events, publisher: tuiHqPublisher, sessionId: tuiSessionId })
24239
+ );
24240
+ } catch {
24241
+ }
24242
+ try {
24243
+ stopHqAuxBridges.push(
24244
+ startToolTelemetryBridge({ events: opts.events, publisher: tuiHqPublisher, projectRoot: opts.projectRoot, sessionId: tuiSessionId })
24245
+ );
24246
+ } catch {
24247
+ }
24248
+ try {
24249
+ stopHqAuxBridges.push(
24250
+ startCostTelemetryBridge({ events: opts.events, publisher: tuiHqPublisher, sessionId: tuiSessionId })
24251
+ );
24252
+ } catch {
24253
+ }
24254
+ }
24255
+ }
23339
24256
  clientHeartbeatTimer = setInterval(() => {
23340
24257
  mailbox.clientHeartbeat({ clientId, sessionId: opts.getSessionId?.() ?? opts.projectRoot }).catch(() => {
23341
24258
  });
@@ -23346,20 +24263,18 @@ async function runTui(opts) {
23346
24263
  const statuses = await mailbox.getClientStatuses();
23347
24264
  const counts = { tui: 0, webui: 0, repl: 0 };
23348
24265
  for (const s2 of statuses) {
23349
- if (s2.online && s2.source in counts) {
23350
- counts[s2.source]++;
23351
- }
24266
+ if (s2.online && s2.source in counts) counts[s2.source]++;
23352
24267
  }
23353
24268
  opts.events.emitCustom("mailbox.sync_clients", counts);
23354
24269
  } catch {
23355
24270
  }
23356
24271
  };
23357
- setTimeout(() => {
24272
+ initialClientSyncTimer = setTimeout(() => {
24273
+ initialClientSyncTimer = null;
23358
24274
  void syncClients();
23359
24275
  }, 5e3);
23360
- clientSyncTimer = setInterval(() => {
23361
- void syncClients();
23362
- }, CLIENT_SYNC_MS);
24276
+ initialClientSyncTimer.unref?.();
24277
+ clientSyncTimer = setInterval(() => void syncClients(), CLIENT_SYNC_MS);
23363
24278
  clientSyncTimer.unref();
23364
24279
  return clientId;
23365
24280
  } catch {
@@ -23367,6 +24282,7 @@ async function runTui(opts) {
23367
24282
  }
23368
24283
  };
23369
24284
  const unregisterTuiClient = () => {
24285
+ registrationGeneration++;
23370
24286
  if (clientHeartbeatTimer) {
23371
24287
  clearInterval(clientHeartbeatTimer);
23372
24288
  clientHeartbeatTimer = null;
@@ -23375,6 +24291,10 @@ async function runTui(opts) {
23375
24291
  clearInterval(clientSyncTimer);
23376
24292
  clientSyncTimer = null;
23377
24293
  }
24294
+ if (initialClientSyncTimer) {
24295
+ clearTimeout(initialClientSyncTimer);
24296
+ initialClientSyncTimer = null;
24297
+ }
23378
24298
  for (const stop of stopHqAuxBridges) {
23379
24299
  try {
23380
24300
  stop();
@@ -23382,6 +24302,13 @@ async function runTui(opts) {
23382
24302
  }
23383
24303
  }
23384
24304
  stopHqAuxBridges.length = 0;
24305
+ tuiHqPublisher?.close();
24306
+ tuiHqPublisher = void 0;
24307
+ const mailbox = registeredMailbox;
24308
+ const clientId = registeredClientId;
24309
+ registeredMailbox = null;
24310
+ registeredClientId = null;
24311
+ if (mailbox && clientId) void mailbox.deregisterClient(clientId).catch(() => void 0);
23385
24312
  };
23386
24313
  registerTuiClient();
23387
24314
  return new Promise((resolve) => {
@@ -23416,7 +24343,7 @@ async function runTui(opts) {
23416
24343
  };
23417
24344
  try {
23418
24345
  instance = render(
23419
- React8.createElement(App, {
24346
+ React10.createElement(App, {
23420
24347
  agent: opts.agent,
23421
24348
  slashRegistry: opts.slashRegistry,
23422
24349
  attachments: opts.attachments,
@@ -23454,6 +24381,7 @@ async function runTui(opts) {
23454
24381
  onClearHistory: opts.onClearHistory ? (dispatch) => opts.onClearHistory?.(dispatch) : void 0,
23455
24382
  clearTerminal,
23456
24383
  fleetStreamController: opts.fleetStreamController,
24384
+ agentTranscripts: opts.agentTranscripts,
23457
24385
  interruptController: opts.interruptController,
23458
24386
  enhanceController: opts.enhanceController,
23459
24387
  enhanceEnabled: opts.enhanceController?.enabled ?? true,
@@ -23479,6 +24407,7 @@ async function runTui(opts) {
23479
24407
  onToolToggle: opts.onToolToggle,
23480
24408
  getBrainData: opts.getBrainData,
23481
24409
  onBrainRiskLevel: opts.onBrainRiskLevel,
24410
+ brainPanelHost: opts.brainPanelHost,
23482
24411
  getShadowData: opts.getShadowData,
23483
24412
  onShadowStart: opts.onShadowStart,
23484
24413
  onShadowStop: opts.onShadowStop,