@wrongstack/tui 0.155.0 → 0.250.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.
package/dist/index.js CHANGED
@@ -1,17 +1,91 @@
1
- import { expectDefined, writeErr, resolveWstackPaths, loadGoal, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, buildChildEnv } from '@wrongstack/core';
1
+ import { writeErr, resolveWstackPaths, loadGoal, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, expectDefined as expectDefined$1, wstackGlobalRoot, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, buildChildEnv } from '@wrongstack/core';
2
2
  export { buildGoalPreamble } from '@wrongstack/core';
3
- import { Box, Text, useInput, useStdin, useStdout, render, useApp, Static } from 'ink';
4
- import * as path3 from 'path';
5
- import React5, { useState, useEffect, useMemo, memo, useRef, useCallback, useReducer } from 'react';
3
+ import { Box as Box$1, useInput, useStdin, useStdout, Text as Text$1, render, useApp, measureElement, Static } from 'ink';
4
+ import * as path4 from 'path';
5
+ import React5, { forwardRef, useState, useEffect, useMemo, memo, useRef, useCallback, useReducer, useLayoutEffect } from 'react';
6
6
  import * as fs2 from 'fs/promises';
7
7
  import { routeImagesForModel } from '@wrongstack/runtime/vision';
8
8
  import { getIndexState, onIndexStateChange, getProcessRegistry } from '@wrongstack/tools';
9
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
10
  import { readClipboardImage } from '@wrongstack/runtime/clipboard';
10
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
11
- import { expectDefined as expectDefined$1 } from '@wrongstack/core/utils';
11
+ import { expectDefined } from '@wrongstack/core/utils';
12
+ import * as v8 from 'v8';
12
13
  import { spawn } from 'child_process';
13
14
 
14
15
  // src/run-tui.ts
16
+
17
+ // src/theme.ts
18
+ var pastel = Object.freeze({
19
+ // Base 8
20
+ black: "#11111b",
21
+ red: "#f38ba8",
22
+ green: "#a6e3a1",
23
+ yellow: "#f9e2af",
24
+ blue: "#89b4fa",
25
+ magenta: "#cba6f7",
26
+ cyan: "#94e2d5",
27
+ white: "#cdd6f4",
28
+ // Greys
29
+ gray: "#7f849c",
30
+ grey: "#7f849c",
31
+ // Bright variants — a touch lighter / shifted within the same family
32
+ blackBright: "#585b70",
33
+ redBright: "#eba0ac",
34
+ greenBright: "#b8e8b0",
35
+ yellowBright: "#f5e6b8",
36
+ blueBright: "#89dceb",
37
+ magentaBright: "#b4befe",
38
+ cyanBright: "#99e6da",
39
+ whiteBright: "#ffffff"
40
+ });
41
+ function softColor(color) {
42
+ if (!color) return color;
43
+ return pastel[color] ?? color;
44
+ }
45
+ var theme = Object.freeze({
46
+ accent: pastel.cyan,
47
+ user: pastel.yellow,
48
+ assistant: pastel.cyan,
49
+ tool: pastel.cyan,
50
+ success: pastel.green,
51
+ warn: pastel.yellow,
52
+ error: pastel.red,
53
+ dim: true,
54
+ // Subtle slate border — present but never harsh.
55
+ borderDefault: pastel.blackBright,
56
+ borderActive: pastel.yellow,
57
+ brand: pastel.magenta,
58
+ monitor: {
59
+ fleet: pastel.cyan,
60
+ agents: pastel.magenta,
61
+ worktree: pastel.green,
62
+ phase: pastel.cyan
63
+ },
64
+ // Diff blocks render dark text on a pastel wash (see DiffBlock).
65
+ diffAddBg: pastel.green,
66
+ diffDelBg: pastel.red
67
+ });
68
+ var colorProps = (color, backgroundColor) => {
69
+ const c = softColor(color);
70
+ const bg = softColor(backgroundColor);
71
+ return { ...c ? { color: c } : {}, ...bg ? { backgroundColor: bg } : {} };
72
+ };
73
+ function Text({ color, backgroundColor, ...rest }) {
74
+ return /* @__PURE__ */ jsx(Text$1, { ...rest, ...colorProps(color, backgroundColor) });
75
+ }
76
+ var Box = forwardRef(function Box2({ borderColor, backgroundColor, ...rest }, ref) {
77
+ const bc = softColor(borderColor);
78
+ const bg = softColor(backgroundColor);
79
+ return /* @__PURE__ */ jsx(
80
+ Box$1,
81
+ {
82
+ ref,
83
+ ...rest,
84
+ ...bc ? { borderColor: bc } : {},
85
+ ...bg ? { backgroundColor: bg } : {}
86
+ }
87
+ );
88
+ });
15
89
  var MODE_ICONS = {
16
90
  teach: "\u{1F9D1}\u200D\u{1F3EB}",
17
91
  brief: "\u26A1",
@@ -46,12 +120,14 @@ function StatusBar({
46
120
  startedAt,
47
121
  todos,
48
122
  plan,
123
+ tasks,
49
124
  fleet,
50
125
  fleetAgents,
51
126
  git,
52
127
  subagentCount = 0,
53
128
  brain,
54
129
  projectName,
130
+ workingDir,
55
131
  processCount,
56
132
  context,
57
133
  hiddenItems,
@@ -61,7 +137,10 @@ function StatusBar({
61
137
  modeLabel,
62
138
  debugStreamStats,
63
139
  enhanceCountdown,
64
- autoProceedCountdown
140
+ nextStepsAutoSubmitCountdown,
141
+ autoProceedCountdown,
142
+ sessionCount,
143
+ mailbox
65
144
  }) {
66
145
  const { stdout } = useStdout();
67
146
  const [termWidth, setTermWidth] = useState(stdout?.columns ?? 90);
@@ -94,17 +173,19 @@ function StatusBar({
94
173
  );
95
174
  return () => clearInterval(t);
96
175
  }, [state]);
97
- const spinner = expectDefined(SPINNER_FRAMES[spinnerIdx]);
176
+ const spinner = expectDefined$1(SPINNER_FRAMES[spinnerIdx]);
98
177
  const { label: stateLabel, color: stateColor } = stateChip(state, fleet?.running ?? 0);
99
178
  const statePrefix = state === "idle" || state === "aborting" ? "\u25CF" : spinner;
100
179
  const thinking = state === "running" || state === "streaming";
101
180
  const hasAutoProceed = autoProceedCountdown != null && autoProceedCountdown > 0;
102
- const hasSecondLine = yolo || autonomy && autonomy !== "off" || startedAt != null || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0 || goalSummary !== null && goalSummary !== void 0 || !!modeLabel || hasAutoProceed;
181
+ const hasSecondLine = yolo || autonomy && autonomy !== "off" || startedAt != null || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0 || workingDir !== void 0 && workingDir.length > 0 || goalSummary !== null && goalSummary !== void 0 || !!modeLabel || hasAutoProceed;
103
182
  const fleetHasActivity = fleet && (fleet.running > 0 || fleet.idle > 0 || fleet.pending > 0 || fleet.completed > 0) || subagentCount > 0;
104
183
  const hasBrainActivity = !!brain && brain.state !== "idle";
105
184
  const hasDebugStream = !!debugStreamStats;
106
185
  const hasEnhanceCountdown = enhanceCountdown != null && enhanceCountdown > 0;
107
- const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity || hasBrainActivity || hasDebugStream || hasEnhanceCountdown;
186
+ const hasNextStepsAutoSubmit = nextStepsAutoSubmitCountdown != null && nextStepsAutoSubmitCountdown > 0;
187
+ const hasTaskActivity = tasks && (tasks.pending > 0 || tasks.inProgress > 0 || tasks.completed > 0 || tasks.blocked > 0 || tasks.failed > 0);
188
+ const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || hasTaskActivity || fleetHasActivity || hasBrainActivity || hasDebugStream || hasEnhanceCountdown || hasNextStepsAutoSubmit;
108
189
  return /* @__PURE__ */ jsxs(
109
190
  Box,
110
191
  {
@@ -148,13 +229,17 @@ function StatusBar({
148
229
  /* @__PURE__ */ jsx(Text, { color: "magenta", children: model }),
149
230
  context && !hiddenSet.has("context") ? /* @__PURE__ */ jsxs(Fragment, { children: [
150
231
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
151
- /* @__PURE__ */ jsxs(Text, { color: context.used / context.max < 0.6 ? "green" : context.used / context.max < 0.75 ? "yellow" : "red", children: [
152
- "ctx ",
153
- renderMeter(context.used / context.max, 8),
154
- " ",
155
- Math.round(context.used / context.max * 100),
156
- "%"
157
- ] })
232
+ (() => {
233
+ const ratio = context.used / context.max;
234
+ const clampedRatio = Math.min(ratio, 1);
235
+ const pctText = `${Math.min(Math.round(ratio * 100), 100)}%`;
236
+ return /* @__PURE__ */ jsxs(Text, { color: clampedRatio < 0.6 ? "green" : clampedRatio < 0.75 ? "yellow" : "red", children: [
237
+ "ctx ",
238
+ renderMeter(clampedRatio, 8),
239
+ " ",
240
+ pctText
241
+ ] });
242
+ })()
158
243
  ] }) : null,
159
244
  usage && isComfortable ? /* @__PURE__ */ jsxs(Fragment, { children: [
160
245
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
@@ -210,6 +295,9 @@ function StatusBar({
210
295
  "/",
211
296
  indexState.totalFiles
212
297
  ] })
298
+ ] }) : indexState?.circuit?.state === "open" ? /* @__PURE__ */ jsxs(Fragment, { children: [
299
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
300
+ /* @__PURE__ */ jsx(Text, { color: "red", children: "\u2699 index paused (/reindex)" })
213
301
  ] }) : null
214
302
  ] })
215
303
  ) }),
@@ -247,8 +335,15 @@ function StatusBar({
247
335
  projectName
248
336
  ] })
249
337
  ] }) : null,
338
+ workingDir && !hiddenSet.has("working_dir") ? /* @__PURE__ */ jsxs(Fragment, { children: [
339
+ yolo || startedAt != null || projectName || goalSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
340
+ /* @__PURE__ */ jsxs(Text, { color: "blue", children: [
341
+ "\u{1F4C2} ",
342
+ workingDir
343
+ ] })
344
+ ] }) : null,
250
345
  goalSummary ? /* @__PURE__ */ jsxs(Fragment, { children: [
251
- yolo || startedAt != null || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
346
+ yolo || startedAt != null || projectName || workingDir ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
252
347
  /* @__PURE__ */ jsxs(
253
348
  Text,
254
349
  {
@@ -268,11 +363,11 @@ function StatusBar({
268
363
  )
269
364
  ] }) : null,
270
365
  modeLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
271
- yolo || autonomy && autonomy !== "off" || eternalStage || startedAt != null || projectName || goalSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
366
+ yolo || autonomy && autonomy !== "off" || eternalStage || startedAt != null || projectName || workingDir || goalSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
272
367
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: modeIcon(modeLabel) })
273
368
  ] }) : null,
274
369
  hasAutoProceed ? /* @__PURE__ */ jsxs(Fragment, { children: [
275
- yolo || autonomy && autonomy !== "off" || eternalStage || startedAt != null || projectName || goalSummary || modeLabel ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
370
+ yolo || autonomy && autonomy !== "off" || eternalStage || startedAt != null || projectName || workingDir || goalSummary || modeLabel ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
276
371
  /* @__PURE__ */ jsxs(Text, { color: autoProceedCountdown != null && autoProceedCountdown <= 5 ? "yellow" : "cyan", children: [
277
372
  "\u23F3 auto in ",
278
373
  autoProceedCountdown,
@@ -280,16 +375,12 @@ function StatusBar({
280
375
  ] })
281
376
  ] }) : null,
282
377
  git ? /* @__PURE__ */ jsxs(Fragment, { children: [
283
- yolo || startedAt != null || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
378
+ yolo || startedAt != null || projectName || workingDir ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
284
379
  /* @__PURE__ */ jsxs(Text, { children: [
285
380
  /* @__PURE__ */ jsxs(Text, { color: "magenta", children: [
286
381
  "\u2387 ",
287
382
  git.branch
288
383
  ] }),
289
- git.added > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
290
- " +",
291
- git.added
292
- ] }) : null,
293
384
  git.deleted > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
294
385
  " -",
295
386
  git.deleted
@@ -299,6 +390,15 @@ function StatusBar({
299
390
  git.untracked
300
391
  ] }) : null
301
392
  ] })
393
+ ] }) : null,
394
+ sessionCount != null && sessionCount > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
395
+ yolo || startedAt != null || projectName || workingDir || git ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
396
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
397
+ "\u29C9 ",
398
+ sessionCount,
399
+ " session",
400
+ sessionCount === 1 ? "" : "s"
401
+ ] })
302
402
  ] }) : null
303
403
  ] }) : /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }),
304
404
  hasThirdLine ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
@@ -339,6 +439,36 @@ function StatusBar({
339
439
  ] }) : null
340
440
  ] })
341
441
  ] }) : null,
442
+ hasTaskActivity ? /* @__PURE__ */ jsxs(Fragment, { children: [
443
+ todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
444
+ /* @__PURE__ */ jsxs(Text, { children: [
445
+ /* @__PURE__ */ jsx(Text, { color: "magenta", children: "\u26A1 " }),
446
+ tasks.inProgress > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
447
+ "\u231B",
448
+ tasks.inProgress
449
+ ] }) : null,
450
+ tasks.inProgress > 0 && (tasks.pending > 0 || tasks.blocked > 0) ? " " : "",
451
+ tasks.pending > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
452
+ "\u2610",
453
+ tasks.pending
454
+ ] }) : null,
455
+ tasks.pending > 0 && tasks.blocked > 0 ? " " : "",
456
+ tasks.blocked > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
457
+ "\u2298",
458
+ tasks.blocked
459
+ ] }) : null,
460
+ (tasks.pending > 0 || tasks.blocked > 0) && (tasks.completed > 0 || tasks.failed > 0) ? " " : "",
461
+ tasks.completed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
462
+ "\u2713",
463
+ tasks.completed
464
+ ] }) : null,
465
+ tasks.completed > 0 && tasks.failed > 0 ? " " : "",
466
+ tasks.failed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
467
+ "\u2717",
468
+ tasks.failed
469
+ ] }) : null
470
+ ] })
471
+ ] }) : null,
342
472
  fleetHasActivity ? /* @__PURE__ */ jsxs(Fragment, { children: [
343
473
  todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
344
474
  fleet ? /* @__PURE__ */ jsxs(Text, { children: [
@@ -405,9 +535,64 @@ function StatusBar({
405
535
  enhanceCountdown,
406
536
  "s"
407
537
  ] })
538
+ ] }) : null,
539
+ hasNextStepsAutoSubmit && nextStepsAutoSubmitCountdown != null ? /* @__PURE__ */ jsxs(Fragment, { children: [
540
+ todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity || hasBrainActivity || hasDebugStream || hasEnhanceCountdown ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
541
+ /* @__PURE__ */ jsxs(Text, { color: nextStepsAutoSubmitCountdown <= 3 ? "yellow" : "cyan", children: [
542
+ "\u23F3 next step in ",
543
+ nextStepsAutoSubmitCountdown,
544
+ "s"
545
+ ] })
408
546
  ] }) : null
409
547
  ] }) : /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }),
410
- fleetAgents && fleetAgents.length > 0 ? /* @__PURE__ */ jsx(Box, { flexDirection: "row", gap: 2, children: fleetAgents.map((a, i) => (
548
+ mailbox ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
549
+ mailbox.unread > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", bold: true, children: [
550
+ "\u2709 ",
551
+ mailbox.unread,
552
+ " new"
553
+ ] }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2709 0" }),
554
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
555
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
556
+ "\u{1F465} ",
557
+ mailbox.onlineAgents,
558
+ " online"
559
+ ] }),
560
+ mailbox.lastSubject ? /* @__PURE__ */ jsxs(Fragment, { children: [
561
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
562
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
563
+ mailbox.lastFrom ? `${mailbox.lastFrom}: ` : "",
564
+ mailbox.lastSubject.length > 40 ? `${mailbox.lastSubject.slice(0, 37)}\u2026` : mailbox.lastSubject
565
+ ] })
566
+ ] }) : null,
567
+ fleetAgents && fleetAgents.length > 0 ? fleetAgents.map((a, i) => (
568
+ // biome-ignore lint/suspicious/noArrayIndexKey: agent list is stable per render
569
+ /* @__PURE__ */ jsxs(Text, { children: [
570
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
571
+ " ",
572
+ /* @__PURE__ */ jsx(Text, { color: a.color, bold: true, children: a.label }),
573
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
574
+ /* @__PURE__ */ jsx(Text, { dimColor: !a.running, ...a.running ? { color: "yellow" } : {}, children: a.running ? "\u25B6" : "\xB7" }),
575
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
576
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtElapsed(a.elapsedMs) }),
577
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
578
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
579
+ a.toolCalls,
580
+ "t"
581
+ ] }),
582
+ a.tool ? /* @__PURE__ */ jsxs(Fragment, { children: [
583
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
584
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: a.tool })
585
+ ] }) : null,
586
+ a.extensions && a.extensions > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
587
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
588
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
589
+ "\u26A1\xD7",
590
+ a.extensions
591
+ ] })
592
+ ] }) : null
593
+ ] }, i)
594
+ )) : null
595
+ ] }) : fleetAgents && fleetAgents.length > 0 ? /* @__PURE__ */ jsx(Box, { flexDirection: "row", gap: 2, children: fleetAgents.map((a, i) => (
411
596
  // biome-ignore lint/suspicious/noArrayIndexKey: agent list is stable per render
412
597
  /* @__PURE__ */ jsxs(Text, { children: [
413
598
  /* @__PURE__ */ jsx(Text, { color: a.color, bold: true, children: a.label }),
@@ -513,6 +698,32 @@ function EternalStageChip({
513
698
  ] });
514
699
  }
515
700
  }
701
+ var SB_GAP = 2;
702
+ var SB_PADX = 1;
703
+ function statusBarModelSpan(opts) {
704
+ let col = SB_PADX;
705
+ if (opts.version) {
706
+ col += `WS v${opts.version}`.length + SB_GAP;
707
+ col += 1 + SB_GAP;
708
+ }
709
+ const { label } = stateChip(opts.state, opts.fleetRunning ?? 0);
710
+ col += 2 + label.length + SB_GAP;
711
+ col += 1 + SB_GAP;
712
+ return { start: col, len: opts.model.length };
713
+ }
714
+ function statusBarAutonomySpan(opts) {
715
+ if (!opts.autonomy || opts.autonomy === "off") return null;
716
+ let col = SB_PADX;
717
+ if (opts.yolo) {
718
+ col += "\u26A0 YOLO".length + SB_GAP;
719
+ col += 1 + SB_GAP;
720
+ }
721
+ return { start: col, len: 2 + opts.autonomy.toUpperCase().length };
722
+ }
723
+ function statusBarTodosSpan() {
724
+ const LABEL_MAX = 20;
725
+ return { start: SB_PADX, len: LABEL_MAX };
726
+ }
516
727
  function stateChip(state, fleetRunning) {
517
728
  if (state === "idle" && fleetRunning > 0) {
518
729
  return { label: `agents \u25B6${fleetRunning}`, color: "magenta" };
@@ -522,18 +733,30 @@ function stateChip(state, fleetRunning) {
522
733
  return { label: "thinking\u2026", color: "green" };
523
734
  }
524
735
  var WAVE_COLORS = [
525
- "#ff5f5f",
526
- "#ff8f3f",
527
- "#ffd23f",
528
- "#bce84a",
529
- "#6bcb77",
530
- "#3dd9c0",
531
- "#3fb6ff",
532
- "#5f8bff",
533
- "#845ef7",
534
- "#b15bff",
535
- "#f06595",
536
- "#ff5fa2"
736
+ "#f38ba8",
737
+ // red
738
+ "#eba0ac",
739
+ // maroon
740
+ "#fab387",
741
+ // peach
742
+ "#f9e2af",
743
+ // yellow
744
+ "#a6e3a1",
745
+ // green
746
+ "#94e2d5",
747
+ // teal
748
+ "#89dceb",
749
+ // sky
750
+ "#89b4fa",
751
+ // blue
752
+ "#b4befe",
753
+ // lavender
754
+ "#cba6f7",
755
+ // mauve
756
+ "#f5c2e7",
757
+ // pink
758
+ "#f2cdcd"
759
+ // flamingo
537
760
  ];
538
761
  function WaveText({ text, phase }) {
539
762
  return /* @__PURE__ */ jsx(Text, { bold: true, children: Array.from(text).map((ch, i) => (
@@ -854,12 +1077,12 @@ function ContextBar({
854
1077
  tokens,
855
1078
  maxTokens
856
1079
  }) {
857
- const clamped = Math.max(0, Math.min(2, pct));
1080
+ const clamped = Math.max(0, Math.min(1, pct));
858
1081
  const totalBars = 10;
859
1082
  const filled = Math.round(clamped * totalBars);
860
1083
  const empty = totalBars - filled;
861
- const color = pct < 0.6 ? "green" : pct < 0.75 ? "yellow" : "red";
862
- const pctText = pct >= 1 ? `${Math.round(pct * 100)}%+` : `${Math.round(pct * 100)}%`;
1084
+ const color = clamped < 0.6 ? "green" : clamped < 0.75 ? "yellow" : "red";
1085
+ const pctText = `${Math.min(Math.round(pct * 100), 100)}%`;
863
1086
  const tokenText = tokens ? ` ${fmtTokens2(tokens)}/${fmtTokens2(maxTokens ?? 2e5)}` : "";
864
1087
  return /* @__PURE__ */ jsxs(Text, { color, children: [
865
1088
  "\u2588".repeat(filled),
@@ -1467,8 +1690,8 @@ function FilePicker({ query, matches, selected }) {
1467
1690
  ] }, m))
1468
1691
  ] });
1469
1692
  }
1470
- function highlight(path5, _query) {
1471
- return path5;
1693
+ function highlight(path6, _query) {
1694
+ return path6;
1472
1695
  }
1473
1696
  function FleetPanel({
1474
1697
  entries,
@@ -1535,29 +1758,103 @@ function FleetPanel({
1535
1758
  ] }) : null
1536
1759
  ] });
1537
1760
  }
1538
-
1539
- // src/theme.ts
1540
- var theme = Object.freeze({
1541
- accent: "cyan",
1542
- user: "yellow",
1543
- assistant: "cyan",
1544
- tool: "cyan",
1545
- success: "green",
1546
- warn: "yellow",
1547
- error: "red",
1548
- dim: true,
1549
- borderDefault: "gray",
1550
- borderActive: "yellow",
1551
- brand: "magenta",
1552
- monitor: {
1553
- fleet: "cyan",
1554
- agents: "magenta",
1555
- worktree: "green",
1556
- phase: "cyan"
1557
- },
1558
- diffAddBg: "greenBright",
1559
- diffDelBg: "redBright"
1560
- });
1761
+ function fmtTime(iso) {
1762
+ const d = new Date(iso);
1763
+ const now = Date.now();
1764
+ const diff = now - d.getTime();
1765
+ if (diff < 6e4) return "just now";
1766
+ if (diff < 36e5) return `${Math.round(diff / 6e4)}m ago`;
1767
+ if (diff < 864e5) return `${Math.round(diff / 36e5)}h ago`;
1768
+ return d.toLocaleDateString();
1769
+ }
1770
+ function fmtBody(body, maxLen) {
1771
+ const oneLine2 = body.replace(/\n/g, " ");
1772
+ return oneLine2.length > maxLen ? `${oneLine2.slice(0, maxLen - 1)}\u2026` : oneLine2;
1773
+ }
1774
+ var TYPE_ICONS = {
1775
+ note: "\u{1F4DD}",
1776
+ ask: "\u2753",
1777
+ assign: "\u{1F4CB}",
1778
+ steer: "\u{1F504}",
1779
+ btw: "\u{1F4AC}",
1780
+ broadcast: "\u{1F4E2}",
1781
+ status: "\u{1F7E2}",
1782
+ result: "\u2705"
1783
+ };
1784
+ function MailboxPanel({
1785
+ messages,
1786
+ agents,
1787
+ unreadCount,
1788
+ open
1789
+ }) {
1790
+ const { stdout } = useStdout();
1791
+ const [termWidth, setTermWidth] = useState(stdout?.columns ?? 90);
1792
+ useEffect(() => {
1793
+ const handleResize = () => setTermWidth(stdout?.columns ?? 90);
1794
+ handleResize();
1795
+ process.stdout.on("resize", handleResize);
1796
+ return () => {
1797
+ process.stdout.off("resize", handleResize);
1798
+ };
1799
+ }, [stdout]);
1800
+ if (!open) return null;
1801
+ const showMessages = messages.slice(0, 6);
1802
+ const showAgents = agents.slice(0, 8);
1803
+ const maxSubjectLen = Math.max(15, Math.min(30, termWidth - 55));
1804
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, flexShrink: 0, children: [
1805
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
1806
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u{1F4EC} Mailbox" }),
1807
+ unreadCount > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", bold: true, children: [
1808
+ unreadCount,
1809
+ " unread"
1810
+ ] }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "0 unread" }),
1811
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1812
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1813
+ agents.length,
1814
+ " agent",
1815
+ agents.length === 1 ? "" : "s",
1816
+ " online"
1817
+ ] })
1818
+ ] }),
1819
+ showMessages.length > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
1820
+ /* @__PURE__ */ jsx(Text, { bold: true, dimColor: true, children: "Messages" }),
1821
+ showMessages.map((m) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1822
+ /* @__PURE__ */ jsx(Text, { children: TYPE_ICONS[m.type] ?? "\u{1F4E8}" }),
1823
+ /* @__PURE__ */ jsx(Text, { color: m.readByMe ? void 0 : "yellow", bold: !m.readByMe, children: m.from }),
1824
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: m.subject.length > maxSubjectLen ? `${m.subject.slice(0, maxSubjectLen - 1)}\u2026` : m.subject }),
1825
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtBody(m.body, 40) }),
1826
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtTime(m.timestamp) }),
1827
+ m.readByCount > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1828
+ "\u{1F441} ",
1829
+ m.readByCount
1830
+ ] }) : /* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: "\u2709 new" }),
1831
+ m.completed ? /* @__PURE__ */ jsx(Text, { color: "green", children: "\u2713" }) : null
1832
+ ] }, m.id))
1833
+ ] }) : /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No messages yet." }) }),
1834
+ showAgents.length > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
1835
+ /* @__PURE__ */ jsx(Text, { bold: true, dimColor: true, children: "Online agents" }),
1836
+ showAgents.map((a) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1837
+ /* @__PURE__ */ jsx(Text, { color: a.online ? "green" : "dim", children: a.online ? "\u25CF" : "\u25CB" }),
1838
+ /* @__PURE__ */ jsx(Text, { children: a.name }),
1839
+ a.role ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1840
+ "(",
1841
+ a.role,
1842
+ ")"
1843
+ ] }) : null,
1844
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: a.status }),
1845
+ a.currentTool ? /* @__PURE__ */ jsx(Text, { color: "cyan", children: a.currentTool }) : null,
1846
+ a.currentTask ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: a.currentTask.length > 25 ? `${a.currentTask.slice(0, 24)}\u2026` : a.currentTask }) : null,
1847
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtTime(a.lastSeenAt) }),
1848
+ a.source ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1849
+ "[",
1850
+ a.source,
1851
+ "]"
1852
+ ] }) : null
1853
+ ] }, a.agentId))
1854
+ ] }) : null,
1855
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "/mailbox \u2014 Esc to close" }) })
1856
+ ] });
1857
+ }
1561
1858
  function helpSections() {
1562
1859
  const nav = [];
1563
1860
  nav.push(
@@ -1569,12 +1866,16 @@ function helpSections() {
1569
1866
  {
1570
1867
  title: "Monitors",
1571
1868
  entries: [
1869
+ { keys: "F1", desc: "project switcher (also /project)" },
1572
1870
  { keys: "Ctrl+F / F2", desc: "fleet orchestration monitor" },
1573
1871
  { keys: "Ctrl+G / F3", desc: "agents live monitor" },
1574
1872
  { keys: "Ctrl+T / F4", desc: "worktree monitor" },
1575
1873
  { keys: "F5", desc: "autonomy settings (also Ctrl+S)" },
1576
1874
  { keys: "F6", desc: "todos monitor overlay" },
1577
1875
  { keys: "F7", desc: "queue panel" },
1876
+ { keys: "F8", desc: "process list overlay" },
1877
+ { keys: "F9", desc: "goal panel" },
1878
+ { keys: "F10", desc: "live sessions panel" },
1578
1879
  { keys: "Esc", desc: "close the open monitor / overlay" }
1579
1880
  ]
1580
1881
  },
@@ -1591,6 +1892,7 @@ function helpSections() {
1591
1892
  {
1592
1893
  title: "Commands",
1593
1894
  entries: [
1895
+ { keys: "/project", desc: "switch projects (also F1)" },
1594
1896
  { keys: "/help", desc: "list all slash commands" },
1595
1897
  { keys: "/model", desc: "switch the active model" },
1596
1898
  { keys: "/fleet", desc: "multi-agent fleet controls" },
@@ -2148,14 +2450,14 @@ function computeWidths(allRows, cols, maxWidth, sepWidths) {
2148
2450
  const cell = row[c] ?? "";
2149
2451
  const stripped = stripInlineMarkers(cell);
2150
2452
  const total = strWidth(stripped);
2151
- natural[c] = Math.max(expectDefined$1(natural[c]), total);
2453
+ natural[c] = Math.max(expectDefined(natural[c]), total);
2152
2454
  }
2153
2455
  }
2154
2456
  if (sepWidths) {
2155
2457
  for (let c = 0; c < cols && c < sepWidths.length; c++) {
2156
2458
  const sepW = sepWidths[c];
2157
2459
  if (sepW != null) {
2158
- natural[c] = Math.max(expectDefined$1(natural[c]), sepW);
2460
+ natural[c] = Math.max(expectDefined(natural[c]), sepW);
2159
2461
  }
2160
2462
  }
2161
2463
  }
@@ -2167,7 +2469,7 @@ function computeWidths(allRows, cols, maxWidth, sepWidths) {
2167
2469
  let maxIdx = -1;
2168
2470
  let maxVal = MIN_COL_WIDTH;
2169
2471
  for (let i = 0; i < cols; i++) {
2170
- const w = expectDefined$1(widths[i]);
2472
+ const w = expectDefined(widths[i]);
2171
2473
  if (w > maxVal) {
2172
2474
  maxVal = w;
2173
2475
  maxIdx = i;
@@ -2227,7 +2529,7 @@ function strWidth(s2) {
2227
2529
  if (i < len) i++;
2228
2530
  continue;
2229
2531
  }
2230
- const code = expectDefined$1(s2.codePointAt(i));
2532
+ const code = expectDefined(s2.codePointAt(i));
2231
2533
  const cpLen = code > 65535 ? 2 : 1;
2232
2534
  if (code === 8205 || // ZWJ — Zero Width Joiner (emoji sequences)
2233
2535
  code === 8203 || // ZWSP — Zero Width Space
@@ -2299,7 +2601,7 @@ function stripInlineMarkers(text) {
2299
2601
  }
2300
2602
  var ANSI_BOLD = "\x1B[1m";
2301
2603
  var ANSI_DIM = "\x1B[2m";
2302
- var ANSI_CYAN = "\x1B[36m";
2604
+ var ANSI_CYAN = "\x1B[38;2;148;226;213m";
2303
2605
  var ANSI_STRIKE = "\x1B[9m";
2304
2606
  var ANSI_RESET_ALL = "\x1B[0m";
2305
2607
  function applyInlineAnsi(text) {
@@ -2404,7 +2706,11 @@ function padCell(text, width, align) {
2404
2706
  }
2405
2707
  return displayText + " ".repeat(pad);
2406
2708
  }
2709
+ var _parseCache = /* @__PURE__ */ new Map();
2710
+ var _PARSE_CACHE_MAX = 5e3;
2407
2711
  function parseInline(text) {
2712
+ const cached = _parseCache.get(text);
2713
+ if (cached) return cached;
2408
2714
  const tokens = [];
2409
2715
  let plain = "";
2410
2716
  let i = 0;
@@ -2457,6 +2763,15 @@ function parseInline(text) {
2457
2763
  i += 1;
2458
2764
  }
2459
2765
  flush();
2766
+ if (_parseCache.size >= _PARSE_CACHE_MAX) {
2767
+ let dropped = 0;
2768
+ const target = Math.floor(_PARSE_CACHE_MAX / 4);
2769
+ for (const key of _parseCache.keys()) {
2770
+ _parseCache.delete(key);
2771
+ if (++dropped >= target) break;
2772
+ }
2773
+ }
2774
+ _parseCache.set(text, tokens);
2460
2775
  return tokens;
2461
2776
  }
2462
2777
  function InlineLine({ tokens, dim }) {
@@ -3123,7 +3438,7 @@ function streamBoxRows(text, maxLines, contentWidth) {
3123
3438
  }
3124
3439
  return rows;
3125
3440
  }
3126
- React5.memo(function ToolStreamBox2({
3441
+ var ToolStreamBox = React5.memo(function ToolStreamBox2({
3127
3442
  name,
3128
3443
  text,
3129
3444
  startedAt,
@@ -3408,7 +3723,7 @@ function Banner({
3408
3723
  entry
3409
3724
  }) {
3410
3725
  const cwdShort = shortenPath(entry.cwd, 48);
3411
- const projectLabel = path3.basename(entry.cwd);
3726
+ const projectLabel = path4.basename(entry.cwd);
3412
3727
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 0, children: [
3413
3728
  /* @__PURE__ */ jsxs(Text, { children: [
3414
3729
  /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: " \u259F\u259B " }),
@@ -3440,23 +3755,111 @@ function Banner({
3440
3755
  ] }),
3441
3756
  /* @__PURE__ */ jsxs(Text, { children: [
3442
3757
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: " hints " }),
3443
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "/help \xB7 /init \xB7 /memory \xB7 /queue \xB7 /exit" })
3758
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "/help \xB7 F1 projects \xB7 F10 sessions \xB7 /exit" })
3444
3759
  ] })
3445
3760
  ] });
3446
3761
  }
3447
- var NEXT_STEPS_RE = /💡\s*Next steps?\s*\n+((?:\d+\.\s+.+\n?)+)/i;
3448
- function parseNextSteps(content) {
3449
- const match = NEXT_STEPS_RE.exec(content);
3450
- if (!match?.[1]) return { steps: [], stripped: content };
3451
- const block = match[1];
3762
+
3763
+ // src/components/suggestions.ts
3764
+ var STRICT_HEADING_RE = /💡\s*Next steps?\s*\n+/i;
3765
+ var PERMISSIVE_HEADING_PATTERNS = [
3766
+ { re: /💡\s*Next steps?\s*\n+/i, label: "emoji" },
3767
+ { re: /##?\s*Next steps?\s*\n+/i, label: "markdown" },
3768
+ { re: /\n{1,2}Next steps?\s*\n+/i, label: "plain" }
3769
+ ];
3770
+ var ITEM_RE = /^(?:(\d+)[.)]\s*|[-*•]\s*)(.+)$/;
3771
+ var MAX_STEPS = 6;
3772
+ function parseNextSteps(content, strict = false, requireHeading = true) {
3773
+ if (requireHeading) {
3774
+ return parseWithHeading(content, strict);
3775
+ }
3776
+ return parseRawNumbered(content);
3777
+ }
3778
+ function parseRawNumbered(content) {
3779
+ const lines = content.split("\n");
3452
3780
  const steps = [];
3453
- const lines = block.split("\n").filter(Boolean);
3454
- for (const line of lines) {
3455
- const m = /^(\d+)\.\s+(.+)$/.exec(line.trim());
3456
- if (m) steps.push({ index: Number.parseInt(m[1], 10), text: m[2].trim() });
3781
+ const seenNumbers = /* @__PURE__ */ new Set();
3782
+ for (const rawLine of lines) {
3783
+ const line = rawLine.trim();
3784
+ if (!line) continue;
3785
+ const m = ITEM_RE.exec(line);
3786
+ if (!m) continue;
3787
+ const numPart = m[1];
3788
+ let text = m[2].trim();
3789
+ let index;
3790
+ if (numPart !== void 0) {
3791
+ index = Number.parseInt(numPart, 10);
3792
+ } else {
3793
+ index = steps.length + 1;
3794
+ }
3795
+ if (seenNumbers.has(index)) continue;
3796
+ if (text.length < 3) continue;
3797
+ seenNumbers.add(index);
3798
+ steps.push({ index, text });
3799
+ if (steps.length >= MAX_STEPS) break;
3800
+ }
3801
+ return { steps, texts: steps.map((s2) => s2.text), stripped: content };
3802
+ }
3803
+ function parseWithHeading(content, strict) {
3804
+ const headingRe = strict ? STRICT_HEADING_RE : buildPermissiveHeadingRe();
3805
+ const headingMatch = headingRe.exec(content);
3806
+ if (!headingMatch) {
3807
+ return { steps: [], texts: [], stripped: content };
3808
+ }
3809
+ const headingEnd = headingMatch.index + headingMatch[0].length;
3810
+ const afterHeading = content.slice(headingEnd);
3811
+ const lines = afterHeading.split("\n");
3812
+ const steps = [];
3813
+ const seenNumbers = /* @__PURE__ */ new Set();
3814
+ for (const rawLine of lines) {
3815
+ const line = rawLine.trim();
3816
+ if (!line) continue;
3817
+ const m = ITEM_RE.exec(line);
3818
+ if (!m) break;
3819
+ const numPart = m[1];
3820
+ let text = m[2].trim();
3821
+ let index;
3822
+ if (numPart !== void 0) {
3823
+ index = Number.parseInt(numPart, 10);
3824
+ } else {
3825
+ index = steps.length + 1;
3826
+ }
3827
+ if (seenNumbers.has(index)) continue;
3828
+ if (text.length < 3) continue;
3829
+ seenNumbers.add(index);
3830
+ steps.push({ index, text });
3831
+ if (steps.length >= MAX_STEPS) break;
3832
+ }
3833
+ if (steps.length === 0) {
3834
+ return { steps: [], texts: [], stripped: content };
3457
3835
  }
3458
- const stripped = content.replace(NEXT_STEPS_RE, "").replace(/\n{3,}/g, "\n\n").trim();
3459
- return { steps: steps.slice(0, 6), stripped };
3836
+ const texts = steps.map((s2) => s2.text);
3837
+ const blockStart = headingMatch.index;
3838
+ const blockEnd = headingEnd + findBlockEnd(afterHeading, steps.length);
3839
+ const stripped = (content.slice(0, blockStart) + content.slice(blockStart + blockEnd)).replace(/\n{3,}/g, "\n\n").trim();
3840
+ return { steps, texts, stripped };
3841
+ }
3842
+ function buildPermissiveHeadingRe() {
3843
+ const variants = PERMISSIVE_HEADING_PATTERNS.map(({ re }) => `(?:${re.source})`).join("|");
3844
+ return new RegExp(variants, "i");
3845
+ }
3846
+ function findBlockEnd(afterHeading, stepCount) {
3847
+ const lines = afterHeading.split("\n");
3848
+ let consumed = 0;
3849
+ let found = 0;
3850
+ for (const rawLine of lines) {
3851
+ const line = rawLine.trim();
3852
+ if (!line) {
3853
+ consumed += rawLine.length + 1;
3854
+ continue;
3855
+ }
3856
+ const m = ITEM_RE.exec(line);
3857
+ if (!m) break;
3858
+ consumed += rawLine.length + 1;
3859
+ found++;
3860
+ if (found >= stepCount) break;
3861
+ }
3862
+ return consumed;
3460
3863
  }
3461
3864
  function brainStatusStyle(status) {
3462
3865
  switch (status) {
@@ -3468,6 +3871,8 @@ function brainStatusStyle(status) {
3468
3871
  return { icon: "?", color: "yellow" };
3469
3872
  case "denied":
3470
3873
  return { icon: "\xD7", color: "red" };
3874
+ case "intervention":
3875
+ return { icon: "\u26A1", color: "yellow" };
3471
3876
  }
3472
3877
  }
3473
3878
  function brainRiskColor(risk) {
@@ -3484,12 +3889,19 @@ function brainRiskColor(risk) {
3484
3889
  }
3485
3890
  var Entry = React5.memo(function Entry2({
3486
3891
  entry,
3487
- termWidth
3892
+ termWidth,
3893
+ setSuggestions
3488
3894
  }) {
3489
3895
  const nextSteps = useMemo(() => {
3490
3896
  if (entry.kind !== "assistant") return { steps: [], stripped: "" };
3491
- return parseNextSteps(entry.text);
3897
+ return parseNextSteps(entry.text, true);
3492
3898
  }, [entry.kind, entry.text]);
3899
+ useEffect(() => {
3900
+ if (!setSuggestions) return;
3901
+ const text = entry.text ?? "";
3902
+ const { texts } = parseNextSteps(text, true);
3903
+ if (texts.length > 0) setSuggestions(texts);
3904
+ }, [entry.kind, entry.text, setSuggestions]);
3493
3905
  switch (entry.kind) {
3494
3906
  case "user":
3495
3907
  return /* @__PURE__ */ jsx(
@@ -3618,8 +4030,10 @@ var Entry = React5.memo(function Entry2({
3618
4030
  diff ? /* @__PURE__ */ jsx(DiffBlock, { rows: diff.rows, hidden: diff.hidden }) : null
3619
4031
  ] });
3620
4032
  }
3621
- case "info":
3622
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.text });
4033
+ case "info": {
4034
+ const hasAnsi = /\x1b\[/.test(entry.text);
4035
+ return /* @__PURE__ */ jsx(Text, { dimColor: !hasAnsi, children: entry.text });
4036
+ }
3623
4037
  case "warn":
3624
4038
  return /* @__PURE__ */ jsx(
3625
4039
  Box,
@@ -3728,7 +4142,7 @@ var Entry = React5.memo(function Entry2({
3728
4142
  }
3729
4143
  }
3730
4144
  });
3731
- function History({ entries, streamingText, toolStream }) {
4145
+ function History({ entries, generation, streamingText, toolStream, setSuggestions }) {
3732
4146
  const { stdout } = useStdout();
3733
4147
  const [termSize, setTermSize] = useState({
3734
4148
  columns: stdout?.columns ?? 80,
@@ -3746,10 +4160,210 @@ function History({ entries, streamingText, toolStream }) {
3746
4160
  const termWidth = termSize.columns;
3747
4161
  const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
3748
4162
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3749
- /* @__PURE__ */ jsx(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth }) }, entry.id) }),
4163
+ /* @__PURE__ */ jsx(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth, setSuggestions }) }, entry.id) }, generation ?? 0),
3750
4164
  /* @__PURE__ */ jsx(Box, { flexGrow: 1, children: tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail, termWidth }) : null })
3751
4165
  ] });
3752
4166
  }
4167
+ var MAX_MOUNTED = 500;
4168
+ function scrollbarThumb(rows, offset, total) {
4169
+ const scrollable = total > rows;
4170
+ if (!scrollable) return { top: 0, size: rows, scrollable: false };
4171
+ const windowTop = Math.max(0, total - rows - offset);
4172
+ const size = Math.max(1, Math.round(rows / total * rows));
4173
+ const rawTop = Math.round(windowTop / total * rows);
4174
+ const top = Math.max(0, Math.min(rawTop, rows - size));
4175
+ return { top, size, scrollable: true };
4176
+ }
4177
+ function scrollOffsetForTrackRow(rows, total, cell) {
4178
+ if (total <= rows) return 0;
4179
+ const maxOffset = total - rows;
4180
+ const clampedCell = Math.max(0, Math.min(rows - 1, cell));
4181
+ const windowTop = Math.round(clampedCell / Math.max(1, rows - 1) * maxOffset);
4182
+ return Math.max(0, Math.min(maxOffset, maxOffset - windowTop));
4183
+ }
4184
+ function Scrollbar({
4185
+ rows,
4186
+ offset,
4187
+ total
4188
+ }) {
4189
+ const { top: thumbTop, size: thumbSize, scrollable } = scrollbarThumb(rows, offset, total);
4190
+ const cells = [];
4191
+ for (let i = 0; i < rows; i++) {
4192
+ cells.push(i >= thumbTop && i < thumbTop + thumbSize ? "\u2588" : "\u2502");
4193
+ }
4194
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginLeft: 1, flexShrink: 0, children: cells.map((c, i) => /* @__PURE__ */ jsx(
4195
+ Text,
4196
+ {
4197
+ ...scrollable ? { color: theme.accent } : {},
4198
+ dimColor: !scrollable || c === "\u2502",
4199
+ children: c
4200
+ },
4201
+ i
4202
+ )) });
4203
+ }
4204
+ function ScrollableHistory({
4205
+ entries,
4206
+ streamingText,
4207
+ toolStream,
4208
+ scrollOffset,
4209
+ viewportRows,
4210
+ totalLines,
4211
+ onMeasure,
4212
+ maxWidth,
4213
+ setSuggestions
4214
+ }) {
4215
+ const { stdout } = useStdout();
4216
+ const rawWidth = stdout?.columns ?? 80;
4217
+ const termWidth = maxWidth ? Math.min(rawWidth, maxWidth) : rawWidth;
4218
+ const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
4219
+ const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
4220
+ const hiddenCount = Math.max(0, entries.length - MAX_MOUNTED);
4221
+ const shown = hiddenCount > 0 ? entries.slice(-MAX_MOUNTED) : entries;
4222
+ const contentRef = useRef(null);
4223
+ const lastReported = useRef(-1);
4224
+ useLayoutEffect(() => {
4225
+ const node = contentRef.current;
4226
+ if (!node) return;
4227
+ const { height } = measureElement(node);
4228
+ if (height !== lastReported.current) {
4229
+ lastReported.current = height;
4230
+ onMeasure(height);
4231
+ }
4232
+ }, [onMeasure]);
4233
+ const vp = Math.max(1, viewportRows);
4234
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
4235
+ /* @__PURE__ */ jsx(
4236
+ Box,
4237
+ {
4238
+ flexDirection: "column",
4239
+ flexGrow: 1,
4240
+ height: vp,
4241
+ overflowY: "hidden",
4242
+ justifyContent: "flex-end",
4243
+ children: /* @__PURE__ */ jsxs(
4244
+ Box,
4245
+ {
4246
+ ref: contentRef,
4247
+ flexDirection: "column",
4248
+ marginBottom: Math.max(0, scrollOffset),
4249
+ flexShrink: 0,
4250
+ children: [
4251
+ hiddenCount > 0 ? /* @__PURE__ */ jsx(Box, { flexShrink: 0, children: /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: ` \u2191 ${hiddenCount} earlier ${hiddenCount === 1 ? "entry" : "entries"} (scroll lives in this session; full log on disk)` }) }) : null,
4252
+ shown.map((entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, flexShrink: 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth, setSuggestions }) }, entry.id)),
4253
+ tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail, termWidth }) : null,
4254
+ toolTail && toolStream ? /* @__PURE__ */ jsx(
4255
+ ToolStreamBox,
4256
+ {
4257
+ name: toolStream.name,
4258
+ text: toolTail,
4259
+ startedAt: toolStream.startedAt,
4260
+ termWidth
4261
+ }
4262
+ ) : null
4263
+ ]
4264
+ }
4265
+ )
4266
+ }
4267
+ ),
4268
+ /* @__PURE__ */ jsx(Scrollbar, { rows: vp, offset: Math.max(0, scrollOffset), total: totalLines })
4269
+ ] });
4270
+ }
4271
+ var MB = 1024 * 1024;
4272
+ function defaultHeapLogPath() {
4273
+ return path4.join(wstackGlobalRoot(), "logs", "heap.jsonl");
4274
+ }
4275
+ function takeHeapSample() {
4276
+ const m = process.memoryUsage();
4277
+ const limit = v8.getHeapStatistics().heap_size_limit || 0;
4278
+ return {
4279
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4280
+ rss: m.rss,
4281
+ heapUsed: m.heapUsed,
4282
+ heapTotal: m.heapTotal,
4283
+ external: m.external,
4284
+ heapLimit: limit,
4285
+ load: limit > 0 ? m.heapUsed / limit : 0
4286
+ };
4287
+ }
4288
+ function startHeapWatchdog(opts = {}) {
4289
+ const sampleEveryMs = opts.sampleEveryMs ?? 6e4;
4290
+ const logEveryMs = opts.logEveryMs ?? 3e5;
4291
+ const logPath = opts.logPath ?? defaultHeapLogPath();
4292
+ const warnAt = opts.warnAt ?? 0.6;
4293
+ const criticalAt = opts.criticalAt ?? 0.85;
4294
+ const REARM_MARGIN = 0.05;
4295
+ let warnArmed = true;
4296
+ let criticalArmed = true;
4297
+ let lastLogAt = 0;
4298
+ let writeChain = Promise.resolve();
4299
+ let dirReady = false;
4300
+ const append = (line) => {
4301
+ writeChain = writeChain.then(async () => {
4302
+ if (!dirReady) {
4303
+ await fs2.mkdir(path4.dirname(logPath), { recursive: true });
4304
+ dirReady = true;
4305
+ }
4306
+ await fs2.appendFile(logPath, `${line}
4307
+ `, "utf8");
4308
+ }).catch(() => void 0);
4309
+ };
4310
+ const tick = () => {
4311
+ const s2 = takeHeapSample();
4312
+ if (s2.load >= criticalAt && criticalArmed) {
4313
+ criticalArmed = false;
4314
+ warnArmed = false;
4315
+ opts.onWarn?.(
4316
+ "critical",
4317
+ `Heap critical: ${Math.round(s2.heapUsed / MB)} MB of ~${Math.round(s2.heapLimit / MB)} MB V8 limit (${Math.round(s2.load * 100)}%). An out-of-memory crash is likely soon \u2014 finish/checkpoint work and restart the session. Diagnostics: ${logPath}`,
4318
+ s2
4319
+ );
4320
+ } else if (s2.load >= warnAt && warnArmed) {
4321
+ warnArmed = false;
4322
+ opts.onWarn?.(
4323
+ "warn",
4324
+ `Heap high: ${Math.round(s2.heapUsed / MB)} MB of ~${Math.round(s2.heapLimit / MB)} MB V8 limit (${Math.round(s2.load * 100)}%). Memory diagnostics are being recorded to ${logPath}`,
4325
+ s2
4326
+ );
4327
+ }
4328
+ if (!warnArmed && s2.load < warnAt - REARM_MARGIN) warnArmed = true;
4329
+ if (!criticalArmed && s2.load < criticalAt - REARM_MARGIN) criticalArmed = true;
4330
+ const due = Date.now() - lastLogAt >= logEveryMs;
4331
+ const crossed = s2.load >= warnAt;
4332
+ if (due || crossed) {
4333
+ lastLogAt = Date.now();
4334
+ let extras = {};
4335
+ try {
4336
+ extras = opts.collectStats?.() ?? {};
4337
+ } catch {
4338
+ }
4339
+ append(JSON.stringify({ pid: process.pid, ...s2, ...extras }));
4340
+ }
4341
+ };
4342
+ const timer = setInterval(tick, sampleEveryMs);
4343
+ timer.unref?.();
4344
+ tick();
4345
+ return () => {
4346
+ clearInterval(timer);
4347
+ };
4348
+ }
4349
+
4350
+ // src/hit-test.ts
4351
+ var SCROLLBAR_HIT_WIDTH = 2;
4352
+ function hitRegion(layout, x, y) {
4353
+ const { termRows, termCols, viewportRows } = layout;
4354
+ if (y < 1 || y > termRows || x < 1 || x > termCols) return null;
4355
+ if (y <= viewportRows) {
4356
+ if (x > termCols - SCROLLBAR_HIT_WIDTH) return { kind: "scrollbar", cell: y - 1 };
4357
+ return { kind: "history", row: y - 1 };
4358
+ }
4359
+ return { kind: "bottom", row: y - viewportRows - 1 };
4360
+ }
4361
+ function statusBarLineRow(opts) {
4362
+ const contentLines = opts.statusBarHeight - opts.headerRows;
4363
+ if (opts.line < 0 || opts.line >= contentLines) return null;
4364
+ const bandTop = opts.termRows - opts.belowHeight - opts.statusBarHeight + 1;
4365
+ return bandTop + opts.headerRows + opts.line;
4366
+ }
3753
4367
 
3754
4368
  // src/fn-keys.ts
3755
4369
  function fnKey(data) {
@@ -3853,6 +4467,45 @@ function layoutInputRows(prompt, value, cursor, width) {
3853
4467
  if (row.length > 0 || rows.length === 0) rows.push(row);
3854
4468
  return rows;
3855
4469
  }
4470
+
4471
+ // src/mouse.ts
4472
+ var ESC = String.fromCharCode(27);
4473
+ var MOUSE_CLICK_ON = `${ESC}[?1000h${ESC}[?1006h`;
4474
+ var MOUSE_OFF = `${ESC}[?1003l${ESC}[?1002l${ESC}[?1000l${ESC}[?1006l`;
4475
+ var SGR_MOUSE_GLOBAL = new RegExp(`${ESC}\\[<(\\d+);(\\d+);(\\d+)([Mm])`, "gu");
4476
+ var LEAKED_MOUSE_RE = /\[<\d+;\d+;\d+[Mm]/;
4477
+ function decodeMouse(cb, x, y, released) {
4478
+ const shift = (cb & 4) !== 0;
4479
+ const meta = (cb & 8) !== 0;
4480
+ const ctrl = (cb & 16) !== 0;
4481
+ const motion = (cb & 32) !== 0;
4482
+ const wheel = (cb & 64) !== 0;
4483
+ const low = cb & 3;
4484
+ if (wheel) {
4485
+ const dir = low === 0 ? 1 : low === 1 ? -1 : 0;
4486
+ return { kind: "wheel", button: "none", x, y, wheel: dir, shift, meta, ctrl, motion: false };
4487
+ }
4488
+ const button = low === 0 ? "left" : low === 1 ? "middle" : low === 2 ? "right" : "none";
4489
+ const kind = motion ? "move" : released ? "release" : "press";
4490
+ return { kind, button, x, y, wheel: 0, shift, meta, ctrl, motion };
4491
+ }
4492
+ function parseMouseEvents(data) {
4493
+ const events = [];
4494
+ for (const m of data.matchAll(SGR_MOUSE_GLOBAL)) {
4495
+ events.push(
4496
+ decodeMouse(
4497
+ Number.parseInt(m[1], 10),
4498
+ Number.parseInt(m[2], 10),
4499
+ Number.parseInt(m[3], 10),
4500
+ m[4] === "m"
4501
+ )
4502
+ );
4503
+ }
4504
+ return events;
4505
+ }
4506
+ function isLeakedMouseInput(input) {
4507
+ return LEAKED_MOUSE_RE.test(input);
4508
+ }
3856
4509
  function renderRow2(cells, rowKey, promptColor) {
3857
4510
  const out = [];
3858
4511
  let run = "";
@@ -3907,14 +4560,6 @@ function isBackspaceOrDelete(data) {
3907
4560
  if (data === "\x1B[3~") return "delete";
3908
4561
  return null;
3909
4562
  }
3910
- function parseMouseWheel(data) {
3911
- const m = data.match(new RegExp(`^${String.fromCharCode(27)}\\[<(\\d+);(\\d+);(\\d+)([Mm])$`, "u"));
3912
- if (!m) return null;
3913
- const cb = Number.parseInt(expectDefined(m[1]), 10);
3914
- if (cb === 64) return 1;
3915
- if (cb === 65) return -1;
3916
- return null;
3917
- }
3918
4563
  var EMPTY_KEY = {
3919
4564
  upArrow: false,
3920
4565
  downArrow: false,
@@ -3948,6 +4593,7 @@ var Input = memo(function Input2({
3948
4593
  const suppressInkDeleteRef = useRef(false);
3949
4594
  useInput((input, key) => {
3950
4595
  if (disabled) return;
4596
+ if (input && isLeakedMouseInput(input)) return;
3951
4597
  if (key.escape && suppressInkEscRef.current) {
3952
4598
  suppressInkEscRef.current = false;
3953
4599
  return;
@@ -4011,9 +4657,15 @@ var Input = memo(function Input2({
4011
4657
  onKey("", { ...EMPTY_KEY, backspace: true, ctrl: true });
4012
4658
  return;
4013
4659
  }
4014
- const wheelDelta = parseMouseWheel(s2);
4015
- if (wheelDelta !== null) {
4016
- onKey("", { ...EMPTY_KEY, wheelDeltaY: wheelDelta });
4660
+ const mouseEvents = parseMouseEvents(s2);
4661
+ if (mouseEvents.length > 0) {
4662
+ for (const ev of mouseEvents) {
4663
+ onKey("", {
4664
+ ...EMPTY_KEY,
4665
+ mouse: ev,
4666
+ wheelDeltaY: ev.kind === "wheel" ? ev.wheel : void 0
4667
+ });
4668
+ }
4017
4669
  return;
4018
4670
  }
4019
4671
  const fn = fnKey(s2);
@@ -4319,6 +4971,80 @@ function PhasePanel({ phases, nowTick }) {
4319
4971
  }
4320
4972
  );
4321
4973
  }
4974
+ var MAX_VISIBLE2 = 12;
4975
+ function visibleWindow(selected, total) {
4976
+ if (total <= MAX_VISIBLE2) return { start: 0, end: total };
4977
+ const half = Math.floor(MAX_VISIBLE2 / 2);
4978
+ let start = selected - half;
4979
+ let end = start + MAX_VISIBLE2;
4980
+ if (start < 0) {
4981
+ start = 0;
4982
+ end = MAX_VISIBLE2;
4983
+ }
4984
+ if (end > total) {
4985
+ end = total;
4986
+ start = total - MAX_VISIBLE2;
4987
+ }
4988
+ return { start, end };
4989
+ }
4990
+ function ProjectPicker({
4991
+ items,
4992
+ selected,
4993
+ filter,
4994
+ hint
4995
+ }) {
4996
+ const total = items.length;
4997
+ const { start, end } = visibleWindow(selected, total);
4998
+ const visible = items.slice(start, end);
4999
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, flexShrink: 0, children: [
5000
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Switch Project \u2501\u2501" }),
5001
+ filter ? /* @__PURE__ */ jsxs(Box, { children: [
5002
+ /* @__PURE__ */ jsx(Text, { color: "yellow", children: "Filter: " }),
5003
+ /* @__PURE__ */ jsx(Text, { children: filter }),
5004
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2588" })
5005
+ ] }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "type to filter \xB7 \u2191\u2193 navigate \xB7 Enter select \xB7 Esc cancel" }),
5006
+ start > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5007
+ "\u2191 ",
5008
+ start,
5009
+ " more above"
5010
+ ] }) : null,
5011
+ visible.map((item) => {
5012
+ const idx = items.indexOf(item);
5013
+ const isSelected = idx === selected;
5014
+ const isDivider = item.key === "__divider__";
5015
+ if (isDivider) {
5016
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(36) }, item.key);
5017
+ }
5018
+ const marker = isSelected ? "\u25B8" : " ";
5019
+ const labelColor = isSelected ? "cyan" : void 0;
5020
+ const metaColor = "grey";
5021
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
5022
+ /* @__PURE__ */ jsxs(Box, { children: [
5023
+ /* @__PURE__ */ jsxs(Text, { inverse: isSelected, color: labelColor, children: [
5024
+ ` ${marker} `,
5025
+ /* @__PURE__ */ jsx(Text, { bold: isSelected, children: item.label })
5026
+ ] }),
5027
+ item.meta ? /* @__PURE__ */ jsxs(Text, { dimColor: !isSelected, color: isSelected ? "cyan" : metaColor, children: [
5028
+ " ",
5029
+ item.meta
5030
+ ] }) : null
5031
+ ] }),
5032
+ item.subtitle ? /* @__PURE__ */ jsxs(Text, { dimColor: !isSelected, color: isSelected ? "cyan" : metaColor, children: [
5033
+ " ",
5034
+ item.subtitle
5035
+ ] }) : null
5036
+ ] }, item.key);
5037
+ }),
5038
+ end < total ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5039
+ "\u2193 ",
5040
+ total - end,
5041
+ " more below \xB7 ",
5042
+ total,
5043
+ " total"
5044
+ ] }) : null,
5045
+ hint ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) }) : null
5046
+ ] });
5047
+ }
4322
5048
  function QueuePanel({ items }) {
4323
5049
  const { stdout } = useStdout();
4324
5050
  const w = stdout?.columns ?? 80;
@@ -4549,32 +5275,227 @@ function renderProgressBar(progress, trend) {
4549
5275
  ] })
4550
5276
  ] });
4551
5277
  }
4552
- var DELAY_PRESETS_MS = [0, 15e3, 3e4, 45e3, 6e4, 12e4];
4553
- var SETTINGS_MODES = ["off", "suggest", "auto"];
4554
- var LOG_LEVELS = ["error", "warn", "info", "debug", "trace"];
4555
- var AUDIT_LEVELS = ["minimal", "standard", "full"];
4556
- var COMPACTOR_STRATEGIES = ["hybrid", "intelligent", "selective"];
4557
- var MAX_ITERATIONS_PRESETS = [100, 200, 500, 1e3, 0];
4558
- var AUTO_PROCEED_MAX_PRESETS = [10, 25, 50, 100, 250, 0];
4559
- var ENHANCE_DELAY_PRESETS = [3e4, 45e3, 6e4, 9e4, 12e4];
4560
- function formatSettingsDelay(ms) {
4561
- if (ms === 0) return "disabled";
4562
- if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
4563
- return `${Math.round(ms / 1e3)}s`;
5278
+ function ResumePicker({
5279
+ sessions,
5280
+ selected,
5281
+ busy,
5282
+ error,
5283
+ hint
5284
+ }) {
5285
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
5286
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Resume Session \u2501\u2501" }),
5287
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: busy ? "Resuming selected session\u2026" : "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel" }),
5288
+ error ? /* @__PURE__ */ jsx(Text, { color: "red", children: error }) : null,
5289
+ sessions.length === 0 && !busy ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No sessions found." }) : sessions.map((s2, i) => {
5290
+ const isCurrent = s2.isCurrent;
5291
+ const isSelected = i === selected;
5292
+ const date = s2.startedAt.slice(0, 16).replace("T", " ");
5293
+ const outcomeBadge = s2.outcome === "completed" ? "\u2713 " : s2.outcome === "aborted" ? "\u26A0 " : s2.outcome === "error" ? "\u2717 " : s2.outcome === "timeout" ? "\u23F1 " : " ";
5294
+ const toolStr = s2.toolCallCount > 0 ? `${s2.toolCallCount} tool${s2.toolCallCount === 1 ? "" : "s"}` : "";
5295
+ const iterStr = s2.iterationCount > 0 ? `${s2.iterationCount} iter${s2.iterationCount === 1 ? "" : "s"}` : "";
5296
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
5297
+ /* @__PURE__ */ jsxs(
5298
+ Text,
5299
+ {
5300
+ inverse: isSelected,
5301
+ dimColor: isCurrent ?? false,
5302
+ ...isSelected ? { color: isCurrent ? "gray" : "cyan" } : {},
5303
+ children: [
5304
+ isSelected ? "\u203A " : " ",
5305
+ /* @__PURE__ */ jsx(Text, { bold: true, dimColor: isCurrent ?? false, children: s2.id }),
5306
+ isCurrent ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: " (current)" }) : null,
5307
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5308
+ " ",
5309
+ date
5310
+ ] })
5311
+ ]
5312
+ }
5313
+ ),
5314
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5315
+ isSelected ? " " : " ",
5316
+ outcomeBadge,
5317
+ s2.tokenTotal.toLocaleString(),
5318
+ " tok",
5319
+ toolStr ? ` \xB7 ${toolStr}` : "",
5320
+ iterStr ? ` \xB7 ${iterStr}` : "",
5321
+ s2.toolErrorCount > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
5322
+ " \xB7 ",
5323
+ s2.toolErrorCount,
5324
+ " err"
5325
+ ] }) : null
5326
+ ] }),
5327
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5328
+ isSelected ? " " : " ",
5329
+ s2.title.length > 72 ? `${s2.title.slice(0, 71)}\u2026` : s2.title
5330
+ ] })
5331
+ ] }, s2.id);
5332
+ }),
5333
+ hint ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) : null
5334
+ ] });
4564
5335
  }
4565
- function formatMaxIterations(n) {
4566
- if (n === 0) return "unlimited";
4567
- return String(n);
5336
+ function statusIcon(status) {
5337
+ switch (status) {
5338
+ case "active":
5339
+ return "\u25CF";
5340
+ case "idle":
5341
+ return "\u25C9";
5342
+ case "closing":
5343
+ return "\u25D0";
5344
+ case "stale":
5345
+ return "\u25CB";
5346
+ default:
5347
+ return "?";
5348
+ }
4568
5349
  }
4569
- function formatEnhanceDelay(ms) {
4570
- return `${Math.round(ms / 1e3)}s`;
5350
+ function agentIcon(status) {
5351
+ switch (status) {
5352
+ case "running":
5353
+ return "\u25B6";
5354
+ case "streaming":
5355
+ return "\u21BB";
5356
+ case "waiting_user":
5357
+ return "\u23F3";
5358
+ case "error":
5359
+ return "\u2717";
5360
+ case "idle":
5361
+ return "\u25A0";
5362
+ default:
5363
+ return "?";
5364
+ }
4571
5365
  }
4572
- var MODE_DESC = {
4573
- off: "Agent stops after each turn (normal)",
4574
- suggest: "Shows next-step suggestions after each turn",
4575
- auto: "Self-driving \u2014 agent continues automatically"
4576
- };
4577
- var SETTINGS_FIELD_COUNT = 23;
5366
+ function fmtDuration2(startedAt) {
5367
+ const diff = Date.now() - new Date(startedAt).getTime();
5368
+ const min = Math.floor(diff / 6e4);
5369
+ if (min < 1) return "<1m";
5370
+ if (min < 60) return `${min}m`;
5371
+ const h = Math.floor(min / 60);
5372
+ if (h < 24) return `${h}h ${min % 60}m`;
5373
+ return `${Math.floor(h / 24)}d ${h % 24}h`;
5374
+ }
5375
+ function SessionsPanel({
5376
+ sessions,
5377
+ busy,
5378
+ selected,
5379
+ resumeConfirm,
5380
+ currentSessionId
5381
+ }) {
5382
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, flexShrink: 0, children: [
5383
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
5384
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u29C9 Sessions" }),
5385
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 F10 or Esc to close" }),
5386
+ busy && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 loading\u2026" })
5387
+ ] }),
5388
+ resumeConfirm ? /* @__PURE__ */ jsxs(Box, { marginY: 1, borderStyle: "single", borderColor: "yellow", paddingX: 1, children: [
5389
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", bold: true, children: [
5390
+ '\u26A0 Resume session "',
5391
+ resumeConfirm.sessionName,
5392
+ '"?'
5393
+ ] }),
5394
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "This will replace the current conversation. Press Enter to confirm, Esc to cancel." })
5395
+ ] }) : null,
5396
+ sessions.length === 0 ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: busy ? "Loading sessions..." : "No live sessions. Open another wstack instance to see it here." }) }) : sessions.map((s2, idx) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
5397
+ /* @__PURE__ */ jsxs(Box, { children: [
5398
+ /* @__PURE__ */ jsxs(Text, { inverse: idx === selected, color: s2.status === "active" ? "green" : s2.status === "idle" ? "cyan" : "yellow", children: [
5399
+ s2.sessionId === currentSessionId ? "\u25CF " : "",
5400
+ statusIcon(s2.status),
5401
+ " "
5402
+ ] }),
5403
+ /* @__PURE__ */ jsx(Text, { bold: true, children: s2.projectName }),
5404
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5405
+ " [",
5406
+ s2.projectSlug,
5407
+ "]"
5408
+ ] }),
5409
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5410
+ " \xB7 ",
5411
+ s2.sessionId.slice(0, 8)
5412
+ ] }),
5413
+ s2.gitBranch ? /* @__PURE__ */ jsxs(Text, { color: "magenta", children: [
5414
+ " \u2387 ",
5415
+ s2.gitBranch
5416
+ ] }) : null,
5417
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5418
+ " \xB7 ",
5419
+ fmtDuration2(s2.startedAt)
5420
+ ] }),
5421
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5422
+ " \xB7 PID ",
5423
+ s2.pid
5424
+ ] })
5425
+ ] }),
5426
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5427
+ " wd: ",
5428
+ s2.workingDir
5429
+ ] }),
5430
+ /* @__PURE__ */ jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [
5431
+ s2.agents.slice(0, 8).map((a) => /* @__PURE__ */ jsxs(Box, { children: [
5432
+ /* @__PURE__ */ jsxs(Text, { color: a.status === "running" ? "green" : a.status === "streaming" ? "cyan" : a.status === "error" ? "red" : a.status === "waiting_user" ? "yellow" : "grey", children: [
5433
+ agentIcon(a.status),
5434
+ " "
5435
+ ] }),
5436
+ /* @__PURE__ */ jsx(Text, { children: a.name }),
5437
+ a.currentTool ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5438
+ " [",
5439
+ a.currentTool,
5440
+ "]"
5441
+ ] }) : null,
5442
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5443
+ " \xB7 ",
5444
+ a.iterations,
5445
+ " iter \xB7 ",
5446
+ a.toolCalls,
5447
+ " tools"
5448
+ ] })
5449
+ ] }, a.id)),
5450
+ s2.agents.length > 8 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5451
+ " ... and ",
5452
+ s2.agents.length - 8,
5453
+ " more"
5454
+ ] }) : null
5455
+ ] })
5456
+ ] }, s2.sessionId)),
5457
+ sessions.length > 0 && /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
5458
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5459
+ sessions.length,
5460
+ " session",
5461
+ sessions.length === 1 ? "" : "s",
5462
+ " \xB7 \u2191\u2193 navigate \xB7 Enter to resume/switch \xB7 Esc close"
5463
+ ] }),
5464
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5465
+ "Tip: /sessions kill ",
5466
+ "<id>",
5467
+ " to stop a background session"
5468
+ ] })
5469
+ ] })
5470
+ ] });
5471
+ }
5472
+ var DELAY_PRESETS_MS = [0, 15e3, 3e4, 45e3, 6e4, 12e4];
5473
+ var SETTINGS_MODES = ["off", "suggest", "auto"];
5474
+ var LOG_LEVELS = ["error", "warn", "info", "debug", "trace"];
5475
+ var AUDIT_LEVELS = ["minimal", "standard", "full"];
5476
+ var COMPACTOR_STRATEGIES = ["hybrid", "intelligent", "selective"];
5477
+ var MAX_ITERATIONS_PRESETS = [100, 200, 500, 1e3, 0];
5478
+ var AUTO_PROCEED_MAX_PRESETS = [10, 25, 50, 100, 250, 0];
5479
+ var ENHANCE_DELAY_PRESETS = [3e4, 45e3, 6e4, 9e4, 12e4];
5480
+ var ENHANCE_LANGUAGES = ["original", "english"];
5481
+ function formatSettingsDelay(ms) {
5482
+ if (ms === 0) return "disabled";
5483
+ if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
5484
+ return `${Math.round(ms / 1e3)}s`;
5485
+ }
5486
+ function formatMaxIterations(n) {
5487
+ if (n === 0) return "unlimited";
5488
+ return String(n);
5489
+ }
5490
+ function formatEnhanceDelay(ms) {
5491
+ return `${Math.round(ms / 1e3)}s`;
5492
+ }
5493
+ var MODE_DESC = {
5494
+ off: "Agent stops after each turn (normal)",
5495
+ suggest: "Shows next-step suggestions after each turn",
5496
+ auto: "Self-driving \u2014 agent continues automatically"
5497
+ };
5498
+ var SETTINGS_FIELD_COUNT = 25;
4578
5499
  var CONFIG_SCOPES = ["global", "project"];
4579
5500
  function SettingsPicker({
4580
5501
  field,
@@ -4599,6 +5520,8 @@ function SettingsPicker({
4599
5520
  maxIterations,
4600
5521
  autoProceedMaxIterations,
4601
5522
  enhanceDelayMs,
5523
+ enhanceEnabled,
5524
+ enhanceLanguage,
4602
5525
  debugStream,
4603
5526
  configScope,
4604
5527
  hint
@@ -4722,6 +5645,16 @@ function SettingsPicker({
4722
5645
  value: formatEnhanceDelay(enhanceDelayMs),
4723
5646
  detail: "Timeout for prompt refinement preview (30s\u2013120s)"
4724
5647
  },
5648
+ {
5649
+ label: "Refine",
5650
+ value: boolVal(enhanceEnabled),
5651
+ detail: "Enable prompt refinement before sending"
5652
+ },
5653
+ {
5654
+ label: "Refine language",
5655
+ value: enhanceLanguage,
5656
+ detail: "original (keep language) | english (translate)"
5657
+ },
4725
5658
  // ── Debug ──
4726
5659
  { section: "Debug" },
4727
5660
  {
@@ -4883,6 +5816,54 @@ function windowRows(rows, focus, max) {
4883
5816
  }
4884
5817
  return { rows: rows.slice(start, end), start, end, contextHeader };
4885
5818
  }
5819
+ function hintsFor(ctx) {
5820
+ if (ctx.confirm) {
5821
+ return [
5822
+ { key: "y", label: "yes" },
5823
+ { key: "n", label: "no" },
5824
+ { key: "a", label: "always" },
5825
+ { key: "d", label: "deny" }
5826
+ ];
5827
+ }
5828
+ if (ctx.picker) {
5829
+ return [
5830
+ { key: "\u2191\u2193", label: "move" },
5831
+ { key: "\u21B5", label: "select" },
5832
+ { key: "Esc", label: "cancel" }
5833
+ ];
5834
+ }
5835
+ if (ctx.monitor) {
5836
+ const hints = [
5837
+ { key: "Esc", label: "close" },
5838
+ { key: "^F", label: "fleet" },
5839
+ { key: "^G", label: "agents" },
5840
+ { key: "^T", label: "worktrees" },
5841
+ { key: "F6", label: "todos" },
5842
+ { key: "F9", label: "goal" }
5843
+ ];
5844
+ if (ctx.nextPanelHint) {
5845
+ hints.push({ key: ctx.nextPanelHint.key, label: ctx.nextPanelHint.label, discovery: true });
5846
+ }
5847
+ return hints;
5848
+ }
5849
+ const base = [{ key: "?", label: "help" }];
5850
+ if (ctx.managed) base.push({ key: "PgUp/PgDn", label: "scroll" });
5851
+ base.push({ key: "^G", label: "agents" }, { key: "^C", label: "stop" });
5852
+ if (ctx.nextPanelHint) {
5853
+ base.push({ key: ctx.nextPanelHint.key, label: ctx.nextPanelHint.label, discovery: true });
5854
+ }
5855
+ return base;
5856
+ }
5857
+ function KeyHintBar({ context }) {
5858
+ const hints = hintsFor(context);
5859
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "row", paddingX: 1, children: hints.map((h, i) => (
5860
+ // biome-ignore lint/suspicious/noArrayIndexKey: hints are positional + stable
5861
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", marginRight: 2, children: [
5862
+ /* @__PURE__ */ jsx(Text, { color: h.discovery ? theme.monitor.agents : theme.accent, children: h.key }),
5863
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${h.label}` })
5864
+ ] }, i)
5865
+ )) });
5866
+ }
4886
5867
  function TodosMonitor({ todos }) {
4887
5868
  const { stdout } = useStdout();
4888
5869
  const done = todos.filter((t) => t.status === "completed").length;
@@ -5224,7 +6205,7 @@ async function loadIndex(root) {
5224
6205
  async function walk(root, rel, depth, out) {
5225
6206
  if (out.length >= MAX_FILES_INDEXED) return;
5226
6207
  if (depth > MAX_DEPTH) return;
5227
- const dir = rel ? path3.join(root, rel) : root;
6208
+ const dir = rel ? path4.join(root, rel) : root;
5228
6209
  let entries;
5229
6210
  try {
5230
6211
  entries = await fs2.readdir(dir, { withFileTypes: true });
@@ -5796,11 +6777,28 @@ function useBrainEvents(events, dispatch) {
5796
6777
  const offAnswered = events.on("brain.decision_answered", (payload) => addBrainEntry("answered", payload));
5797
6778
  const offAskHuman = events.on("brain.decision_ask_human", (payload) => addBrainEntry("ask_human", payload));
5798
6779
  const offDenied = events.on("brain.decision_denied", (payload) => addBrainEntry("denied", payload));
6780
+ const offIntervention = events.on("brain.intervention", (payload) => {
6781
+ const decision = payload.intervened ? `steered the agent (${payload.kind.replace(/_/g, " ")})` : "observed \u2014 no action needed";
6782
+ const rationale = payload.decision.type === "answer" ? payload.decision.rationale : void 0;
6783
+ dispatch({
6784
+ type: "addEntry",
6785
+ entry: {
6786
+ kind: "brain",
6787
+ status: "intervention",
6788
+ source: "monitor",
6789
+ risk: payload.request.risk,
6790
+ question: payload.request.question,
6791
+ decision,
6792
+ rationale
6793
+ }
6794
+ });
6795
+ });
5799
6796
  return () => {
5800
6797
  offRequested();
5801
6798
  offAnswered();
5802
6799
  offAskHuman();
5803
6800
  offDenied();
6801
+ offIntervention();
5804
6802
  };
5805
6803
  }, [events, dispatch]);
5806
6804
  }
@@ -5812,7 +6810,7 @@ function labelFor2(labelsRef, id, name) {
5812
6810
  const n = m.size + 1;
5813
6811
  const v = {
5814
6812
  label: name && name !== id ? name : `AGENT#${n}`,
5815
- color: expectDefined(STREAM_COLORS2[(n - 1) % STREAM_COLORS2.length])
6813
+ color: expectDefined$1(STREAM_COLORS2[(n - 1) % STREAM_COLORS2.length])
5816
6814
  };
5817
6815
  m.set(id, v);
5818
6816
  return v;
@@ -6081,6 +7079,10 @@ function useAutoPhaseEvents(subscribeAutoPhase, dispatch, stateRef) {
6081
7079
  if (!p.kept) dispatch({ type: "worktreeRemove", handleId: p.handleId });
6082
7080
  break;
6083
7081
  }
7082
+ case "countdown.tick": {
7083
+ dispatch({ type: "countdownTick", remainingSeconds: payload.remaining });
7084
+ break;
7085
+ }
6084
7086
  }
6085
7087
  };
6086
7088
  return subscribeAutoPhase(handler);
@@ -6341,6 +7343,52 @@ function buildSteeringPreamble(snapshot, newDirection) {
6341
7343
  lines.push("]");
6342
7344
  return lines.join("\n");
6343
7345
  }
7346
+ var MAX_TOOL_STREAM_RETAINED_CHARS = 1e5;
7347
+ var MAX_RETAINED_INPUT_CHARS = 2048;
7348
+ var MAX_RETAINED_INPUT_DEPTH = 4;
7349
+ var MAX_RETAINED_INPUT_ITEMS = 64;
7350
+ function pruneToolInput(value, depth = 0) {
7351
+ if (typeof value === "string") {
7352
+ return value.length > MAX_RETAINED_INPUT_CHARS ? `${value.slice(0, MAX_RETAINED_INPUT_CHARS)}\u2026 [truncated, ${value.length} chars \u2014 full payload in session log]` : value;
7353
+ }
7354
+ if (value === null || typeof value !== "object") return value;
7355
+ if (depth >= MAX_RETAINED_INPUT_DEPTH) return "[pruned: too deep]";
7356
+ if (Array.isArray(value)) {
7357
+ const head = value.slice(0, MAX_RETAINED_INPUT_ITEMS).map((v) => pruneToolInput(v, depth + 1));
7358
+ if (value.length > MAX_RETAINED_INPUT_ITEMS) {
7359
+ head.push(`[pruned: ${value.length - MAX_RETAINED_INPUT_ITEMS} more items]`);
7360
+ }
7361
+ return head;
7362
+ }
7363
+ const out = {};
7364
+ let n = 0;
7365
+ for (const [k, v] of Object.entries(value)) {
7366
+ if (n++ >= MAX_RETAINED_INPUT_ITEMS) {
7367
+ out["\u2026"] = "[pruned: more keys]";
7368
+ break;
7369
+ }
7370
+ out[k] = pruneToolInput(v, depth + 1);
7371
+ }
7372
+ return out;
7373
+ }
7374
+ function firstSelectable(items) {
7375
+ const idx = items.findIndex((it) => it.key !== "__divider__");
7376
+ return idx >= 0 ? idx : 0;
7377
+ }
7378
+ function skipDivider(items, idx, dir) {
7379
+ let i = idx;
7380
+ for (let steps = 0; steps < items.length; steps++) {
7381
+ const item = items[i];
7382
+ if (!item || item.key === "__divider__") {
7383
+ i += dir;
7384
+ if (i < 0) i = items.length - 1;
7385
+ if (i >= items.length) i = 0;
7386
+ continue;
7387
+ }
7388
+ return i;
7389
+ }
7390
+ return idx;
7391
+ }
6344
7392
  function reducer(state, action) {
6345
7393
  switch (action.type) {
6346
7394
  case "addEntry": {
@@ -6348,7 +7396,8 @@ function reducer(state, action) {
6348
7396
  if ((e.kind === "user" || e.kind === "assistant" || e.kind === "info" || e.kind === "warn" || e.kind === "error" || e.kind === "turn-summary") && !e.text?.trim()) {
6349
7397
  return state;
6350
7398
  }
6351
- const appended = [...state.entries, { ...action.entry, id: state.nextId }];
7399
+ const stored = e.kind === "tool" && e.input !== void 0 ? { ...e, input: pruneToolInput(e.input) } : e;
7400
+ const appended = [...state.entries, { ...stored, id: state.nextId }];
6352
7401
  return { ...state, entries: appended, nextId: state.nextId + 1 };
6353
7402
  }
6354
7403
  case "setBuffer":
@@ -6472,9 +7521,11 @@ function reducer(state, action) {
6472
7521
  case "toolStreamAppend": {
6473
7522
  const cur = state.toolStream;
6474
7523
  if (cur && cur.toolUseId === action.toolUseId) {
7524
+ const combined = cur.text + action.text;
7525
+ const text = combined.length > MAX_TOOL_STREAM_RETAINED_CHARS ? combined.slice(-MAX_TOOL_STREAM_RETAINED_CHARS) : combined;
6475
7526
  return {
6476
7527
  ...state,
6477
- toolStream: { ...cur, text: cur.text + action.text }
7528
+ toolStream: { ...cur, text }
6478
7529
  };
6479
7530
  }
6480
7531
  return {
@@ -6661,6 +7712,35 @@ function reducer(state, action) {
6661
7712
  ...state,
6662
7713
  autonomyPicker: { ...state.autonomyPicker, hint: action.text }
6663
7714
  };
7715
+ case "resumePickerOpen":
7716
+ return {
7717
+ ...state,
7718
+ resumePicker: { open: true, sessions: action.sessions, selected: 0, busy: false, hint: void 0, error: void 0 }
7719
+ };
7720
+ case "resumePickerClose":
7721
+ return {
7722
+ ...state,
7723
+ resumePicker: { open: false, sessions: [], selected: 0, busy: false, hint: void 0, error: void 0 }
7724
+ };
7725
+ case "resumePickerMove": {
7726
+ const nr = state.resumePicker.sessions.length;
7727
+ if (nr === 0) return state;
7728
+ const nextR = (state.resumePicker.selected + action.delta + nr) % nr;
7729
+ return { ...state, resumePicker: { ...state.resumePicker, selected: nextR } };
7730
+ }
7731
+ case "resumePickerBusy":
7732
+ return { ...state, resumePicker: { ...state.resumePicker, busy: action.on } };
7733
+ case "resumePickerHint":
7734
+ return { ...state, resumePicker: { ...state.resumePicker, hint: action.text } };
7735
+ case "resumePickerError":
7736
+ return { ...state, resumePicker: { ...state.resumePicker, error: action.text, busy: false } };
7737
+ case "replaceHistory": {
7738
+ const banners = state.entries.filter((e) => e.kind === "banner");
7739
+ const maxBannerId = banners.length > 0 ? Math.max(...banners.map((b) => b.id)) : 0;
7740
+ const shifted = action.entries.map((e, i) => ({ ...e, id: maxBannerId + 1 + i }));
7741
+ const nextId = maxBannerId + 1 + shifted.length;
7742
+ return { ...state, entries: [...banners, ...shifted], nextId, historyGen: state.historyGen + 1 };
7743
+ }
6664
7744
  case "settingsOpen":
6665
7745
  return {
6666
7746
  ...state,
@@ -6688,6 +7768,8 @@ function reducer(state, action) {
6688
7768
  maxIterations: action.maxIterations,
6689
7769
  autoProceedMaxIterations: action.autoProceedMaxIterations,
6690
7770
  enhanceDelayMs: action.enhanceDelayMs,
7771
+ enhanceEnabled: action.enhanceEnabled,
7772
+ enhanceLanguage: action.enhanceLanguage,
6691
7773
  debugStream: action.debugStream,
6692
7774
  configScope: action.configScope,
6693
7775
  hint: void 0
@@ -6716,13 +7798,13 @@ function reducer(state, action) {
6716
7798
  const i = SETTINGS_MODES.indexOf(sp.mode);
6717
7799
  const base = i < 0 ? 0 : i;
6718
7800
  const next = (base + action.delta + SETTINGS_MODES.length) % SETTINGS_MODES.length;
6719
- return { ...state, settingsPicker: { ...sp, mode: expectDefined(SETTINGS_MODES[next]), hint: void 0 } };
7801
+ return { ...state, settingsPicker: { ...sp, mode: expectDefined$1(SETTINGS_MODES[next]), hint: void 0 } };
6720
7802
  }
6721
7803
  if (f === 1) {
6722
7804
  const j = DELAY_PRESETS_MS.indexOf(sp.delayMs);
6723
7805
  const base = j < 0 ? 0 : j;
6724
7806
  const next = (base + action.delta + DELAY_PRESETS_MS.length) % DELAY_PRESETS_MS.length;
6725
- return { ...state, settingsPicker: { ...sp, delayMs: expectDefined(DELAY_PRESETS_MS[next]), hint: void 0 } };
7807
+ return { ...state, settingsPicker: { ...sp, delayMs: expectDefined$1(DELAY_PRESETS_MS[next]), hint: void 0 } };
6726
7808
  }
6727
7809
  if (f === 2) return { ...state, settingsPicker: { ...sp, titleAnimation: !sp.titleAnimation, hint: void 0 } };
6728
7810
  if (f === 3) return { ...state, settingsPicker: { ...sp, yolo: !sp.yolo, hint: void 0 } };
@@ -6740,50 +7822,99 @@ function reducer(state, action) {
6740
7822
  const i = COMPACTOR_STRATEGIES.indexOf(sp.contextStrategy);
6741
7823
  const base = i < 0 ? 0 : i;
6742
7824
  const next = (base + action.delta + COMPACTOR_STRATEGIES.length) % COMPACTOR_STRATEGIES.length;
6743
- return { ...state, settingsPicker: { ...sp, contextStrategy: expectDefined(COMPACTOR_STRATEGIES[next]), hint: void 0 } };
7825
+ return { ...state, settingsPicker: { ...sp, contextStrategy: expectDefined$1(COMPACTOR_STRATEGIES[next]), hint: void 0 } };
6744
7826
  }
6745
7827
  if (f === 15) {
6746
7828
  const i = LOG_LEVELS.indexOf(sp.logLevel);
6747
7829
  const base = i < 0 ? 0 : i;
6748
7830
  const next = (base + action.delta + LOG_LEVELS.length) % LOG_LEVELS.length;
6749
- return { ...state, settingsPicker: { ...sp, logLevel: expectDefined(LOG_LEVELS[next]), hint: void 0 } };
7831
+ return { ...state, settingsPicker: { ...sp, logLevel: expectDefined$1(LOG_LEVELS[next]), hint: void 0 } };
6750
7832
  }
6751
7833
  if (f === 16) {
6752
7834
  const i = AUDIT_LEVELS.indexOf(sp.auditLevel);
6753
7835
  const base = i < 0 ? 0 : i;
6754
7836
  const next = (base + action.delta + AUDIT_LEVELS.length) % AUDIT_LEVELS.length;
6755
- return { ...state, settingsPicker: { ...sp, auditLevel: expectDefined(AUDIT_LEVELS[next]), hint: void 0 } };
7837
+ return { ...state, settingsPicker: { ...sp, auditLevel: expectDefined$1(AUDIT_LEVELS[next]), hint: void 0 } };
6756
7838
  }
6757
7839
  if (f === 17) return { ...state, settingsPicker: { ...sp, indexOnStart: !sp.indexOnStart, hint: void 0 } };
6758
7840
  if (f === 18) {
6759
7841
  const j = MAX_ITERATIONS_PRESETS.indexOf(sp.maxIterations);
6760
7842
  const base = j < 0 ? 0 : j;
6761
7843
  const next = (base + action.delta + MAX_ITERATIONS_PRESETS.length) % MAX_ITERATIONS_PRESETS.length;
6762
- return { ...state, settingsPicker: { ...sp, maxIterations: expectDefined(MAX_ITERATIONS_PRESETS[next]), hint: void 0 } };
7844
+ return { ...state, settingsPicker: { ...sp, maxIterations: expectDefined$1(MAX_ITERATIONS_PRESETS[next]), hint: void 0 } };
6763
7845
  }
6764
7846
  if (f === 19) {
6765
7847
  const aj = AUTO_PROCEED_MAX_PRESETS.indexOf(sp.autoProceedMaxIterations);
6766
7848
  const abase = aj < 0 ? 0 : aj;
6767
7849
  const anext = (abase + action.delta + AUTO_PROCEED_MAX_PRESETS.length) % AUTO_PROCEED_MAX_PRESETS.length;
6768
- return { ...state, settingsPicker: { ...sp, autoProceedMaxIterations: expectDefined(AUTO_PROCEED_MAX_PRESETS[anext]), hint: void 0 } };
7850
+ return { ...state, settingsPicker: { ...sp, autoProceedMaxIterations: expectDefined$1(AUTO_PROCEED_MAX_PRESETS[anext]), hint: void 0 } };
6769
7851
  }
6770
7852
  if (f === 20) {
6771
7853
  const ej = ENHANCE_DELAY_PRESETS.indexOf(sp.enhanceDelayMs);
6772
7854
  const ebase = ej < 0 ? 0 : ej;
6773
7855
  const enext = (ebase + action.delta + ENHANCE_DELAY_PRESETS.length) % ENHANCE_DELAY_PRESETS.length;
6774
- return { ...state, settingsPicker: { ...sp, enhanceDelayMs: expectDefined(ENHANCE_DELAY_PRESETS[enext]), hint: void 0 } };
7856
+ return { ...state, settingsPicker: { ...sp, enhanceDelayMs: expectDefined$1(ENHANCE_DELAY_PRESETS[enext]), hint: void 0 } };
6775
7857
  }
6776
- if (f === 21) return { ...state, settingsPicker: { ...sp, debugStream: !sp.debugStream, hint: void 0 } };
7858
+ if (f === 21) return { ...state, settingsPicker: { ...sp, enhanceEnabled: !sp.enhanceEnabled, hint: void 0 } };
6777
7859
  if (f === 22) {
7860
+ const i = ENHANCE_LANGUAGES.indexOf(sp.enhanceLanguage);
7861
+ const base = i < 0 ? 0 : i;
7862
+ const next = (base + action.delta + ENHANCE_LANGUAGES.length) % ENHANCE_LANGUAGES.length;
7863
+ return { ...state, settingsPicker: { ...sp, enhanceLanguage: expectDefined$1(ENHANCE_LANGUAGES[next]), hint: void 0 } };
7864
+ }
7865
+ if (f === 23) return { ...state, settingsPicker: { ...sp, debugStream: !sp.debugStream, hint: void 0 } };
7866
+ if (f === 24) {
6778
7867
  const i = CONFIG_SCOPES.indexOf(sp.configScope);
6779
7868
  const base = i < 0 ? 0 : i;
6780
7869
  const next = (base + action.delta + CONFIG_SCOPES.length) % CONFIG_SCOPES.length;
6781
- return { ...state, settingsPicker: { ...sp, configScope: expectDefined(CONFIG_SCOPES[next]), hint: void 0 } };
7870
+ return { ...state, settingsPicker: { ...sp, configScope: expectDefined$1(CONFIG_SCOPES[next]), hint: void 0 } };
6782
7871
  }
6783
7872
  return state;
6784
7873
  }
6785
7874
  case "settingsHint":
6786
7875
  return { ...state, settingsPicker: { ...state.settingsPicker, hint: action.text } };
7876
+ case "projectPickerOpen":
7877
+ return {
7878
+ ...state,
7879
+ projectPicker: {
7880
+ open: true,
7881
+ allItems: action.items,
7882
+ items: action.items,
7883
+ selected: firstSelectable(action.items),
7884
+ filter: "",
7885
+ hint: void 0
7886
+ }
7887
+ };
7888
+ case "projectPickerClose":
7889
+ return {
7890
+ ...state,
7891
+ projectPicker: { open: false, allItems: [], items: [], selected: 0, filter: "", hint: void 0 }
7892
+ };
7893
+ case "projectPickerMove": {
7894
+ const cur = state.projectPicker;
7895
+ const list = cur.items;
7896
+ if (list.length === 0) return state;
7897
+ const nextRaw = (cur.selected + action.delta + list.length) % list.length;
7898
+ const next = skipDivider(list, nextRaw, action.delta > 0 ? 1 : -1);
7899
+ return { ...state, projectPicker: { ...cur, selected: next } };
7900
+ }
7901
+ case "projectPickerFilter": {
7902
+ const cur = state.projectPicker;
7903
+ const filtered = action.filter ? cur.allItems.filter(
7904
+ (item) => item.kind !== "project" || item.label.toLowerCase().includes(action.filter.toLowerCase()) || (item.subtitle ?? "").toLowerCase().includes(action.filter.toLowerCase())
7905
+ ) : cur.allItems;
7906
+ return {
7907
+ ...state,
7908
+ projectPicker: {
7909
+ ...cur,
7910
+ filter: action.filter,
7911
+ items: filtered,
7912
+ selected: firstSelectable(filtered)
7913
+ }
7914
+ };
7915
+ }
7916
+ case "projectPickerHint":
7917
+ return { ...state, projectPicker: { ...state.projectPicker, hint: action.text } };
6787
7918
  case "confirmOpen":
6788
7919
  return { ...state, confirmQueue: [...state.confirmQueue, action.info] };
6789
7920
  case "confirmClose":
@@ -7459,6 +8590,47 @@ function reducer(state, action) {
7459
8590
  if (state.debugStreamStats === null) return state;
7460
8591
  return { ...state, debugStreamStats: null };
7461
8592
  }
8593
+ case "toggleSessionsPanel": {
8594
+ return { ...state, sessionsPanelOpen: !state.sessionsPanelOpen };
8595
+ }
8596
+ case "sessionsPanelSet": {
8597
+ return {
8598
+ ...state,
8599
+ sessionsPanel: { sessions: action.sessions, busy: false, selected: action.sessions.length > 0 ? 0 : -1 }
8600
+ };
8601
+ }
8602
+ case "sessionsPanelMove": {
8603
+ const cur = state.sessionsPanel;
8604
+ if (cur.sessions.length === 0) return state;
8605
+ const next = (cur.selected + action.delta + cur.sessions.length) % cur.sessions.length;
8606
+ return { ...state, sessionsPanel: { ...cur, selected: next } };
8607
+ }
8608
+ case "sessionsPanelBusy": {
8609
+ return {
8610
+ ...state,
8611
+ sessionsPanel: { ...state.sessionsPanel, busy: action.on }
8612
+ };
8613
+ }
8614
+ case "sessionResumeConfirmSet": {
8615
+ return {
8616
+ ...state,
8617
+ sessionResumeConfirm: { sessionId: action.sessionId, sessionName: action.sessionName }
8618
+ };
8619
+ }
8620
+ case "sessionResumeConfirmClear": {
8621
+ return { ...state, sessionResumeConfirm: null };
8622
+ }
8623
+ // --- Auto-proceed countdown ---
8624
+ case "countdownTick": {
8625
+ if (action.remainingSeconds <= 0) {
8626
+ return state.countdown ? { ...state, countdown: null } : state;
8627
+ }
8628
+ return { ...state, countdown: { remainingSeconds: action.remainingSeconds } };
8629
+ }
8630
+ case "countdownEnded": {
8631
+ if (state.countdown === null) return state;
8632
+ return { ...state, countdown: null };
8633
+ }
7462
8634
  }
7463
8635
  }
7464
8636
  var INPUT_PROMPT = "\u203A ";
@@ -7467,7 +8639,7 @@ function selectedSlashCommandLine(picker) {
7467
8639
  const picked = picker.matches[picker.selected];
7468
8640
  return picked ? `/${picked.name}` : null;
7469
8641
  }
7470
- function rehydrateHistory(messages, startId) {
8642
+ function rehydrateHistory(messages, startId, toolCalls) {
7471
8643
  const entries = [];
7472
8644
  let nextId = startId;
7473
8645
  for (const msg of messages) {
@@ -7481,6 +8653,20 @@ function rehydrateHistory(messages, startId) {
7481
8653
  entries.push({ id: nextId++, kind: "assistant", text: trimmed });
7482
8654
  }
7483
8655
  }
8656
+ if (toolCalls && toolCalls.length > 0) {
8657
+ for (const tc of toolCalls) {
8658
+ entries.push({
8659
+ id: nextId++,
8660
+ kind: "tool",
8661
+ name: tc.name,
8662
+ durationMs: tc.durationMs,
8663
+ ok: tc.ok,
8664
+ outputBytes: tc.outputBytes,
8665
+ outputTokens: tc.outputTokens,
8666
+ outputLines: tc.outputLines
8667
+ });
8668
+ }
8669
+ }
7484
8670
  return entries;
7485
8671
  }
7486
8672
  var PASTE_THRESHOLD_CHARS = 200;
@@ -7495,9 +8681,11 @@ function App({
7495
8681
  model,
7496
8682
  banner = true,
7497
8683
  queueStore,
8684
+ onQueueChange,
7498
8685
  yolo = false,
7499
8686
  chime = false,
7500
8687
  confirmExit = true,
8688
+ mouse = false,
7501
8689
  enhanceEnabled = true,
7502
8690
  enhanceController,
7503
8691
  enhanceDelayMs = 15e3,
@@ -7519,12 +8707,17 @@ function App({
7519
8707
  getSettings,
7520
8708
  saveSettings,
7521
8709
  predictNext,
8710
+ onSuggestionsParsed,
8711
+ getSuggestions,
8712
+ setSuggestions,
7522
8713
  switchAutonomy,
7523
8714
  effectiveMaxContext,
7524
8715
  onExit,
7525
8716
  director,
7526
8717
  fleetRoster,
7527
8718
  onClearHistory,
8719
+ listSessions,
8720
+ onResumeSession,
7528
8721
  fleetStreamController,
7529
8722
  statuslineHiddenItems,
7530
8723
  setStatuslineHiddenItems,
@@ -7535,7 +8728,14 @@ function App({
7535
8728
  modeLabel,
7536
8729
  getModeLabel,
7537
8730
  registerDebugStreamCallback,
7538
- restoreDebugStreamCallback
8731
+ restoreDebugStreamCallback,
8732
+ restoredToolCalls,
8733
+ getProjectPickerItems,
8734
+ onProjectSelect,
8735
+ requestExit,
8736
+ getLiveSessions,
8737
+ onSwitchToSession,
8738
+ initialAgentsMonitorOpen
7539
8739
  }) {
7540
8740
  const { exit } = useApp();
7541
8741
  const { stdout } = useStdout();
@@ -7546,6 +8746,8 @@ function App({
7546
8746
  const [autonomyLive, setAutonomyLive] = useState(getAutonomy?.() ?? "off");
7547
8747
  const [liveModeLabel, setLiveModeLabel] = useState(modeLabel ?? "");
7548
8748
  const [hiddenItems, setHiddenItems] = useState(statuslineHiddenItems);
8749
+ const [sessionCount, setSessionCount] = useState(0);
8750
+ const prevBranchRef = useRef(null);
7549
8751
  const [indexState, setIndexState] = useState(() => getIndexState());
7550
8752
  useEffect(() => {
7551
8753
  setIndexState(getIndexState());
@@ -7596,7 +8798,8 @@ function App({
7596
8798
  return rehydrateHistory(
7597
8799
  visible,
7598
8800
  /* startId */
7599
- 1
8801
+ 1,
8802
+ restoredToolCalls
7600
8803
  );
7601
8804
  })();
7602
8805
  const initialNextId = 1 + restoredEntries.length;
@@ -7616,6 +8819,7 @@ function App({
7616
8819
  ] : [],
7617
8820
  ...restoredEntries
7618
8821
  ],
8822
+ historyGen: 0,
7619
8823
  buffer: "",
7620
8824
  cursor: 0,
7621
8825
  streamingText: "",
@@ -7645,7 +8849,9 @@ function App({
7645
8849
  searchQuery: ""
7646
8850
  },
7647
8851
  autonomyPicker: { open: false, options: [], selected: 0 },
7648
- settingsPicker: { open: false, field: 0, mode: "off", delayMs: 0, titleAnimation: true, yolo: false, streamFleet: true, chime: false, confirmExit: true, nextPrediction: false, featureMcp: true, featurePlugins: true, featureMemory: true, featureSkills: true, featureModelsRegistry: true, contextAutoCompact: true, contextStrategy: "hybrid", logLevel: "info", auditLevel: "standard", indexOnStart: true, maxIterations: 500, autoProceedMaxIterations: 50, enhanceDelayMs: 6e4, debugStream: false, configScope: "global" },
8852
+ resumePicker: { open: false, sessions: [], selected: 0, busy: false, hint: void 0, error: void 0 },
8853
+ settingsPicker: { open: false, field: 0, mode: "off", delayMs: 0, titleAnimation: true, yolo: false, streamFleet: true, chime: false, confirmExit: true, nextPrediction: false, featureMcp: true, featurePlugins: true, featureMemory: true, featureSkills: true, featureModelsRegistry: true, contextAutoCompact: true, contextStrategy: "hybrid", logLevel: "info", auditLevel: "standard", indexOnStart: true, maxIterations: 500, autoProceedMaxIterations: 50, enhanceDelayMs: 6e4, enhanceEnabled: true, enhanceLanguage: "original", debugStream: false, configScope: "global" },
8854
+ projectPicker: { open: false, allItems: [], items: [], selected: 0, filter: "", hint: void 0 },
7649
8855
  confirmQueue: [],
7650
8856
  enhance: null,
7651
8857
  enhanceEnabled,
@@ -7667,12 +8873,15 @@ function App({
7667
8873
  fleetConcurrency: 4,
7668
8874
  streamFleet: true,
7669
8875
  monitorOpen: false,
7670
- agentsMonitorOpen: false,
8876
+ agentsMonitorOpen: initialAgentsMonitorOpen ?? false,
7671
8877
  helpOpen: false,
7672
8878
  todosMonitorOpen: false,
7673
8879
  queuePanelOpen: false,
7674
8880
  processListOpen: false,
7675
8881
  goalPanelOpen: false,
8882
+ sessionsPanelOpen: false,
8883
+ sessionsPanel: { sessions: [], busy: false, selected: -1 },
8884
+ sessionResumeConfirm: null,
7676
8885
  collabSession: null,
7677
8886
  checkpoints: [],
7678
8887
  rewindOverlay: null,
@@ -7685,7 +8894,8 @@ function App({
7685
8894
  totalLines: 0,
7686
8895
  viewportRows: 0,
7687
8896
  pendingNewLines: 0,
7688
- debugStreamStats: null
8897
+ debugStreamStats: null,
8898
+ countdown: null
7689
8899
  });
7690
8900
  const builderRef = useRef(null);
7691
8901
  if (builderRef.current === null) {
@@ -7699,9 +8909,23 @@ function App({
7699
8909
  const lastEnterAtRef = useRef(0);
7700
8910
  const tokenPreviewsRef = useRef(/* @__PURE__ */ new Map());
7701
8911
  const projectName = React5.useMemo(() => {
7702
- const base = path3.basename(projectRoot);
7703
- return base && base !== path3.sep ? base : void 0;
8912
+ const base = path4.basename(projectRoot);
8913
+ return base && base !== path4.sep ? base : void 0;
7704
8914
  }, [projectRoot]);
8915
+ const [workingDirChip, setWorkingDirChip] = React5.useState(() => {
8916
+ const ctx = agent.ctx;
8917
+ if (ctx.workingDir && ctx.workingDir !== projectRoot) {
8918
+ return path4.relative(projectRoot, ctx.workingDir) || ".";
8919
+ }
8920
+ return void 0;
8921
+ });
8922
+ React5.useEffect(() => {
8923
+ const ctx = agent.ctx;
8924
+ return ctx.onWorkingDirChanged((newDir) => {
8925
+ const rel = path4.relative(projectRoot, newDir) || ".";
8926
+ setWorkingDirChip(rel === "." ? void 0 : rel);
8927
+ });
8928
+ }, [agent.ctx, projectRoot]);
7705
8929
  const chimeRef = useRef(chime);
7706
8930
  chimeRef.current = chime;
7707
8931
  const confirmExitRef = useRef(confirmExit);
@@ -7713,6 +8937,48 @@ function App({
7713
8937
  stateRef.current = state;
7714
8938
  const draftRef = useRef({ buffer: state.buffer, cursor: state.cursor });
7715
8939
  draftRef.current = { buffer: state.buffer, cursor: state.cursor };
8940
+ const [mouseMode, setMouseMode] = useState(mouse);
8941
+ const pickerOverlayOpen = state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || state.projectPicker.open || state.slashPicker.open || state.picker.open;
8942
+ const mouseTrackingOn = mouseMode || pickerOverlayOpen;
8943
+ const mouseWrittenRef = useRef(false);
8944
+ useEffect(() => {
8945
+ if (mouseWrittenRef.current === mouseTrackingOn) return;
8946
+ mouseWrittenRef.current = mouseTrackingOn;
8947
+ try {
8948
+ process.stdout.write(mouseTrackingOn ? MOUSE_CLICK_ON : MOUSE_OFF);
8949
+ } catch {
8950
+ }
8951
+ }, [mouseTrackingOn]);
8952
+ useEffect(
8953
+ () => () => {
8954
+ try {
8955
+ process.stdout.write(MOUSE_OFF);
8956
+ } catch {
8957
+ }
8958
+ },
8959
+ []
8960
+ );
8961
+ const bottomRegionRef = useRef(null);
8962
+ const statusBarWrapRef = useRef(null);
8963
+ const belowStatusBarRef = useRef(null);
8964
+ const [termRows, setTermRows] = useState(stdout?.rows ?? 24);
8965
+ useEffect(() => {
8966
+ const onResize = () => setTermRows(process.stdout.rows ?? 24);
8967
+ process.stdout.on("resize", onResize);
8968
+ return () => {
8969
+ process.stdout.off("resize", onResize);
8970
+ };
8971
+ }, []);
8972
+ useLayoutEffect(() => {
8973
+ if (!mouseMode) return;
8974
+ const node = bottomRegionRef.current;
8975
+ if (!node) return;
8976
+ const { height } = measureElement(node);
8977
+ const vp = Math.max(1, termRows - height);
8978
+ if (vp !== stateRef.current.viewportRows) {
8979
+ dispatch({ type: "setViewportRows", rows: vp });
8980
+ }
8981
+ });
7716
8982
  const handleKeyRef = useRef(null);
7717
8983
  const handleRewindTo = React5.useCallback(
7718
8984
  async (checkpointIndex) => {
@@ -7738,6 +9004,35 @@ function App({
7738
9004
  const t = setInterval(() => setNowTick(Date.now()), 1e4);
7739
9005
  return () => clearInterval(t);
7740
9006
  }, []);
9007
+ useEffect(() => {
9008
+ const approxChars = (v) => {
9009
+ try {
9010
+ return JSON.stringify(v)?.length ?? 0;
9011
+ } catch {
9012
+ return -1;
9013
+ }
9014
+ };
9015
+ return startHeapWatchdog({
9016
+ collectStats: () => ({
9017
+ historyEntries: stateRef.current.entries.length,
9018
+ historyChars: approxChars(stateRef.current.entries),
9019
+ messages: agent.ctx.state.messages.length,
9020
+ messagesChars: approxChars(agent.ctx.state.messages),
9021
+ runningTools: stateRef.current.runningTools.size,
9022
+ // Bytes queued in stdout's writable buffer. On Windows, TTY writes
9023
+ // are asynchronous — a render storm (e.g. high-frequency tool
9024
+ // progress dispatches) queues whole ANSI frames here as live heap
9025
+ // strings, invisible to every other counter.
9026
+ stdoutQueued: process.stdout.writableLength ?? 0
9027
+ }),
9028
+ onWarn: (level, message) => {
9029
+ dispatch({
9030
+ type: "addEntry",
9031
+ entry: { kind: level === "critical" ? "error" : "warn", text: message }
9032
+ });
9033
+ }
9034
+ });
9035
+ }, [agent.ctx]);
7741
9036
  useEffect(() => {
7742
9037
  if (state.goalPanelOpen) refreshGoalSummary();
7743
9038
  }, [state.goalPanelOpen, nowTick, refreshGoalSummary]);
@@ -7748,20 +9043,14 @@ function App({
7748
9043
  return () => clearInterval(t);
7749
9044
  }, [state.enhanceBusy]);
7750
9045
  const todosRef = useRef(JSON.stringify([]));
9046
+ const staleGuardRef = useRef(JSON.stringify({ a: "", y: false, m: "", model: "", provider: "" }));
7751
9047
  useEffect(() => {
7752
9048
  const poll = () => {
7753
- const snap = JSON.stringify(agent.ctx.todos.map((t2) => ({ s: t2.status })));
7754
- if (snap !== todosRef.current) {
7755
- todosRef.current = snap;
9049
+ const todoSnap = JSON.stringify(agent.ctx.todos.map((t2) => ({ s: t2.status })));
9050
+ if (todoSnap !== todosRef.current) {
9051
+ todosRef.current = todoSnap;
7756
9052
  setNowTick(Date.now());
7757
9053
  }
7758
- };
7759
- const t = setInterval(poll, 2e3);
7760
- return () => clearInterval(t);
7761
- }, [agent.ctx.todos]);
7762
- const staleGuardRef = useRef(JSON.stringify({ a: "", y: false, m: "", model: "", provider: "" }));
7763
- useEffect(() => {
7764
- const poll = () => {
7765
9054
  const a = getAutonomy?.() ?? "off";
7766
9055
  const y = getYolo?.() ?? false;
7767
9056
  const m = getModeLabel?.() ?? "";
@@ -7793,23 +9082,162 @@ function App({
7793
9082
  liveModel,
7794
9083
  liveProvider,
7795
9084
  agent.ctx.model,
7796
- agent.ctx.provider
9085
+ agent.ctx.provider,
9086
+ agent.ctx.todos
7797
9087
  ]);
7798
9088
  const [gitInfo, setGitInfo] = React5.useState(null);
7799
9089
  useEffect(() => {
7800
9090
  let cancelled = false;
7801
9091
  const refresh = () => {
7802
9092
  readGitInfo(agent.ctx.cwd).then((info) => {
7803
- if (!cancelled) setGitInfo(info);
7804
- }).catch(() => void 0);
9093
+ if (cancelled) return;
9094
+ setGitInfo(info);
9095
+ if (info && info.branch) {
9096
+ const prev = prevBranchRef.current;
9097
+ if (prev !== null && prev !== info.branch) {
9098
+ const msg = {
9099
+ role: "user",
9100
+ content: [{ type: "text", text: `[system] Git branch switched: \u2387 ${prev} \u2192 \u2387 ${info.branch}. The working tree is now on branch "${info.branch}". Any file changes from the previous branch are no longer visible.` }]
9101
+ };
9102
+ agent.ctx.messages.push(msg);
9103
+ try {
9104
+ import('@wrongstack/core').then(({ getSessionRegistry }) => {
9105
+ const reg = getSessionRegistry();
9106
+ if (reg) {
9107
+ reg.updateAgents([]).catch(() => {
9108
+ });
9109
+ }
9110
+ }).catch(() => {
9111
+ });
9112
+ } catch {
9113
+ }
9114
+ }
9115
+ prevBranchRef.current = info.branch;
9116
+ }
9117
+ }).catch(() => {
9118
+ if (!cancelled) setGitInfo(null);
9119
+ });
7805
9120
  };
7806
9121
  refresh();
9122
+ if (gitInfo?.branch && prevBranchRef.current === null) {
9123
+ prevBranchRef.current = gitInfo.branch;
9124
+ }
7807
9125
  const t = setInterval(refresh, 5e3);
7808
9126
  return () => {
7809
9127
  cancelled = true;
7810
9128
  clearInterval(t);
7811
9129
  };
7812
- }, [agent.ctx.cwd]);
9130
+ }, [agent.ctx.cwd, gitInfo?.branch]);
9131
+ useEffect(() => {
9132
+ if (!getLiveSessions) return;
9133
+ let cancelled = false;
9134
+ const poll = async () => {
9135
+ try {
9136
+ const sessions = await getLiveSessions();
9137
+ if (!cancelled) setSessionCount(sessions.length);
9138
+ } catch {
9139
+ }
9140
+ };
9141
+ void poll();
9142
+ const t = setInterval(poll, 3e4);
9143
+ if (t.unref) t.unref();
9144
+ return () => {
9145
+ cancelled = true;
9146
+ clearInterval(t);
9147
+ };
9148
+ }, [getLiveSessions]);
9149
+ const [mailboxStatus, setMailboxStatus] = useState({
9150
+ unread: 0,
9151
+ onlineAgents: 0
9152
+ });
9153
+ useEffect(() => {
9154
+ const seenAgents = /* @__PURE__ */ new Set();
9155
+ const unsub1 = events.onPattern("mailbox.unread_count", (_e, payload) => {
9156
+ const p = payload;
9157
+ setMailboxStatus((prev) => ({ ...prev, unread: p?.count ?? 0 }));
9158
+ });
9159
+ const unsub2 = events.onPattern("mailbox.received", (_e, payload) => {
9160
+ const p = payload;
9161
+ setMailboxStatus((prev) => ({
9162
+ ...prev,
9163
+ lastSubject: p?.subject ?? prev.lastSubject,
9164
+ lastFrom: p?.from ?? prev.lastFrom
9165
+ }));
9166
+ });
9167
+ const unsub3 = events.onPattern("mailbox.agent_registered", (_e, payload) => {
9168
+ const p = payload;
9169
+ if (p?.agentId) seenAgents.add(p.agentId);
9170
+ setMailboxStatus((prev) => ({ ...prev, onlineAgents: seenAgents.size }));
9171
+ });
9172
+ const unsub4 = events.onPattern("mailbox.agent_heartbeat", (_e, payload) => {
9173
+ const p = payload;
9174
+ if (p?.agentId) seenAgents.add(p.agentId);
9175
+ setMailboxStatus((prev) => ({ ...prev, onlineAgents: seenAgents.size }));
9176
+ });
9177
+ return () => {
9178
+ unsub1();
9179
+ unsub2();
9180
+ unsub3();
9181
+ unsub4();
9182
+ };
9183
+ }, [events]);
9184
+ const [mailboxPanelOpen, setMailboxPanelOpen] = useState(false);
9185
+ const [mailboxMessages, setMailboxMessages] = useState([]);
9186
+ const [mailboxAgents, setMailboxAgents] = useState([]);
9187
+ useEffect(() => {
9188
+ if (!mailboxPanelOpen) return;
9189
+ const poll = async () => {
9190
+ };
9191
+ void poll();
9192
+ const t = setInterval(poll, 1e4);
9193
+ return () => clearInterval(t);
9194
+ }, [mailboxPanelOpen]);
9195
+ useEffect(() => {
9196
+ const unsub = events.onPattern("mailbox.received", (_e, payload) => {
9197
+ const p = payload;
9198
+ if (!p?.messageId) return;
9199
+ setMailboxMessages((prev) => {
9200
+ if (prev.some((m) => m.id === p.messageId)) return prev;
9201
+ return [
9202
+ {
9203
+ id: p.messageId,
9204
+ from: p.from ?? "unknown",
9205
+ to: "*",
9206
+ type: p.type ?? "note",
9207
+ subject: p.subject ?? "",
9208
+ body: "",
9209
+ priority: "normal",
9210
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
9211
+ readByCount: 0,
9212
+ readByMe: false,
9213
+ completed: false
9214
+ },
9215
+ ...prev
9216
+ ].slice(0, 50);
9217
+ });
9218
+ });
9219
+ const unsub2 = events.onPattern("mailbox.agent_registered", (_e, payload) => {
9220
+ const p = payload;
9221
+ if (!p?.agentId) return;
9222
+ setMailboxAgents((prev) => {
9223
+ if (prev.some((a) => a.agentId === p.agentId)) return prev;
9224
+ return [...prev, {
9225
+ agentId: p.agentId,
9226
+ name: p.name ?? p.agentId,
9227
+ role: p.role,
9228
+ sessionId: p.sessionId ?? "?",
9229
+ status: "idle",
9230
+ lastSeenAt: (/* @__PURE__ */ new Date()).toISOString(),
9231
+ online: true,
9232
+ source: p.source
9233
+ }].slice(0, 30);
9234
+ });
9235
+ });
9236
+ return () => {
9237
+ unsub();
9238
+ unsub2();
9239
+ };
9240
+ }, [events]);
7813
9241
  const maxContext = activeMaxContext ?? agent.ctx.provider.capabilities.maxContext;
7814
9242
  const currentContextTokens = (tokenCounter?.currentRequestTokens()?.input ?? 0) + (tokenCounter?.currentRequestTokens()?.cacheRead ?? 0);
7815
9243
  const contextWindow = useMemo(() => {
@@ -7897,6 +9325,53 @@ function App({
7897
9325
  clearInterval(id);
7898
9326
  };
7899
9327
  }, [agent.ctx.meta]);
9328
+ const [taskCounts, setTaskCounts] = useState(null);
9329
+ useEffect(() => {
9330
+ const taskPath = agent.ctx.meta["task.path"];
9331
+ if (typeof taskPath !== "string" || !taskPath) return;
9332
+ let cancelled = false;
9333
+ const poll = async () => {
9334
+ try {
9335
+ const data = await fs2.readFile(taskPath, "utf8");
9336
+ const parsed = JSON.parse(data);
9337
+ if (cancelled) return;
9338
+ if (!Array.isArray(parsed.tasks)) {
9339
+ setTaskCounts(null);
9340
+ return;
9341
+ }
9342
+ let pending = 0, inProgress = 0, completed = 0, blocked = 0, failed = 0;
9343
+ for (const t of parsed.tasks) {
9344
+ switch (t?.status) {
9345
+ case "completed":
9346
+ completed++;
9347
+ break;
9348
+ case "in_progress":
9349
+ inProgress++;
9350
+ break;
9351
+ case "blocked":
9352
+ blocked++;
9353
+ break;
9354
+ case "failed":
9355
+ failed++;
9356
+ break;
9357
+ default:
9358
+ pending++;
9359
+ break;
9360
+ }
9361
+ }
9362
+ const total = pending + inProgress + completed + blocked + failed;
9363
+ setTaskCounts(total > 0 ? { pending, inProgress, completed, blocked, failed } : null);
9364
+ } catch {
9365
+ if (!cancelled) setTaskCounts(null);
9366
+ }
9367
+ };
9368
+ void poll();
9369
+ const id = setInterval(poll, 3e3);
9370
+ return () => {
9371
+ cancelled = true;
9372
+ clearInterval(id);
9373
+ };
9374
+ }, [agent.ctx.meta]);
7900
9375
  const prevAnyOverlayOpen = useRef(false);
7901
9376
  const prevEntriesCount = useRef(0);
7902
9377
  const prevToolStreamLen = useRef(0);
@@ -7907,7 +9382,7 @@ function App({
7907
9382
  }
7908
9383
  }, []);
7909
9384
  React5.useLayoutEffect(() => {
7910
- const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || state.enhanceBusy || state.enhance != null || state.escConfirm != null || state.confirmQueue.length > 0;
9385
+ const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.resumePicker.open || state.settingsPicker.open || state.enhanceBusy || state.enhance != null || state.escConfirm != null || state.confirmQueue.length > 0;
7911
9386
  const overlayClosed = prevAnyOverlayOpen.current && !anyOpenNow;
7912
9387
  const newEntryCommitted = state.entries.length > prevEntriesCount.current;
7913
9388
  const curToolStreamLen = state.toolStream?.text.length ?? 0;
@@ -7934,22 +9409,29 @@ function App({
7934
9409
  ]);
7935
9410
  const resizeGateRef = useRef(0);
7936
9411
  const preResizePanelsRef = useRef(null);
9412
+ const resizeRestoreTimerRef = useRef(null);
9413
+ const mountedRef = useRef(true);
7937
9414
  useEffect(() => {
7938
9415
  const handleResize = () => {
7939
9416
  const seq = ++resizeGateRef.current;
7940
9417
  preResizePanelsRef.current = {
7941
9418
  settings: stateRef.current.settingsPicker.open,
9419
+ projectPicker: stateRef.current.projectPicker.open,
7942
9420
  help: stateRef.current.helpOpen,
7943
9421
  monitor: stateRef.current.monitorOpen,
7944
9422
  agents: stateRef.current.agentsMonitorOpen,
7945
9423
  worktree: stateRef.current.worktreeMonitorOpen,
7946
9424
  todos: stateRef.current.todosMonitorOpen,
7947
9425
  queue: stateRef.current.queuePanelOpen,
7948
- processList: stateRef.current.processListOpen
9426
+ processList: stateRef.current.processListOpen,
9427
+ goalPanel: stateRef.current.goalPanelOpen,
9428
+ sessionsPanel: stateRef.current.sessionsPanelOpen
7949
9429
  };
7950
9430
  if (stateRef.current.settingsPicker.open) dispatch({ type: "settingsClose" });
9431
+ if (stateRef.current.projectPicker.open) dispatch({ type: "projectPickerClose" });
7951
9432
  if (stateRef.current.modelPicker.open) dispatch({ type: "modelPickerClose" });
7952
9433
  if (stateRef.current.autonomyPicker.open) dispatch({ type: "autonomyPickerClose" });
9434
+ if (stateRef.current.resumePicker.open) dispatch({ type: "resumePickerClose" });
7953
9435
  if (stateRef.current.slashPicker.open) dispatch({ type: "slashPickerClose" });
7954
9436
  if (stateRef.current.picker.open) dispatch({ type: "pickerClose" });
7955
9437
  if (stateRef.current.rewindOverlay) dispatch({ type: "rewindOverlayClose" });
@@ -7960,8 +9442,11 @@ function App({
7960
9442
  if (stateRef.current.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
7961
9443
  if (stateRef.current.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
7962
9444
  if (stateRef.current.processListOpen) dispatch({ type: "toggleProcessList" });
9445
+ if (stateRef.current.goalPanelOpen) dispatch({ type: "toggleGoalPanel" });
9446
+ if (stateRef.current.sessionsPanelOpen) dispatch({ type: "toggleSessionsPanel" });
7963
9447
  eraseLiveRegion();
7964
- setTimeout(() => {
9448
+ resizeRestoreTimerRef.current = setTimeout(() => {
9449
+ if (!mountedRef.current) return;
7965
9450
  if (resizeGateRef.current !== seq) return;
7966
9451
  const prev = preResizePanelsRef.current;
7967
9452
  if (!prev) return;
@@ -7990,10 +9475,16 @@ function App({
7990
9475
  maxIterations: sp.maxIterations,
7991
9476
  autoProceedMaxIterations: sp.autoProceedMaxIterations,
7992
9477
  enhanceDelayMs: sp.enhanceDelayMs,
9478
+ enhanceEnabled: sp.enhanceEnabled,
9479
+ enhanceLanguage: sp.enhanceLanguage,
7993
9480
  debugStream: sp.debugStream,
7994
9481
  configScope: sp.configScope
7995
9482
  });
7996
9483
  }
9484
+ if (prev.projectPicker) {
9485
+ const pp = stateRef.current.projectPicker;
9486
+ dispatch({ type: "projectPickerOpen", items: pp.allItems });
9487
+ }
7997
9488
  if (prev.help) dispatch({ type: "toggleHelp" });
7998
9489
  if (prev.monitor) dispatch({ type: "toggleMonitor" });
7999
9490
  if (prev.agents) dispatch({ type: "toggleAgentsMonitor" });
@@ -8001,11 +9492,19 @@ function App({
8001
9492
  if (prev.todos) dispatch({ type: "toggleTodosMonitor" });
8002
9493
  if (prev.queue) dispatch({ type: "toggleQueuePanel" });
8003
9494
  if (prev.processList) dispatch({ type: "toggleProcessList" });
9495
+ if (prev.goalPanel) dispatch({ type: "toggleGoalPanel" });
9496
+ if (prev.sessionsPanel) dispatch({ type: "toggleSessionsPanel" });
8004
9497
  preResizePanelsRef.current = null;
9498
+ resizeRestoreTimerRef.current = null;
8005
9499
  }, 300);
8006
9500
  };
8007
9501
  process.stdout.on("resize", handleResize);
8008
9502
  return () => {
9503
+ if (resizeRestoreTimerRef.current) {
9504
+ clearTimeout(resizeRestoreTimerRef.current);
9505
+ resizeRestoreTimerRef.current = null;
9506
+ }
9507
+ mountedRef.current = false;
8009
9508
  process.stdout.off("resize", handleResize);
8010
9509
  };
8011
9510
  }, [eraseLiveRegion]);
@@ -8106,7 +9605,7 @@ function App({
8106
9605
  dispatch({ type: "pickerClose" });
8107
9606
  return;
8108
9607
  }
8109
- const absPath = path3.isAbsolute(picked) ? picked : path3.join(projectRoot, picked);
9608
+ const absPath = path4.isAbsolute(picked) ? picked : path4.join(projectRoot, picked);
8110
9609
  try {
8111
9610
  const data = await fs2.readFile(absPath, "utf8");
8112
9611
  const token = await builder.registerFile({
@@ -8167,6 +9666,9 @@ function App({
8167
9666
  if (!queueStore) return;
8168
9667
  queueStore.write(state.queue.map(({ displayText, blocks }) => ({ displayText, blocks }))).catch(() => void 0);
8169
9668
  }, [state.queue, queueStore]);
9669
+ useEffect(() => {
9670
+ onQueueChange?.(state.queue.map((q) => q.displayText));
9671
+ }, [state.queue, onQueueChange]);
8170
9672
  useEffect(() => {
8171
9673
  const cmd = createQueueSlashCommand({
8172
9674
  getQueue: () => stateRef.current.queue,
@@ -8302,6 +9804,28 @@ function App({
8302
9804
  const providers = await getPickableProviders();
8303
9805
  dispatch({ type: "modelPickerOpen", providers });
8304
9806
  }, [getPickableProviders]);
9807
+ const openProjectPicker = React5.useCallback(async () => {
9808
+ if (!getProjectPickerItems) return;
9809
+ const items = await getProjectPickerItems();
9810
+ dispatch({ type: "projectPickerOpen", items });
9811
+ }, [getProjectPickerItems]);
9812
+ const loadLiveSessions = React5.useCallback(async () => {
9813
+ if (!getLiveSessions) return;
9814
+ dispatch({ type: "sessionsPanelBusy", on: true });
9815
+ try {
9816
+ const sessions = await getLiveSessions();
9817
+ dispatch({ type: "sessionsPanelSet", sessions });
9818
+ } catch {
9819
+ dispatch({ type: "sessionsPanelBusy", on: false });
9820
+ }
9821
+ }, [getLiveSessions]);
9822
+ useEffect(() => {
9823
+ if (!state.sessionsPanelOpen || !getLiveSessions) return void 0;
9824
+ const t = setInterval(() => {
9825
+ void loadLiveSessions();
9826
+ }, 5e3);
9827
+ return () => clearInterval(t);
9828
+ }, [state.sessionsPanelOpen, getLiveSessions, loadLiveSessions]);
8305
9829
  const openSettings = React5.useCallback(() => {
8306
9830
  if (!getSettings) return;
8307
9831
  const s2 = getSettings();
@@ -8328,38 +9852,87 @@ function App({
8328
9852
  maxIterations: s2.maxIterations ?? 500,
8329
9853
  autoProceedMaxIterations: s2.autoProceedMaxIterations ?? 50,
8330
9854
  enhanceDelayMs: s2.enhanceDelayMs ?? 6e4,
9855
+ enhanceEnabled: s2.enhanceEnabled ?? true,
9856
+ enhanceLanguage: s2.enhanceLanguage ?? "original",
8331
9857
  debugStream: s2.debugStream ?? false,
8332
9858
  configScope: s2.configScope ?? "global"
8333
9859
  });
8334
9860
  }, [getSettings]);
8335
- const [autoProceedCountdown, setAutoProceedCountdown] = useState(null);
8336
- const autoProceedTimerRef = useRef(void 0);
9861
+ const autoSubmitStreakRef = useRef(0);
9862
+ const autoSubmitCapWarnedRef = useRef(false);
9863
+ const [nextStepsRecheck, setNextStepsRecheck] = useState(0);
9864
+ useEffect(() => {
9865
+ autoSubmitStreakRef.current = 0;
9866
+ autoSubmitCapWarnedRef.current = false;
9867
+ }, [autonomyLive]);
8337
9868
  useEffect(() => {
8338
- if (autonomyLive !== "auto") {
8339
- clearInterval(autoProceedTimerRef.current);
8340
- autoProceedTimerRef.current = void 0;
8341
- setAutoProceedCountdown(null);
9869
+ if (state.status !== "idle" || autonomyLive !== "auto") {
9870
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
9871
+ nextStepsAutoSubmitTimerRef.current = void 0;
9872
+ setNextStepsAutoSubmitCountdown(null);
9873
+ nextStepsAutoSubmitSuggestionRef.current = null;
8342
9874
  return;
8343
9875
  }
9876
+ if (state.enhance != null || state.enhanceBusy) {
9877
+ return;
9878
+ }
9879
+ if (nextStepsAutoSubmitTimerRef.current != null) {
9880
+ return;
9881
+ }
9882
+ const suggestions = getSuggestions?.() ?? [];
9883
+ if (suggestions.length === 0) {
9884
+ const recheck = setTimeout(() => setNextStepsRecheck((t) => t + 1), 1500);
9885
+ return () => clearTimeout(recheck);
9886
+ }
8344
9887
  const cfg = getSettings?.();
9888
+ const maxAuto = cfg?.autoProceedMaxIterations ?? 50;
9889
+ if (maxAuto > 0 && autoSubmitStreakRef.current >= maxAuto) {
9890
+ if (!autoSubmitCapWarnedRef.current) {
9891
+ autoSubmitCapWarnedRef.current = true;
9892
+ dispatch({
9893
+ type: "addEntry",
9894
+ entry: {
9895
+ kind: "warn",
9896
+ text: `Auto-proceed paused after ${maxAuto} consecutive automatic turns \u2014 type anything to continue (autonomy stays on).`
9897
+ }
9898
+ });
9899
+ }
9900
+ return;
9901
+ }
8345
9902
  const delay = cfg?.delayMs ?? 45e3;
9903
+ const top = suggestions[0];
9904
+ if (!top) return;
9905
+ nextStepsAutoSubmitSuggestionRef.current = top;
8346
9906
  const start = Date.now();
8347
- setAutoProceedCountdown(Math.ceil(delay / 1e3));
8348
- autoProceedTimerRef.current = setInterval(() => {
9907
+ setNextStepsAutoSubmitCountdown(Math.ceil(delay / 1e3));
9908
+ nextStepsAutoSubmitTimerRef.current = setInterval(() => {
8349
9909
  const remaining = Math.max(0, Math.ceil((delay - (Date.now() - start)) / 1e3));
8350
9910
  if (remaining <= 0) {
8351
- clearInterval(autoProceedTimerRef.current);
8352
- autoProceedTimerRef.current = void 0;
8353
- setAutoProceedCountdown(null);
9911
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
9912
+ nextStepsAutoSubmitTimerRef.current = void 0;
9913
+ setNextStepsAutoSubmitCountdown(null);
9914
+ const suggestion = nextStepsAutoSubmitSuggestionRef.current;
9915
+ nextStepsAutoSubmitSuggestionRef.current = null;
9916
+ if (suggestion) {
9917
+ autoSubmitStreakRef.current += 1;
9918
+ setDraft(suggestion, suggestion.length);
9919
+ void (async () => {
9920
+ const trimmed = suggestion.trim();
9921
+ if (!trimmed) return;
9922
+ const blocks = [{ type: "text", text: trimmed }];
9923
+ dispatch({ type: "addEntry", entry: { kind: "user", text: trimmed } });
9924
+ await runBlocksRef.current(blocks);
9925
+ })();
9926
+ }
8354
9927
  } else {
8355
- setAutoProceedCountdown(remaining);
9928
+ setNextStepsAutoSubmitCountdown(remaining);
8356
9929
  }
8357
9930
  }, 500);
8358
9931
  return () => {
8359
- clearInterval(autoProceedTimerRef.current);
8360
- autoProceedTimerRef.current = void 0;
9932
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
9933
+ nextStepsAutoSubmitTimerRef.current = void 0;
8361
9934
  };
8362
- }, [autonomyLive, getSettings]);
9935
+ }, [state.status, autonomyLive, state.enhance, state.enhanceBusy, nextStepsRecheck, getSettings, getSuggestions, dispatch]);
8363
9936
  const settingsAutoSaveGateRef = useRef(true);
8364
9937
  useEffect(() => {
8365
9938
  if (state.settingsPicker.open) {
@@ -8396,6 +9969,8 @@ function App({
8396
9969
  maxIterations: sp.maxIterations,
8397
9970
  autoProceedMaxIterations: sp.autoProceedMaxIterations,
8398
9971
  enhanceDelayMs: sp.enhanceDelayMs,
9972
+ enhanceEnabled: sp.enhanceEnabled,
9973
+ enhanceLanguage: sp.enhanceLanguage,
8399
9974
  debugStream: sp.debugStream,
8400
9975
  configScope: sp.configScope
8401
9976
  })).then((err) => {
@@ -8458,6 +10033,21 @@ function App({
8458
10033
  slashRegistry.unregister("settings");
8459
10034
  };
8460
10035
  }, [slashRegistry, getSettings, saveSettings, openSettings]);
10036
+ useEffect(() => {
10037
+ const cmd = {
10038
+ name: "mailbox",
10039
+ aliases: ["inbox", "mail"],
10040
+ description: "Toggle the inter-agent mailbox panel \u2014 messages, read receipts, online agents.",
10041
+ async run() {
10042
+ setMailboxPanelOpen((prev) => !prev);
10043
+ return { message: void 0 };
10044
+ }
10045
+ };
10046
+ slashRegistry.register(cmd, "tui", { official: true });
10047
+ return () => {
10048
+ slashRegistry.unregister("mailbox");
10049
+ };
10050
+ }, [slashRegistry]);
8461
10051
  useEffect(() => {
8462
10052
  if (!switchAutonomy) return;
8463
10053
  const cmd = {
@@ -8474,6 +10064,34 @@ function App({
8474
10064
  slashRegistry.unregister("autonomy");
8475
10065
  };
8476
10066
  }, [slashRegistry, switchAutonomy]);
10067
+ useEffect(() => {
10068
+ const cmd = {
10069
+ name: "resume",
10070
+ aliases: ["load"],
10071
+ description: "Resume a previous session \u2014 pick from a list of recent sessions.",
10072
+ async run() {
10073
+ if (!listSessions) {
10074
+ return { message: "Session listing not available." };
10075
+ }
10076
+ try {
10077
+ const sessions = await listSessions(20);
10078
+ if (sessions.length === 0) {
10079
+ return { message: "No saved sessions." };
10080
+ }
10081
+ dispatch({ type: "resumePickerOpen", sessions });
10082
+ } catch (err) {
10083
+ return {
10084
+ message: err instanceof Error ? err.message : String(err)
10085
+ };
10086
+ }
10087
+ return { message: void 0 };
10088
+ }
10089
+ };
10090
+ slashRegistry.register(cmd, "tui", { official: true });
10091
+ return () => {
10092
+ slashRegistry.unregister("resume");
10093
+ };
10094
+ }, [slashRegistry, listSessions]);
8477
10095
  useEffect(() => {
8478
10096
  const FLUSH_MS2 = 100;
8479
10097
  const flush = () => {
@@ -8666,6 +10284,9 @@ function App({
8666
10284
  }, [state.enhanceEnabled]);
8667
10285
  const enhanceAbortRef = useRef(null);
8668
10286
  const [enhanceCountdown, setEnhanceCountdown] = useState(null);
10287
+ const [nextStepsAutoSubmitCountdown, setNextStepsAutoSubmitCountdown] = useState(null);
10288
+ const nextStepsAutoSubmitSuggestionRef = useRef(null);
10289
+ const nextStepsAutoSubmitTimerRef = useRef(void 0);
8669
10290
  useTuiEventBridge({
8670
10291
  events,
8671
10292
  dispatch,
@@ -8873,7 +10494,28 @@ function App({
8873
10494
  return;
8874
10495
  }
8875
10496
  }
8876
- const isEnter = key.return || input === "\r" || input === "\n";
10497
+ const overlaySelectable = state.modelPicker.open || state.autonomyPicker.open || state.resumePicker.open || state.settingsPicker.open || state.projectPicker.open || state.slashPicker.open || state.picker.open;
10498
+ const clickConfirm = overlaySelectable && key.mouse?.kind === "press" && key.mouse.button === "left";
10499
+ const clickCancel = overlaySelectable && key.mouse?.kind === "press" && key.mouse.button === "right";
10500
+ const isEnter = key.return || input === "\r" || input === "\n" || clickConfirm;
10501
+ if (clickCancel) {
10502
+ if (state.modelPicker.open) {
10503
+ dispatch(
10504
+ state.modelPicker.step === "model" ? { type: "modelPickerBack" } : { type: "modelPickerClose" }
10505
+ );
10506
+ } else if (state.autonomyPicker.open) {
10507
+ dispatch({ type: "autonomyPickerClose" });
10508
+ } else if (state.resumePicker.open) {
10509
+ dispatch({ type: "resumePickerClose" });
10510
+ } else if (state.settingsPicker.open) {
10511
+ dispatch({ type: "settingsClose" });
10512
+ } else if (state.slashPicker.open) {
10513
+ dispatch({ type: "slashPickerClose" });
10514
+ } else if (state.picker.open) {
10515
+ dispatch({ type: "pickerClose" });
10516
+ }
10517
+ return;
10518
+ }
8877
10519
  if (state.modelPicker.open) {
8878
10520
  if (key.escape) {
8879
10521
  if (state.modelPicker.step === "model") {
@@ -8883,6 +10525,10 @@ function App({
8883
10525
  }
8884
10526
  return;
8885
10527
  }
10528
+ if (key.mouse?.kind === "wheel") {
10529
+ dispatch({ type: "modelPickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10530
+ return;
10531
+ }
8886
10532
  if (key.upArrow) {
8887
10533
  dispatch({ type: "modelPickerMove", delta: -1 });
8888
10534
  return;
@@ -8948,6 +10594,10 @@ function App({
8948
10594
  dispatch({ type: "autonomyPickerClose" });
8949
10595
  return;
8950
10596
  }
10597
+ if (key.mouse?.kind === "wheel") {
10598
+ dispatch({ type: "autonomyPickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10599
+ return;
10600
+ }
8951
10601
  if (key.upArrow) {
8952
10602
  dispatch({ type: "autonomyPickerMove", delta: -1 });
8953
10603
  return;
@@ -8972,11 +10622,64 @@ function App({
8972
10622
  }
8973
10623
  return;
8974
10624
  }
10625
+ if (state.resumePicker.open) {
10626
+ if (key.escape) {
10627
+ dispatch({ type: "resumePickerClose" });
10628
+ return;
10629
+ }
10630
+ if (key.mouse?.kind === "wheel") {
10631
+ dispatch({ type: "resumePickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10632
+ return;
10633
+ }
10634
+ if (key.upArrow) {
10635
+ dispatch({ type: "resumePickerMove", delta: -1 });
10636
+ return;
10637
+ }
10638
+ if (key.downArrow) {
10639
+ dispatch({ type: "resumePickerMove", delta: 1 });
10640
+ return;
10641
+ }
10642
+ if (isEnter) {
10643
+ const now = Date.now();
10644
+ if (now - lastEnterAtRef.current < 50) return;
10645
+ lastEnterAtRef.current = now;
10646
+ const session = state.resumePicker.sessions[state.resumePicker.selected];
10647
+ if (!session || session.isCurrent) return;
10648
+ if (state.resumePicker.busy) return;
10649
+ dispatch({ type: "resumePickerBusy", on: true });
10650
+ onResumeSession?.(session.id).then((result) => {
10651
+ if (!result) {
10652
+ dispatch({ type: "resumePickerError", text: `Failed to resume session ${session.id}.` });
10653
+ return;
10654
+ }
10655
+ dispatch({ type: "replaceHistory", entries: result.entries, nextId: result.nextId });
10656
+ dispatch({ type: "resumePickerClose" });
10657
+ dispatch({
10658
+ type: "addEntry",
10659
+ entry: {
10660
+ kind: "info",
10661
+ text: `Resumed session ${result.sessionId} \u2014 ${result.entries.length} entries replayed.`
10662
+ }
10663
+ });
10664
+ }).catch((err) => {
10665
+ dispatch({
10666
+ type: "resumePickerError",
10667
+ text: err instanceof Error ? err.message : String(err)
10668
+ });
10669
+ });
10670
+ return;
10671
+ }
10672
+ return;
10673
+ }
8975
10674
  if (state.settingsPicker.open) {
8976
- if (key.escape || key.ctrl && input === "s" || key.fn === 5) {
10675
+ if (key.escape || key.ctrl && input === "s") {
8977
10676
  dispatch({ type: "settingsClose" });
8978
10677
  return;
8979
10678
  }
10679
+ if (key.mouse?.kind === "wheel") {
10680
+ dispatch({ type: "settingsFieldMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10681
+ return;
10682
+ }
8980
10683
  if (key.upArrow) {
8981
10684
  dispatch({ type: "settingsFieldMove", delta: -1 });
8982
10685
  return;
@@ -9002,11 +10705,166 @@ function App({
9002
10705
  }
9003
10706
  return;
9004
10707
  }
10708
+ if (state.projectPicker.open) {
10709
+ if (key.escape) {
10710
+ if (state.projectPicker.filter) {
10711
+ dispatch({ type: "projectPickerFilter", filter: "" });
10712
+ } else {
10713
+ dispatch({ type: "projectPickerClose" });
10714
+ }
10715
+ return;
10716
+ }
10717
+ if (key.mouse?.kind === "wheel") {
10718
+ dispatch({ type: "projectPickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10719
+ return;
10720
+ }
10721
+ if (key.upArrow) {
10722
+ dispatch({ type: "projectPickerMove", delta: -1 });
10723
+ return;
10724
+ }
10725
+ if (key.downArrow) {
10726
+ dispatch({ type: "projectPickerMove", delta: 1 });
10727
+ return;
10728
+ }
10729
+ if (isEnter) {
10730
+ const now = Date.now();
10731
+ if (now - lastEnterAtRef.current < 50) return;
10732
+ lastEnterAtRef.current = now;
10733
+ const items = state.projectPicker.items;
10734
+ const selected = state.projectPicker.selected;
10735
+ if (selected < 0 || selected >= items.length) return;
10736
+ const item = items[selected];
10737
+ if (!item || item.key === "__divider__" || item.key === "quit") {
10738
+ dispatch({ type: "projectPickerClose" });
10739
+ return;
10740
+ }
10741
+ if (item.kind === "project") {
10742
+ await onProjectSelect?.(item.key, item.kind);
10743
+ dispatch({ type: "projectPickerClose" });
10744
+ requestExit?.(42);
10745
+ return;
10746
+ }
10747
+ dispatch({ type: "projectPickerClose" });
10748
+ if (item.key === "new-session") {
10749
+ await onProjectSelect?.(item.key, item.kind);
10750
+ requestExit?.(42);
10751
+ } else if (item.key === "prev-sessions") {
10752
+ void submit("/resume");
10753
+ }
10754
+ return;
10755
+ }
10756
+ if (input && input.length === 1 && input.charCodeAt(0) >= 32 && input.charCodeAt(0) < 127) {
10757
+ dispatch({ type: "projectPickerFilter", filter: state.projectPicker.filter + input });
10758
+ return;
10759
+ }
10760
+ if (key.backspace) {
10761
+ if (state.projectPicker.filter.length > 0) {
10762
+ dispatch({
10763
+ type: "projectPickerFilter",
10764
+ filter: state.projectPicker.filter.slice(0, -1)
10765
+ });
10766
+ }
10767
+ return;
10768
+ }
10769
+ return;
10770
+ }
10771
+ if (state.sessionsPanelOpen) {
10772
+ if (key.escape) {
10773
+ if (state.sessionResumeConfirm) {
10774
+ dispatch({ type: "sessionResumeConfirmClear" });
10775
+ } else {
10776
+ dispatch({ type: "toggleSessionsPanel" });
10777
+ }
10778
+ return;
10779
+ }
10780
+ if (key.upArrow) {
10781
+ dispatch({ type: "sessionsPanelMove", delta: -1 });
10782
+ return;
10783
+ }
10784
+ if (key.downArrow) {
10785
+ dispatch({ type: "sessionsPanelMove", delta: 1 });
10786
+ return;
10787
+ }
10788
+ if (key.mouse?.kind === "wheel") {
10789
+ dispatch({ type: "sessionsPanelMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10790
+ return;
10791
+ }
10792
+ if (isEnter) {
10793
+ const now = Date.now();
10794
+ if (now - lastEnterAtRef.current < 50) return;
10795
+ lastEnterAtRef.current = now;
10796
+ if (state.sessionResumeConfirm) {
10797
+ const pending = state.sessionResumeConfirm;
10798
+ dispatch({ type: "sessionResumeConfirmClear" });
10799
+ dispatch({ type: "sessionsPanelBusy", on: true });
10800
+ onResumeSession?.(pending.sessionId).then((result) => {
10801
+ if (!result) {
10802
+ dispatch({ type: "sessionsPanelBusy", on: false });
10803
+ return;
10804
+ }
10805
+ dispatch({ type: "replaceHistory", entries: result.entries, nextId: result.nextId });
10806
+ dispatch({ type: "toggleSessionsPanel" });
10807
+ dispatch({
10808
+ type: "addEntry",
10809
+ entry: {
10810
+ kind: "info",
10811
+ text: `Resumed session ${result.sessionId} \u2014 ${result.entries.length} entries replayed.`
10812
+ }
10813
+ });
10814
+ }).catch(() => {
10815
+ dispatch({ type: "sessionsPanelBusy", on: false });
10816
+ });
10817
+ return;
10818
+ }
10819
+ const sessions = state.sessionsPanel.sessions;
10820
+ const sel = state.sessionsPanel.selected;
10821
+ if (sel < 0 || sel >= sessions.length) return;
10822
+ const session = sessions[sel];
10823
+ if (!session) return;
10824
+ const isCurrentProject = session.projectRoot === projectRoot;
10825
+ if (isCurrentProject) {
10826
+ if (session.pid === process.pid) {
10827
+ dispatch({
10828
+ type: "addEntry",
10829
+ entry: { kind: "info", text: "That is this session \u2014 nothing to resume." }
10830
+ });
10831
+ dispatch({ type: "toggleSessionsPanel" });
10832
+ return;
10833
+ }
10834
+ if (session.pid != null) {
10835
+ dispatch({
10836
+ type: "addEntry",
10837
+ entry: {
10838
+ kind: "warn",
10839
+ text: `Session is open in another running wstack (pid ${session.pid}) \u2014 a live session cannot be resumed here. Use /resume for previous sessions.`
10840
+ }
10841
+ });
10842
+ dispatch({ type: "toggleSessionsPanel" });
10843
+ return;
10844
+ }
10845
+ dispatch({
10846
+ type: "sessionResumeConfirmSet",
10847
+ sessionId: session.sessionId,
10848
+ sessionName: session.projectName
10849
+ });
10850
+ } else {
10851
+ onSwitchToSession?.(session.sessionId, session.projectRoot ?? "", session.projectName);
10852
+ dispatch({ type: "toggleSessionsPanel" });
10853
+ requestExit?.(42);
10854
+ }
10855
+ return;
10856
+ }
10857
+ return;
10858
+ }
9005
10859
  if (state.slashPicker.open) {
9006
10860
  if (key.escape) {
9007
10861
  dispatch({ type: "slashPickerClose" });
9008
10862
  return;
9009
10863
  }
10864
+ if (key.mouse?.kind === "wheel") {
10865
+ dispatch({ type: "slashPickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10866
+ return;
10867
+ }
9010
10868
  if (key.upArrow) {
9011
10869
  dispatch({ type: "slashPickerMove", delta: -1 });
9012
10870
  return;
@@ -9043,6 +10901,10 @@ function App({
9043
10901
  dispatch({ type: "pickerClose" });
9044
10902
  return;
9045
10903
  }
10904
+ if (key.mouse?.kind === "wheel") {
10905
+ dispatch({ type: "pickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10906
+ return;
10907
+ }
9046
10908
  if (key.upArrow) {
9047
10909
  dispatch({ type: "pickerMove", delta: -1 });
9048
10910
  return;
@@ -9169,6 +11031,24 @@ function App({
9169
11031
  if (state.helpOpen) dispatch({ type: "toggleHelp" });
9170
11032
  dispatch({ type: "toggleTodosMonitor" });
9171
11033
  };
11034
+ if (key.fn === 1) {
11035
+ if (state.projectPicker.open) {
11036
+ dispatch({ type: "projectPickerClose" });
11037
+ } else {
11038
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
11039
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
11040
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
11041
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
11042
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
11043
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
11044
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
11045
+ if (state.processListOpen) dispatch({ type: "toggleProcessList" });
11046
+ if (state.goalPanelOpen) dispatch({ type: "toggleGoalPanel" });
11047
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
11048
+ openProjectPicker();
11049
+ }
11050
+ return;
11051
+ }
9172
11052
  if (key.ctrl && input === "f" || key.fn === 2) {
9173
11053
  toggleFleetOverlay();
9174
11054
  return;
@@ -9181,47 +11061,6 @@ function App({
9181
11061
  toggleWorktreeOverlay();
9182
11062
  return;
9183
11063
  }
9184
- if (key.fn === 5) {
9185
- if (state.settingsPicker.open) {
9186
- dispatch({ type: "settingsClose" });
9187
- } else if (getSettings && saveSettings) {
9188
- if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
9189
- if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
9190
- if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
9191
- if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
9192
- if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
9193
- if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
9194
- if (state.helpOpen) dispatch({ type: "toggleHelp" });
9195
- const cfg = getSettings();
9196
- dispatch({
9197
- type: "settingsOpen",
9198
- mode: cfg.mode,
9199
- delayMs: cfg.delayMs,
9200
- titleAnimation: cfg.titleAnimation ?? true,
9201
- yolo: cfg.yolo ?? false,
9202
- streamFleet: cfg.streamFleet ?? true,
9203
- chime: cfg.chime ?? false,
9204
- confirmExit: cfg.confirmExit ?? true,
9205
- nextPrediction: cfg.nextPrediction ?? false,
9206
- featureMcp: cfg.featureMcp ?? true,
9207
- featurePlugins: cfg.featurePlugins ?? true,
9208
- featureMemory: cfg.featureMemory ?? true,
9209
- featureSkills: cfg.featureSkills ?? true,
9210
- featureModelsRegistry: cfg.featureModelsRegistry ?? true,
9211
- contextAutoCompact: cfg.contextAutoCompact ?? true,
9212
- contextStrategy: cfg.contextStrategy ?? "hybrid",
9213
- logLevel: cfg.logLevel ?? "info",
9214
- auditLevel: cfg.auditLevel ?? "standard",
9215
- indexOnStart: cfg.indexOnStart ?? true,
9216
- maxIterations: cfg.maxIterations ?? 500,
9217
- autoProceedMaxIterations: cfg.autoProceedMaxIterations ?? 50,
9218
- enhanceDelayMs: cfg.enhanceDelayMs ?? 6e4,
9219
- debugStream: cfg.debugStream ?? false,
9220
- configScope: cfg.configScope ?? "global"
9221
- });
9222
- }
9223
- return;
9224
- }
9225
11064
  if (key.fn === 6) {
9226
11065
  toggleTodosOverlay();
9227
11066
  return;
@@ -9274,6 +11113,25 @@ function App({
9274
11113
  }
9275
11114
  return;
9276
11115
  }
11116
+ if (key.fn === 10) {
11117
+ if (state.sessionsPanelOpen) {
11118
+ dispatch({ type: "toggleSessionsPanel" });
11119
+ } else {
11120
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
11121
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
11122
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
11123
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
11124
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
11125
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
11126
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
11127
+ if (state.processListOpen) dispatch({ type: "toggleProcessList" });
11128
+ if (state.goalPanelOpen) dispatch({ type: "toggleGoalPanel" });
11129
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
11130
+ dispatch({ type: "toggleSessionsPanel" });
11131
+ loadLiveSessions();
11132
+ }
11133
+ return;
11134
+ }
9277
11135
  if (key.ctrl && input === "s") {
9278
11136
  if (state.settingsPicker.open) {
9279
11137
  dispatch({ type: "settingsClose" });
@@ -9309,6 +11167,8 @@ function App({
9309
11167
  maxIterations: cfg.maxIterations ?? 500,
9310
11168
  autoProceedMaxIterations: cfg.autoProceedMaxIterations ?? 50,
9311
11169
  enhanceDelayMs: cfg.enhanceDelayMs ?? 6e4,
11170
+ enhanceEnabled: cfg.enhanceEnabled ?? true,
11171
+ enhanceLanguage: cfg.enhanceLanguage ?? "original",
9312
11172
  debugStream: cfg.debugStream ?? false,
9313
11173
  configScope: cfg.configScope ?? "global"
9314
11174
  });
@@ -9332,6 +11192,10 @@ function App({
9332
11192
  dispatch({ type: "settingsClose" });
9333
11193
  return;
9334
11194
  }
11195
+ if (state.projectPicker.open) {
11196
+ dispatch({ type: "projectPickerClose" });
11197
+ return;
11198
+ }
9335
11199
  if (state.queuePanelOpen) {
9336
11200
  dispatch({ type: "toggleQueuePanel" });
9337
11201
  return;
@@ -9344,11 +11208,15 @@ function App({
9344
11208
  dispatch({ type: "toggleGoalPanel" });
9345
11209
  return;
9346
11210
  }
11211
+ if (state.sessionsPanelOpen) {
11212
+ dispatch({ type: "toggleSessionsPanel" });
11213
+ return;
11214
+ }
9347
11215
  }
9348
11216
  if (state.processListOpen) {
9349
11217
  return;
9350
11218
  }
9351
- if (input === "?" && !key.ctrl && !key.meta && draftRef.current.buffer === "" && !state.slashPicker.open && !state.picker.open && !state.modelPicker.open && !state.autonomyPicker.open && !state.settingsPicker.open && !state.rewindOverlay && !state.monitorOpen && !state.agentsMonitorOpen && !state.worktreeMonitorOpen && !state.todosMonitorOpen && !state.autoPhase?.monitorOpen) {
11219
+ if (input === "?" && !key.ctrl && !key.meta && draftRef.current.buffer === "" && !state.slashPicker.open && !state.picker.open && !state.modelPicker.open && !state.autonomyPicker.open && !state.settingsPicker.open && !state.rewindOverlay && !state.monitorOpen && !state.agentsMonitorOpen && !state.worktreeMonitorOpen && !state.todosMonitorOpen && !state.goalPanelOpen && !state.sessionsPanelOpen && !state.autoPhase?.monitorOpen) {
9352
11220
  dispatch({ type: "toggleHelp" });
9353
11221
  return;
9354
11222
  }
@@ -9382,6 +11250,12 @@ function App({
9382
11250
  }
9383
11251
  if (cursor === 0) return;
9384
11252
  const next2 = buffer.slice(0, cursor - 1) + buffer.slice(cursor);
11253
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11254
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11255
+ nextStepsAutoSubmitTimerRef.current = void 0;
11256
+ setNextStepsAutoSubmitCountdown(null);
11257
+ nextStepsAutoSubmitSuggestionRef.current = null;
11258
+ }
9385
11259
  setDraft(next2, cursor - 1);
9386
11260
  return;
9387
11261
  }
@@ -9392,12 +11266,24 @@ function App({
9392
11266
  const nextWordStart = afterCursor.indexOf(" ");
9393
11267
  const end = nextWordStart === -1 ? buffer.length : cursor + nextWordStart + 1;
9394
11268
  const next3 = buffer.slice(0, cursor) + buffer.slice(end);
11269
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11270
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11271
+ nextStepsAutoSubmitTimerRef.current = void 0;
11272
+ setNextStepsAutoSubmitCountdown(null);
11273
+ nextStepsAutoSubmitSuggestionRef.current = null;
11274
+ }
9395
11275
  setDraft(next3, cursor);
9396
11276
  return;
9397
11277
  }
9398
11278
  if (cursor >= buffer.length) return;
9399
11279
  const span = tokenLengthForward(buffer, cursor) || 1;
9400
11280
  const next2 = buffer.slice(0, cursor) + buffer.slice(cursor + span);
11281
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11282
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11283
+ nextStepsAutoSubmitTimerRef.current = void 0;
11284
+ setNextStepsAutoSubmitCountdown(null);
11285
+ nextStepsAutoSubmitSuggestionRef.current = null;
11286
+ }
9401
11287
  setDraft(next2, cursor);
9402
11288
  return;
9403
11289
  }
@@ -9433,7 +11319,67 @@ function App({
9433
11319
  setDraft(buffer, buffer.length);
9434
11320
  return;
9435
11321
  }
9436
- const overlayOpen = state.monitorOpen || state.agentsMonitorOpen || state.worktreeMonitorOpen || state.todosMonitorOpen || state.queuePanelOpen || state.processListOpen || state.goalPanelOpen || state.helpOpen || (state.autoPhase?.monitorOpen ?? false) || state.rewindOverlay !== null;
11322
+ const overlayOpen = state.monitorOpen || state.agentsMonitorOpen || state.worktreeMonitorOpen || state.todosMonitorOpen || state.queuePanelOpen || state.processListOpen || state.goalPanelOpen || state.sessionsPanelOpen || state.helpOpen || (state.autoPhase?.monitorOpen ?? false) || state.rewindOverlay !== null;
11323
+ if (mouseMode && !overlayOpen) {
11324
+ if (key.mouse?.kind === "wheel") {
11325
+ if (key.mouse.shift) dispatch({ type: "scrollPage", dir: key.mouse.wheel > 0 ? "up" : "down" });
11326
+ else dispatch({ type: "scrollBy", delta: key.mouse.wheel > 0 ? 3 : -3 });
11327
+ return;
11328
+ }
11329
+ if ((key.mouse?.kind === "press" || key.mouse?.kind === "move") && key.mouse.button === "left") {
11330
+ const region = hitRegion(
11331
+ { termRows, termCols: stdout?.columns ?? 80, viewportRows: state.viewportRows },
11332
+ key.mouse.x,
11333
+ key.mouse.y
11334
+ );
11335
+ if (region?.kind === "scrollbar") {
11336
+ dispatch({
11337
+ type: "scrollTo",
11338
+ offset: scrollOffsetForTrackRow(state.viewportRows, state.totalLines, region.cell)
11339
+ });
11340
+ return;
11341
+ }
11342
+ }
11343
+ if (key.mouse?.kind === "press" && key.mouse.button === "left" && statusBarWrapRef.current) {
11344
+ const sbHeight = measureElement(statusBarWrapRef.current).height;
11345
+ const belowHeight = belowStatusBarRef.current ? measureElement(belowStatusBarRef.current).height : 0;
11346
+ const cols = stdout?.columns ?? 80;
11347
+ const mx = key.mouse.x;
11348
+ const my = key.mouse.y;
11349
+ const rowFor = (line) => statusBarLineRow({ termRows, statusBarHeight: sbHeight, belowHeight, headerRows: 1, line });
11350
+ const inSpan = (span) => mx >= span.start + 1 && mx <= span.start + span.len;
11351
+ if (cols >= COMPACT_THRESHOLD && my === rowFor(0)) {
11352
+ const span = statusBarModelSpan({
11353
+ version: appVersion,
11354
+ state: state.status,
11355
+ fleetRunning: fleetCounts?.running ?? 0,
11356
+ model: `${liveProvider}/${liveModel}`
11357
+ });
11358
+ if (inSpan(span)) {
11359
+ await openModelPicker();
11360
+ return;
11361
+ }
11362
+ }
11363
+ const autoSpan = statusBarAutonomySpan({ yolo: yoloLive, autonomy: autonomyLive });
11364
+ if (autoSpan && my === rowFor(1) && inSpan(autoSpan)) {
11365
+ dispatch({ type: "autonomyPickerOpen", options: AUTONOMY_OPTIONS });
11366
+ return;
11367
+ }
11368
+ const todosShown = !!todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0);
11369
+ if (todosShown && my === rowFor(2) && inSpan(statusBarTodosSpan())) {
11370
+ dispatch({ type: "toggleTodosMonitor" });
11371
+ return;
11372
+ }
11373
+ }
11374
+ if (key.pageUp) {
11375
+ dispatch({ type: "scrollPage", dir: "up" });
11376
+ return;
11377
+ }
11378
+ if (key.pageDown) {
11379
+ dispatch({ type: "scrollPage", dir: "down" });
11380
+ return;
11381
+ }
11382
+ }
9437
11383
  if (key.upArrow) {
9438
11384
  if (!overlayOpen && state.inputHistory.length > 0) {
9439
11385
  dispatch({ type: "historyUp" });
@@ -9465,6 +11411,12 @@ function App({
9465
11411
  return;
9466
11412
  }
9467
11413
  if (key.ctrl && input === "u") {
11414
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11415
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11416
+ nextStepsAutoSubmitTimerRef.current = void 0;
11417
+ setNextStepsAutoSubmitCountdown(null);
11418
+ nextStepsAutoSubmitSuggestionRef.current = null;
11419
+ }
9468
11420
  setDraft("", 0);
9469
11421
  return;
9470
11422
  }
@@ -9472,12 +11424,24 @@ function App({
9472
11424
  if (cursor >= buffer.length) return;
9473
11425
  const span = tokenLengthForward(buffer, cursor) || 1;
9474
11426
  const next2 = buffer.slice(0, cursor) + buffer.slice(cursor + span);
11427
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11428
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11429
+ nextStepsAutoSubmitTimerRef.current = void 0;
11430
+ setNextStepsAutoSubmitCountdown(null);
11431
+ nextStepsAutoSubmitSuggestionRef.current = null;
11432
+ }
9475
11433
  setDraft(next2, cursor);
9476
11434
  return;
9477
11435
  }
9478
11436
  if (key.ctrl && input === "k") {
9479
11437
  if (cursor >= buffer.length) return;
9480
11438
  const next2 = buffer.slice(0, cursor);
11439
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11440
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11441
+ nextStepsAutoSubmitTimerRef.current = void 0;
11442
+ setNextStepsAutoSubmitCountdown(null);
11443
+ nextStepsAutoSubmitSuggestionRef.current = null;
11444
+ }
9481
11445
  setDraft(next2, cursor);
9482
11446
  return;
9483
11447
  }
@@ -9495,6 +11459,12 @@ function App({
9495
11459
  await commitPaste(input);
9496
11460
  return;
9497
11461
  }
11462
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11463
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11464
+ nextStepsAutoSubmitTimerRef.current = void 0;
11465
+ setNextStepsAutoSubmitCountdown(null);
11466
+ nextStepsAutoSubmitSuggestionRef.current = null;
11467
+ }
9498
11468
  const next = buffer.slice(0, cursor) + input + buffer.slice(cursor);
9499
11469
  setDraft(next, cursor + input.length);
9500
11470
  };
@@ -9561,6 +11531,12 @@ function App({
9561
11531
  } catch {
9562
11532
  }
9563
11533
  }
11534
+ if (result.status === "done" && result.finalText && onSuggestionsParsed) {
11535
+ try {
11536
+ onSuggestionsParsed(result.finalText);
11537
+ } catch {
11538
+ }
11539
+ }
9564
11540
  if (tokenCounter && before) {
9565
11541
  const after = tokenCounter.total();
9566
11542
  const costAfter = tokenCounter.estimateCost().total;
@@ -9715,6 +11691,9 @@ function App({
9715
11691
  return;
9716
11692
  }
9717
11693
  dispatch({ type: "resetInterrupts" });
11694
+ autoSubmitStreakRef.current = 0;
11695
+ autoSubmitCapWarnedRef.current = false;
11696
+ dispatch({ type: "scrollToBottom" });
9718
11697
  const pushSubmittedHistory = () => {
9719
11698
  if (trimmed) dispatch({ type: "historyPush", text: trimmed });
9720
11699
  };
@@ -9755,6 +11734,25 @@ ${content}
9755
11734
  const m = res.metadata.autoPhaseInit;
9756
11735
  dispatch({ type: "autoPhaseInit", title: m.title });
9757
11736
  }
11737
+ const mouseToggle = res?.metadata?.mouseToggle;
11738
+ if (mouseToggle) {
11739
+ const nextVal = mouseToggle === "on" ? true : mouseToggle === "off" ? false : mouseToggle === "toggle" ? !mouseMode : mouseMode;
11740
+ if (mouseToggle !== "query" && nextVal !== mouseMode) {
11741
+ setMouseMode(nextVal);
11742
+ const cur = getSettings?.();
11743
+ if (cur && saveSettings) {
11744
+ Promise.resolve(saveSettings({ ...cur, mouseMode: nextVal })).catch(() => {
11745
+ });
11746
+ }
11747
+ }
11748
+ dispatch({
11749
+ type: "addEntry",
11750
+ entry: {
11751
+ kind: "info",
11752
+ text: nextVal ? "Mouse mode: ON \u2014 wheel scrolls the chat in-app, clickable UI active. (Native scrollback off; Shift+wheel = page.)" : "Mouse mode: OFF \u2014 terminal native scrollback restored."
11753
+ }
11754
+ });
11755
+ }
9758
11756
  const ctxModel = agent.ctx.model;
9759
11757
  if (ctxModel && ctxModel !== liveModel) setLiveModel(ctxModel);
9760
11758
  const ctxProviderId = agent.ctx.provider?.id;
@@ -9914,6 +11912,9 @@ User message:
9914
11912
  clearDraft();
9915
11913
  const blocks = await builder.submit();
9916
11914
  if (state.status !== "idle") {
11915
+ if (autonomyLive === "auto" && nextStepsAutoSubmitTimerRef.current != null) {
11916
+ switchAutonomy?.("off");
11917
+ }
9917
11918
  dispatch({
9918
11919
  type: "addEntry",
9919
11920
  entry: { kind: "user", text: displayText, queued: true, pasteContent }
@@ -9922,6 +11923,9 @@ User message:
9922
11923
  return;
9923
11924
  }
9924
11925
  dispatch({ type: "addEntry", entry: { kind: "user", text: displayText, pasteContent } });
11926
+ if (autonomyLive === "auto" && nextStepsAutoSubmitTimerRef.current != null) {
11927
+ switchAutonomy?.("off");
11928
+ }
9925
11929
  await runBlocks(blocks);
9926
11930
  };
9927
11931
  const bootInjectedRef = useRef(false);
@@ -9974,15 +11978,29 @@ User message:
9974
11978
  const inputHeight = Math.max(1, inputCellRows.length);
9975
11979
  const hideInput = enhanceActive || state.helpOpen || state.processListOpen;
9976
11980
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 0, children: [
9977
- /* @__PURE__ */ jsx(
11981
+ mouseMode ? /* @__PURE__ */ jsx(
11982
+ ScrollableHistory,
11983
+ {
11984
+ entries: state.entries,
11985
+ streamingText: state.streamingText,
11986
+ toolStream: state.toolStream,
11987
+ scrollOffset: state.scrollOffset,
11988
+ viewportRows: state.viewportRows,
11989
+ totalLines: state.totalLines,
11990
+ onMeasure: (totalLines) => dispatch({ type: "setMeasuredLines", totalLines }),
11991
+ setSuggestions
11992
+ }
11993
+ ) : /* @__PURE__ */ jsx(
9978
11994
  History,
9979
11995
  {
9980
11996
  entries: state.entries,
11997
+ generation: state.historyGen,
9981
11998
  streamingText: state.streamingText,
9982
- toolStream: state.toolStream
11999
+ toolStream: state.toolStream,
12000
+ setSuggestions
9983
12001
  }
9984
12002
  ),
9985
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [
12003
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, ref: bottomRegionRef, children: [
9986
12004
  /* @__PURE__ */ jsx(
9987
12005
  Input,
9988
12006
  {
@@ -10033,6 +12051,16 @@ User message:
10033
12051
  hint: state.autonomyPicker.hint
10034
12052
  }
10035
12053
  ) : null,
12054
+ state.resumePicker.open ? /* @__PURE__ */ jsx(
12055
+ ResumePicker,
12056
+ {
12057
+ sessions: state.resumePicker.sessions,
12058
+ selected: state.resumePicker.selected,
12059
+ busy: state.resumePicker.busy,
12060
+ error: state.resumePicker.error,
12061
+ hint: state.resumePicker.hint
12062
+ }
12063
+ ) : null,
10036
12064
  state.settingsPicker.open ? /* @__PURE__ */ jsx(
10037
12065
  SettingsPicker,
10038
12066
  {
@@ -10058,11 +12086,32 @@ User message:
10058
12086
  maxIterations: state.settingsPicker.maxIterations,
10059
12087
  autoProceedMaxIterations: state.settingsPicker.autoProceedMaxIterations,
10060
12088
  enhanceDelayMs: state.settingsPicker.enhanceDelayMs,
12089
+ enhanceEnabled: state.settingsPicker.enhanceEnabled,
12090
+ enhanceLanguage: state.settingsPicker.enhanceLanguage,
10061
12091
  debugStream: state.settingsPicker.debugStream,
10062
12092
  configScope: state.settingsPicker.configScope,
10063
12093
  hint: state.settingsPicker.hint
10064
12094
  }
10065
12095
  ) : null,
12096
+ state.projectPicker.open ? /* @__PURE__ */ jsx(
12097
+ ProjectPicker,
12098
+ {
12099
+ items: state.projectPicker.items,
12100
+ selected: state.projectPicker.selected,
12101
+ filter: state.projectPicker.filter,
12102
+ hint: state.projectPicker.hint
12103
+ }
12104
+ ) : null,
12105
+ state.sessionsPanelOpen ? /* @__PURE__ */ jsx(
12106
+ SessionsPanel,
12107
+ {
12108
+ sessions: state.sessionsPanel.sessions,
12109
+ busy: state.sessionsPanel.busy,
12110
+ selected: state.sessionsPanel.selected,
12111
+ resumeConfirm: state.sessionResumeConfirm ? { sessionName: state.sessionResumeConfirm.sessionName } : void 0,
12112
+ currentSessionId: agent.ctx.session?.id
12113
+ }
12114
+ ) : null,
10066
12115
  state.rewindOverlay ? (() => {
10067
12116
  const overlay = state.rewindOverlay;
10068
12117
  return /* @__PURE__ */ jsx(
@@ -10090,7 +12139,7 @@ User message:
10090
12139
  }
10091
12140
  ) }) : null,
10092
12141
  state.confirmQueue.length > 0 && (() => {
10093
- const head = expectDefined(state.confirmQueue[0]);
12142
+ const head = expectDefined$1(state.confirmQueue[0]);
10094
12143
  let resolved = false;
10095
12144
  const onDecision = (decision) => {
10096
12145
  if (resolved) return;
@@ -10175,7 +12224,7 @@ User message:
10175
12224
  }
10176
12225
  );
10177
12226
  })() : null,
10178
- /* @__PURE__ */ jsx(
12227
+ /* @__PURE__ */ jsx(Box, { ref: statusBarWrapRef, flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx(
10179
12228
  StatusBar,
10180
12229
  {
10181
12230
  model: `${liveProvider}/${liveModel}`,
@@ -10189,11 +12238,13 @@ User message:
10189
12238
  startedAt: startedAtRef.current,
10190
12239
  todos,
10191
12240
  plan: planCounts ?? void 0,
12241
+ tasks: taskCounts ?? void 0,
10192
12242
  fleet: fleetCounts,
10193
12243
  git: gitInfo,
10194
12244
  context: contextWindow,
10195
12245
  brain: state.brain,
10196
12246
  projectName,
12247
+ workingDir: workingDirChip,
10197
12248
  subagentCount: Object.keys(state.fleet).length,
10198
12249
  processCount: getProcessRegistry().activeCount,
10199
12250
  hiddenItems,
@@ -10203,67 +12254,101 @@ User message:
10203
12254
  modeLabel: liveModeLabel || void 0,
10204
12255
  debugStreamStats: state.debugStreamStats,
10205
12256
  enhanceCountdown,
10206
- autoProceedCountdown
12257
+ nextStepsAutoSubmitCountdown,
12258
+ autoProceedCountdown: state.countdown?.remainingSeconds ?? null,
12259
+ sessionCount,
12260
+ mailbox: mailboxStatus
10207
12261
  }
10208
- ),
10209
- state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, {}) : null,
10210
- state.agentsMonitorOpen ? /* @__PURE__ */ jsx(
10211
- AgentsMonitor,
10212
- {
10213
- entries: entriesWithLeader,
10214
- totalCost: state.fleetCost,
10215
- leaderCost: tokenCounter?.estimateCost().total ?? 0,
10216
- totalTokens: state.fleetTokens,
10217
- nowTick
10218
- }
10219
- ) : state.autoPhase?.monitorOpen ? /* @__PURE__ */ jsx(
10220
- PhaseMonitor,
10221
- {
10222
- phases: state.autoPhase.phases,
10223
- runningPhaseIds: state.autoPhase.runningPhaseIds,
10224
- elapsedMs: state.autoPhase.elapsedMs,
10225
- nowTick,
10226
- onClose: () => dispatch({ type: "autoPhaseMonitorToggle" })
10227
- }
10228
- ) : state.worktreeMonitorOpen ? /* @__PURE__ */ jsx(
10229
- WorktreeMonitor,
10230
- {
10231
- worktrees: state.worktrees,
10232
- baseBranch: state.worktreeBase,
10233
- nowTick,
10234
- onClose: () => dispatch({ type: "worktreeMonitorToggle" })
10235
- }
10236
- ) : state.todosMonitorOpen ? /* @__PURE__ */ jsx(TodosMonitor, { todos: agent.ctx.todos }) : state.monitorOpen ? /* @__PURE__ */ jsx(
10237
- FleetMonitor,
10238
- {
10239
- entries: state.fleet,
10240
- totalCost: state.fleetCost,
10241
- totalTokens: state.fleetTokens,
10242
- maxConcurrent: state.fleetConcurrency,
10243
- nowTick,
10244
- collabSession: state.collabSession
10245
- }
10246
- ) : director ? /* @__PURE__ */ jsx(
10247
- FleetPanel,
10248
- {
10249
- entries: entriesWithLeader,
10250
- totalCost: state.fleetCost,
10251
- roster: fleetRoster,
10252
- collabSession: state.collabSession
10253
- }
10254
- ) : null,
10255
- state.autoPhase && !state.autoPhase.monitorOpen ? /* @__PURE__ */ jsx(
10256
- PhasePanel,
12262
+ ) }),
12263
+ /* @__PURE__ */ jsx(
12264
+ MailboxPanel,
10257
12265
  {
10258
- phases: state.autoPhase.phases,
10259
- runningPhaseIds: state.autoPhase.runningPhaseIds,
10260
- nowTick
12266
+ messages: mailboxMessages,
12267
+ agents: mailboxAgents,
12268
+ unreadCount: mailboxStatus.unread,
12269
+ open: mailboxPanelOpen
10261
12270
  }
10262
- ) : null,
10263
- Object.keys(state.worktrees).length > 0 && !state.worktreeMonitorOpen && !state.monitorOpen ? /* @__PURE__ */ jsx(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null,
10264
- state.queuePanelOpen ? /* @__PURE__ */ jsx(QueuePanel, { items: state.queue }) : null,
10265
- state.processListOpen ? /* @__PURE__ */ jsx(ProcessListMonitor, {}) : null,
10266
- state.goalPanelOpen ? /* @__PURE__ */ jsx(GoalPanel, { goal: state.goalSummary }) : null
12271
+ ),
12272
+ /* @__PURE__ */ jsxs(Box, { ref: belowStatusBarRef, flexDirection: "column", flexShrink: 0, children: [
12273
+ state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, {}) : null,
12274
+ state.agentsMonitorOpen ? /* @__PURE__ */ jsx(
12275
+ AgentsMonitor,
12276
+ {
12277
+ entries: entriesWithLeader,
12278
+ totalCost: state.fleetCost,
12279
+ leaderCost: tokenCounter?.estimateCost().total ?? 0,
12280
+ totalTokens: state.fleetTokens,
12281
+ nowTick
12282
+ }
12283
+ ) : state.autoPhase?.monitorOpen ? /* @__PURE__ */ jsx(
12284
+ PhaseMonitor,
12285
+ {
12286
+ phases: state.autoPhase.phases,
12287
+ runningPhaseIds: state.autoPhase.runningPhaseIds,
12288
+ elapsedMs: state.autoPhase.elapsedMs,
12289
+ nowTick,
12290
+ onClose: () => dispatch({ type: "autoPhaseMonitorToggle" })
12291
+ }
12292
+ ) : state.worktreeMonitorOpen ? /* @__PURE__ */ jsx(
12293
+ WorktreeMonitor,
12294
+ {
12295
+ worktrees: state.worktrees,
12296
+ baseBranch: state.worktreeBase,
12297
+ nowTick,
12298
+ onClose: () => dispatch({ type: "worktreeMonitorToggle" })
12299
+ }
12300
+ ) : state.todosMonitorOpen ? /* @__PURE__ */ jsx(TodosMonitor, { todos: agent.ctx.todos }) : state.monitorOpen ? /* @__PURE__ */ jsx(
12301
+ FleetMonitor,
12302
+ {
12303
+ entries: state.fleet,
12304
+ totalCost: state.fleetCost,
12305
+ totalTokens: state.fleetTokens,
12306
+ maxConcurrent: state.fleetConcurrency,
12307
+ nowTick,
12308
+ collabSession: state.collabSession
12309
+ }
12310
+ ) : director ? /* @__PURE__ */ jsx(
12311
+ FleetPanel,
12312
+ {
12313
+ entries: entriesWithLeader,
12314
+ totalCost: state.fleetCost,
12315
+ roster: fleetRoster,
12316
+ collabSession: state.collabSession
12317
+ }
12318
+ ) : null,
12319
+ state.autoPhase && !state.autoPhase.monitorOpen ? /* @__PURE__ */ jsx(
12320
+ PhasePanel,
12321
+ {
12322
+ phases: state.autoPhase.phases,
12323
+ runningPhaseIds: state.autoPhase.runningPhaseIds,
12324
+ nowTick
12325
+ }
12326
+ ) : null,
12327
+ Object.keys(state.worktrees).length > 0 && !state.worktreeMonitorOpen && !state.monitorOpen ? /* @__PURE__ */ jsx(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null,
12328
+ state.queuePanelOpen ? /* @__PURE__ */ jsx(QueuePanel, { items: state.queue }) : null,
12329
+ state.processListOpen ? /* @__PURE__ */ jsx(ProcessListMonitor, {}) : null,
12330
+ state.goalPanelOpen ? /* @__PURE__ */ jsx(GoalPanel, { goal: state.goalSummary }) : null,
12331
+ (() => {
12332
+ const anyMonitorOpen = state.agentsMonitorOpen || (state.autoPhase?.monitorOpen ?? false) || state.worktreeMonitorOpen || state.todosMonitorOpen || state.monitorOpen || state.processListOpen || state.queuePanelOpen || state.goalPanelOpen;
12333
+ let nextPanelHint;
12334
+ if (state.agentsMonitorOpen) {
12335
+ nextPanelHint = { key: "F6", label: "todos" };
12336
+ } else if (state.autoPhase?.monitorOpen || state.worktreeMonitorOpen || state.todosMonitorOpen) {
12337
+ nextPanelHint = { key: "F9", label: "goal" };
12338
+ } else if (state.queuePanelOpen || state.processListOpen || state.goalPanelOpen) {
12339
+ nextPanelHint = { key: "F3", label: "agents" };
12340
+ } else if (anyMonitorOpen) {
12341
+ nextPanelHint = { key: "F3", label: "agents" };
12342
+ }
12343
+ const ctx = {
12344
+ monitor: anyMonitorOpen,
12345
+ managed: state.scrollOffset > 0,
12346
+ picker: state.settingsPicker.open || state.modelPicker.open || state.autonomyPicker.open,
12347
+ nextPanelHint
12348
+ };
12349
+ return /* @__PURE__ */ jsx(KeyHintBar, { context: ctx });
12350
+ })()
12351
+ ] })
10267
12352
  ] })
10268
12353
  ] }) });
10269
12354
  }
@@ -10365,6 +12450,45 @@ function startTerminalTitle(opts) {
10365
12450
  // src/run-tui.ts
10366
12451
  var BRACKETED_PASTE_ON = "\x1B[?2004h";
10367
12452
  var BRACKETED_PASTE_OFF = "\x1B[?2004l";
12453
+ var origConsoleLog = console.log;
12454
+ var origConsoleWarn = console.warn;
12455
+ var origConsoleError = console.error;
12456
+ var origConsoleDebug = console.debug;
12457
+ var origConsoleInfo = console.info;
12458
+ var origConsoleTable = console.table;
12459
+ var origConsoleTrace = console.trace;
12460
+ var origStderrWrite = process.stderr.write.bind(process.stderr);
12461
+ var consoleNoop = (..._args) => {
12462
+ };
12463
+ var stderrNoop = ((_chunk, _encodingOrCb, _cb) => {
12464
+ if (typeof _encodingOrCb === "function") _encodingOrCb();
12465
+ else if (typeof _cb === "function") _cb();
12466
+ return true;
12467
+ });
12468
+ var warningNoop = (_warning) => {
12469
+ };
12470
+ function silenceTerminal() {
12471
+ console.log = consoleNoop;
12472
+ console.warn = consoleNoop;
12473
+ console.error = consoleNoop;
12474
+ console.debug = consoleNoop;
12475
+ console.info = consoleNoop;
12476
+ console.table = consoleNoop;
12477
+ console.trace = consoleNoop;
12478
+ process.stderr.write = stderrNoop;
12479
+ process.on("warning", warningNoop);
12480
+ }
12481
+ function unsilenceTerminal() {
12482
+ console.log = origConsoleLog;
12483
+ console.warn = origConsoleWarn;
12484
+ console.error = origConsoleError;
12485
+ console.debug = origConsoleDebug;
12486
+ console.info = origConsoleInfo;
12487
+ console.table = origConsoleTable;
12488
+ console.trace = origConsoleTrace;
12489
+ process.stderr.write = origStderrWrite;
12490
+ process.off("warning", warningNoop);
12491
+ }
10368
12492
  async function runTui(opts) {
10369
12493
  const stdout = process.stdout;
10370
12494
  const stdin = process.stdin;
@@ -10374,14 +12498,16 @@ async function runTui(opts) {
10374
12498
  );
10375
12499
  return 2;
10376
12500
  }
12501
+ silenceTerminal();
10377
12502
  stdout.write(BRACKETED_PASTE_ON);
12503
+ const mouseEnabled = opts.mouse ?? opts.getSettings?.().mouseMode ?? process.env.WRONGSTACK_MOUSE === "1";
10378
12504
  stdout.write("\x1B[2J\x1B[H");
10379
12505
  const inkStdin = stdin;
10380
12506
  const stopTitle = opts.titleAnimation !== false ? startTerminalTitle({
10381
12507
  stdout,
10382
12508
  events: opts.events,
10383
12509
  model: opts.model,
10384
- appName: opts.projectRoot ? path3.basename(opts.projectRoot) : void 0
12510
+ appName: opts.projectRoot ? path4.basename(opts.projectRoot) : void 0
10385
12511
  }) : (() => {
10386
12512
  });
10387
12513
  const swallowSignals = ["SIGTSTP", "SIGQUIT", "SIGTTIN", "SIGTTOU"];
@@ -10397,12 +12523,14 @@ async function runTui(opts) {
10397
12523
  const cleanup = () => {
10398
12524
  if (cleaned) return;
10399
12525
  cleaned = true;
12526
+ unsilenceTerminal();
10400
12527
  try {
10401
12528
  stopTitle();
10402
12529
  } catch {
10403
12530
  }
10404
12531
  try {
10405
12532
  stdout.write(BRACKETED_PASTE_OFF);
12533
+ stdout.write(MOUSE_OFF);
10406
12534
  } catch {
10407
12535
  }
10408
12536
  };
@@ -10423,14 +12551,26 @@ async function runTui(opts) {
10423
12551
  };
10424
12552
  return new Promise((resolve) => {
10425
12553
  let exitCode = 0;
12554
+ let hardExitTimer = null;
10426
12555
  const onExit = (code) => {
10427
12556
  exitCode = code;
10428
12557
  };
10429
12558
  const settle = (code) => {
12559
+ if (hardExitTimer) {
12560
+ clearTimeout(hardExitTimer);
12561
+ hardExitTimer = null;
12562
+ }
10430
12563
  cleanup();
10431
12564
  detachListeners();
10432
12565
  resolve(code);
10433
12566
  };
12567
+ const requestExit = (code) => {
12568
+ onExit(code);
12569
+ instance?.unmount();
12570
+ hardExitTimer = setTimeout(() => process.exit(code), 5e3);
12571
+ hardExitTimer.unref();
12572
+ };
12573
+ opts.requestExit = requestExit;
10434
12574
  let instance;
10435
12575
  try {
10436
12576
  instance = render(
@@ -10445,6 +12585,7 @@ async function runTui(opts) {
10445
12585
  model: opts.model,
10446
12586
  banner: opts.banner ?? true,
10447
12587
  queueStore: opts.queueStore,
12588
+ onQueueChange: opts.onQueueChange,
10448
12589
  yolo: opts.yolo,
10449
12590
  getYolo: opts.getYolo,
10450
12591
  getAutonomy: opts.getAutonomy,
@@ -10480,12 +12621,24 @@ async function runTui(opts) {
10480
12621
  getSettings: opts.getSettings,
10481
12622
  saveSettings: opts.saveSettings,
10482
12623
  predictNext: opts.predictNext,
12624
+ onSuggestionsParsed: opts.onSuggestionsParsed,
10483
12625
  chime: opts.chime,
10484
12626
  confirmExit: opts.confirmExit,
12627
+ mouse: mouseEnabled,
10485
12628
  modeLabel: opts.modeLabel,
10486
12629
  getModeLabel: opts.getModeLabel,
10487
12630
  registerDebugStreamCallback: opts.registerDebugStreamCallback,
10488
- restoreDebugStreamCallback: opts.restoreDebugStreamCallback
12631
+ restoreDebugStreamCallback: opts.restoreDebugStreamCallback,
12632
+ restoredMessages: opts.restoredMessages,
12633
+ restoredToolCalls: opts.restoredToolCalls,
12634
+ listSessions: opts.listSessions,
12635
+ onResumeSession: opts.onResumeSession,
12636
+ getProjectPickerItems: opts.getProjectPickerItems,
12637
+ onProjectSelect: opts.onProjectSelect,
12638
+ requestExit: opts.requestExit,
12639
+ getLiveSessions: opts.getLiveSessions,
12640
+ onSwitchToSession: opts.onSwitchToSession,
12641
+ initialAgentsMonitorOpen: opts.initialAgentsMonitorOpen
10489
12642
  }),
10490
12643
  { exitOnCtrlC: false, stdin: inkStdin }
10491
12644
  );
@@ -10516,6 +12669,190 @@ async function runTui(opts) {
10516
12669
  });
10517
12670
  }
10518
12671
 
10519
- export { runTui };
12672
+ // src/components/history/replay.ts
12673
+ function replaySessionEvents(events, startId) {
12674
+ const entries = [];
12675
+ let nextId = startId;
12676
+ const pendingTools = /* @__PURE__ */ new Map();
12677
+ const completedTools = /* @__PURE__ */ new Set();
12678
+ for (const ev of events) {
12679
+ const entry = eventToEntry(ev, pendingTools, completedTools);
12680
+ if (entry) {
12681
+ entries.push({ ...entry, id: nextId++ });
12682
+ }
12683
+ }
12684
+ for (const [, tu] of pendingTools) {
12685
+ entries.push({
12686
+ id: nextId++,
12687
+ kind: "tool",
12688
+ name: tu.name,
12689
+ durationMs: 0,
12690
+ ok: false,
12691
+ input: tu.input
12692
+ });
12693
+ }
12694
+ return entries;
12695
+ }
12696
+ function eventToEntry(ev, pendingTools, completedTools) {
12697
+ switch (ev.type) {
12698
+ case "user_input": {
12699
+ const text = typeof ev.content === "string" ? ev.content : Array.isArray(ev.content) ? ev.content.filter((b) => b.type === "text").map((b) => b.text).join("") : "";
12700
+ if (!text.trim()) return null;
12701
+ return { kind: "user", text };
12702
+ }
12703
+ case "llm_response": {
12704
+ const text = ev.content.filter((b) => b.type === "text").map((b) => b.text).join("");
12705
+ if (!text.trim()) return null;
12706
+ return { kind: "assistant", text };
12707
+ }
12708
+ case "tool_use": {
12709
+ pendingTools.set(ev.id, { name: ev.name, input: ev.input, ts: ev.ts });
12710
+ return null;
12711
+ }
12712
+ case "tool_result": {
12713
+ if (completedTools.has(ev.id)) {
12714
+ completedTools.delete(ev.id);
12715
+ return null;
12716
+ }
12717
+ const tu = pendingTools.get(ev.id);
12718
+ pendingTools.delete(ev.id);
12719
+ return {
12720
+ kind: "tool",
12721
+ name: tu?.name ?? ev.id,
12722
+ durationMs: 0,
12723
+ // duration not available from tool_result alone
12724
+ ok: !ev.isError,
12725
+ input: tu?.input,
12726
+ output: typeof ev.content === "string" ? ev.content.slice(0, 400) : void 0
12727
+ };
12728
+ }
12729
+ case "tool_call_start": {
12730
+ pendingTools.set(ev.id, { name: ev.name, input: ev.input, ts: ev.ts });
12731
+ return null;
12732
+ }
12733
+ case "tool_call_end": {
12734
+ const tu = pendingTools.get(ev.id);
12735
+ pendingTools.delete(ev.id);
12736
+ completedTools.add(ev.id);
12737
+ return {
12738
+ kind: "tool",
12739
+ name: tu?.name ?? ev.name,
12740
+ durationMs: ev.durationMs,
12741
+ ok: ev.ok ?? false,
12742
+ input: tu?.input,
12743
+ outputBytes: ev.outputBytes,
12744
+ outputTokens: ev.outputTokens,
12745
+ outputLines: ev.outputLines
12746
+ };
12747
+ }
12748
+ case "compaction": {
12749
+ const before = (ev.before / 1e3).toFixed(0);
12750
+ const after = (ev.after / 1e3).toFixed(0);
12751
+ const level = ev.level ? ` (${ev.level})` : "";
12752
+ const reductions = ev.reductions && ev.reductions.length > 0 ? ` [${ev.reductions.map((r) => `${r.phase}: \u2212${r.saved}`).join(", ")}]` : "";
12753
+ return {
12754
+ kind: "info",
12755
+ text: `\u27F2 context compacted${level}: ${before}K \u2192 ${after}K tokens${reductions}`
12756
+ };
12757
+ }
12758
+ case "error": {
12759
+ return {
12760
+ kind: "error",
12761
+ text: ev.phase ? `[${ev.phase}] ${ev.message}` : ev.message
12762
+ };
12763
+ }
12764
+ case "provider_retry": {
12765
+ const secs = (ev.delayMs / 1e3).toFixed(ev.delayMs >= 1e3 ? 1 : 2);
12766
+ return {
12767
+ kind: "warn",
12768
+ text: ev.status ? `\u27F3 retry ${ev.attempt} (HTTP ${ev.status}) after ${secs}s \u2014 ${ev.description}` : `\u27F3 retry ${ev.attempt} after ${secs}s \u2014 ${ev.description}`
12769
+ };
12770
+ }
12771
+ case "provider_error": {
12772
+ return {
12773
+ kind: "error",
12774
+ text: ev.status ? `provider error (HTTP ${ev.status}, ${ev.retryable ? "retryable" : "fatal"}): ${ev.description}` : `provider error (${ev.retryable ? "retryable" : "fatal"}): ${ev.description}`
12775
+ };
12776
+ }
12777
+ case "checkpoint": {
12778
+ return {
12779
+ kind: "info",
12780
+ text: `\u2713 checkpoint #${ev.promptIndex}: "${ev.promptPreview.slice(0, 60)}"`
12781
+ };
12782
+ }
12783
+ case "agent_spawned": {
12784
+ return {
12785
+ kind: "subagent",
12786
+ agentLabel: ev.agentId.slice(0, 8),
12787
+ agentColor: "magenta",
12788
+ icon: "\u26A1",
12789
+ text: `spawned as ${ev.role}`
12790
+ };
12791
+ }
12792
+ case "agent_stopped": {
12793
+ return {
12794
+ kind: "subagent",
12795
+ agentLabel: ev.agentId.slice(0, 8),
12796
+ agentColor: "gray",
12797
+ icon: "\u2298",
12798
+ text: "stopped"
12799
+ };
12800
+ }
12801
+ case "agent_error": {
12802
+ return {
12803
+ kind: "subagent",
12804
+ agentLabel: ev.agentId.slice(0, 8),
12805
+ agentColor: "red",
12806
+ icon: "\u2717",
12807
+ text: `error: ${ev.error.slice(0, 80)}`
12808
+ };
12809
+ }
12810
+ case "mode_changed": {
12811
+ return {
12812
+ kind: "info",
12813
+ text: `mode: ${ev.from} \u2192 ${ev.to}`
12814
+ };
12815
+ }
12816
+ case "skill_activated": {
12817
+ return {
12818
+ kind: "info",
12819
+ text: `skill activated: ${ev.skillName}`
12820
+ };
12821
+ }
12822
+ case "skill_deactivated": {
12823
+ return {
12824
+ kind: "info",
12825
+ text: `skill deactivated: ${ev.skillName}`
12826
+ };
12827
+ }
12828
+ case "message_truncated": {
12829
+ return {
12830
+ kind: "warn",
12831
+ text: ev.after < ev.before ? `message truncated: ${ev.before} \u2192 ${ev.after} tokens` : `message truncated at ${ev.after} tokens`
12832
+ };
12833
+ }
12834
+ // Skipped — internal markers not relevant for display
12835
+ case "session_start":
12836
+ case "session_resumed":
12837
+ case "session_end":
12838
+ case "in_flight_start":
12839
+ case "in_flight_end":
12840
+ case "llm_request":
12841
+ case "tool_progress":
12842
+ case "rewound":
12843
+ case "file_snapshot":
12844
+ case "task_created":
12845
+ case "task_updated":
12846
+ case "task_completed":
12847
+ case "task_failed":
12848
+ case "spec_parsed":
12849
+ case "spec_analyzed":
12850
+ return null;
12851
+ default:
12852
+ return null;
12853
+ }
12854
+ }
12855
+
12856
+ export { parseInline, parseNextSteps, replaySessionEvents, runTui };
10520
12857
  //# sourceMappingURL=index.js.map
10521
12858
  //# sourceMappingURL=index.js.map