@wrongstack/tui 0.148.0 → 0.236.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 React5, { useState, useEffect, memo, useRef, useCallback, useReducer, useMemo } 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';
5
6
  import * as fs2 from 'fs/promises';
6
- import * as path2 from 'path';
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" }),
@@ -247,8 +332,15 @@ function StatusBar({
247
332
  projectName
248
333
  ] })
249
334
  ] }) : null,
335
+ workingDir && !hiddenSet.has("working_dir") ? /* @__PURE__ */ jsxs(Fragment, { children: [
336
+ yolo || startedAt != null || projectName || goalSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
337
+ /* @__PURE__ */ jsxs(Text, { color: "blue", children: [
338
+ "\u{1F4C2} ",
339
+ workingDir
340
+ ] })
341
+ ] }) : null,
250
342
  goalSummary ? /* @__PURE__ */ jsxs(Fragment, { children: [
251
- yolo || startedAt != null || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
343
+ yolo || startedAt != null || projectName || workingDir ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
252
344
  /* @__PURE__ */ jsxs(
253
345
  Text,
254
346
  {
@@ -268,11 +360,11 @@ function StatusBar({
268
360
  )
269
361
  ] }) : null,
270
362
  modeLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
271
- yolo || autonomy && autonomy !== "off" || eternalStage || startedAt != null || projectName || goalSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
363
+ yolo || autonomy && autonomy !== "off" || eternalStage || startedAt != null || projectName || workingDir || goalSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
272
364
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: modeIcon(modeLabel) })
273
365
  ] }) : null,
274
366
  hasAutoProceed ? /* @__PURE__ */ jsxs(Fragment, { children: [
275
- yolo || autonomy && autonomy !== "off" || eternalStage || startedAt != null || projectName || goalSummary || modeLabel ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
367
+ yolo || autonomy && autonomy !== "off" || eternalStage || startedAt != null || projectName || workingDir || goalSummary || modeLabel ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
276
368
  /* @__PURE__ */ jsxs(Text, { color: autoProceedCountdown != null && autoProceedCountdown <= 5 ? "yellow" : "cyan", children: [
277
369
  "\u23F3 auto in ",
278
370
  autoProceedCountdown,
@@ -280,16 +372,12 @@ function StatusBar({
280
372
  ] })
281
373
  ] }) : null,
282
374
  git ? /* @__PURE__ */ jsxs(Fragment, { children: [
283
- yolo || startedAt != null || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
375
+ yolo || startedAt != null || projectName || workingDir ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
284
376
  /* @__PURE__ */ jsxs(Text, { children: [
285
377
  /* @__PURE__ */ jsxs(Text, { color: "magenta", children: [
286
378
  "\u2387 ",
287
379
  git.branch
288
380
  ] }),
289
- git.added > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
290
- " +",
291
- git.added
292
- ] }) : null,
293
381
  git.deleted > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
294
382
  " -",
295
383
  git.deleted
@@ -299,6 +387,15 @@ function StatusBar({
299
387
  git.untracked
300
388
  ] }) : null
301
389
  ] })
390
+ ] }) : null,
391
+ sessionCount != null && sessionCount > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
392
+ yolo || startedAt != null || projectName || workingDir || git ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
393
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
394
+ "\u29C9 ",
395
+ sessionCount,
396
+ " session",
397
+ sessionCount === 1 ? "" : "s"
398
+ ] })
302
399
  ] }) : null
303
400
  ] }) : /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }),
304
401
  hasThirdLine ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
@@ -339,6 +436,36 @@ function StatusBar({
339
436
  ] }) : null
340
437
  ] })
341
438
  ] }) : null,
439
+ hasTaskActivity ? /* @__PURE__ */ jsxs(Fragment, { children: [
440
+ 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,
441
+ /* @__PURE__ */ jsxs(Text, { children: [
442
+ /* @__PURE__ */ jsx(Text, { color: "magenta", children: "\u26A1 " }),
443
+ tasks.inProgress > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
444
+ "\u231B",
445
+ tasks.inProgress
446
+ ] }) : null,
447
+ tasks.inProgress > 0 && (tasks.pending > 0 || tasks.blocked > 0) ? " " : "",
448
+ tasks.pending > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
449
+ "\u2610",
450
+ tasks.pending
451
+ ] }) : null,
452
+ tasks.pending > 0 && tasks.blocked > 0 ? " " : "",
453
+ tasks.blocked > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
454
+ "\u2298",
455
+ tasks.blocked
456
+ ] }) : null,
457
+ (tasks.pending > 0 || tasks.blocked > 0) && (tasks.completed > 0 || tasks.failed > 0) ? " " : "",
458
+ tasks.completed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
459
+ "\u2713",
460
+ tasks.completed
461
+ ] }) : null,
462
+ tasks.completed > 0 && tasks.failed > 0 ? " " : "",
463
+ tasks.failed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
464
+ "\u2717",
465
+ tasks.failed
466
+ ] }) : null
467
+ ] })
468
+ ] }) : null,
342
469
  fleetHasActivity ? /* @__PURE__ */ jsxs(Fragment, { children: [
343
470
  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
471
  fleet ? /* @__PURE__ */ jsxs(Text, { children: [
@@ -405,9 +532,64 @@ function StatusBar({
405
532
  enhanceCountdown,
406
533
  "s"
407
534
  ] })
535
+ ] }) : null,
536
+ hasNextStepsAutoSubmit && nextStepsAutoSubmitCountdown != null ? /* @__PURE__ */ jsxs(Fragment, { children: [
537
+ 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,
538
+ /* @__PURE__ */ jsxs(Text, { color: nextStepsAutoSubmitCountdown <= 3 ? "yellow" : "cyan", children: [
539
+ "\u23F3 next step in ",
540
+ nextStepsAutoSubmitCountdown,
541
+ "s"
542
+ ] })
408
543
  ] }) : null
409
544
  ] }) : /* @__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) => (
545
+ mailbox ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
546
+ mailbox.unread > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", bold: true, children: [
547
+ "\u2709 ",
548
+ mailbox.unread,
549
+ " new"
550
+ ] }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2709 0" }),
551
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
552
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
553
+ "\u{1F465} ",
554
+ mailbox.onlineAgents,
555
+ " online"
556
+ ] }),
557
+ mailbox.lastSubject ? /* @__PURE__ */ jsxs(Fragment, { children: [
558
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
559
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
560
+ mailbox.lastFrom ? `${mailbox.lastFrom}: ` : "",
561
+ mailbox.lastSubject.length > 40 ? `${mailbox.lastSubject.slice(0, 37)}\u2026` : mailbox.lastSubject
562
+ ] })
563
+ ] }) : null,
564
+ fleetAgents && fleetAgents.length > 0 ? fleetAgents.map((a, i) => (
565
+ // biome-ignore lint/suspicious/noArrayIndexKey: agent list is stable per render
566
+ /* @__PURE__ */ jsxs(Text, { children: [
567
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
568
+ " ",
569
+ /* @__PURE__ */ jsx(Text, { color: a.color, bold: true, children: a.label }),
570
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
571
+ /* @__PURE__ */ jsx(Text, { dimColor: !a.running, ...a.running ? { color: "yellow" } : {}, children: a.running ? "\u25B6" : "\xB7" }),
572
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
573
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtElapsed(a.elapsedMs) }),
574
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
575
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
576
+ a.toolCalls,
577
+ "t"
578
+ ] }),
579
+ a.tool ? /* @__PURE__ */ jsxs(Fragment, { children: [
580
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
581
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: a.tool })
582
+ ] }) : null,
583
+ a.extensions && a.extensions > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
584
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
585
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
586
+ "\u26A1\xD7",
587
+ a.extensions
588
+ ] })
589
+ ] }) : null
590
+ ] }, i)
591
+ )) : null
592
+ ] }) : fleetAgents && fleetAgents.length > 0 ? /* @__PURE__ */ jsx(Box, { flexDirection: "row", gap: 2, children: fleetAgents.map((a, i) => (
411
593
  // biome-ignore lint/suspicious/noArrayIndexKey: agent list is stable per render
412
594
  /* @__PURE__ */ jsxs(Text, { children: [
413
595
  /* @__PURE__ */ jsx(Text, { color: a.color, bold: true, children: a.label }),
@@ -513,6 +695,32 @@ function EternalStageChip({
513
695
  ] });
514
696
  }
515
697
  }
698
+ var SB_GAP = 2;
699
+ var SB_PADX = 1;
700
+ function statusBarModelSpan(opts) {
701
+ let col = SB_PADX;
702
+ if (opts.version) {
703
+ col += `WS v${opts.version}`.length + SB_GAP;
704
+ col += 1 + SB_GAP;
705
+ }
706
+ const { label } = stateChip(opts.state, opts.fleetRunning ?? 0);
707
+ col += 2 + label.length + SB_GAP;
708
+ col += 1 + SB_GAP;
709
+ return { start: col, len: opts.model.length };
710
+ }
711
+ function statusBarAutonomySpan(opts) {
712
+ if (!opts.autonomy || opts.autonomy === "off") return null;
713
+ let col = SB_PADX;
714
+ if (opts.yolo) {
715
+ col += "\u26A0 YOLO".length + SB_GAP;
716
+ col += 1 + SB_GAP;
717
+ }
718
+ return { start: col, len: 2 + opts.autonomy.toUpperCase().length };
719
+ }
720
+ function statusBarTodosSpan() {
721
+ const LABEL_MAX = 20;
722
+ return { start: SB_PADX, len: LABEL_MAX };
723
+ }
516
724
  function stateChip(state, fleetRunning) {
517
725
  if (state === "idle" && fleetRunning > 0) {
518
726
  return { label: `agents \u25B6${fleetRunning}`, color: "magenta" };
@@ -522,18 +730,30 @@ function stateChip(state, fleetRunning) {
522
730
  return { label: "thinking\u2026", color: "green" };
523
731
  }
524
732
  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"
733
+ "#f38ba8",
734
+ // red
735
+ "#eba0ac",
736
+ // maroon
737
+ "#fab387",
738
+ // peach
739
+ "#f9e2af",
740
+ // yellow
741
+ "#a6e3a1",
742
+ // green
743
+ "#94e2d5",
744
+ // teal
745
+ "#89dceb",
746
+ // sky
747
+ "#89b4fa",
748
+ // blue
749
+ "#b4befe",
750
+ // lavender
751
+ "#cba6f7",
752
+ // mauve
753
+ "#f5c2e7",
754
+ // pink
755
+ "#f2cdcd"
756
+ // flamingo
537
757
  ];
538
758
  function WaveText({ text, phase }) {
539
759
  return /* @__PURE__ */ jsx(Text, { bold: true, children: Array.from(text).map((ch, i) => (
@@ -854,12 +1074,12 @@ function ContextBar({
854
1074
  tokens,
855
1075
  maxTokens
856
1076
  }) {
857
- const clamped = Math.max(0, Math.min(2, pct));
1077
+ const clamped = Math.max(0, Math.min(1, pct));
858
1078
  const totalBars = 10;
859
1079
  const filled = Math.round(clamped * totalBars);
860
1080
  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)}%`;
1081
+ const color = clamped < 0.6 ? "green" : clamped < 0.75 ? "yellow" : "red";
1082
+ const pctText = `${Math.min(Math.round(pct * 100), 100)}%`;
863
1083
  const tokenText = tokens ? ` ${fmtTokens2(tokens)}/${fmtTokens2(maxTokens ?? 2e5)}` : "";
864
1084
  return /* @__PURE__ */ jsxs(Text, { color, children: [
865
1085
  "\u2588".repeat(filled),
@@ -1467,8 +1687,8 @@ function FilePicker({ query, matches, selected }) {
1467
1687
  ] }, m))
1468
1688
  ] });
1469
1689
  }
1470
- function highlight(path3, _query) {
1471
- return path3;
1690
+ function highlight(path6, _query) {
1691
+ return path6;
1472
1692
  }
1473
1693
  function FleetPanel({
1474
1694
  entries,
@@ -1535,29 +1755,103 @@ function FleetPanel({
1535
1755
  ] }) : null
1536
1756
  ] });
1537
1757
  }
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
- });
1758
+ function fmtTime(iso) {
1759
+ const d = new Date(iso);
1760
+ const now = Date.now();
1761
+ const diff = now - d.getTime();
1762
+ if (diff < 6e4) return "just now";
1763
+ if (diff < 36e5) return `${Math.round(diff / 6e4)}m ago`;
1764
+ if (diff < 864e5) return `${Math.round(diff / 36e5)}h ago`;
1765
+ return d.toLocaleDateString();
1766
+ }
1767
+ function fmtBody(body, maxLen) {
1768
+ const oneLine2 = body.replace(/\n/g, " ");
1769
+ return oneLine2.length > maxLen ? `${oneLine2.slice(0, maxLen - 1)}\u2026` : oneLine2;
1770
+ }
1771
+ var TYPE_ICONS = {
1772
+ note: "\u{1F4DD}",
1773
+ ask: "\u2753",
1774
+ assign: "\u{1F4CB}",
1775
+ steer: "\u{1F504}",
1776
+ btw: "\u{1F4AC}",
1777
+ broadcast: "\u{1F4E2}",
1778
+ status: "\u{1F7E2}",
1779
+ result: "\u2705"
1780
+ };
1781
+ function MailboxPanel({
1782
+ messages,
1783
+ agents,
1784
+ unreadCount,
1785
+ open
1786
+ }) {
1787
+ const { stdout } = useStdout();
1788
+ const [termWidth, setTermWidth] = useState(stdout?.columns ?? 90);
1789
+ useEffect(() => {
1790
+ const handleResize = () => setTermWidth(stdout?.columns ?? 90);
1791
+ handleResize();
1792
+ process.stdout.on("resize", handleResize);
1793
+ return () => {
1794
+ process.stdout.off("resize", handleResize);
1795
+ };
1796
+ }, [stdout]);
1797
+ if (!open) return null;
1798
+ const showMessages = messages.slice(0, 6);
1799
+ const showAgents = agents.slice(0, 8);
1800
+ const maxSubjectLen = Math.max(15, Math.min(30, termWidth - 55));
1801
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, flexShrink: 0, children: [
1802
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
1803
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u{1F4EC} Mailbox" }),
1804
+ unreadCount > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", bold: true, children: [
1805
+ unreadCount,
1806
+ " unread"
1807
+ ] }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "0 unread" }),
1808
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1809
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1810
+ agents.length,
1811
+ " agent",
1812
+ agents.length === 1 ? "" : "s",
1813
+ " online"
1814
+ ] })
1815
+ ] }),
1816
+ showMessages.length > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
1817
+ /* @__PURE__ */ jsx(Text, { bold: true, dimColor: true, children: "Messages" }),
1818
+ showMessages.map((m) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1819
+ /* @__PURE__ */ jsx(Text, { children: TYPE_ICONS[m.type] ?? "\u{1F4E8}" }),
1820
+ /* @__PURE__ */ jsx(Text, { color: m.readByMe ? void 0 : "yellow", bold: !m.readByMe, children: m.from }),
1821
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: m.subject.length > maxSubjectLen ? `${m.subject.slice(0, maxSubjectLen - 1)}\u2026` : m.subject }),
1822
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtBody(m.body, 40) }),
1823
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtTime(m.timestamp) }),
1824
+ m.readByCount > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1825
+ "\u{1F441} ",
1826
+ m.readByCount
1827
+ ] }) : /* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: "\u2709 new" }),
1828
+ m.completed ? /* @__PURE__ */ jsx(Text, { color: "green", children: "\u2713" }) : null
1829
+ ] }, m.id))
1830
+ ] }) : /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No messages yet." }) }),
1831
+ showAgents.length > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
1832
+ /* @__PURE__ */ jsx(Text, { bold: true, dimColor: true, children: "Online agents" }),
1833
+ showAgents.map((a) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1834
+ /* @__PURE__ */ jsx(Text, { color: a.online ? "green" : "dim", children: a.online ? "\u25CF" : "\u25CB" }),
1835
+ /* @__PURE__ */ jsx(Text, { children: a.name }),
1836
+ a.role ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1837
+ "(",
1838
+ a.role,
1839
+ ")"
1840
+ ] }) : null,
1841
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: a.status }),
1842
+ a.currentTool ? /* @__PURE__ */ jsx(Text, { color: "cyan", children: a.currentTool }) : null,
1843
+ a.currentTask ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: a.currentTask.length > 25 ? `${a.currentTask.slice(0, 24)}\u2026` : a.currentTask }) : null,
1844
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtTime(a.lastSeenAt) }),
1845
+ a.source ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1846
+ "[",
1847
+ a.source,
1848
+ "]"
1849
+ ] }) : null
1850
+ ] }, a.agentId))
1851
+ ] }) : null,
1852
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "/mailbox \u2014 Esc to close" }) })
1853
+ ] });
1854
+ }
1561
1855
  function helpSections() {
1562
1856
  const nav = [];
1563
1857
  nav.push(
@@ -1569,12 +1863,16 @@ function helpSections() {
1569
1863
  {
1570
1864
  title: "Monitors",
1571
1865
  entries: [
1866
+ { keys: "F1", desc: "project switcher (also /project)" },
1572
1867
  { keys: "Ctrl+F / F2", desc: "fleet orchestration monitor" },
1573
1868
  { keys: "Ctrl+G / F3", desc: "agents live monitor" },
1574
1869
  { keys: "Ctrl+T / F4", desc: "worktree monitor" },
1575
1870
  { keys: "F5", desc: "autonomy settings (also Ctrl+S)" },
1576
1871
  { keys: "F6", desc: "todos monitor overlay" },
1577
1872
  { keys: "F7", desc: "queue panel" },
1873
+ { keys: "F8", desc: "process list overlay" },
1874
+ { keys: "F9", desc: "goal panel" },
1875
+ { keys: "F10", desc: "live sessions panel" },
1578
1876
  { keys: "Esc", desc: "close the open monitor / overlay" }
1579
1877
  ]
1580
1878
  },
@@ -1591,6 +1889,7 @@ function helpSections() {
1591
1889
  {
1592
1890
  title: "Commands",
1593
1891
  entries: [
1892
+ { keys: "/project", desc: "switch projects (also F1)" },
1594
1893
  { keys: "/help", desc: "list all slash commands" },
1595
1894
  { keys: "/model", desc: "switch the active model" },
1596
1895
  { keys: "/fleet", desc: "multi-agent fleet controls" },
@@ -1607,7 +1906,7 @@ function HelpOverlay() {
1607
1906
  const keyWidth = Math.max(...sections.flatMap((s2) => s2.entries.map((e) => e.keys.length)), 0);
1608
1907
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1609
1908
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1610
- /* @__PURE__ */ jsx(Text, { bold: true, color: theme.accent, children: "WrongStack \u2014 keys & commands" }),
1909
+ /* @__PURE__ */ jsx(Text, { bold: true, color: theme.accent, children: "Keyboard shortcuts" }),
1611
1910
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 Esc to close" })
1612
1911
  ] }),
1613
1912
  sections.map((sec) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
@@ -2148,14 +2447,14 @@ function computeWidths(allRows, cols, maxWidth, sepWidths) {
2148
2447
  const cell = row[c] ?? "";
2149
2448
  const stripped = stripInlineMarkers(cell);
2150
2449
  const total = strWidth(stripped);
2151
- natural[c] = Math.max(expectDefined$1(natural[c]), total);
2450
+ natural[c] = Math.max(expectDefined(natural[c]), total);
2152
2451
  }
2153
2452
  }
2154
2453
  if (sepWidths) {
2155
2454
  for (let c = 0; c < cols && c < sepWidths.length; c++) {
2156
2455
  const sepW = sepWidths[c];
2157
2456
  if (sepW != null) {
2158
- natural[c] = Math.max(expectDefined$1(natural[c]), sepW);
2457
+ natural[c] = Math.max(expectDefined(natural[c]), sepW);
2159
2458
  }
2160
2459
  }
2161
2460
  }
@@ -2167,7 +2466,7 @@ function computeWidths(allRows, cols, maxWidth, sepWidths) {
2167
2466
  let maxIdx = -1;
2168
2467
  let maxVal = MIN_COL_WIDTH;
2169
2468
  for (let i = 0; i < cols; i++) {
2170
- const w = expectDefined$1(widths[i]);
2469
+ const w = expectDefined(widths[i]);
2171
2470
  if (w > maxVal) {
2172
2471
  maxVal = w;
2173
2472
  maxIdx = i;
@@ -2227,7 +2526,7 @@ function strWidth(s2) {
2227
2526
  if (i < len) i++;
2228
2527
  continue;
2229
2528
  }
2230
- const code = expectDefined$1(s2.codePointAt(i));
2529
+ const code = expectDefined(s2.codePointAt(i));
2231
2530
  const cpLen = code > 65535 ? 2 : 1;
2232
2531
  if (code === 8205 || // ZWJ — Zero Width Joiner (emoji sequences)
2233
2532
  code === 8203 || // ZWSP — Zero Width Space
@@ -2299,7 +2598,7 @@ function stripInlineMarkers(text) {
2299
2598
  }
2300
2599
  var ANSI_BOLD = "\x1B[1m";
2301
2600
  var ANSI_DIM = "\x1B[2m";
2302
- var ANSI_CYAN = "\x1B[36m";
2601
+ var ANSI_CYAN = "\x1B[38;2;148;226;213m";
2303
2602
  var ANSI_STRIKE = "\x1B[9m";
2304
2603
  var ANSI_RESET_ALL = "\x1B[0m";
2305
2604
  function applyInlineAnsi(text) {
@@ -2404,7 +2703,11 @@ function padCell(text, width, align) {
2404
2703
  }
2405
2704
  return displayText + " ".repeat(pad);
2406
2705
  }
2706
+ var _parseCache = /* @__PURE__ */ new Map();
2707
+ var _PARSE_CACHE_MAX = 5e3;
2407
2708
  function parseInline(text) {
2709
+ const cached = _parseCache.get(text);
2710
+ if (cached) return cached;
2408
2711
  const tokens = [];
2409
2712
  let plain = "";
2410
2713
  let i = 0;
@@ -2457,6 +2760,15 @@ function parseInline(text) {
2457
2760
  i += 1;
2458
2761
  }
2459
2762
  flush();
2763
+ if (_parseCache.size >= _PARSE_CACHE_MAX) {
2764
+ let dropped = 0;
2765
+ const target = Math.floor(_PARSE_CACHE_MAX / 4);
2766
+ for (const key of _parseCache.keys()) {
2767
+ _parseCache.delete(key);
2768
+ if (++dropped >= target) break;
2769
+ }
2770
+ }
2771
+ _parseCache.set(text, tokens);
2460
2772
  return tokens;
2461
2773
  }
2462
2774
  function InlineLine({ tokens, dim }) {
@@ -3123,7 +3435,7 @@ function streamBoxRows(text, maxLines, contentWidth) {
3123
3435
  }
3124
3436
  return rows;
3125
3437
  }
3126
- React5.memo(function ToolStreamBox2({
3438
+ var ToolStreamBox = React5.memo(function ToolStreamBox2({
3127
3439
  name,
3128
3440
  text,
3129
3441
  startedAt,
@@ -3408,10 +3720,11 @@ function Banner({
3408
3720
  entry
3409
3721
  }) {
3410
3722
  const cwdShort = shortenPath(entry.cwd, 48);
3723
+ const projectLabel = path4.basename(entry.cwd);
3411
3724
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 0, children: [
3412
3725
  /* @__PURE__ */ jsxs(Text, { children: [
3413
3726
  /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: " \u259F\u259B " }),
3414
- /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "WrongStack" }),
3727
+ /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: projectLabel }),
3415
3728
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " v" }),
3416
3729
  /* @__PURE__ */ jsx(Text, { children: entry.version })
3417
3730
  ] }),
@@ -3439,10 +3752,24 @@ function Banner({
3439
3752
  ] }),
3440
3753
  /* @__PURE__ */ jsxs(Text, { children: [
3441
3754
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: " hints " }),
3442
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "/help \xB7 /init \xB7 /memory \xB7 /queue \xB7 /exit" })
3755
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "/help \xB7 F1 projects \xB7 F10 sessions \xB7 /exit" })
3443
3756
  ] })
3444
3757
  ] });
3445
3758
  }
3759
+ var NEXT_STEPS_RE = /💡\s*Next steps?\s*\n+((?:\d+\.\s+.+\n?)+)/i;
3760
+ function parseNextSteps(content) {
3761
+ const match = NEXT_STEPS_RE.exec(content);
3762
+ if (!match?.[1]) return { steps: [], stripped: content };
3763
+ const block = match[1];
3764
+ const steps = [];
3765
+ const lines = block.split("\n").filter(Boolean);
3766
+ for (const line of lines) {
3767
+ const m = /^(\d+)\.\s+(.+)$/.exec(line.trim());
3768
+ if (m) steps.push({ index: Number.parseInt(m[1], 10), text: m[2].trim() });
3769
+ }
3770
+ const stripped = content.replace(NEXT_STEPS_RE, "").replace(/\n{3,}/g, "\n\n").trim();
3771
+ return { steps: steps.slice(0, 6), stripped };
3772
+ }
3446
3773
  function brainStatusStyle(status) {
3447
3774
  switch (status) {
3448
3775
  case "thinking":
@@ -3453,6 +3780,8 @@ function brainStatusStyle(status) {
3453
3780
  return { icon: "?", color: "yellow" };
3454
3781
  case "denied":
3455
3782
  return { icon: "\xD7", color: "red" };
3783
+ case "intervention":
3784
+ return { icon: "\u26A1", color: "yellow" };
3456
3785
  }
3457
3786
  }
3458
3787
  function brainRiskColor(risk) {
@@ -3471,6 +3800,10 @@ var Entry = React5.memo(function Entry2({
3471
3800
  entry,
3472
3801
  termWidth
3473
3802
  }) {
3803
+ const nextSteps = useMemo(() => {
3804
+ if (entry.kind !== "assistant") return { steps: [], stripped: "" };
3805
+ return parseNextSteps(entry.text);
3806
+ }, [entry.kind, entry.text]);
3474
3807
  switch (entry.kind) {
3475
3808
  case "user":
3476
3809
  return /* @__PURE__ */ jsx(
@@ -3499,24 +3832,52 @@ var Entry = React5.memo(function Entry2({
3499
3832
  );
3500
3833
  case "assistant": {
3501
3834
  const contentWidth = assistantContentWidth(termWidth);
3502
- return /* @__PURE__ */ jsxs(
3503
- Box,
3504
- {
3505
- flexDirection: "column",
3506
- marginX: MESSAGE_PANEL_MARGIN,
3507
- marginY: 1,
3508
- borderStyle: "single",
3509
- borderTop: false,
3510
- borderRight: false,
3511
- borderBottom: false,
3512
- borderColor: theme.assistant,
3513
- paddingLeft: 1,
3514
- children: [
3515
- /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { bold: true, color: theme.assistant, children: "ASSISTANT" }) }),
3516
- /* @__PURE__ */ jsx(AssistantBody, { text: entry.text, termWidth, contentWidth })
3517
- ]
3518
- }
3519
- );
3835
+ const { steps, stripped } = nextSteps;
3836
+ const hasNext = steps.length > 0;
3837
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
3838
+ /* @__PURE__ */ jsxs(
3839
+ Box,
3840
+ {
3841
+ flexDirection: "column",
3842
+ marginX: MESSAGE_PANEL_MARGIN,
3843
+ marginY: 1,
3844
+ borderStyle: "single",
3845
+ borderTop: false,
3846
+ borderRight: false,
3847
+ borderBottom: hasNext ? false : void 0,
3848
+ borderColor: theme.assistant,
3849
+ paddingLeft: 1,
3850
+ children: [
3851
+ /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { bold: true, color: theme.assistant, children: "ASSISTANT" }) }),
3852
+ /* @__PURE__ */ jsx(AssistantBody, { text: stripped, termWidth, contentWidth })
3853
+ ]
3854
+ }
3855
+ ),
3856
+ hasNext && /* @__PURE__ */ jsxs(
3857
+ Box,
3858
+ {
3859
+ flexDirection: "column",
3860
+ marginX: MESSAGE_PANEL_MARGIN,
3861
+ marginY: 1,
3862
+ borderStyle: "single",
3863
+ borderTop: false,
3864
+ borderRight: false,
3865
+ borderBottom: false,
3866
+ borderColor: theme.accent,
3867
+ paddingLeft: 1,
3868
+ children: [
3869
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
3870
+ /* @__PURE__ */ jsx(Text, { bold: true, color: theme.accent, children: "\u{1F4A1} NEXT STEPS " }),
3871
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(use /next 1, /next 1 2 3 to select)" })
3872
+ ] }),
3873
+ steps.map((s2) => /* @__PURE__ */ jsx(Box, { flexDirection: "row", marginTop: 0, children: /* @__PURE__ */ jsxs(Text, { children: [
3874
+ /* @__PURE__ */ jsx(Text, { bold: true, color: theme.accent, children: ` ${s2.index}. ` }),
3875
+ /* @__PURE__ */ jsx(Text, { children: s2.text })
3876
+ ] }) }, s2.index))
3877
+ ]
3878
+ }
3879
+ )
3880
+ ] });
3520
3881
  }
3521
3882
  case "tool": {
3522
3883
  const argSummary = formatToolArgs(entry.name, entry.input);
@@ -3571,8 +3932,10 @@ var Entry = React5.memo(function Entry2({
3571
3932
  diff ? /* @__PURE__ */ jsx(DiffBlock, { rows: diff.rows, hidden: diff.hidden }) : null
3572
3933
  ] });
3573
3934
  }
3574
- case "info":
3575
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.text });
3935
+ case "info": {
3936
+ const hasAnsi = /\x1b\[/.test(entry.text);
3937
+ return /* @__PURE__ */ jsx(Text, { dimColor: !hasAnsi, children: entry.text });
3938
+ }
3576
3939
  case "warn":
3577
3940
  return /* @__PURE__ */ jsx(
3578
3941
  Box,
@@ -3681,7 +4044,7 @@ var Entry = React5.memo(function Entry2({
3681
4044
  }
3682
4045
  }
3683
4046
  });
3684
- function History({ entries, streamingText, toolStream }) {
4047
+ function History({ entries, generation, streamingText, toolStream }) {
3685
4048
  const { stdout } = useStdout();
3686
4049
  const [termSize, setTermSize] = useState({
3687
4050
  columns: stdout?.columns ?? 80,
@@ -3699,10 +4062,209 @@ function History({ entries, streamingText, toolStream }) {
3699
4062
  const termWidth = termSize.columns;
3700
4063
  const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
3701
4064
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3702
- /* @__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) }),
4065
+ /* @__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) }, generation ?? 0),
3703
4066
  /* @__PURE__ */ jsx(Box, { flexGrow: 1, children: tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail, termWidth }) : null })
3704
4067
  ] });
3705
4068
  }
4069
+ var MAX_MOUNTED = 500;
4070
+ function scrollbarThumb(rows, offset, total) {
4071
+ const scrollable = total > rows;
4072
+ if (!scrollable) return { top: 0, size: rows, scrollable: false };
4073
+ const windowTop = Math.max(0, total - rows - offset);
4074
+ const size = Math.max(1, Math.round(rows / total * rows));
4075
+ const rawTop = Math.round(windowTop / total * rows);
4076
+ const top = Math.max(0, Math.min(rawTop, rows - size));
4077
+ return { top, size, scrollable: true };
4078
+ }
4079
+ function scrollOffsetForTrackRow(rows, total, cell) {
4080
+ if (total <= rows) return 0;
4081
+ const maxOffset = total - rows;
4082
+ const clampedCell = Math.max(0, Math.min(rows - 1, cell));
4083
+ const windowTop = Math.round(clampedCell / Math.max(1, rows - 1) * maxOffset);
4084
+ return Math.max(0, Math.min(maxOffset, maxOffset - windowTop));
4085
+ }
4086
+ function Scrollbar({
4087
+ rows,
4088
+ offset,
4089
+ total
4090
+ }) {
4091
+ const { top: thumbTop, size: thumbSize, scrollable } = scrollbarThumb(rows, offset, total);
4092
+ const cells = [];
4093
+ for (let i = 0; i < rows; i++) {
4094
+ cells.push(i >= thumbTop && i < thumbTop + thumbSize ? "\u2588" : "\u2502");
4095
+ }
4096
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginLeft: 1, flexShrink: 0, children: cells.map((c, i) => /* @__PURE__ */ jsx(
4097
+ Text,
4098
+ {
4099
+ ...scrollable ? { color: theme.accent } : {},
4100
+ dimColor: !scrollable || c === "\u2502",
4101
+ children: c
4102
+ },
4103
+ i
4104
+ )) });
4105
+ }
4106
+ function ScrollableHistory({
4107
+ entries,
4108
+ streamingText,
4109
+ toolStream,
4110
+ scrollOffset,
4111
+ viewportRows,
4112
+ totalLines,
4113
+ onMeasure,
4114
+ maxWidth
4115
+ }) {
4116
+ const { stdout } = useStdout();
4117
+ const rawWidth = stdout?.columns ?? 80;
4118
+ const termWidth = maxWidth ? Math.min(rawWidth, maxWidth) : rawWidth;
4119
+ const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
4120
+ const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
4121
+ const hiddenCount = Math.max(0, entries.length - MAX_MOUNTED);
4122
+ const shown = hiddenCount > 0 ? entries.slice(-MAX_MOUNTED) : entries;
4123
+ const contentRef = useRef(null);
4124
+ const lastReported = useRef(-1);
4125
+ useLayoutEffect(() => {
4126
+ const node = contentRef.current;
4127
+ if (!node) return;
4128
+ const { height } = measureElement(node);
4129
+ if (height !== lastReported.current) {
4130
+ lastReported.current = height;
4131
+ onMeasure(height);
4132
+ }
4133
+ }, [onMeasure]);
4134
+ const vp = Math.max(1, viewportRows);
4135
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
4136
+ /* @__PURE__ */ jsx(
4137
+ Box,
4138
+ {
4139
+ flexDirection: "column",
4140
+ flexGrow: 1,
4141
+ height: vp,
4142
+ overflowY: "hidden",
4143
+ justifyContent: "flex-end",
4144
+ children: /* @__PURE__ */ jsxs(
4145
+ Box,
4146
+ {
4147
+ ref: contentRef,
4148
+ flexDirection: "column",
4149
+ marginBottom: Math.max(0, scrollOffset),
4150
+ flexShrink: 0,
4151
+ children: [
4152
+ 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,
4153
+ shown.map((entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, flexShrink: 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth }) }, entry.id)),
4154
+ tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail, termWidth }) : null,
4155
+ toolTail && toolStream ? /* @__PURE__ */ jsx(
4156
+ ToolStreamBox,
4157
+ {
4158
+ name: toolStream.name,
4159
+ text: toolTail,
4160
+ startedAt: toolStream.startedAt,
4161
+ termWidth
4162
+ }
4163
+ ) : null
4164
+ ]
4165
+ }
4166
+ )
4167
+ }
4168
+ ),
4169
+ /* @__PURE__ */ jsx(Scrollbar, { rows: vp, offset: Math.max(0, scrollOffset), total: totalLines })
4170
+ ] });
4171
+ }
4172
+ var MB = 1024 * 1024;
4173
+ function defaultHeapLogPath() {
4174
+ return path4.join(wstackGlobalRoot(), "logs", "heap.jsonl");
4175
+ }
4176
+ function takeHeapSample() {
4177
+ const m = process.memoryUsage();
4178
+ const limit = v8.getHeapStatistics().heap_size_limit || 0;
4179
+ return {
4180
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4181
+ rss: m.rss,
4182
+ heapUsed: m.heapUsed,
4183
+ heapTotal: m.heapTotal,
4184
+ external: m.external,
4185
+ heapLimit: limit,
4186
+ load: limit > 0 ? m.heapUsed / limit : 0
4187
+ };
4188
+ }
4189
+ function startHeapWatchdog(opts = {}) {
4190
+ const sampleEveryMs = opts.sampleEveryMs ?? 6e4;
4191
+ const logEveryMs = opts.logEveryMs ?? 3e5;
4192
+ const logPath = opts.logPath ?? defaultHeapLogPath();
4193
+ const warnAt = opts.warnAt ?? 0.6;
4194
+ const criticalAt = opts.criticalAt ?? 0.85;
4195
+ const REARM_MARGIN = 0.05;
4196
+ let warnArmed = true;
4197
+ let criticalArmed = true;
4198
+ let lastLogAt = 0;
4199
+ let writeChain = Promise.resolve();
4200
+ let dirReady = false;
4201
+ const append = (line) => {
4202
+ writeChain = writeChain.then(async () => {
4203
+ if (!dirReady) {
4204
+ await fs2.mkdir(path4.dirname(logPath), { recursive: true });
4205
+ dirReady = true;
4206
+ }
4207
+ await fs2.appendFile(logPath, `${line}
4208
+ `, "utf8");
4209
+ }).catch(() => void 0);
4210
+ };
4211
+ const tick = () => {
4212
+ const s2 = takeHeapSample();
4213
+ if (s2.load >= criticalAt && criticalArmed) {
4214
+ criticalArmed = false;
4215
+ warnArmed = false;
4216
+ opts.onWarn?.(
4217
+ "critical",
4218
+ `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}`,
4219
+ s2
4220
+ );
4221
+ } else if (s2.load >= warnAt && warnArmed) {
4222
+ warnArmed = false;
4223
+ opts.onWarn?.(
4224
+ "warn",
4225
+ `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}`,
4226
+ s2
4227
+ );
4228
+ }
4229
+ if (!warnArmed && s2.load < warnAt - REARM_MARGIN) warnArmed = true;
4230
+ if (!criticalArmed && s2.load < criticalAt - REARM_MARGIN) criticalArmed = true;
4231
+ const due = Date.now() - lastLogAt >= logEveryMs;
4232
+ const crossed = s2.load >= warnAt;
4233
+ if (due || crossed) {
4234
+ lastLogAt = Date.now();
4235
+ let extras = {};
4236
+ try {
4237
+ extras = opts.collectStats?.() ?? {};
4238
+ } catch {
4239
+ }
4240
+ append(JSON.stringify({ pid: process.pid, ...s2, ...extras }));
4241
+ }
4242
+ };
4243
+ const timer = setInterval(tick, sampleEveryMs);
4244
+ timer.unref?.();
4245
+ tick();
4246
+ return () => {
4247
+ clearInterval(timer);
4248
+ };
4249
+ }
4250
+
4251
+ // src/hit-test.ts
4252
+ var SCROLLBAR_HIT_WIDTH = 2;
4253
+ function hitRegion(layout, x, y) {
4254
+ const { termRows, termCols, viewportRows } = layout;
4255
+ if (y < 1 || y > termRows || x < 1 || x > termCols) return null;
4256
+ if (y <= viewportRows) {
4257
+ if (x > termCols - SCROLLBAR_HIT_WIDTH) return { kind: "scrollbar", cell: y - 1 };
4258
+ return { kind: "history", row: y - 1 };
4259
+ }
4260
+ return { kind: "bottom", row: y - viewportRows - 1 };
4261
+ }
4262
+ function statusBarLineRow(opts) {
4263
+ const contentLines = opts.statusBarHeight - opts.headerRows;
4264
+ if (opts.line < 0 || opts.line >= contentLines) return null;
4265
+ const bandTop = opts.termRows - opts.belowHeight - opts.statusBarHeight + 1;
4266
+ return bandTop + opts.headerRows + opts.line;
4267
+ }
3706
4268
 
3707
4269
  // src/fn-keys.ts
3708
4270
  function fnKey(data) {
@@ -3806,6 +4368,45 @@ function layoutInputRows(prompt, value, cursor, width) {
3806
4368
  if (row.length > 0 || rows.length === 0) rows.push(row);
3807
4369
  return rows;
3808
4370
  }
4371
+
4372
+ // src/mouse.ts
4373
+ var ESC = String.fromCharCode(27);
4374
+ var MOUSE_CLICK_ON = `${ESC}[?1000h${ESC}[?1006h`;
4375
+ var MOUSE_OFF = `${ESC}[?1003l${ESC}[?1002l${ESC}[?1000l${ESC}[?1006l`;
4376
+ var SGR_MOUSE_GLOBAL = new RegExp(`${ESC}\\[<(\\d+);(\\d+);(\\d+)([Mm])`, "gu");
4377
+ var LEAKED_MOUSE_RE = /\[<\d+;\d+;\d+[Mm]/;
4378
+ function decodeMouse(cb, x, y, released) {
4379
+ const shift = (cb & 4) !== 0;
4380
+ const meta = (cb & 8) !== 0;
4381
+ const ctrl = (cb & 16) !== 0;
4382
+ const motion = (cb & 32) !== 0;
4383
+ const wheel = (cb & 64) !== 0;
4384
+ const low = cb & 3;
4385
+ if (wheel) {
4386
+ const dir = low === 0 ? 1 : low === 1 ? -1 : 0;
4387
+ return { kind: "wheel", button: "none", x, y, wheel: dir, shift, meta, ctrl, motion: false };
4388
+ }
4389
+ const button = low === 0 ? "left" : low === 1 ? "middle" : low === 2 ? "right" : "none";
4390
+ const kind = motion ? "move" : released ? "release" : "press";
4391
+ return { kind, button, x, y, wheel: 0, shift, meta, ctrl, motion };
4392
+ }
4393
+ function parseMouseEvents(data) {
4394
+ const events = [];
4395
+ for (const m of data.matchAll(SGR_MOUSE_GLOBAL)) {
4396
+ events.push(
4397
+ decodeMouse(
4398
+ Number.parseInt(m[1], 10),
4399
+ Number.parseInt(m[2], 10),
4400
+ Number.parseInt(m[3], 10),
4401
+ m[4] === "m"
4402
+ )
4403
+ );
4404
+ }
4405
+ return events;
4406
+ }
4407
+ function isLeakedMouseInput(input) {
4408
+ return LEAKED_MOUSE_RE.test(input);
4409
+ }
3809
4410
  function renderRow2(cells, rowKey, promptColor) {
3810
4411
  const out = [];
3811
4412
  let run = "";
@@ -3860,14 +4461,6 @@ function isBackspaceOrDelete(data) {
3860
4461
  if (data === "\x1B[3~") return "delete";
3861
4462
  return null;
3862
4463
  }
3863
- function parseMouseWheel(data) {
3864
- const m = data.match(new RegExp(`^${String.fromCharCode(27)}\\[<(\\d+);(\\d+);(\\d+)([Mm])$`, "u"));
3865
- if (!m) return null;
3866
- const cb = Number.parseInt(expectDefined(m[1]), 10);
3867
- if (cb === 64) return 1;
3868
- if (cb === 65) return -1;
3869
- return null;
3870
- }
3871
4464
  var EMPTY_KEY = {
3872
4465
  upArrow: false,
3873
4466
  downArrow: false,
@@ -3901,6 +4494,7 @@ var Input = memo(function Input2({
3901
4494
  const suppressInkDeleteRef = useRef(false);
3902
4495
  useInput((input, key) => {
3903
4496
  if (disabled) return;
4497
+ if (input && isLeakedMouseInput(input)) return;
3904
4498
  if (key.escape && suppressInkEscRef.current) {
3905
4499
  suppressInkEscRef.current = false;
3906
4500
  return;
@@ -3964,9 +4558,15 @@ var Input = memo(function Input2({
3964
4558
  onKey("", { ...EMPTY_KEY, backspace: true, ctrl: true });
3965
4559
  return;
3966
4560
  }
3967
- const wheelDelta = parseMouseWheel(s2);
3968
- if (wheelDelta !== null) {
3969
- onKey("", { ...EMPTY_KEY, wheelDeltaY: wheelDelta });
4561
+ const mouseEvents = parseMouseEvents(s2);
4562
+ if (mouseEvents.length > 0) {
4563
+ for (const ev of mouseEvents) {
4564
+ onKey("", {
4565
+ ...EMPTY_KEY,
4566
+ mouse: ev,
4567
+ wheelDeltaY: ev.kind === "wheel" ? ev.wheel : void 0
4568
+ });
4569
+ }
3970
4570
  return;
3971
4571
  }
3972
4572
  const fn = fnKey(s2);
@@ -4272,6 +4872,80 @@ function PhasePanel({ phases, nowTick }) {
4272
4872
  }
4273
4873
  );
4274
4874
  }
4875
+ var MAX_VISIBLE2 = 12;
4876
+ function visibleWindow(selected, total) {
4877
+ if (total <= MAX_VISIBLE2) return { start: 0, end: total };
4878
+ const half = Math.floor(MAX_VISIBLE2 / 2);
4879
+ let start = selected - half;
4880
+ let end = start + MAX_VISIBLE2;
4881
+ if (start < 0) {
4882
+ start = 0;
4883
+ end = MAX_VISIBLE2;
4884
+ }
4885
+ if (end > total) {
4886
+ end = total;
4887
+ start = total - MAX_VISIBLE2;
4888
+ }
4889
+ return { start, end };
4890
+ }
4891
+ function ProjectPicker({
4892
+ items,
4893
+ selected,
4894
+ filter,
4895
+ hint
4896
+ }) {
4897
+ const total = items.length;
4898
+ const { start, end } = visibleWindow(selected, total);
4899
+ const visible = items.slice(start, end);
4900
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, flexShrink: 0, children: [
4901
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Switch Project \u2501\u2501" }),
4902
+ filter ? /* @__PURE__ */ jsxs(Box, { children: [
4903
+ /* @__PURE__ */ jsx(Text, { color: "yellow", children: "Filter: " }),
4904
+ /* @__PURE__ */ jsx(Text, { children: filter }),
4905
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2588" })
4906
+ ] }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "type to filter \xB7 \u2191\u2193 navigate \xB7 Enter select \xB7 Esc cancel" }),
4907
+ start > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4908
+ "\u2191 ",
4909
+ start,
4910
+ " more above"
4911
+ ] }) : null,
4912
+ visible.map((item) => {
4913
+ const idx = items.indexOf(item);
4914
+ const isSelected = idx === selected;
4915
+ const isDivider = item.key === "__divider__";
4916
+ if (isDivider) {
4917
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(36) }, item.key);
4918
+ }
4919
+ const marker = isSelected ? "\u25B8" : " ";
4920
+ const labelColor = isSelected ? "cyan" : void 0;
4921
+ const metaColor = "grey";
4922
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
4923
+ /* @__PURE__ */ jsxs(Box, { children: [
4924
+ /* @__PURE__ */ jsxs(Text, { inverse: isSelected, color: labelColor, children: [
4925
+ ` ${marker} `,
4926
+ /* @__PURE__ */ jsx(Text, { bold: isSelected, children: item.label })
4927
+ ] }),
4928
+ item.meta ? /* @__PURE__ */ jsxs(Text, { dimColor: !isSelected, color: isSelected ? "cyan" : metaColor, children: [
4929
+ " ",
4930
+ item.meta
4931
+ ] }) : null
4932
+ ] }),
4933
+ item.subtitle ? /* @__PURE__ */ jsxs(Text, { dimColor: !isSelected, color: isSelected ? "cyan" : metaColor, children: [
4934
+ " ",
4935
+ item.subtitle
4936
+ ] }) : null
4937
+ ] }, item.key);
4938
+ }),
4939
+ end < total ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
4940
+ "\u2193 ",
4941
+ total - end,
4942
+ " more below \xB7 ",
4943
+ total,
4944
+ " total"
4945
+ ] }) : null,
4946
+ hint ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) }) : null
4947
+ ] });
4948
+ }
4275
4949
  function QueuePanel({ items }) {
4276
4950
  const { stdout } = useStdout();
4277
4951
  const w = stdout?.columns ?? 80;
@@ -4502,32 +5176,227 @@ function renderProgressBar(progress, trend) {
4502
5176
  ] })
4503
5177
  ] });
4504
5178
  }
4505
- var DELAY_PRESETS_MS = [0, 15e3, 3e4, 45e3, 6e4, 12e4];
4506
- var SETTINGS_MODES = ["off", "suggest", "auto"];
4507
- var LOG_LEVELS = ["error", "warn", "info", "debug", "trace"];
4508
- var AUDIT_LEVELS = ["minimal", "standard", "full"];
4509
- var COMPACTOR_STRATEGIES = ["hybrid", "intelligent", "selective"];
4510
- var MAX_ITERATIONS_PRESETS = [100, 200, 500, 1e3, 0];
4511
- var AUTO_PROCEED_MAX_PRESETS = [10, 25, 50, 100, 250, 0];
4512
- var ENHANCE_DELAY_PRESETS = [3e4, 45e3, 6e4, 9e4, 12e4];
4513
- function formatSettingsDelay(ms) {
4514
- if (ms === 0) return "disabled";
4515
- if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
4516
- return `${Math.round(ms / 1e3)}s`;
5179
+ function ResumePicker({
5180
+ sessions,
5181
+ selected,
5182
+ busy,
5183
+ error,
5184
+ hint
5185
+ }) {
5186
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
5187
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Resume Session \u2501\u2501" }),
5188
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: busy ? "Resuming selected session\u2026" : "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel" }),
5189
+ error ? /* @__PURE__ */ jsx(Text, { color: "red", children: error }) : null,
5190
+ sessions.length === 0 && !busy ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No sessions found." }) : sessions.map((s2, i) => {
5191
+ const isCurrent = s2.isCurrent;
5192
+ const isSelected = i === selected;
5193
+ const date = s2.startedAt.slice(0, 16).replace("T", " ");
5194
+ const outcomeBadge = s2.outcome === "completed" ? "\u2713 " : s2.outcome === "aborted" ? "\u26A0 " : s2.outcome === "error" ? "\u2717 " : s2.outcome === "timeout" ? "\u23F1 " : " ";
5195
+ const toolStr = s2.toolCallCount > 0 ? `${s2.toolCallCount} tool${s2.toolCallCount === 1 ? "" : "s"}` : "";
5196
+ const iterStr = s2.iterationCount > 0 ? `${s2.iterationCount} iter${s2.iterationCount === 1 ? "" : "s"}` : "";
5197
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
5198
+ /* @__PURE__ */ jsxs(
5199
+ Text,
5200
+ {
5201
+ inverse: isSelected,
5202
+ dimColor: isCurrent ?? false,
5203
+ ...isSelected ? { color: isCurrent ? "gray" : "cyan" } : {},
5204
+ children: [
5205
+ isSelected ? "\u203A " : " ",
5206
+ /* @__PURE__ */ jsx(Text, { bold: true, dimColor: isCurrent ?? false, children: s2.id }),
5207
+ isCurrent ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: " (current)" }) : null,
5208
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5209
+ " ",
5210
+ date
5211
+ ] })
5212
+ ]
5213
+ }
5214
+ ),
5215
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5216
+ isSelected ? " " : " ",
5217
+ outcomeBadge,
5218
+ s2.tokenTotal.toLocaleString(),
5219
+ " tok",
5220
+ toolStr ? ` \xB7 ${toolStr}` : "",
5221
+ iterStr ? ` \xB7 ${iterStr}` : "",
5222
+ s2.toolErrorCount > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
5223
+ " \xB7 ",
5224
+ s2.toolErrorCount,
5225
+ " err"
5226
+ ] }) : null
5227
+ ] }),
5228
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5229
+ isSelected ? " " : " ",
5230
+ s2.title.length > 72 ? `${s2.title.slice(0, 71)}\u2026` : s2.title
5231
+ ] })
5232
+ ] }, s2.id);
5233
+ }),
5234
+ hint ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) : null
5235
+ ] });
4517
5236
  }
4518
- function formatMaxIterations(n) {
4519
- if (n === 0) return "unlimited";
4520
- return String(n);
5237
+ function statusIcon(status) {
5238
+ switch (status) {
5239
+ case "active":
5240
+ return "\u25CF";
5241
+ case "idle":
5242
+ return "\u25C9";
5243
+ case "closing":
5244
+ return "\u25D0";
5245
+ case "stale":
5246
+ return "\u25CB";
5247
+ default:
5248
+ return "?";
5249
+ }
4521
5250
  }
4522
- function formatEnhanceDelay(ms) {
4523
- return `${Math.round(ms / 1e3)}s`;
5251
+ function agentIcon(status) {
5252
+ switch (status) {
5253
+ case "running":
5254
+ return "\u25B6";
5255
+ case "streaming":
5256
+ return "\u21BB";
5257
+ case "waiting_user":
5258
+ return "\u23F3";
5259
+ case "error":
5260
+ return "\u2717";
5261
+ case "idle":
5262
+ return "\u25A0";
5263
+ default:
5264
+ return "?";
5265
+ }
4524
5266
  }
4525
- var MODE_DESC = {
4526
- off: "Agent stops after each turn (normal)",
4527
- suggest: "Shows next-step suggestions after each turn",
4528
- auto: "Self-driving \u2014 agent continues automatically"
5267
+ function fmtDuration2(startedAt) {
5268
+ const diff = Date.now() - new Date(startedAt).getTime();
5269
+ const min = Math.floor(diff / 6e4);
5270
+ if (min < 1) return "<1m";
5271
+ if (min < 60) return `${min}m`;
5272
+ const h = Math.floor(min / 60);
5273
+ if (h < 24) return `${h}h ${min % 60}m`;
5274
+ return `${Math.floor(h / 24)}d ${h % 24}h`;
5275
+ }
5276
+ function SessionsPanel({
5277
+ sessions,
5278
+ busy,
5279
+ selected,
5280
+ resumeConfirm,
5281
+ currentSessionId
5282
+ }) {
5283
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, flexShrink: 0, children: [
5284
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
5285
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u29C9 Sessions" }),
5286
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 F10 or Esc to close" }),
5287
+ busy && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 loading\u2026" })
5288
+ ] }),
5289
+ resumeConfirm ? /* @__PURE__ */ jsxs(Box, { marginY: 1, borderStyle: "single", borderColor: "yellow", paddingX: 1, children: [
5290
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", bold: true, children: [
5291
+ '\u26A0 Resume session "',
5292
+ resumeConfirm.sessionName,
5293
+ '"?'
5294
+ ] }),
5295
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "This will replace the current conversation. Press Enter to confirm, Esc to cancel." })
5296
+ ] }) : null,
5297
+ 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: [
5298
+ /* @__PURE__ */ jsxs(Box, { children: [
5299
+ /* @__PURE__ */ jsxs(Text, { inverse: idx === selected, color: s2.status === "active" ? "green" : s2.status === "idle" ? "cyan" : "yellow", children: [
5300
+ s2.sessionId === currentSessionId ? "\u25CF " : "",
5301
+ statusIcon(s2.status),
5302
+ " "
5303
+ ] }),
5304
+ /* @__PURE__ */ jsx(Text, { bold: true, children: s2.projectName }),
5305
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5306
+ " [",
5307
+ s2.projectSlug,
5308
+ "]"
5309
+ ] }),
5310
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5311
+ " \xB7 ",
5312
+ s2.sessionId.slice(0, 8)
5313
+ ] }),
5314
+ s2.gitBranch ? /* @__PURE__ */ jsxs(Text, { color: "magenta", children: [
5315
+ " \u2387 ",
5316
+ s2.gitBranch
5317
+ ] }) : null,
5318
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5319
+ " \xB7 ",
5320
+ fmtDuration2(s2.startedAt)
5321
+ ] }),
5322
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5323
+ " \xB7 PID ",
5324
+ s2.pid
5325
+ ] })
5326
+ ] }),
5327
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5328
+ " wd: ",
5329
+ s2.workingDir
5330
+ ] }),
5331
+ /* @__PURE__ */ jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [
5332
+ s2.agents.slice(0, 8).map((a) => /* @__PURE__ */ jsxs(Box, { children: [
5333
+ /* @__PURE__ */ jsxs(Text, { color: a.status === "running" ? "green" : a.status === "streaming" ? "cyan" : a.status === "error" ? "red" : a.status === "waiting_user" ? "yellow" : "grey", children: [
5334
+ agentIcon(a.status),
5335
+ " "
5336
+ ] }),
5337
+ /* @__PURE__ */ jsx(Text, { children: a.name }),
5338
+ a.currentTool ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5339
+ " [",
5340
+ a.currentTool,
5341
+ "]"
5342
+ ] }) : null,
5343
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5344
+ " \xB7 ",
5345
+ a.iterations,
5346
+ " iter \xB7 ",
5347
+ a.toolCalls,
5348
+ " tools"
5349
+ ] })
5350
+ ] }, a.id)),
5351
+ s2.agents.length > 8 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5352
+ " ... and ",
5353
+ s2.agents.length - 8,
5354
+ " more"
5355
+ ] }) : null
5356
+ ] })
5357
+ ] }, s2.sessionId)),
5358
+ sessions.length > 0 && /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
5359
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5360
+ sessions.length,
5361
+ " session",
5362
+ sessions.length === 1 ? "" : "s",
5363
+ " \xB7 \u2191\u2193 navigate \xB7 Enter to resume/switch \xB7 Esc close"
5364
+ ] }),
5365
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5366
+ "Tip: /sessions kill ",
5367
+ "<id>",
5368
+ " to stop a background session"
5369
+ ] })
5370
+ ] })
5371
+ ] });
5372
+ }
5373
+ var DELAY_PRESETS_MS = [0, 15e3, 3e4, 45e3, 6e4, 12e4];
5374
+ var SETTINGS_MODES = ["off", "suggest", "auto"];
5375
+ var LOG_LEVELS = ["error", "warn", "info", "debug", "trace"];
5376
+ var AUDIT_LEVELS = ["minimal", "standard", "full"];
5377
+ var COMPACTOR_STRATEGIES = ["hybrid", "intelligent", "selective"];
5378
+ var MAX_ITERATIONS_PRESETS = [100, 200, 500, 1e3, 0];
5379
+ var AUTO_PROCEED_MAX_PRESETS = [10, 25, 50, 100, 250, 0];
5380
+ var ENHANCE_DELAY_PRESETS = [3e4, 45e3, 6e4, 9e4, 12e4];
5381
+ var ENHANCE_LANGUAGES = ["original", "english"];
5382
+ function formatSettingsDelay(ms) {
5383
+ if (ms === 0) return "disabled";
5384
+ if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
5385
+ return `${Math.round(ms / 1e3)}s`;
5386
+ }
5387
+ function formatMaxIterations(n) {
5388
+ if (n === 0) return "unlimited";
5389
+ return String(n);
5390
+ }
5391
+ function formatEnhanceDelay(ms) {
5392
+ return `${Math.round(ms / 1e3)}s`;
5393
+ }
5394
+ var MODE_DESC = {
5395
+ off: "Agent stops after each turn (normal)",
5396
+ suggest: "Shows next-step suggestions after each turn",
5397
+ auto: "Self-driving \u2014 agent continues automatically"
4529
5398
  };
4530
- var SETTINGS_FIELD_COUNT = 23;
5399
+ var SETTINGS_FIELD_COUNT = 24;
4531
5400
  var CONFIG_SCOPES = ["global", "project"];
4532
5401
  function SettingsPicker({
4533
5402
  field,
@@ -4552,6 +5421,8 @@ function SettingsPicker({
4552
5421
  maxIterations,
4553
5422
  autoProceedMaxIterations,
4554
5423
  enhanceDelayMs,
5424
+ enhanceEnabled,
5425
+ enhanceLanguage,
4555
5426
  debugStream,
4556
5427
  configScope,
4557
5428
  hint
@@ -4675,6 +5546,16 @@ function SettingsPicker({
4675
5546
  value: formatEnhanceDelay(enhanceDelayMs),
4676
5547
  detail: "Timeout for prompt refinement preview (30s\u2013120s)"
4677
5548
  },
5549
+ {
5550
+ label: "Refine",
5551
+ value: boolVal(enhanceEnabled),
5552
+ detail: "Enable prompt refinement before sending"
5553
+ },
5554
+ {
5555
+ label: "Refine language",
5556
+ value: enhanceLanguage,
5557
+ detail: "original (keep language) | english (translate)"
5558
+ },
4678
5559
  // ── Debug ──
4679
5560
  { section: "Debug" },
4680
5561
  {
@@ -4836,6 +5717,54 @@ function windowRows(rows, focus, max) {
4836
5717
  }
4837
5718
  return { rows: rows.slice(start, end), start, end, contextHeader };
4838
5719
  }
5720
+ function hintsFor(ctx) {
5721
+ if (ctx.confirm) {
5722
+ return [
5723
+ { key: "y", label: "yes" },
5724
+ { key: "n", label: "no" },
5725
+ { key: "a", label: "always" },
5726
+ { key: "d", label: "deny" }
5727
+ ];
5728
+ }
5729
+ if (ctx.picker) {
5730
+ return [
5731
+ { key: "\u2191\u2193", label: "move" },
5732
+ { key: "\u21B5", label: "select" },
5733
+ { key: "Esc", label: "cancel" }
5734
+ ];
5735
+ }
5736
+ if (ctx.monitor) {
5737
+ const hints = [
5738
+ { key: "Esc", label: "close" },
5739
+ { key: "^F", label: "fleet" },
5740
+ { key: "^G", label: "agents" },
5741
+ { key: "^T", label: "worktrees" },
5742
+ { key: "F6", label: "todos" },
5743
+ { key: "F9", label: "goal" }
5744
+ ];
5745
+ if (ctx.nextPanelHint) {
5746
+ hints.push({ key: ctx.nextPanelHint.key, label: ctx.nextPanelHint.label, discovery: true });
5747
+ }
5748
+ return hints;
5749
+ }
5750
+ const base = [{ key: "?", label: "help" }];
5751
+ if (ctx.managed) base.push({ key: "PgUp/PgDn", label: "scroll" });
5752
+ base.push({ key: "^G", label: "agents" }, { key: "^C", label: "stop" });
5753
+ if (ctx.nextPanelHint) {
5754
+ base.push({ key: ctx.nextPanelHint.key, label: ctx.nextPanelHint.label, discovery: true });
5755
+ }
5756
+ return base;
5757
+ }
5758
+ function KeyHintBar({ context }) {
5759
+ const hints = hintsFor(context);
5760
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "row", paddingX: 1, children: hints.map((h, i) => (
5761
+ // biome-ignore lint/suspicious/noArrayIndexKey: hints are positional + stable
5762
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", marginRight: 2, children: [
5763
+ /* @__PURE__ */ jsx(Text, { color: h.discovery ? theme.monitor.agents : theme.accent, children: h.key }),
5764
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${h.label}` })
5765
+ ] }, i)
5766
+ )) });
5767
+ }
4839
5768
  function TodosMonitor({ todos }) {
4840
5769
  const { stdout } = useStdout();
4841
5770
  const done = todos.filter((t) => t.status === "completed").length;
@@ -5177,7 +6106,7 @@ async function loadIndex(root) {
5177
6106
  async function walk(root, rel, depth, out) {
5178
6107
  if (out.length >= MAX_FILES_INDEXED) return;
5179
6108
  if (depth > MAX_DEPTH) return;
5180
- const dir = rel ? path2.join(root, rel) : root;
6109
+ const dir = rel ? path4.join(root, rel) : root;
5181
6110
  let entries;
5182
6111
  try {
5183
6112
  entries = await fs2.readdir(dir, { withFileTypes: true });
@@ -5749,11 +6678,28 @@ function useBrainEvents(events, dispatch) {
5749
6678
  const offAnswered = events.on("brain.decision_answered", (payload) => addBrainEntry("answered", payload));
5750
6679
  const offAskHuman = events.on("brain.decision_ask_human", (payload) => addBrainEntry("ask_human", payload));
5751
6680
  const offDenied = events.on("brain.decision_denied", (payload) => addBrainEntry("denied", payload));
6681
+ const offIntervention = events.on("brain.intervention", (payload) => {
6682
+ const decision = payload.intervened ? `steered the agent (${payload.kind.replace(/_/g, " ")})` : "observed \u2014 no action needed";
6683
+ const rationale = payload.decision.type === "answer" ? payload.decision.rationale : void 0;
6684
+ dispatch({
6685
+ type: "addEntry",
6686
+ entry: {
6687
+ kind: "brain",
6688
+ status: "intervention",
6689
+ source: "monitor",
6690
+ risk: payload.request.risk,
6691
+ question: payload.request.question,
6692
+ decision,
6693
+ rationale
6694
+ }
6695
+ });
6696
+ });
5752
6697
  return () => {
5753
6698
  offRequested();
5754
6699
  offAnswered();
5755
6700
  offAskHuman();
5756
6701
  offDenied();
6702
+ offIntervention();
5757
6703
  };
5758
6704
  }, [events, dispatch]);
5759
6705
  }
@@ -5765,7 +6711,7 @@ function labelFor2(labelsRef, id, name) {
5765
6711
  const n = m.size + 1;
5766
6712
  const v = {
5767
6713
  label: name && name !== id ? name : `AGENT#${n}`,
5768
- color: expectDefined(STREAM_COLORS2[(n - 1) % STREAM_COLORS2.length])
6714
+ color: expectDefined$1(STREAM_COLORS2[(n - 1) % STREAM_COLORS2.length])
5769
6715
  };
5770
6716
  m.set(id, v);
5771
6717
  return v;
@@ -6034,6 +6980,10 @@ function useAutoPhaseEvents(subscribeAutoPhase, dispatch, stateRef) {
6034
6980
  if (!p.kept) dispatch({ type: "worktreeRemove", handleId: p.handleId });
6035
6981
  break;
6036
6982
  }
6983
+ case "countdown.tick": {
6984
+ dispatch({ type: "countdownTick", remainingSeconds: payload.remaining });
6985
+ break;
6986
+ }
6037
6987
  }
6038
6988
  };
6039
6989
  return subscribeAutoPhase(handler);
@@ -6294,6 +7244,52 @@ function buildSteeringPreamble(snapshot, newDirection) {
6294
7244
  lines.push("]");
6295
7245
  return lines.join("\n");
6296
7246
  }
7247
+ var MAX_TOOL_STREAM_RETAINED_CHARS = 1e5;
7248
+ var MAX_RETAINED_INPUT_CHARS = 2048;
7249
+ var MAX_RETAINED_INPUT_DEPTH = 4;
7250
+ var MAX_RETAINED_INPUT_ITEMS = 64;
7251
+ function pruneToolInput(value, depth = 0) {
7252
+ if (typeof value === "string") {
7253
+ 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;
7254
+ }
7255
+ if (value === null || typeof value !== "object") return value;
7256
+ if (depth >= MAX_RETAINED_INPUT_DEPTH) return "[pruned: too deep]";
7257
+ if (Array.isArray(value)) {
7258
+ const head = value.slice(0, MAX_RETAINED_INPUT_ITEMS).map((v) => pruneToolInput(v, depth + 1));
7259
+ if (value.length > MAX_RETAINED_INPUT_ITEMS) {
7260
+ head.push(`[pruned: ${value.length - MAX_RETAINED_INPUT_ITEMS} more items]`);
7261
+ }
7262
+ return head;
7263
+ }
7264
+ const out = {};
7265
+ let n = 0;
7266
+ for (const [k, v] of Object.entries(value)) {
7267
+ if (n++ >= MAX_RETAINED_INPUT_ITEMS) {
7268
+ out["\u2026"] = "[pruned: more keys]";
7269
+ break;
7270
+ }
7271
+ out[k] = pruneToolInput(v, depth + 1);
7272
+ }
7273
+ return out;
7274
+ }
7275
+ function firstSelectable(items) {
7276
+ const idx = items.findIndex((it) => it.key !== "__divider__");
7277
+ return idx >= 0 ? idx : 0;
7278
+ }
7279
+ function skipDivider(items, idx, dir) {
7280
+ let i = idx;
7281
+ for (let steps = 0; steps < items.length; steps++) {
7282
+ const item = items[i];
7283
+ if (!item || item.key === "__divider__") {
7284
+ i += dir;
7285
+ if (i < 0) i = items.length - 1;
7286
+ if (i >= items.length) i = 0;
7287
+ continue;
7288
+ }
7289
+ return i;
7290
+ }
7291
+ return idx;
7292
+ }
6297
7293
  function reducer(state, action) {
6298
7294
  switch (action.type) {
6299
7295
  case "addEntry": {
@@ -6301,7 +7297,8 @@ function reducer(state, action) {
6301
7297
  if ((e.kind === "user" || e.kind === "assistant" || e.kind === "info" || e.kind === "warn" || e.kind === "error" || e.kind === "turn-summary") && !e.text?.trim()) {
6302
7298
  return state;
6303
7299
  }
6304
- const appended = [...state.entries, { ...action.entry, id: state.nextId }];
7300
+ const stored = e.kind === "tool" && e.input !== void 0 ? { ...e, input: pruneToolInput(e.input) } : e;
7301
+ const appended = [...state.entries, { ...stored, id: state.nextId }];
6305
7302
  return { ...state, entries: appended, nextId: state.nextId + 1 };
6306
7303
  }
6307
7304
  case "setBuffer":
@@ -6425,9 +7422,11 @@ function reducer(state, action) {
6425
7422
  case "toolStreamAppend": {
6426
7423
  const cur = state.toolStream;
6427
7424
  if (cur && cur.toolUseId === action.toolUseId) {
7425
+ const combined = cur.text + action.text;
7426
+ const text = combined.length > MAX_TOOL_STREAM_RETAINED_CHARS ? combined.slice(-MAX_TOOL_STREAM_RETAINED_CHARS) : combined;
6428
7427
  return {
6429
7428
  ...state,
6430
- toolStream: { ...cur, text: cur.text + action.text }
7429
+ toolStream: { ...cur, text }
6431
7430
  };
6432
7431
  }
6433
7432
  return {
@@ -6614,6 +7613,35 @@ function reducer(state, action) {
6614
7613
  ...state,
6615
7614
  autonomyPicker: { ...state.autonomyPicker, hint: action.text }
6616
7615
  };
7616
+ case "resumePickerOpen":
7617
+ return {
7618
+ ...state,
7619
+ resumePicker: { open: true, sessions: action.sessions, selected: 0, busy: false, hint: void 0, error: void 0 }
7620
+ };
7621
+ case "resumePickerClose":
7622
+ return {
7623
+ ...state,
7624
+ resumePicker: { open: false, sessions: [], selected: 0, busy: false, hint: void 0, error: void 0 }
7625
+ };
7626
+ case "resumePickerMove": {
7627
+ const nr = state.resumePicker.sessions.length;
7628
+ if (nr === 0) return state;
7629
+ const nextR = (state.resumePicker.selected + action.delta + nr) % nr;
7630
+ return { ...state, resumePicker: { ...state.resumePicker, selected: nextR } };
7631
+ }
7632
+ case "resumePickerBusy":
7633
+ return { ...state, resumePicker: { ...state.resumePicker, busy: action.on } };
7634
+ case "resumePickerHint":
7635
+ return { ...state, resumePicker: { ...state.resumePicker, hint: action.text } };
7636
+ case "resumePickerError":
7637
+ return { ...state, resumePicker: { ...state.resumePicker, error: action.text, busy: false } };
7638
+ case "replaceHistory": {
7639
+ const banners = state.entries.filter((e) => e.kind === "banner");
7640
+ const maxBannerId = banners.length > 0 ? Math.max(...banners.map((b) => b.id)) : 0;
7641
+ const shifted = action.entries.map((e, i) => ({ ...e, id: maxBannerId + 1 + i }));
7642
+ const nextId = maxBannerId + 1 + shifted.length;
7643
+ return { ...state, entries: [...banners, ...shifted], nextId, historyGen: state.historyGen + 1 };
7644
+ }
6617
7645
  case "settingsOpen":
6618
7646
  return {
6619
7647
  ...state,
@@ -6641,6 +7669,8 @@ function reducer(state, action) {
6641
7669
  maxIterations: action.maxIterations,
6642
7670
  autoProceedMaxIterations: action.autoProceedMaxIterations,
6643
7671
  enhanceDelayMs: action.enhanceDelayMs,
7672
+ enhanceEnabled: action.enhanceEnabled,
7673
+ enhanceLanguage: action.enhanceLanguage,
6644
7674
  debugStream: action.debugStream,
6645
7675
  configScope: action.configScope,
6646
7676
  hint: void 0
@@ -6669,13 +7699,13 @@ function reducer(state, action) {
6669
7699
  const i = SETTINGS_MODES.indexOf(sp.mode);
6670
7700
  const base = i < 0 ? 0 : i;
6671
7701
  const next = (base + action.delta + SETTINGS_MODES.length) % SETTINGS_MODES.length;
6672
- return { ...state, settingsPicker: { ...sp, mode: expectDefined(SETTINGS_MODES[next]), hint: void 0 } };
7702
+ return { ...state, settingsPicker: { ...sp, mode: expectDefined$1(SETTINGS_MODES[next]), hint: void 0 } };
6673
7703
  }
6674
7704
  if (f === 1) {
6675
7705
  const j = DELAY_PRESETS_MS.indexOf(sp.delayMs);
6676
7706
  const base = j < 0 ? 0 : j;
6677
7707
  const next = (base + action.delta + DELAY_PRESETS_MS.length) % DELAY_PRESETS_MS.length;
6678
- return { ...state, settingsPicker: { ...sp, delayMs: expectDefined(DELAY_PRESETS_MS[next]), hint: void 0 } };
7708
+ return { ...state, settingsPicker: { ...sp, delayMs: expectDefined$1(DELAY_PRESETS_MS[next]), hint: void 0 } };
6679
7709
  }
6680
7710
  if (f === 2) return { ...state, settingsPicker: { ...sp, titleAnimation: !sp.titleAnimation, hint: void 0 } };
6681
7711
  if (f === 3) return { ...state, settingsPicker: { ...sp, yolo: !sp.yolo, hint: void 0 } };
@@ -6693,50 +7723,99 @@ function reducer(state, action) {
6693
7723
  const i = COMPACTOR_STRATEGIES.indexOf(sp.contextStrategy);
6694
7724
  const base = i < 0 ? 0 : i;
6695
7725
  const next = (base + action.delta + COMPACTOR_STRATEGIES.length) % COMPACTOR_STRATEGIES.length;
6696
- return { ...state, settingsPicker: { ...sp, contextStrategy: expectDefined(COMPACTOR_STRATEGIES[next]), hint: void 0 } };
7726
+ return { ...state, settingsPicker: { ...sp, contextStrategy: expectDefined$1(COMPACTOR_STRATEGIES[next]), hint: void 0 } };
6697
7727
  }
6698
7728
  if (f === 15) {
6699
7729
  const i = LOG_LEVELS.indexOf(sp.logLevel);
6700
7730
  const base = i < 0 ? 0 : i;
6701
7731
  const next = (base + action.delta + LOG_LEVELS.length) % LOG_LEVELS.length;
6702
- return { ...state, settingsPicker: { ...sp, logLevel: expectDefined(LOG_LEVELS[next]), hint: void 0 } };
7732
+ return { ...state, settingsPicker: { ...sp, logLevel: expectDefined$1(LOG_LEVELS[next]), hint: void 0 } };
6703
7733
  }
6704
7734
  if (f === 16) {
6705
7735
  const i = AUDIT_LEVELS.indexOf(sp.auditLevel);
6706
7736
  const base = i < 0 ? 0 : i;
6707
7737
  const next = (base + action.delta + AUDIT_LEVELS.length) % AUDIT_LEVELS.length;
6708
- return { ...state, settingsPicker: { ...sp, auditLevel: expectDefined(AUDIT_LEVELS[next]), hint: void 0 } };
7738
+ return { ...state, settingsPicker: { ...sp, auditLevel: expectDefined$1(AUDIT_LEVELS[next]), hint: void 0 } };
6709
7739
  }
6710
7740
  if (f === 17) return { ...state, settingsPicker: { ...sp, indexOnStart: !sp.indexOnStart, hint: void 0 } };
6711
7741
  if (f === 18) {
6712
7742
  const j = MAX_ITERATIONS_PRESETS.indexOf(sp.maxIterations);
6713
7743
  const base = j < 0 ? 0 : j;
6714
7744
  const next = (base + action.delta + MAX_ITERATIONS_PRESETS.length) % MAX_ITERATIONS_PRESETS.length;
6715
- return { ...state, settingsPicker: { ...sp, maxIterations: expectDefined(MAX_ITERATIONS_PRESETS[next]), hint: void 0 } };
7745
+ return { ...state, settingsPicker: { ...sp, maxIterations: expectDefined$1(MAX_ITERATIONS_PRESETS[next]), hint: void 0 } };
6716
7746
  }
6717
7747
  if (f === 19) {
6718
7748
  const aj = AUTO_PROCEED_MAX_PRESETS.indexOf(sp.autoProceedMaxIterations);
6719
7749
  const abase = aj < 0 ? 0 : aj;
6720
7750
  const anext = (abase + action.delta + AUTO_PROCEED_MAX_PRESETS.length) % AUTO_PROCEED_MAX_PRESETS.length;
6721
- return { ...state, settingsPicker: { ...sp, autoProceedMaxIterations: expectDefined(AUTO_PROCEED_MAX_PRESETS[anext]), hint: void 0 } };
7751
+ return { ...state, settingsPicker: { ...sp, autoProceedMaxIterations: expectDefined$1(AUTO_PROCEED_MAX_PRESETS[anext]), hint: void 0 } };
6722
7752
  }
6723
7753
  if (f === 20) {
6724
7754
  const ej = ENHANCE_DELAY_PRESETS.indexOf(sp.enhanceDelayMs);
6725
7755
  const ebase = ej < 0 ? 0 : ej;
6726
7756
  const enext = (ebase + action.delta + ENHANCE_DELAY_PRESETS.length) % ENHANCE_DELAY_PRESETS.length;
6727
- return { ...state, settingsPicker: { ...sp, enhanceDelayMs: expectDefined(ENHANCE_DELAY_PRESETS[enext]), hint: void 0 } };
7757
+ return { ...state, settingsPicker: { ...sp, enhanceDelayMs: expectDefined$1(ENHANCE_DELAY_PRESETS[enext]), hint: void 0 } };
6728
7758
  }
6729
- if (f === 21) return { ...state, settingsPicker: { ...sp, debugStream: !sp.debugStream, hint: void 0 } };
6730
- if (f === 22) {
7759
+ if (f === 20) return { ...state, settingsPicker: { ...sp, debugStream: !sp.debugStream, hint: void 0 } };
7760
+ if (f === 21) {
6731
7761
  const i = CONFIG_SCOPES.indexOf(sp.configScope);
6732
7762
  const base = i < 0 ? 0 : i;
6733
7763
  const next = (base + action.delta + CONFIG_SCOPES.length) % CONFIG_SCOPES.length;
6734
- return { ...state, settingsPicker: { ...sp, configScope: expectDefined(CONFIG_SCOPES[next]), hint: void 0 } };
7764
+ return { ...state, settingsPicker: { ...sp, configScope: expectDefined$1(CONFIG_SCOPES[next]), hint: void 0 } };
7765
+ }
7766
+ if (f === 22) return { ...state, settingsPicker: { ...sp, enhanceEnabled: !sp.enhanceEnabled, hint: void 0 } };
7767
+ if (f === 23) {
7768
+ const i = ENHANCE_LANGUAGES.indexOf(sp.enhanceLanguage);
7769
+ const base = i < 0 ? 0 : i;
7770
+ const next = (base + action.delta + ENHANCE_LANGUAGES.length) % ENHANCE_LANGUAGES.length;
7771
+ return { ...state, settingsPicker: { ...sp, enhanceLanguage: expectDefined$1(ENHANCE_LANGUAGES[next]), hint: void 0 } };
6735
7772
  }
6736
7773
  return state;
6737
7774
  }
6738
7775
  case "settingsHint":
6739
7776
  return { ...state, settingsPicker: { ...state.settingsPicker, hint: action.text } };
7777
+ case "projectPickerOpen":
7778
+ return {
7779
+ ...state,
7780
+ projectPicker: {
7781
+ open: true,
7782
+ allItems: action.items,
7783
+ items: action.items,
7784
+ selected: firstSelectable(action.items),
7785
+ filter: "",
7786
+ hint: void 0
7787
+ }
7788
+ };
7789
+ case "projectPickerClose":
7790
+ return {
7791
+ ...state,
7792
+ projectPicker: { open: false, allItems: [], items: [], selected: 0, filter: "", hint: void 0 }
7793
+ };
7794
+ case "projectPickerMove": {
7795
+ const cur = state.projectPicker;
7796
+ const list = cur.items;
7797
+ if (list.length === 0) return state;
7798
+ const nextRaw = (cur.selected + action.delta + list.length) % list.length;
7799
+ const next = skipDivider(list, nextRaw, action.delta > 0 ? 1 : -1);
7800
+ return { ...state, projectPicker: { ...cur, selected: next } };
7801
+ }
7802
+ case "projectPickerFilter": {
7803
+ const cur = state.projectPicker;
7804
+ const filtered = action.filter ? cur.allItems.filter(
7805
+ (item) => item.kind !== "project" || item.label.toLowerCase().includes(action.filter.toLowerCase()) || (item.subtitle ?? "").toLowerCase().includes(action.filter.toLowerCase())
7806
+ ) : cur.allItems;
7807
+ return {
7808
+ ...state,
7809
+ projectPicker: {
7810
+ ...cur,
7811
+ filter: action.filter,
7812
+ items: filtered,
7813
+ selected: firstSelectable(filtered)
7814
+ }
7815
+ };
7816
+ }
7817
+ case "projectPickerHint":
7818
+ return { ...state, projectPicker: { ...state.projectPicker, hint: action.text } };
6740
7819
  case "confirmOpen":
6741
7820
  return { ...state, confirmQueue: [...state.confirmQueue, action.info] };
6742
7821
  case "confirmClose":
@@ -7412,6 +8491,47 @@ function reducer(state, action) {
7412
8491
  if (state.debugStreamStats === null) return state;
7413
8492
  return { ...state, debugStreamStats: null };
7414
8493
  }
8494
+ case "toggleSessionsPanel": {
8495
+ return { ...state, sessionsPanelOpen: !state.sessionsPanelOpen };
8496
+ }
8497
+ case "sessionsPanelSet": {
8498
+ return {
8499
+ ...state,
8500
+ sessionsPanel: { sessions: action.sessions, busy: false, selected: action.sessions.length > 0 ? 0 : -1 }
8501
+ };
8502
+ }
8503
+ case "sessionsPanelMove": {
8504
+ const cur = state.sessionsPanel;
8505
+ if (cur.sessions.length === 0) return state;
8506
+ const next = (cur.selected + action.delta + cur.sessions.length) % cur.sessions.length;
8507
+ return { ...state, sessionsPanel: { ...cur, selected: next } };
8508
+ }
8509
+ case "sessionsPanelBusy": {
8510
+ return {
8511
+ ...state,
8512
+ sessionsPanel: { ...state.sessionsPanel, busy: action.on }
8513
+ };
8514
+ }
8515
+ case "sessionResumeConfirmSet": {
8516
+ return {
8517
+ ...state,
8518
+ sessionResumeConfirm: { sessionId: action.sessionId, sessionName: action.sessionName }
8519
+ };
8520
+ }
8521
+ case "sessionResumeConfirmClear": {
8522
+ return { ...state, sessionResumeConfirm: null };
8523
+ }
8524
+ // --- Auto-proceed countdown ---
8525
+ case "countdownTick": {
8526
+ if (action.remainingSeconds <= 0) {
8527
+ return state.countdown ? { ...state, countdown: null } : state;
8528
+ }
8529
+ return { ...state, countdown: { remainingSeconds: action.remainingSeconds } };
8530
+ }
8531
+ case "countdownEnded": {
8532
+ if (state.countdown === null) return state;
8533
+ return { ...state, countdown: null };
8534
+ }
7415
8535
  }
7416
8536
  }
7417
8537
  var INPUT_PROMPT = "\u203A ";
@@ -7420,7 +8540,7 @@ function selectedSlashCommandLine(picker) {
7420
8540
  const picked = picker.matches[picker.selected];
7421
8541
  return picked ? `/${picked.name}` : null;
7422
8542
  }
7423
- function rehydrateHistory(messages, startId) {
8543
+ function rehydrateHistory(messages, startId, toolCalls) {
7424
8544
  const entries = [];
7425
8545
  let nextId = startId;
7426
8546
  for (const msg of messages) {
@@ -7434,6 +8554,20 @@ function rehydrateHistory(messages, startId) {
7434
8554
  entries.push({ id: nextId++, kind: "assistant", text: trimmed });
7435
8555
  }
7436
8556
  }
8557
+ if (toolCalls && toolCalls.length > 0) {
8558
+ for (const tc of toolCalls) {
8559
+ entries.push({
8560
+ id: nextId++,
8561
+ kind: "tool",
8562
+ name: tc.name,
8563
+ durationMs: tc.durationMs,
8564
+ ok: tc.ok,
8565
+ outputBytes: tc.outputBytes,
8566
+ outputTokens: tc.outputTokens,
8567
+ outputLines: tc.outputLines
8568
+ });
8569
+ }
8570
+ }
7437
8571
  return entries;
7438
8572
  }
7439
8573
  var PASTE_THRESHOLD_CHARS = 200;
@@ -7448,9 +8582,11 @@ function App({
7448
8582
  model,
7449
8583
  banner = true,
7450
8584
  queueStore,
8585
+ onQueueChange,
7451
8586
  yolo = false,
7452
8587
  chime = false,
7453
8588
  confirmExit = true,
8589
+ mouse = false,
7454
8590
  enhanceEnabled = true,
7455
8591
  enhanceController,
7456
8592
  enhanceDelayMs = 15e3,
@@ -7472,12 +8608,16 @@ function App({
7472
8608
  getSettings,
7473
8609
  saveSettings,
7474
8610
  predictNext,
8611
+ onSuggestionsParsed,
8612
+ getSuggestions,
7475
8613
  switchAutonomy,
7476
8614
  effectiveMaxContext,
7477
8615
  onExit,
7478
8616
  director,
7479
8617
  fleetRoster,
7480
8618
  onClearHistory,
8619
+ listSessions,
8620
+ onResumeSession,
7481
8621
  fleetStreamController,
7482
8622
  statuslineHiddenItems,
7483
8623
  setStatuslineHiddenItems,
@@ -7488,7 +8628,14 @@ function App({
7488
8628
  modeLabel,
7489
8629
  getModeLabel,
7490
8630
  registerDebugStreamCallback,
7491
- restoreDebugStreamCallback
8631
+ restoreDebugStreamCallback,
8632
+ restoredToolCalls,
8633
+ getProjectPickerItems,
8634
+ onProjectSelect,
8635
+ requestExit,
8636
+ getLiveSessions,
8637
+ onSwitchToSession,
8638
+ initialAgentsMonitorOpen
7492
8639
  }) {
7493
8640
  const { exit } = useApp();
7494
8641
  const { stdout } = useStdout();
@@ -7499,6 +8646,8 @@ function App({
7499
8646
  const [autonomyLive, setAutonomyLive] = useState(getAutonomy?.() ?? "off");
7500
8647
  const [liveModeLabel, setLiveModeLabel] = useState(modeLabel ?? "");
7501
8648
  const [hiddenItems, setHiddenItems] = useState(statuslineHiddenItems);
8649
+ const [sessionCount, setSessionCount] = useState(0);
8650
+ const prevBranchRef = useRef(null);
7502
8651
  const [indexState, setIndexState] = useState(() => getIndexState());
7503
8652
  useEffect(() => {
7504
8653
  setIndexState(getIndexState());
@@ -7549,7 +8698,8 @@ function App({
7549
8698
  return rehydrateHistory(
7550
8699
  visible,
7551
8700
  /* startId */
7552
- 1
8701
+ 1,
8702
+ restoredToolCalls
7553
8703
  );
7554
8704
  })();
7555
8705
  const initialNextId = 1 + restoredEntries.length;
@@ -7569,6 +8719,7 @@ function App({
7569
8719
  ] : [],
7570
8720
  ...restoredEntries
7571
8721
  ],
8722
+ historyGen: 0,
7572
8723
  buffer: "",
7573
8724
  cursor: 0,
7574
8725
  streamingText: "",
@@ -7598,7 +8749,9 @@ function App({
7598
8749
  searchQuery: ""
7599
8750
  },
7600
8751
  autonomyPicker: { open: false, options: [], selected: 0 },
7601
- 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" },
8752
+ resumePicker: { open: false, sessions: [], selected: 0, busy: false, hint: void 0, error: void 0 },
8753
+ 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" },
8754
+ projectPicker: { open: false, allItems: [], items: [], selected: 0, filter: "", hint: void 0 },
7602
8755
  confirmQueue: [],
7603
8756
  enhance: null,
7604
8757
  enhanceEnabled,
@@ -7620,12 +8773,15 @@ function App({
7620
8773
  fleetConcurrency: 4,
7621
8774
  streamFleet: true,
7622
8775
  monitorOpen: false,
7623
- agentsMonitorOpen: false,
8776
+ agentsMonitorOpen: initialAgentsMonitorOpen ?? false,
7624
8777
  helpOpen: false,
7625
8778
  todosMonitorOpen: false,
7626
8779
  queuePanelOpen: false,
7627
8780
  processListOpen: false,
7628
8781
  goalPanelOpen: false,
8782
+ sessionsPanelOpen: false,
8783
+ sessionsPanel: { sessions: [], busy: false, selected: -1 },
8784
+ sessionResumeConfirm: null,
7629
8785
  collabSession: null,
7630
8786
  checkpoints: [],
7631
8787
  rewindOverlay: null,
@@ -7638,7 +8794,8 @@ function App({
7638
8794
  totalLines: 0,
7639
8795
  viewportRows: 0,
7640
8796
  pendingNewLines: 0,
7641
- debugStreamStats: null
8797
+ debugStreamStats: null,
8798
+ countdown: null
7642
8799
  });
7643
8800
  const builderRef = useRef(null);
7644
8801
  if (builderRef.current === null) {
@@ -7652,9 +8809,23 @@ function App({
7652
8809
  const lastEnterAtRef = useRef(0);
7653
8810
  const tokenPreviewsRef = useRef(/* @__PURE__ */ new Map());
7654
8811
  const projectName = React5.useMemo(() => {
7655
- const base = path2.basename(projectRoot);
7656
- return base && base !== path2.sep ? base : void 0;
8812
+ const base = path4.basename(projectRoot);
8813
+ return base && base !== path4.sep ? base : void 0;
7657
8814
  }, [projectRoot]);
8815
+ const [workingDirChip, setWorkingDirChip] = React5.useState(() => {
8816
+ const ctx = agent.ctx;
8817
+ if (ctx.workingDir && ctx.workingDir !== projectRoot) {
8818
+ return path4.relative(projectRoot, ctx.workingDir) || ".";
8819
+ }
8820
+ return void 0;
8821
+ });
8822
+ React5.useEffect(() => {
8823
+ const ctx = agent.ctx;
8824
+ return ctx.onWorkingDirChanged((newDir) => {
8825
+ const rel = path4.relative(projectRoot, newDir) || ".";
8826
+ setWorkingDirChip(rel === "." ? void 0 : rel);
8827
+ });
8828
+ }, [agent.ctx, projectRoot]);
7658
8829
  const chimeRef = useRef(chime);
7659
8830
  chimeRef.current = chime;
7660
8831
  const confirmExitRef = useRef(confirmExit);
@@ -7666,6 +8837,48 @@ function App({
7666
8837
  stateRef.current = state;
7667
8838
  const draftRef = useRef({ buffer: state.buffer, cursor: state.cursor });
7668
8839
  draftRef.current = { buffer: state.buffer, cursor: state.cursor };
8840
+ const [mouseMode, setMouseMode] = useState(mouse);
8841
+ const pickerOverlayOpen = state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || state.projectPicker.open || state.slashPicker.open || state.picker.open;
8842
+ const mouseTrackingOn = mouseMode || pickerOverlayOpen;
8843
+ const mouseWrittenRef = useRef(false);
8844
+ useEffect(() => {
8845
+ if (mouseWrittenRef.current === mouseTrackingOn) return;
8846
+ mouseWrittenRef.current = mouseTrackingOn;
8847
+ try {
8848
+ process.stdout.write(mouseTrackingOn ? MOUSE_CLICK_ON : MOUSE_OFF);
8849
+ } catch {
8850
+ }
8851
+ }, [mouseTrackingOn]);
8852
+ useEffect(
8853
+ () => () => {
8854
+ try {
8855
+ process.stdout.write(MOUSE_OFF);
8856
+ } catch {
8857
+ }
8858
+ },
8859
+ []
8860
+ );
8861
+ const bottomRegionRef = useRef(null);
8862
+ const statusBarWrapRef = useRef(null);
8863
+ const belowStatusBarRef = useRef(null);
8864
+ const [termRows, setTermRows] = useState(stdout?.rows ?? 24);
8865
+ useEffect(() => {
8866
+ const onResize = () => setTermRows(process.stdout.rows ?? 24);
8867
+ process.stdout.on("resize", onResize);
8868
+ return () => {
8869
+ process.stdout.off("resize", onResize);
8870
+ };
8871
+ }, []);
8872
+ useLayoutEffect(() => {
8873
+ if (!mouseMode) return;
8874
+ const node = bottomRegionRef.current;
8875
+ if (!node) return;
8876
+ const { height } = measureElement(node);
8877
+ const vp = Math.max(1, termRows - height);
8878
+ if (vp !== stateRef.current.viewportRows) {
8879
+ dispatch({ type: "setViewportRows", rows: vp });
8880
+ }
8881
+ });
7669
8882
  const handleKeyRef = useRef(null);
7670
8883
  const handleRewindTo = React5.useCallback(
7671
8884
  async (checkpointIndex) => {
@@ -7691,6 +8904,35 @@ function App({
7691
8904
  const t = setInterval(() => setNowTick(Date.now()), 1e4);
7692
8905
  return () => clearInterval(t);
7693
8906
  }, []);
8907
+ useEffect(() => {
8908
+ const approxChars = (v) => {
8909
+ try {
8910
+ return JSON.stringify(v)?.length ?? 0;
8911
+ } catch {
8912
+ return -1;
8913
+ }
8914
+ };
8915
+ return startHeapWatchdog({
8916
+ collectStats: () => ({
8917
+ historyEntries: stateRef.current.entries.length,
8918
+ historyChars: approxChars(stateRef.current.entries),
8919
+ messages: agent.ctx.state.messages.length,
8920
+ messagesChars: approxChars(agent.ctx.state.messages),
8921
+ runningTools: stateRef.current.runningTools.size,
8922
+ // Bytes queued in stdout's writable buffer. On Windows, TTY writes
8923
+ // are asynchronous — a render storm (e.g. high-frequency tool
8924
+ // progress dispatches) queues whole ANSI frames here as live heap
8925
+ // strings, invisible to every other counter.
8926
+ stdoutQueued: process.stdout.writableLength ?? 0
8927
+ }),
8928
+ onWarn: (level, message) => {
8929
+ dispatch({
8930
+ type: "addEntry",
8931
+ entry: { kind: level === "critical" ? "error" : "warn", text: message }
8932
+ });
8933
+ }
8934
+ });
8935
+ }, [agent.ctx]);
7694
8936
  useEffect(() => {
7695
8937
  if (state.goalPanelOpen) refreshGoalSummary();
7696
8938
  }, [state.goalPanelOpen, nowTick, refreshGoalSummary]);
@@ -7701,20 +8943,14 @@ function App({
7701
8943
  return () => clearInterval(t);
7702
8944
  }, [state.enhanceBusy]);
7703
8945
  const todosRef = useRef(JSON.stringify([]));
8946
+ const staleGuardRef = useRef(JSON.stringify({ a: "", y: false, m: "", model: "", provider: "" }));
7704
8947
  useEffect(() => {
7705
8948
  const poll = () => {
7706
- const snap = JSON.stringify(agent.ctx.todos.map((t2) => ({ s: t2.status })));
7707
- if (snap !== todosRef.current) {
7708
- todosRef.current = snap;
8949
+ const todoSnap = JSON.stringify(agent.ctx.todos.map((t2) => ({ s: t2.status })));
8950
+ if (todoSnap !== todosRef.current) {
8951
+ todosRef.current = todoSnap;
7709
8952
  setNowTick(Date.now());
7710
8953
  }
7711
- };
7712
- const t = setInterval(poll, 2e3);
7713
- return () => clearInterval(t);
7714
- }, [agent.ctx.todos]);
7715
- const staleGuardRef = useRef(JSON.stringify({ a: "", y: false, m: "", model: "", provider: "" }));
7716
- useEffect(() => {
7717
- const poll = () => {
7718
8954
  const a = getAutonomy?.() ?? "off";
7719
8955
  const y = getYolo?.() ?? false;
7720
8956
  const m = getModeLabel?.() ?? "";
@@ -7746,23 +8982,162 @@ function App({
7746
8982
  liveModel,
7747
8983
  liveProvider,
7748
8984
  agent.ctx.model,
7749
- agent.ctx.provider
8985
+ agent.ctx.provider,
8986
+ agent.ctx.todos
7750
8987
  ]);
7751
8988
  const [gitInfo, setGitInfo] = React5.useState(null);
7752
8989
  useEffect(() => {
7753
8990
  let cancelled = false;
7754
8991
  const refresh = () => {
7755
8992
  readGitInfo(agent.ctx.cwd).then((info) => {
7756
- if (!cancelled) setGitInfo(info);
7757
- }).catch(() => void 0);
8993
+ if (cancelled) return;
8994
+ setGitInfo(info);
8995
+ if (info && info.branch) {
8996
+ const prev = prevBranchRef.current;
8997
+ if (prev !== null && prev !== info.branch) {
8998
+ const msg = {
8999
+ role: "user",
9000
+ 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.` }]
9001
+ };
9002
+ agent.ctx.messages.push(msg);
9003
+ try {
9004
+ import('@wrongstack/core').then(({ getSessionRegistry }) => {
9005
+ const reg = getSessionRegistry();
9006
+ if (reg) {
9007
+ reg.updateAgents([]).catch(() => {
9008
+ });
9009
+ }
9010
+ }).catch(() => {
9011
+ });
9012
+ } catch {
9013
+ }
9014
+ }
9015
+ prevBranchRef.current = info.branch;
9016
+ }
9017
+ }).catch(() => {
9018
+ if (!cancelled) setGitInfo(null);
9019
+ });
7758
9020
  };
7759
9021
  refresh();
9022
+ if (gitInfo?.branch && prevBranchRef.current === null) {
9023
+ prevBranchRef.current = gitInfo.branch;
9024
+ }
7760
9025
  const t = setInterval(refresh, 5e3);
7761
9026
  return () => {
7762
9027
  cancelled = true;
7763
9028
  clearInterval(t);
7764
9029
  };
7765
- }, [agent.ctx.cwd]);
9030
+ }, [agent.ctx.cwd, gitInfo?.branch]);
9031
+ useEffect(() => {
9032
+ if (!getLiveSessions) return;
9033
+ let cancelled = false;
9034
+ const poll = async () => {
9035
+ try {
9036
+ const sessions = await getLiveSessions();
9037
+ if (!cancelled) setSessionCount(sessions.length);
9038
+ } catch {
9039
+ }
9040
+ };
9041
+ void poll();
9042
+ const t = setInterval(poll, 3e4);
9043
+ if (t.unref) t.unref();
9044
+ return () => {
9045
+ cancelled = true;
9046
+ clearInterval(t);
9047
+ };
9048
+ }, [getLiveSessions]);
9049
+ const [mailboxStatus, setMailboxStatus] = useState({
9050
+ unread: 0,
9051
+ onlineAgents: 0
9052
+ });
9053
+ useEffect(() => {
9054
+ const seenAgents = /* @__PURE__ */ new Set();
9055
+ const unsub1 = events.onPattern("mailbox.unread_count", (_e, payload) => {
9056
+ const p = payload;
9057
+ setMailboxStatus((prev) => ({ ...prev, unread: p?.count ?? 0 }));
9058
+ });
9059
+ const unsub2 = events.onPattern("mailbox.received", (_e, payload) => {
9060
+ const p = payload;
9061
+ setMailboxStatus((prev) => ({
9062
+ ...prev,
9063
+ lastSubject: p?.subject ?? prev.lastSubject,
9064
+ lastFrom: p?.from ?? prev.lastFrom
9065
+ }));
9066
+ });
9067
+ const unsub3 = events.onPattern("mailbox.agent_registered", (_e, payload) => {
9068
+ const p = payload;
9069
+ if (p?.agentId) seenAgents.add(p.agentId);
9070
+ setMailboxStatus((prev) => ({ ...prev, onlineAgents: seenAgents.size }));
9071
+ });
9072
+ const unsub4 = events.onPattern("mailbox.agent_heartbeat", (_e, payload) => {
9073
+ const p = payload;
9074
+ if (p?.agentId) seenAgents.add(p.agentId);
9075
+ setMailboxStatus((prev) => ({ ...prev, onlineAgents: seenAgents.size }));
9076
+ });
9077
+ return () => {
9078
+ unsub1();
9079
+ unsub2();
9080
+ unsub3();
9081
+ unsub4();
9082
+ };
9083
+ }, [events]);
9084
+ const [mailboxPanelOpen, setMailboxPanelOpen] = useState(false);
9085
+ const [mailboxMessages, setMailboxMessages] = useState([]);
9086
+ const [mailboxAgents, setMailboxAgents] = useState([]);
9087
+ useEffect(() => {
9088
+ if (!mailboxPanelOpen) return;
9089
+ const poll = async () => {
9090
+ };
9091
+ void poll();
9092
+ const t = setInterval(poll, 1e4);
9093
+ return () => clearInterval(t);
9094
+ }, [mailboxPanelOpen]);
9095
+ useEffect(() => {
9096
+ const unsub = events.onPattern("mailbox.received", (_e, payload) => {
9097
+ const p = payload;
9098
+ if (!p?.messageId) return;
9099
+ setMailboxMessages((prev) => {
9100
+ if (prev.some((m) => m.id === p.messageId)) return prev;
9101
+ return [
9102
+ {
9103
+ id: p.messageId,
9104
+ from: p.from ?? "unknown",
9105
+ to: "*",
9106
+ type: p.type ?? "note",
9107
+ subject: p.subject ?? "",
9108
+ body: "",
9109
+ priority: "normal",
9110
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
9111
+ readByCount: 0,
9112
+ readByMe: false,
9113
+ completed: false
9114
+ },
9115
+ ...prev
9116
+ ].slice(0, 50);
9117
+ });
9118
+ });
9119
+ const unsub2 = events.onPattern("mailbox.agent_registered", (_e, payload) => {
9120
+ const p = payload;
9121
+ if (!p?.agentId) return;
9122
+ setMailboxAgents((prev) => {
9123
+ if (prev.some((a) => a.agentId === p.agentId)) return prev;
9124
+ return [...prev, {
9125
+ agentId: p.agentId,
9126
+ name: p.name ?? p.agentId,
9127
+ role: p.role,
9128
+ sessionId: p.sessionId ?? "?",
9129
+ status: "idle",
9130
+ lastSeenAt: (/* @__PURE__ */ new Date()).toISOString(),
9131
+ online: true,
9132
+ source: p.source
9133
+ }].slice(0, 30);
9134
+ });
9135
+ });
9136
+ return () => {
9137
+ unsub();
9138
+ unsub2();
9139
+ };
9140
+ }, [events]);
7766
9141
  const maxContext = activeMaxContext ?? agent.ctx.provider.capabilities.maxContext;
7767
9142
  const currentContextTokens = (tokenCounter?.currentRequestTokens()?.input ?? 0) + (tokenCounter?.currentRequestTokens()?.cacheRead ?? 0);
7768
9143
  const contextWindow = useMemo(() => {
@@ -7850,6 +9225,53 @@ function App({
7850
9225
  clearInterval(id);
7851
9226
  };
7852
9227
  }, [agent.ctx.meta]);
9228
+ const [taskCounts, setTaskCounts] = useState(null);
9229
+ useEffect(() => {
9230
+ const taskPath = agent.ctx.meta["task.path"];
9231
+ if (typeof taskPath !== "string" || !taskPath) return;
9232
+ let cancelled = false;
9233
+ const poll = async () => {
9234
+ try {
9235
+ const data = await fs2.readFile(taskPath, "utf8");
9236
+ const parsed = JSON.parse(data);
9237
+ if (cancelled) return;
9238
+ if (!Array.isArray(parsed.tasks)) {
9239
+ setTaskCounts(null);
9240
+ return;
9241
+ }
9242
+ let pending = 0, inProgress = 0, completed = 0, blocked = 0, failed = 0;
9243
+ for (const t of parsed.tasks) {
9244
+ switch (t?.status) {
9245
+ case "completed":
9246
+ completed++;
9247
+ break;
9248
+ case "in_progress":
9249
+ inProgress++;
9250
+ break;
9251
+ case "blocked":
9252
+ blocked++;
9253
+ break;
9254
+ case "failed":
9255
+ failed++;
9256
+ break;
9257
+ default:
9258
+ pending++;
9259
+ break;
9260
+ }
9261
+ }
9262
+ const total = pending + inProgress + completed + blocked + failed;
9263
+ setTaskCounts(total > 0 ? { pending, inProgress, completed, blocked, failed } : null);
9264
+ } catch {
9265
+ if (!cancelled) setTaskCounts(null);
9266
+ }
9267
+ };
9268
+ void poll();
9269
+ const id = setInterval(poll, 3e3);
9270
+ return () => {
9271
+ cancelled = true;
9272
+ clearInterval(id);
9273
+ };
9274
+ }, [agent.ctx.meta]);
7853
9275
  const prevAnyOverlayOpen = useRef(false);
7854
9276
  const prevEntriesCount = useRef(0);
7855
9277
  const prevToolStreamLen = useRef(0);
@@ -7860,7 +9282,7 @@ function App({
7860
9282
  }
7861
9283
  }, []);
7862
9284
  React5.useLayoutEffect(() => {
7863
- 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;
9285
+ 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;
7864
9286
  const overlayClosed = prevAnyOverlayOpen.current && !anyOpenNow;
7865
9287
  const newEntryCommitted = state.entries.length > prevEntriesCount.current;
7866
9288
  const curToolStreamLen = state.toolStream?.text.length ?? 0;
@@ -7887,22 +9309,29 @@ function App({
7887
9309
  ]);
7888
9310
  const resizeGateRef = useRef(0);
7889
9311
  const preResizePanelsRef = useRef(null);
9312
+ const resizeRestoreTimerRef = useRef(null);
9313
+ const mountedRef = useRef(true);
7890
9314
  useEffect(() => {
7891
9315
  const handleResize = () => {
7892
9316
  const seq = ++resizeGateRef.current;
7893
9317
  preResizePanelsRef.current = {
7894
9318
  settings: stateRef.current.settingsPicker.open,
9319
+ projectPicker: stateRef.current.projectPicker.open,
7895
9320
  help: stateRef.current.helpOpen,
7896
9321
  monitor: stateRef.current.monitorOpen,
7897
9322
  agents: stateRef.current.agentsMonitorOpen,
7898
9323
  worktree: stateRef.current.worktreeMonitorOpen,
7899
9324
  todos: stateRef.current.todosMonitorOpen,
7900
9325
  queue: stateRef.current.queuePanelOpen,
7901
- processList: stateRef.current.processListOpen
9326
+ processList: stateRef.current.processListOpen,
9327
+ goalPanel: stateRef.current.goalPanelOpen,
9328
+ sessionsPanel: stateRef.current.sessionsPanelOpen
7902
9329
  };
7903
9330
  if (stateRef.current.settingsPicker.open) dispatch({ type: "settingsClose" });
9331
+ if (stateRef.current.projectPicker.open) dispatch({ type: "projectPickerClose" });
7904
9332
  if (stateRef.current.modelPicker.open) dispatch({ type: "modelPickerClose" });
7905
9333
  if (stateRef.current.autonomyPicker.open) dispatch({ type: "autonomyPickerClose" });
9334
+ if (stateRef.current.resumePicker.open) dispatch({ type: "resumePickerClose" });
7906
9335
  if (stateRef.current.slashPicker.open) dispatch({ type: "slashPickerClose" });
7907
9336
  if (stateRef.current.picker.open) dispatch({ type: "pickerClose" });
7908
9337
  if (stateRef.current.rewindOverlay) dispatch({ type: "rewindOverlayClose" });
@@ -7913,8 +9342,11 @@ function App({
7913
9342
  if (stateRef.current.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
7914
9343
  if (stateRef.current.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
7915
9344
  if (stateRef.current.processListOpen) dispatch({ type: "toggleProcessList" });
9345
+ if (stateRef.current.goalPanelOpen) dispatch({ type: "toggleGoalPanel" });
9346
+ if (stateRef.current.sessionsPanelOpen) dispatch({ type: "toggleSessionsPanel" });
7916
9347
  eraseLiveRegion();
7917
- setTimeout(() => {
9348
+ resizeRestoreTimerRef.current = setTimeout(() => {
9349
+ if (!mountedRef.current) return;
7918
9350
  if (resizeGateRef.current !== seq) return;
7919
9351
  const prev = preResizePanelsRef.current;
7920
9352
  if (!prev) return;
@@ -7943,10 +9375,16 @@ function App({
7943
9375
  maxIterations: sp.maxIterations,
7944
9376
  autoProceedMaxIterations: sp.autoProceedMaxIterations,
7945
9377
  enhanceDelayMs: sp.enhanceDelayMs,
9378
+ enhanceEnabled: sp.enhanceEnabled,
9379
+ enhanceLanguage: sp.enhanceLanguage,
7946
9380
  debugStream: sp.debugStream,
7947
9381
  configScope: sp.configScope
7948
9382
  });
7949
9383
  }
9384
+ if (prev.projectPicker) {
9385
+ const pp = stateRef.current.projectPicker;
9386
+ dispatch({ type: "projectPickerOpen", items: pp.allItems });
9387
+ }
7950
9388
  if (prev.help) dispatch({ type: "toggleHelp" });
7951
9389
  if (prev.monitor) dispatch({ type: "toggleMonitor" });
7952
9390
  if (prev.agents) dispatch({ type: "toggleAgentsMonitor" });
@@ -7954,11 +9392,19 @@ function App({
7954
9392
  if (prev.todos) dispatch({ type: "toggleTodosMonitor" });
7955
9393
  if (prev.queue) dispatch({ type: "toggleQueuePanel" });
7956
9394
  if (prev.processList) dispatch({ type: "toggleProcessList" });
9395
+ if (prev.goalPanel) dispatch({ type: "toggleGoalPanel" });
9396
+ if (prev.sessionsPanel) dispatch({ type: "toggleSessionsPanel" });
7957
9397
  preResizePanelsRef.current = null;
9398
+ resizeRestoreTimerRef.current = null;
7958
9399
  }, 300);
7959
9400
  };
7960
9401
  process.stdout.on("resize", handleResize);
7961
9402
  return () => {
9403
+ if (resizeRestoreTimerRef.current) {
9404
+ clearTimeout(resizeRestoreTimerRef.current);
9405
+ resizeRestoreTimerRef.current = null;
9406
+ }
9407
+ mountedRef.current = false;
7962
9408
  process.stdout.off("resize", handleResize);
7963
9409
  };
7964
9410
  }, [eraseLiveRegion]);
@@ -8059,7 +9505,7 @@ function App({
8059
9505
  dispatch({ type: "pickerClose" });
8060
9506
  return;
8061
9507
  }
8062
- const absPath = path2.isAbsolute(picked) ? picked : path2.join(projectRoot, picked);
9508
+ const absPath = path4.isAbsolute(picked) ? picked : path4.join(projectRoot, picked);
8063
9509
  try {
8064
9510
  const data = await fs2.readFile(absPath, "utf8");
8065
9511
  const token = await builder.registerFile({
@@ -8120,6 +9566,9 @@ function App({
8120
9566
  if (!queueStore) return;
8121
9567
  queueStore.write(state.queue.map(({ displayText, blocks }) => ({ displayText, blocks }))).catch(() => void 0);
8122
9568
  }, [state.queue, queueStore]);
9569
+ useEffect(() => {
9570
+ onQueueChange?.(state.queue.map((q) => q.displayText));
9571
+ }, [state.queue, onQueueChange]);
8123
9572
  useEffect(() => {
8124
9573
  const cmd = createQueueSlashCommand({
8125
9574
  getQueue: () => stateRef.current.queue,
@@ -8255,6 +9704,28 @@ function App({
8255
9704
  const providers = await getPickableProviders();
8256
9705
  dispatch({ type: "modelPickerOpen", providers });
8257
9706
  }, [getPickableProviders]);
9707
+ const openProjectPicker = React5.useCallback(async () => {
9708
+ if (!getProjectPickerItems) return;
9709
+ const items = await getProjectPickerItems();
9710
+ dispatch({ type: "projectPickerOpen", items });
9711
+ }, [getProjectPickerItems]);
9712
+ const loadLiveSessions = React5.useCallback(async () => {
9713
+ if (!getLiveSessions) return;
9714
+ dispatch({ type: "sessionsPanelBusy", on: true });
9715
+ try {
9716
+ const sessions = await getLiveSessions();
9717
+ dispatch({ type: "sessionsPanelSet", sessions });
9718
+ } catch {
9719
+ dispatch({ type: "sessionsPanelBusy", on: false });
9720
+ }
9721
+ }, [getLiveSessions]);
9722
+ useEffect(() => {
9723
+ if (!state.sessionsPanelOpen || !getLiveSessions) return void 0;
9724
+ const t = setInterval(() => {
9725
+ void loadLiveSessions();
9726
+ }, 5e3);
9727
+ return () => clearInterval(t);
9728
+ }, [state.sessionsPanelOpen, getLiveSessions, loadLiveSessions]);
8258
9729
  const openSettings = React5.useCallback(() => {
8259
9730
  if (!getSettings) return;
8260
9731
  const s2 = getSettings();
@@ -8281,38 +9752,87 @@ function App({
8281
9752
  maxIterations: s2.maxIterations ?? 500,
8282
9753
  autoProceedMaxIterations: s2.autoProceedMaxIterations ?? 50,
8283
9754
  enhanceDelayMs: s2.enhanceDelayMs ?? 6e4,
9755
+ enhanceEnabled: s2.enhanceEnabled ?? true,
9756
+ enhanceLanguage: s2.enhanceLanguage ?? "original",
8284
9757
  debugStream: s2.debugStream ?? false,
8285
9758
  configScope: s2.configScope ?? "global"
8286
9759
  });
8287
9760
  }, [getSettings]);
8288
- const [autoProceedCountdown, setAutoProceedCountdown] = useState(null);
8289
- const autoProceedTimerRef = useRef(void 0);
9761
+ const autoSubmitStreakRef = useRef(0);
9762
+ const autoSubmitCapWarnedRef = useRef(false);
9763
+ const [nextStepsRecheck, setNextStepsRecheck] = useState(0);
8290
9764
  useEffect(() => {
8291
- if (autonomyLive !== "auto") {
8292
- clearInterval(autoProceedTimerRef.current);
8293
- autoProceedTimerRef.current = void 0;
8294
- setAutoProceedCountdown(null);
9765
+ autoSubmitStreakRef.current = 0;
9766
+ autoSubmitCapWarnedRef.current = false;
9767
+ }, [autonomyLive]);
9768
+ useEffect(() => {
9769
+ if (state.status !== "idle" || autonomyLive !== "auto") {
9770
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
9771
+ nextStepsAutoSubmitTimerRef.current = void 0;
9772
+ setNextStepsAutoSubmitCountdown(null);
9773
+ nextStepsAutoSubmitSuggestionRef.current = null;
9774
+ return;
9775
+ }
9776
+ if (state.enhance != null || state.enhanceBusy) {
9777
+ return;
9778
+ }
9779
+ if (nextStepsAutoSubmitTimerRef.current != null) {
9780
+ return;
9781
+ }
9782
+ const suggestions = getSuggestions?.() ?? [];
9783
+ if (suggestions.length === 0) {
9784
+ const recheck = setTimeout(() => setNextStepsRecheck((t) => t + 1), 1500);
9785
+ return () => clearTimeout(recheck);
9786
+ }
9787
+ const cfg = getSettings?.();
9788
+ const maxAuto = cfg?.autoProceedMaxIterations ?? 50;
9789
+ if (maxAuto > 0 && autoSubmitStreakRef.current >= maxAuto) {
9790
+ if (!autoSubmitCapWarnedRef.current) {
9791
+ autoSubmitCapWarnedRef.current = true;
9792
+ dispatch({
9793
+ type: "addEntry",
9794
+ entry: {
9795
+ kind: "warn",
9796
+ text: `Auto-proceed paused after ${maxAuto} consecutive automatic turns \u2014 type anything to continue (autonomy stays on).`
9797
+ }
9798
+ });
9799
+ }
8295
9800
  return;
8296
9801
  }
8297
- const cfg = getSettings?.();
8298
9802
  const delay = cfg?.delayMs ?? 45e3;
9803
+ const top = suggestions[0];
9804
+ if (!top) return;
9805
+ nextStepsAutoSubmitSuggestionRef.current = top;
8299
9806
  const start = Date.now();
8300
- setAutoProceedCountdown(Math.ceil(delay / 1e3));
8301
- autoProceedTimerRef.current = setInterval(() => {
9807
+ setNextStepsAutoSubmitCountdown(Math.ceil(delay / 1e3));
9808
+ nextStepsAutoSubmitTimerRef.current = setInterval(() => {
8302
9809
  const remaining = Math.max(0, Math.ceil((delay - (Date.now() - start)) / 1e3));
8303
9810
  if (remaining <= 0) {
8304
- clearInterval(autoProceedTimerRef.current);
8305
- autoProceedTimerRef.current = void 0;
8306
- setAutoProceedCountdown(null);
9811
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
9812
+ nextStepsAutoSubmitTimerRef.current = void 0;
9813
+ setNextStepsAutoSubmitCountdown(null);
9814
+ const suggestion = nextStepsAutoSubmitSuggestionRef.current;
9815
+ nextStepsAutoSubmitSuggestionRef.current = null;
9816
+ if (suggestion) {
9817
+ autoSubmitStreakRef.current += 1;
9818
+ setDraft(suggestion, suggestion.length);
9819
+ void (async () => {
9820
+ const trimmed = suggestion.trim();
9821
+ if (!trimmed) return;
9822
+ const blocks = [{ type: "text", text: trimmed }];
9823
+ dispatch({ type: "addEntry", entry: { kind: "user", text: trimmed } });
9824
+ await runBlocksRef.current(blocks);
9825
+ })();
9826
+ }
8307
9827
  } else {
8308
- setAutoProceedCountdown(remaining);
9828
+ setNextStepsAutoSubmitCountdown(remaining);
8309
9829
  }
8310
9830
  }, 500);
8311
9831
  return () => {
8312
- clearInterval(autoProceedTimerRef.current);
8313
- autoProceedTimerRef.current = void 0;
9832
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
9833
+ nextStepsAutoSubmitTimerRef.current = void 0;
8314
9834
  };
8315
- }, [autonomyLive, getSettings]);
9835
+ }, [state.status, autonomyLive, state.enhance, state.enhanceBusy, nextStepsRecheck, getSettings, getSuggestions, dispatch]);
8316
9836
  const settingsAutoSaveGateRef = useRef(true);
8317
9837
  useEffect(() => {
8318
9838
  if (state.settingsPicker.open) {
@@ -8349,6 +9869,8 @@ function App({
8349
9869
  maxIterations: sp.maxIterations,
8350
9870
  autoProceedMaxIterations: sp.autoProceedMaxIterations,
8351
9871
  enhanceDelayMs: sp.enhanceDelayMs,
9872
+ enhanceEnabled: sp.enhanceEnabled,
9873
+ enhanceLanguage: sp.enhanceLanguage,
8352
9874
  debugStream: sp.debugStream,
8353
9875
  configScope: sp.configScope
8354
9876
  })).then((err) => {
@@ -8411,6 +9933,21 @@ function App({
8411
9933
  slashRegistry.unregister("settings");
8412
9934
  };
8413
9935
  }, [slashRegistry, getSettings, saveSettings, openSettings]);
9936
+ useEffect(() => {
9937
+ const cmd = {
9938
+ name: "mailbox",
9939
+ aliases: ["inbox", "mail"],
9940
+ description: "Toggle the inter-agent mailbox panel \u2014 messages, read receipts, online agents.",
9941
+ async run() {
9942
+ setMailboxPanelOpen((prev) => !prev);
9943
+ return { message: void 0 };
9944
+ }
9945
+ };
9946
+ slashRegistry.register(cmd, "tui", { official: true });
9947
+ return () => {
9948
+ slashRegistry.unregister("mailbox");
9949
+ };
9950
+ }, [slashRegistry]);
8414
9951
  useEffect(() => {
8415
9952
  if (!switchAutonomy) return;
8416
9953
  const cmd = {
@@ -8427,6 +9964,34 @@ function App({
8427
9964
  slashRegistry.unregister("autonomy");
8428
9965
  };
8429
9966
  }, [slashRegistry, switchAutonomy]);
9967
+ useEffect(() => {
9968
+ const cmd = {
9969
+ name: "resume",
9970
+ aliases: ["load"],
9971
+ description: "Resume a previous session \u2014 pick from a list of recent sessions.",
9972
+ async run() {
9973
+ if (!listSessions) {
9974
+ return { message: "Session listing not available." };
9975
+ }
9976
+ try {
9977
+ const sessions = await listSessions(20);
9978
+ if (sessions.length === 0) {
9979
+ return { message: "No saved sessions." };
9980
+ }
9981
+ dispatch({ type: "resumePickerOpen", sessions });
9982
+ } catch (err) {
9983
+ return {
9984
+ message: err instanceof Error ? err.message : String(err)
9985
+ };
9986
+ }
9987
+ return { message: void 0 };
9988
+ }
9989
+ };
9990
+ slashRegistry.register(cmd, "tui", { official: true });
9991
+ return () => {
9992
+ slashRegistry.unregister("resume");
9993
+ };
9994
+ }, [slashRegistry, listSessions]);
8430
9995
  useEffect(() => {
8431
9996
  const FLUSH_MS2 = 100;
8432
9997
  const flush = () => {
@@ -8619,6 +10184,9 @@ function App({
8619
10184
  }, [state.enhanceEnabled]);
8620
10185
  const enhanceAbortRef = useRef(null);
8621
10186
  const [enhanceCountdown, setEnhanceCountdown] = useState(null);
10187
+ const [nextStepsAutoSubmitCountdown, setNextStepsAutoSubmitCountdown] = useState(null);
10188
+ const nextStepsAutoSubmitSuggestionRef = useRef(null);
10189
+ const nextStepsAutoSubmitTimerRef = useRef(void 0);
8622
10190
  useTuiEventBridge({
8623
10191
  events,
8624
10192
  dispatch,
@@ -8826,7 +10394,28 @@ function App({
8826
10394
  return;
8827
10395
  }
8828
10396
  }
8829
- const isEnter = key.return || input === "\r" || input === "\n";
10397
+ const overlaySelectable = state.modelPicker.open || state.autonomyPicker.open || state.resumePicker.open || state.settingsPicker.open || state.projectPicker.open || state.slashPicker.open || state.picker.open;
10398
+ const clickConfirm = overlaySelectable && key.mouse?.kind === "press" && key.mouse.button === "left";
10399
+ const clickCancel = overlaySelectable && key.mouse?.kind === "press" && key.mouse.button === "right";
10400
+ const isEnter = key.return || input === "\r" || input === "\n" || clickConfirm;
10401
+ if (clickCancel) {
10402
+ if (state.modelPicker.open) {
10403
+ dispatch(
10404
+ state.modelPicker.step === "model" ? { type: "modelPickerBack" } : { type: "modelPickerClose" }
10405
+ );
10406
+ } else if (state.autonomyPicker.open) {
10407
+ dispatch({ type: "autonomyPickerClose" });
10408
+ } else if (state.resumePicker.open) {
10409
+ dispatch({ type: "resumePickerClose" });
10410
+ } else if (state.settingsPicker.open) {
10411
+ dispatch({ type: "settingsClose" });
10412
+ } else if (state.slashPicker.open) {
10413
+ dispatch({ type: "slashPickerClose" });
10414
+ } else if (state.picker.open) {
10415
+ dispatch({ type: "pickerClose" });
10416
+ }
10417
+ return;
10418
+ }
8830
10419
  if (state.modelPicker.open) {
8831
10420
  if (key.escape) {
8832
10421
  if (state.modelPicker.step === "model") {
@@ -8836,6 +10425,10 @@ function App({
8836
10425
  }
8837
10426
  return;
8838
10427
  }
10428
+ if (key.mouse?.kind === "wheel") {
10429
+ dispatch({ type: "modelPickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10430
+ return;
10431
+ }
8839
10432
  if (key.upArrow) {
8840
10433
  dispatch({ type: "modelPickerMove", delta: -1 });
8841
10434
  return;
@@ -8901,6 +10494,10 @@ function App({
8901
10494
  dispatch({ type: "autonomyPickerClose" });
8902
10495
  return;
8903
10496
  }
10497
+ if (key.mouse?.kind === "wheel") {
10498
+ dispatch({ type: "autonomyPickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10499
+ return;
10500
+ }
8904
10501
  if (key.upArrow) {
8905
10502
  dispatch({ type: "autonomyPickerMove", delta: -1 });
8906
10503
  return;
@@ -8925,11 +10522,64 @@ function App({
8925
10522
  }
8926
10523
  return;
8927
10524
  }
10525
+ if (state.resumePicker.open) {
10526
+ if (key.escape) {
10527
+ dispatch({ type: "resumePickerClose" });
10528
+ return;
10529
+ }
10530
+ if (key.mouse?.kind === "wheel") {
10531
+ dispatch({ type: "resumePickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10532
+ return;
10533
+ }
10534
+ if (key.upArrow) {
10535
+ dispatch({ type: "resumePickerMove", delta: -1 });
10536
+ return;
10537
+ }
10538
+ if (key.downArrow) {
10539
+ dispatch({ type: "resumePickerMove", delta: 1 });
10540
+ return;
10541
+ }
10542
+ if (isEnter) {
10543
+ const now = Date.now();
10544
+ if (now - lastEnterAtRef.current < 50) return;
10545
+ lastEnterAtRef.current = now;
10546
+ const session = state.resumePicker.sessions[state.resumePicker.selected];
10547
+ if (!session || session.isCurrent) return;
10548
+ if (state.resumePicker.busy) return;
10549
+ dispatch({ type: "resumePickerBusy", on: true });
10550
+ onResumeSession?.(session.id).then((result) => {
10551
+ if (!result) {
10552
+ dispatch({ type: "resumePickerError", text: `Failed to resume session ${session.id}.` });
10553
+ return;
10554
+ }
10555
+ dispatch({ type: "replaceHistory", entries: result.entries, nextId: result.nextId });
10556
+ dispatch({ type: "resumePickerClose" });
10557
+ dispatch({
10558
+ type: "addEntry",
10559
+ entry: {
10560
+ kind: "info",
10561
+ text: `Resumed session ${result.sessionId} \u2014 ${result.entries.length} entries replayed.`
10562
+ }
10563
+ });
10564
+ }).catch((err) => {
10565
+ dispatch({
10566
+ type: "resumePickerError",
10567
+ text: err instanceof Error ? err.message : String(err)
10568
+ });
10569
+ });
10570
+ return;
10571
+ }
10572
+ return;
10573
+ }
8928
10574
  if (state.settingsPicker.open) {
8929
- if (key.escape || key.ctrl && input === "s" || key.fn === 5) {
10575
+ if (key.escape || key.ctrl && input === "s") {
8930
10576
  dispatch({ type: "settingsClose" });
8931
10577
  return;
8932
10578
  }
10579
+ if (key.mouse?.kind === "wheel") {
10580
+ dispatch({ type: "settingsFieldMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10581
+ return;
10582
+ }
8933
10583
  if (key.upArrow) {
8934
10584
  dispatch({ type: "settingsFieldMove", delta: -1 });
8935
10585
  return;
@@ -8955,11 +10605,166 @@ function App({
8955
10605
  }
8956
10606
  return;
8957
10607
  }
10608
+ if (state.projectPicker.open) {
10609
+ if (key.escape) {
10610
+ if (state.projectPicker.filter) {
10611
+ dispatch({ type: "projectPickerFilter", filter: "" });
10612
+ } else {
10613
+ dispatch({ type: "projectPickerClose" });
10614
+ }
10615
+ return;
10616
+ }
10617
+ if (key.mouse?.kind === "wheel") {
10618
+ dispatch({ type: "projectPickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10619
+ return;
10620
+ }
10621
+ if (key.upArrow) {
10622
+ dispatch({ type: "projectPickerMove", delta: -1 });
10623
+ return;
10624
+ }
10625
+ if (key.downArrow) {
10626
+ dispatch({ type: "projectPickerMove", delta: 1 });
10627
+ return;
10628
+ }
10629
+ if (isEnter) {
10630
+ const now = Date.now();
10631
+ if (now - lastEnterAtRef.current < 50) return;
10632
+ lastEnterAtRef.current = now;
10633
+ const items = state.projectPicker.items;
10634
+ const selected = state.projectPicker.selected;
10635
+ if (selected < 0 || selected >= items.length) return;
10636
+ const item = items[selected];
10637
+ if (!item || item.key === "__divider__" || item.key === "quit") {
10638
+ dispatch({ type: "projectPickerClose" });
10639
+ return;
10640
+ }
10641
+ if (item.kind === "project") {
10642
+ onProjectSelect?.(item.key, item.kind);
10643
+ dispatch({ type: "projectPickerClose" });
10644
+ requestExit?.(42);
10645
+ return;
10646
+ }
10647
+ dispatch({ type: "projectPickerClose" });
10648
+ if (item.key === "new-session") {
10649
+ onProjectSelect?.(item.key, item.kind);
10650
+ requestExit?.(42);
10651
+ } else if (item.key === "prev-sessions") {
10652
+ void submit("/resume");
10653
+ }
10654
+ return;
10655
+ }
10656
+ if (input && input.length === 1 && input.charCodeAt(0) >= 32 && input.charCodeAt(0) < 127) {
10657
+ dispatch({ type: "projectPickerFilter", filter: state.projectPicker.filter + input });
10658
+ return;
10659
+ }
10660
+ if (key.backspace) {
10661
+ if (state.projectPicker.filter.length > 0) {
10662
+ dispatch({
10663
+ type: "projectPickerFilter",
10664
+ filter: state.projectPicker.filter.slice(0, -1)
10665
+ });
10666
+ }
10667
+ return;
10668
+ }
10669
+ return;
10670
+ }
10671
+ if (state.sessionsPanelOpen) {
10672
+ if (key.escape) {
10673
+ if (state.sessionResumeConfirm) {
10674
+ dispatch({ type: "sessionResumeConfirmClear" });
10675
+ } else {
10676
+ dispatch({ type: "toggleSessionsPanel" });
10677
+ }
10678
+ return;
10679
+ }
10680
+ if (key.upArrow) {
10681
+ dispatch({ type: "sessionsPanelMove", delta: -1 });
10682
+ return;
10683
+ }
10684
+ if (key.downArrow) {
10685
+ dispatch({ type: "sessionsPanelMove", delta: 1 });
10686
+ return;
10687
+ }
10688
+ if (key.mouse?.kind === "wheel") {
10689
+ dispatch({ type: "sessionsPanelMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10690
+ return;
10691
+ }
10692
+ if (isEnter) {
10693
+ const now = Date.now();
10694
+ if (now - lastEnterAtRef.current < 50) return;
10695
+ lastEnterAtRef.current = now;
10696
+ if (state.sessionResumeConfirm) {
10697
+ const pending = state.sessionResumeConfirm;
10698
+ dispatch({ type: "sessionResumeConfirmClear" });
10699
+ dispatch({ type: "sessionsPanelBusy", on: true });
10700
+ onResumeSession?.(pending.sessionId).then((result) => {
10701
+ if (!result) {
10702
+ dispatch({ type: "sessionsPanelBusy", on: false });
10703
+ return;
10704
+ }
10705
+ dispatch({ type: "replaceHistory", entries: result.entries, nextId: result.nextId });
10706
+ dispatch({ type: "toggleSessionsPanel" });
10707
+ dispatch({
10708
+ type: "addEntry",
10709
+ entry: {
10710
+ kind: "info",
10711
+ text: `Resumed session ${result.sessionId} \u2014 ${result.entries.length} entries replayed.`
10712
+ }
10713
+ });
10714
+ }).catch(() => {
10715
+ dispatch({ type: "sessionsPanelBusy", on: false });
10716
+ });
10717
+ return;
10718
+ }
10719
+ const sessions = state.sessionsPanel.sessions;
10720
+ const sel = state.sessionsPanel.selected;
10721
+ if (sel < 0 || sel >= sessions.length) return;
10722
+ const session = sessions[sel];
10723
+ if (!session) return;
10724
+ const isCurrentProject = session.projectRoot === projectRoot;
10725
+ if (isCurrentProject) {
10726
+ if (session.pid === process.pid) {
10727
+ dispatch({
10728
+ type: "addEntry",
10729
+ entry: { kind: "info", text: "That is this session \u2014 nothing to resume." }
10730
+ });
10731
+ dispatch({ type: "toggleSessionsPanel" });
10732
+ return;
10733
+ }
10734
+ if (session.pid != null) {
10735
+ dispatch({
10736
+ type: "addEntry",
10737
+ entry: {
10738
+ kind: "warn",
10739
+ text: `Session is open in another running wstack (pid ${session.pid}) \u2014 a live session cannot be resumed here. Use /resume for previous sessions.`
10740
+ }
10741
+ });
10742
+ dispatch({ type: "toggleSessionsPanel" });
10743
+ return;
10744
+ }
10745
+ dispatch({
10746
+ type: "sessionResumeConfirmSet",
10747
+ sessionId: session.sessionId,
10748
+ sessionName: session.projectName
10749
+ });
10750
+ } else {
10751
+ onSwitchToSession?.(session.sessionId, session.projectRoot ?? "", session.projectName);
10752
+ dispatch({ type: "toggleSessionsPanel" });
10753
+ requestExit?.(42);
10754
+ }
10755
+ return;
10756
+ }
10757
+ return;
10758
+ }
8958
10759
  if (state.slashPicker.open) {
8959
10760
  if (key.escape) {
8960
10761
  dispatch({ type: "slashPickerClose" });
8961
10762
  return;
8962
10763
  }
10764
+ if (key.mouse?.kind === "wheel") {
10765
+ dispatch({ type: "slashPickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10766
+ return;
10767
+ }
8963
10768
  if (key.upArrow) {
8964
10769
  dispatch({ type: "slashPickerMove", delta: -1 });
8965
10770
  return;
@@ -8996,6 +10801,10 @@ function App({
8996
10801
  dispatch({ type: "pickerClose" });
8997
10802
  return;
8998
10803
  }
10804
+ if (key.mouse?.kind === "wheel") {
10805
+ dispatch({ type: "pickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10806
+ return;
10807
+ }
8999
10808
  if (key.upArrow) {
9000
10809
  dispatch({ type: "pickerMove", delta: -1 });
9001
10810
  return;
@@ -9122,6 +10931,24 @@ function App({
9122
10931
  if (state.helpOpen) dispatch({ type: "toggleHelp" });
9123
10932
  dispatch({ type: "toggleTodosMonitor" });
9124
10933
  };
10934
+ if (key.fn === 1) {
10935
+ if (state.projectPicker.open) {
10936
+ dispatch({ type: "projectPickerClose" });
10937
+ } else {
10938
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
10939
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
10940
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
10941
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
10942
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
10943
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
10944
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
10945
+ if (state.processListOpen) dispatch({ type: "toggleProcessList" });
10946
+ if (state.goalPanelOpen) dispatch({ type: "toggleGoalPanel" });
10947
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
10948
+ openProjectPicker();
10949
+ }
10950
+ return;
10951
+ }
9125
10952
  if (key.ctrl && input === "f" || key.fn === 2) {
9126
10953
  toggleFleetOverlay();
9127
10954
  return;
@@ -9134,47 +10961,6 @@ function App({
9134
10961
  toggleWorktreeOverlay();
9135
10962
  return;
9136
10963
  }
9137
- if (key.fn === 5) {
9138
- if (state.settingsPicker.open) {
9139
- dispatch({ type: "settingsClose" });
9140
- } else if (getSettings && saveSettings) {
9141
- if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
9142
- if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
9143
- if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
9144
- if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
9145
- if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
9146
- if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
9147
- if (state.helpOpen) dispatch({ type: "toggleHelp" });
9148
- const cfg = getSettings();
9149
- dispatch({
9150
- type: "settingsOpen",
9151
- mode: cfg.mode,
9152
- delayMs: cfg.delayMs,
9153
- titleAnimation: cfg.titleAnimation ?? true,
9154
- yolo: cfg.yolo ?? false,
9155
- streamFleet: cfg.streamFleet ?? true,
9156
- chime: cfg.chime ?? false,
9157
- confirmExit: cfg.confirmExit ?? true,
9158
- nextPrediction: cfg.nextPrediction ?? false,
9159
- featureMcp: cfg.featureMcp ?? true,
9160
- featurePlugins: cfg.featurePlugins ?? true,
9161
- featureMemory: cfg.featureMemory ?? true,
9162
- featureSkills: cfg.featureSkills ?? true,
9163
- featureModelsRegistry: cfg.featureModelsRegistry ?? true,
9164
- contextAutoCompact: cfg.contextAutoCompact ?? true,
9165
- contextStrategy: cfg.contextStrategy ?? "hybrid",
9166
- logLevel: cfg.logLevel ?? "info",
9167
- auditLevel: cfg.auditLevel ?? "standard",
9168
- indexOnStart: cfg.indexOnStart ?? true,
9169
- maxIterations: cfg.maxIterations ?? 500,
9170
- autoProceedMaxIterations: cfg.autoProceedMaxIterations ?? 50,
9171
- enhanceDelayMs: cfg.enhanceDelayMs ?? 6e4,
9172
- debugStream: cfg.debugStream ?? false,
9173
- configScope: cfg.configScope ?? "global"
9174
- });
9175
- }
9176
- return;
9177
- }
9178
10964
  if (key.fn === 6) {
9179
10965
  toggleTodosOverlay();
9180
10966
  return;
@@ -9227,6 +11013,25 @@ function App({
9227
11013
  }
9228
11014
  return;
9229
11015
  }
11016
+ if (key.fn === 10) {
11017
+ if (state.sessionsPanelOpen) {
11018
+ dispatch({ type: "toggleSessionsPanel" });
11019
+ } else {
11020
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
11021
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
11022
+ if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
11023
+ if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
11024
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
11025
+ if (state.settingsPicker.open) dispatch({ type: "settingsClose" });
11026
+ if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
11027
+ if (state.processListOpen) dispatch({ type: "toggleProcessList" });
11028
+ if (state.goalPanelOpen) dispatch({ type: "toggleGoalPanel" });
11029
+ if (state.helpOpen) dispatch({ type: "toggleHelp" });
11030
+ dispatch({ type: "toggleSessionsPanel" });
11031
+ loadLiveSessions();
11032
+ }
11033
+ return;
11034
+ }
9230
11035
  if (key.ctrl && input === "s") {
9231
11036
  if (state.settingsPicker.open) {
9232
11037
  dispatch({ type: "settingsClose" });
@@ -9262,6 +11067,8 @@ function App({
9262
11067
  maxIterations: cfg.maxIterations ?? 500,
9263
11068
  autoProceedMaxIterations: cfg.autoProceedMaxIterations ?? 50,
9264
11069
  enhanceDelayMs: cfg.enhanceDelayMs ?? 6e4,
11070
+ enhanceEnabled: cfg.enhanceEnabled ?? true,
11071
+ enhanceLanguage: cfg.enhanceLanguage ?? "original",
9265
11072
  debugStream: cfg.debugStream ?? false,
9266
11073
  configScope: cfg.configScope ?? "global"
9267
11074
  });
@@ -9285,6 +11092,10 @@ function App({
9285
11092
  dispatch({ type: "settingsClose" });
9286
11093
  return;
9287
11094
  }
11095
+ if (state.projectPicker.open) {
11096
+ dispatch({ type: "projectPickerClose" });
11097
+ return;
11098
+ }
9288
11099
  if (state.queuePanelOpen) {
9289
11100
  dispatch({ type: "toggleQueuePanel" });
9290
11101
  return;
@@ -9297,11 +11108,15 @@ function App({
9297
11108
  dispatch({ type: "toggleGoalPanel" });
9298
11109
  return;
9299
11110
  }
11111
+ if (state.sessionsPanelOpen) {
11112
+ dispatch({ type: "toggleSessionsPanel" });
11113
+ return;
11114
+ }
9300
11115
  }
9301
11116
  if (state.processListOpen) {
9302
11117
  return;
9303
11118
  }
9304
- 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) {
11119
+ 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) {
9305
11120
  dispatch({ type: "toggleHelp" });
9306
11121
  return;
9307
11122
  }
@@ -9335,6 +11150,12 @@ function App({
9335
11150
  }
9336
11151
  if (cursor === 0) return;
9337
11152
  const next2 = buffer.slice(0, cursor - 1) + buffer.slice(cursor);
11153
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11154
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11155
+ nextStepsAutoSubmitTimerRef.current = void 0;
11156
+ setNextStepsAutoSubmitCountdown(null);
11157
+ nextStepsAutoSubmitSuggestionRef.current = null;
11158
+ }
9338
11159
  setDraft(next2, cursor - 1);
9339
11160
  return;
9340
11161
  }
@@ -9345,12 +11166,24 @@ function App({
9345
11166
  const nextWordStart = afterCursor.indexOf(" ");
9346
11167
  const end = nextWordStart === -1 ? buffer.length : cursor + nextWordStart + 1;
9347
11168
  const next3 = buffer.slice(0, cursor) + buffer.slice(end);
11169
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11170
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11171
+ nextStepsAutoSubmitTimerRef.current = void 0;
11172
+ setNextStepsAutoSubmitCountdown(null);
11173
+ nextStepsAutoSubmitSuggestionRef.current = null;
11174
+ }
9348
11175
  setDraft(next3, cursor);
9349
11176
  return;
9350
11177
  }
9351
11178
  if (cursor >= buffer.length) return;
9352
11179
  const span = tokenLengthForward(buffer, cursor) || 1;
9353
11180
  const next2 = buffer.slice(0, cursor) + buffer.slice(cursor + span);
11181
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11182
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11183
+ nextStepsAutoSubmitTimerRef.current = void 0;
11184
+ setNextStepsAutoSubmitCountdown(null);
11185
+ nextStepsAutoSubmitSuggestionRef.current = null;
11186
+ }
9354
11187
  setDraft(next2, cursor);
9355
11188
  return;
9356
11189
  }
@@ -9386,7 +11219,67 @@ function App({
9386
11219
  setDraft(buffer, buffer.length);
9387
11220
  return;
9388
11221
  }
9389
- 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;
11222
+ 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;
11223
+ if (mouseMode && !overlayOpen) {
11224
+ if (key.mouse?.kind === "wheel") {
11225
+ if (key.mouse.shift) dispatch({ type: "scrollPage", dir: key.mouse.wheel > 0 ? "up" : "down" });
11226
+ else dispatch({ type: "scrollBy", delta: key.mouse.wheel > 0 ? 3 : -3 });
11227
+ return;
11228
+ }
11229
+ if ((key.mouse?.kind === "press" || key.mouse?.kind === "move") && key.mouse.button === "left") {
11230
+ const region = hitRegion(
11231
+ { termRows, termCols: stdout?.columns ?? 80, viewportRows: state.viewportRows },
11232
+ key.mouse.x,
11233
+ key.mouse.y
11234
+ );
11235
+ if (region?.kind === "scrollbar") {
11236
+ dispatch({
11237
+ type: "scrollTo",
11238
+ offset: scrollOffsetForTrackRow(state.viewportRows, state.totalLines, region.cell)
11239
+ });
11240
+ return;
11241
+ }
11242
+ }
11243
+ if (key.mouse?.kind === "press" && key.mouse.button === "left" && statusBarWrapRef.current) {
11244
+ const sbHeight = measureElement(statusBarWrapRef.current).height;
11245
+ const belowHeight = belowStatusBarRef.current ? measureElement(belowStatusBarRef.current).height : 0;
11246
+ const cols = stdout?.columns ?? 80;
11247
+ const mx = key.mouse.x;
11248
+ const my = key.mouse.y;
11249
+ const rowFor = (line) => statusBarLineRow({ termRows, statusBarHeight: sbHeight, belowHeight, headerRows: 1, line });
11250
+ const inSpan = (span) => mx >= span.start + 1 && mx <= span.start + span.len;
11251
+ if (cols >= COMPACT_THRESHOLD && my === rowFor(0)) {
11252
+ const span = statusBarModelSpan({
11253
+ version: appVersion,
11254
+ state: state.status,
11255
+ fleetRunning: fleetCounts?.running ?? 0,
11256
+ model: `${liveProvider}/${liveModel}`
11257
+ });
11258
+ if (inSpan(span)) {
11259
+ await openModelPicker();
11260
+ return;
11261
+ }
11262
+ }
11263
+ const autoSpan = statusBarAutonomySpan({ yolo: yoloLive, autonomy: autonomyLive });
11264
+ if (autoSpan && my === rowFor(1) && inSpan(autoSpan)) {
11265
+ dispatch({ type: "autonomyPickerOpen", options: AUTONOMY_OPTIONS });
11266
+ return;
11267
+ }
11268
+ const todosShown = !!todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0);
11269
+ if (todosShown && my === rowFor(2) && inSpan(statusBarTodosSpan())) {
11270
+ dispatch({ type: "toggleTodosMonitor" });
11271
+ return;
11272
+ }
11273
+ }
11274
+ if (key.pageUp) {
11275
+ dispatch({ type: "scrollPage", dir: "up" });
11276
+ return;
11277
+ }
11278
+ if (key.pageDown) {
11279
+ dispatch({ type: "scrollPage", dir: "down" });
11280
+ return;
11281
+ }
11282
+ }
9390
11283
  if (key.upArrow) {
9391
11284
  if (!overlayOpen && state.inputHistory.length > 0) {
9392
11285
  dispatch({ type: "historyUp" });
@@ -9418,6 +11311,12 @@ function App({
9418
11311
  return;
9419
11312
  }
9420
11313
  if (key.ctrl && input === "u") {
11314
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11315
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11316
+ nextStepsAutoSubmitTimerRef.current = void 0;
11317
+ setNextStepsAutoSubmitCountdown(null);
11318
+ nextStepsAutoSubmitSuggestionRef.current = null;
11319
+ }
9421
11320
  setDraft("", 0);
9422
11321
  return;
9423
11322
  }
@@ -9425,12 +11324,24 @@ function App({
9425
11324
  if (cursor >= buffer.length) return;
9426
11325
  const span = tokenLengthForward(buffer, cursor) || 1;
9427
11326
  const next2 = buffer.slice(0, cursor) + buffer.slice(cursor + span);
11327
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11328
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11329
+ nextStepsAutoSubmitTimerRef.current = void 0;
11330
+ setNextStepsAutoSubmitCountdown(null);
11331
+ nextStepsAutoSubmitSuggestionRef.current = null;
11332
+ }
9428
11333
  setDraft(next2, cursor);
9429
11334
  return;
9430
11335
  }
9431
11336
  if (key.ctrl && input === "k") {
9432
11337
  if (cursor >= buffer.length) return;
9433
11338
  const next2 = buffer.slice(0, cursor);
11339
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11340
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11341
+ nextStepsAutoSubmitTimerRef.current = void 0;
11342
+ setNextStepsAutoSubmitCountdown(null);
11343
+ nextStepsAutoSubmitSuggestionRef.current = null;
11344
+ }
9434
11345
  setDraft(next2, cursor);
9435
11346
  return;
9436
11347
  }
@@ -9448,6 +11359,12 @@ function App({
9448
11359
  await commitPaste(input);
9449
11360
  return;
9450
11361
  }
11362
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11363
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11364
+ nextStepsAutoSubmitTimerRef.current = void 0;
11365
+ setNextStepsAutoSubmitCountdown(null);
11366
+ nextStepsAutoSubmitSuggestionRef.current = null;
11367
+ }
9451
11368
  const next = buffer.slice(0, cursor) + input + buffer.slice(cursor);
9452
11369
  setDraft(next, cursor + input.length);
9453
11370
  };
@@ -9514,6 +11431,12 @@ function App({
9514
11431
  } catch {
9515
11432
  }
9516
11433
  }
11434
+ if (result.status === "done" && result.finalText && onSuggestionsParsed) {
11435
+ try {
11436
+ onSuggestionsParsed(result.finalText);
11437
+ } catch {
11438
+ }
11439
+ }
9517
11440
  if (tokenCounter && before) {
9518
11441
  const after = tokenCounter.total();
9519
11442
  const costAfter = tokenCounter.estimateCost().total;
@@ -9668,6 +11591,9 @@ function App({
9668
11591
  return;
9669
11592
  }
9670
11593
  dispatch({ type: "resetInterrupts" });
11594
+ autoSubmitStreakRef.current = 0;
11595
+ autoSubmitCapWarnedRef.current = false;
11596
+ dispatch({ type: "scrollToBottom" });
9671
11597
  const pushSubmittedHistory = () => {
9672
11598
  if (trimmed) dispatch({ type: "historyPush", text: trimmed });
9673
11599
  };
@@ -9708,6 +11634,25 @@ ${content}
9708
11634
  const m = res.metadata.autoPhaseInit;
9709
11635
  dispatch({ type: "autoPhaseInit", title: m.title });
9710
11636
  }
11637
+ const mouseToggle = res?.metadata?.mouseToggle;
11638
+ if (mouseToggle) {
11639
+ const nextVal = mouseToggle === "on" ? true : mouseToggle === "off" ? false : mouseToggle === "toggle" ? !mouseMode : mouseMode;
11640
+ if (mouseToggle !== "query" && nextVal !== mouseMode) {
11641
+ setMouseMode(nextVal);
11642
+ const cur = getSettings?.();
11643
+ if (cur && saveSettings) {
11644
+ Promise.resolve(saveSettings({ ...cur, mouseMode: nextVal })).catch(() => {
11645
+ });
11646
+ }
11647
+ }
11648
+ dispatch({
11649
+ type: "addEntry",
11650
+ entry: {
11651
+ kind: "info",
11652
+ 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."
11653
+ }
11654
+ });
11655
+ }
9711
11656
  const ctxModel = agent.ctx.model;
9712
11657
  if (ctxModel && ctxModel !== liveModel) setLiveModel(ctxModel);
9713
11658
  const ctxProviderId = agent.ctx.provider?.id;
@@ -9867,6 +11812,9 @@ User message:
9867
11812
  clearDraft();
9868
11813
  const blocks = await builder.submit();
9869
11814
  if (state.status !== "idle") {
11815
+ if (autonomyLive === "auto" && nextStepsAutoSubmitTimerRef.current != null) {
11816
+ switchAutonomy?.("off");
11817
+ }
9870
11818
  dispatch({
9871
11819
  type: "addEntry",
9872
11820
  entry: { kind: "user", text: displayText, queued: true, pasteContent }
@@ -9875,6 +11823,9 @@ User message:
9875
11823
  return;
9876
11824
  }
9877
11825
  dispatch({ type: "addEntry", entry: { kind: "user", text: displayText, pasteContent } });
11826
+ if (autonomyLive === "auto" && nextStepsAutoSubmitTimerRef.current != null) {
11827
+ switchAutonomy?.("off");
11828
+ }
9878
11829
  await runBlocks(blocks);
9879
11830
  };
9880
11831
  const bootInjectedRef = useRef(false);
@@ -9927,15 +11878,27 @@ User message:
9927
11878
  const inputHeight = Math.max(1, inputCellRows.length);
9928
11879
  const hideInput = enhanceActive || state.helpOpen || state.processListOpen;
9929
11880
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 0, children: [
9930
- /* @__PURE__ */ jsx(
11881
+ mouseMode ? /* @__PURE__ */ jsx(
11882
+ ScrollableHistory,
11883
+ {
11884
+ entries: state.entries,
11885
+ streamingText: state.streamingText,
11886
+ toolStream: state.toolStream,
11887
+ scrollOffset: state.scrollOffset,
11888
+ viewportRows: state.viewportRows,
11889
+ totalLines: state.totalLines,
11890
+ onMeasure: (totalLines) => dispatch({ type: "setMeasuredLines", totalLines })
11891
+ }
11892
+ ) : /* @__PURE__ */ jsx(
9931
11893
  History,
9932
11894
  {
9933
11895
  entries: state.entries,
11896
+ generation: state.historyGen,
9934
11897
  streamingText: state.streamingText,
9935
11898
  toolStream: state.toolStream
9936
11899
  }
9937
11900
  ),
9938
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [
11901
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, ref: bottomRegionRef, children: [
9939
11902
  /* @__PURE__ */ jsx(
9940
11903
  Input,
9941
11904
  {
@@ -9986,6 +11949,16 @@ User message:
9986
11949
  hint: state.autonomyPicker.hint
9987
11950
  }
9988
11951
  ) : null,
11952
+ state.resumePicker.open ? /* @__PURE__ */ jsx(
11953
+ ResumePicker,
11954
+ {
11955
+ sessions: state.resumePicker.sessions,
11956
+ selected: state.resumePicker.selected,
11957
+ busy: state.resumePicker.busy,
11958
+ error: state.resumePicker.error,
11959
+ hint: state.resumePicker.hint
11960
+ }
11961
+ ) : null,
9989
11962
  state.settingsPicker.open ? /* @__PURE__ */ jsx(
9990
11963
  SettingsPicker,
9991
11964
  {
@@ -10011,11 +11984,32 @@ User message:
10011
11984
  maxIterations: state.settingsPicker.maxIterations,
10012
11985
  autoProceedMaxIterations: state.settingsPicker.autoProceedMaxIterations,
10013
11986
  enhanceDelayMs: state.settingsPicker.enhanceDelayMs,
11987
+ enhanceEnabled: state.settingsPicker.enhanceEnabled,
11988
+ enhanceLanguage: state.settingsPicker.enhanceLanguage,
10014
11989
  debugStream: state.settingsPicker.debugStream,
10015
11990
  configScope: state.settingsPicker.configScope,
10016
11991
  hint: state.settingsPicker.hint
10017
11992
  }
10018
11993
  ) : null,
11994
+ state.projectPicker.open ? /* @__PURE__ */ jsx(
11995
+ ProjectPicker,
11996
+ {
11997
+ items: state.projectPicker.items,
11998
+ selected: state.projectPicker.selected,
11999
+ filter: state.projectPicker.filter,
12000
+ hint: state.projectPicker.hint
12001
+ }
12002
+ ) : null,
12003
+ state.sessionsPanelOpen ? /* @__PURE__ */ jsx(
12004
+ SessionsPanel,
12005
+ {
12006
+ sessions: state.sessionsPanel.sessions,
12007
+ busy: state.sessionsPanel.busy,
12008
+ selected: state.sessionsPanel.selected,
12009
+ resumeConfirm: state.sessionResumeConfirm ? { sessionName: state.sessionResumeConfirm.sessionName } : void 0,
12010
+ currentSessionId: agent.ctx.session?.id
12011
+ }
12012
+ ) : null,
10019
12013
  state.rewindOverlay ? (() => {
10020
12014
  const overlay = state.rewindOverlay;
10021
12015
  return /* @__PURE__ */ jsx(
@@ -10043,7 +12037,7 @@ User message:
10043
12037
  }
10044
12038
  ) }) : null,
10045
12039
  state.confirmQueue.length > 0 && (() => {
10046
- const head = expectDefined(state.confirmQueue[0]);
12040
+ const head = expectDefined$1(state.confirmQueue[0]);
10047
12041
  let resolved = false;
10048
12042
  const onDecision = (decision) => {
10049
12043
  if (resolved) return;
@@ -10128,7 +12122,7 @@ User message:
10128
12122
  }
10129
12123
  );
10130
12124
  })() : null,
10131
- /* @__PURE__ */ jsx(
12125
+ /* @__PURE__ */ jsx(Box, { ref: statusBarWrapRef, flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx(
10132
12126
  StatusBar,
10133
12127
  {
10134
12128
  model: `${liveProvider}/${liveModel}`,
@@ -10142,11 +12136,13 @@ User message:
10142
12136
  startedAt: startedAtRef.current,
10143
12137
  todos,
10144
12138
  plan: planCounts ?? void 0,
12139
+ tasks: taskCounts ?? void 0,
10145
12140
  fleet: fleetCounts,
10146
12141
  git: gitInfo,
10147
12142
  context: contextWindow,
10148
12143
  brain: state.brain,
10149
12144
  projectName,
12145
+ workingDir: workingDirChip,
10150
12146
  subagentCount: Object.keys(state.fleet).length,
10151
12147
  processCount: getProcessRegistry().activeCount,
10152
12148
  hiddenItems,
@@ -10156,67 +12152,101 @@ User message:
10156
12152
  modeLabel: liveModeLabel || void 0,
10157
12153
  debugStreamStats: state.debugStreamStats,
10158
12154
  enhanceCountdown,
10159
- autoProceedCountdown
12155
+ nextStepsAutoSubmitCountdown,
12156
+ autoProceedCountdown: state.countdown?.remainingSeconds ?? null,
12157
+ sessionCount,
12158
+ mailbox: mailboxStatus
10160
12159
  }
10161
- ),
10162
- state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, {}) : null,
10163
- state.agentsMonitorOpen ? /* @__PURE__ */ jsx(
10164
- AgentsMonitor,
10165
- {
10166
- entries: entriesWithLeader,
10167
- totalCost: state.fleetCost,
10168
- leaderCost: tokenCounter?.estimateCost().total ?? 0,
10169
- totalTokens: state.fleetTokens,
10170
- nowTick
10171
- }
10172
- ) : state.autoPhase?.monitorOpen ? /* @__PURE__ */ jsx(
10173
- PhaseMonitor,
10174
- {
10175
- phases: state.autoPhase.phases,
10176
- runningPhaseIds: state.autoPhase.runningPhaseIds,
10177
- elapsedMs: state.autoPhase.elapsedMs,
10178
- nowTick,
10179
- onClose: () => dispatch({ type: "autoPhaseMonitorToggle" })
10180
- }
10181
- ) : state.worktreeMonitorOpen ? /* @__PURE__ */ jsx(
10182
- WorktreeMonitor,
10183
- {
10184
- worktrees: state.worktrees,
10185
- baseBranch: state.worktreeBase,
10186
- nowTick,
10187
- onClose: () => dispatch({ type: "worktreeMonitorToggle" })
10188
- }
10189
- ) : state.todosMonitorOpen ? /* @__PURE__ */ jsx(TodosMonitor, { todos: agent.ctx.todos }) : state.monitorOpen ? /* @__PURE__ */ jsx(
10190
- FleetMonitor,
10191
- {
10192
- entries: state.fleet,
10193
- totalCost: state.fleetCost,
10194
- totalTokens: state.fleetTokens,
10195
- maxConcurrent: state.fleetConcurrency,
10196
- nowTick,
10197
- collabSession: state.collabSession
10198
- }
10199
- ) : director ? /* @__PURE__ */ jsx(
10200
- FleetPanel,
10201
- {
10202
- entries: entriesWithLeader,
10203
- totalCost: state.fleetCost,
10204
- roster: fleetRoster,
10205
- collabSession: state.collabSession
10206
- }
10207
- ) : null,
10208
- state.autoPhase && !state.autoPhase.monitorOpen ? /* @__PURE__ */ jsx(
10209
- PhasePanel,
12160
+ ) }),
12161
+ /* @__PURE__ */ jsx(
12162
+ MailboxPanel,
10210
12163
  {
10211
- phases: state.autoPhase.phases,
10212
- runningPhaseIds: state.autoPhase.runningPhaseIds,
10213
- nowTick
12164
+ messages: mailboxMessages,
12165
+ agents: mailboxAgents,
12166
+ unreadCount: mailboxStatus.unread,
12167
+ open: mailboxPanelOpen
10214
12168
  }
10215
- ) : null,
10216
- Object.keys(state.worktrees).length > 0 && !state.worktreeMonitorOpen && !state.monitorOpen ? /* @__PURE__ */ jsx(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null,
10217
- state.queuePanelOpen ? /* @__PURE__ */ jsx(QueuePanel, { items: state.queue }) : null,
10218
- state.processListOpen ? /* @__PURE__ */ jsx(ProcessListMonitor, {}) : null,
10219
- state.goalPanelOpen ? /* @__PURE__ */ jsx(GoalPanel, { goal: state.goalSummary }) : null
12169
+ ),
12170
+ /* @__PURE__ */ jsxs(Box, { ref: belowStatusBarRef, flexDirection: "column", flexShrink: 0, children: [
12171
+ state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, {}) : null,
12172
+ state.agentsMonitorOpen ? /* @__PURE__ */ jsx(
12173
+ AgentsMonitor,
12174
+ {
12175
+ entries: entriesWithLeader,
12176
+ totalCost: state.fleetCost,
12177
+ leaderCost: tokenCounter?.estimateCost().total ?? 0,
12178
+ totalTokens: state.fleetTokens,
12179
+ nowTick
12180
+ }
12181
+ ) : state.autoPhase?.monitorOpen ? /* @__PURE__ */ jsx(
12182
+ PhaseMonitor,
12183
+ {
12184
+ phases: state.autoPhase.phases,
12185
+ runningPhaseIds: state.autoPhase.runningPhaseIds,
12186
+ elapsedMs: state.autoPhase.elapsedMs,
12187
+ nowTick,
12188
+ onClose: () => dispatch({ type: "autoPhaseMonitorToggle" })
12189
+ }
12190
+ ) : state.worktreeMonitorOpen ? /* @__PURE__ */ jsx(
12191
+ WorktreeMonitor,
12192
+ {
12193
+ worktrees: state.worktrees,
12194
+ baseBranch: state.worktreeBase,
12195
+ nowTick,
12196
+ onClose: () => dispatch({ type: "worktreeMonitorToggle" })
12197
+ }
12198
+ ) : state.todosMonitorOpen ? /* @__PURE__ */ jsx(TodosMonitor, { todos: agent.ctx.todos }) : state.monitorOpen ? /* @__PURE__ */ jsx(
12199
+ FleetMonitor,
12200
+ {
12201
+ entries: state.fleet,
12202
+ totalCost: state.fleetCost,
12203
+ totalTokens: state.fleetTokens,
12204
+ maxConcurrent: state.fleetConcurrency,
12205
+ nowTick,
12206
+ collabSession: state.collabSession
12207
+ }
12208
+ ) : director ? /* @__PURE__ */ jsx(
12209
+ FleetPanel,
12210
+ {
12211
+ entries: entriesWithLeader,
12212
+ totalCost: state.fleetCost,
12213
+ roster: fleetRoster,
12214
+ collabSession: state.collabSession
12215
+ }
12216
+ ) : null,
12217
+ state.autoPhase && !state.autoPhase.monitorOpen ? /* @__PURE__ */ jsx(
12218
+ PhasePanel,
12219
+ {
12220
+ phases: state.autoPhase.phases,
12221
+ runningPhaseIds: state.autoPhase.runningPhaseIds,
12222
+ nowTick
12223
+ }
12224
+ ) : null,
12225
+ Object.keys(state.worktrees).length > 0 && !state.worktreeMonitorOpen && !state.monitorOpen ? /* @__PURE__ */ jsx(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null,
12226
+ state.queuePanelOpen ? /* @__PURE__ */ jsx(QueuePanel, { items: state.queue }) : null,
12227
+ state.processListOpen ? /* @__PURE__ */ jsx(ProcessListMonitor, {}) : null,
12228
+ state.goalPanelOpen ? /* @__PURE__ */ jsx(GoalPanel, { goal: state.goalSummary }) : null,
12229
+ (() => {
12230
+ const anyMonitorOpen = state.agentsMonitorOpen || (state.autoPhase?.monitorOpen ?? false) || state.worktreeMonitorOpen || state.todosMonitorOpen || state.monitorOpen || state.processListOpen || state.queuePanelOpen || state.goalPanelOpen;
12231
+ let nextPanelHint;
12232
+ if (state.agentsMonitorOpen) {
12233
+ nextPanelHint = { key: "F6", label: "todos" };
12234
+ } else if (state.autoPhase?.monitorOpen || state.worktreeMonitorOpen || state.todosMonitorOpen) {
12235
+ nextPanelHint = { key: "F9", label: "goal" };
12236
+ } else if (state.queuePanelOpen || state.processListOpen || state.goalPanelOpen) {
12237
+ nextPanelHint = { key: "F3", label: "agents" };
12238
+ } else if (anyMonitorOpen) {
12239
+ nextPanelHint = { key: "F3", label: "agents" };
12240
+ }
12241
+ const ctx = {
12242
+ monitor: anyMonitorOpen,
12243
+ managed: state.scrollOffset > 0,
12244
+ picker: state.settingsPicker.open || state.modelPicker.open || state.autonomyPicker.open,
12245
+ nextPanelHint
12246
+ };
12247
+ return /* @__PURE__ */ jsx(KeyHintBar, { context: ctx });
12248
+ })()
12249
+ ] })
10220
12250
  ] })
10221
12251
  ] }) });
10222
12252
  }
@@ -10318,6 +12348,45 @@ function startTerminalTitle(opts) {
10318
12348
  // src/run-tui.ts
10319
12349
  var BRACKETED_PASTE_ON = "\x1B[?2004h";
10320
12350
  var BRACKETED_PASTE_OFF = "\x1B[?2004l";
12351
+ var origConsoleLog = console.log;
12352
+ var origConsoleWarn = console.warn;
12353
+ var origConsoleError = console.error;
12354
+ var origConsoleDebug = console.debug;
12355
+ var origConsoleInfo = console.info;
12356
+ var origConsoleTable = console.table;
12357
+ var origConsoleTrace = console.trace;
12358
+ var origStderrWrite = process.stderr.write.bind(process.stderr);
12359
+ var consoleNoop = (..._args) => {
12360
+ };
12361
+ var stderrNoop = ((_chunk, _encodingOrCb, _cb) => {
12362
+ if (typeof _encodingOrCb === "function") _encodingOrCb();
12363
+ else if (typeof _cb === "function") _cb();
12364
+ return true;
12365
+ });
12366
+ var warningNoop = (_warning) => {
12367
+ };
12368
+ function silenceTerminal() {
12369
+ console.log = consoleNoop;
12370
+ console.warn = consoleNoop;
12371
+ console.error = consoleNoop;
12372
+ console.debug = consoleNoop;
12373
+ console.info = consoleNoop;
12374
+ console.table = consoleNoop;
12375
+ console.trace = consoleNoop;
12376
+ process.stderr.write = stderrNoop;
12377
+ process.on("warning", warningNoop);
12378
+ }
12379
+ function unsilenceTerminal() {
12380
+ console.log = origConsoleLog;
12381
+ console.warn = origConsoleWarn;
12382
+ console.error = origConsoleError;
12383
+ console.debug = origConsoleDebug;
12384
+ console.info = origConsoleInfo;
12385
+ console.table = origConsoleTable;
12386
+ console.trace = origConsoleTrace;
12387
+ process.stderr.write = origStderrWrite;
12388
+ process.off("warning", warningNoop);
12389
+ }
10321
12390
  async function runTui(opts) {
10322
12391
  const stdout = process.stdout;
10323
12392
  const stdin = process.stdin;
@@ -10327,10 +12396,17 @@ async function runTui(opts) {
10327
12396
  );
10328
12397
  return 2;
10329
12398
  }
12399
+ silenceTerminal();
10330
12400
  stdout.write(BRACKETED_PASTE_ON);
12401
+ const mouseEnabled = opts.mouse ?? opts.getSettings?.().mouseMode ?? process.env.WRONGSTACK_MOUSE === "1";
10331
12402
  stdout.write("\x1B[2J\x1B[H");
10332
12403
  const inkStdin = stdin;
10333
- const stopTitle = opts.titleAnimation !== false ? startTerminalTitle({ stdout, events: opts.events, model: opts.model }) : (() => {
12404
+ const stopTitle = opts.titleAnimation !== false ? startTerminalTitle({
12405
+ stdout,
12406
+ events: opts.events,
12407
+ model: opts.model,
12408
+ appName: opts.projectRoot ? path4.basename(opts.projectRoot) : void 0
12409
+ }) : (() => {
10334
12410
  });
10335
12411
  const swallowSignals = ["SIGTSTP", "SIGQUIT", "SIGTTIN", "SIGTTOU"];
10336
12412
  const swallow = () => {
@@ -10345,12 +12421,14 @@ async function runTui(opts) {
10345
12421
  const cleanup = () => {
10346
12422
  if (cleaned) return;
10347
12423
  cleaned = true;
12424
+ unsilenceTerminal();
10348
12425
  try {
10349
12426
  stopTitle();
10350
12427
  } catch {
10351
12428
  }
10352
12429
  try {
10353
12430
  stdout.write(BRACKETED_PASTE_OFF);
12431
+ stdout.write(MOUSE_OFF);
10354
12432
  } catch {
10355
12433
  }
10356
12434
  };
@@ -10371,14 +12449,26 @@ async function runTui(opts) {
10371
12449
  };
10372
12450
  return new Promise((resolve) => {
10373
12451
  let exitCode = 0;
12452
+ let hardExitTimer = null;
10374
12453
  const onExit = (code) => {
10375
12454
  exitCode = code;
10376
12455
  };
10377
12456
  const settle = (code) => {
12457
+ if (hardExitTimer) {
12458
+ clearTimeout(hardExitTimer);
12459
+ hardExitTimer = null;
12460
+ }
10378
12461
  cleanup();
10379
12462
  detachListeners();
10380
12463
  resolve(code);
10381
12464
  };
12465
+ const requestExit = (code) => {
12466
+ onExit(code);
12467
+ instance?.unmount();
12468
+ hardExitTimer = setTimeout(() => process.exit(code), 5e3);
12469
+ hardExitTimer.unref();
12470
+ };
12471
+ opts.requestExit = requestExit;
10382
12472
  let instance;
10383
12473
  try {
10384
12474
  instance = render(
@@ -10393,6 +12483,7 @@ async function runTui(opts) {
10393
12483
  model: opts.model,
10394
12484
  banner: opts.banner ?? true,
10395
12485
  queueStore: opts.queueStore,
12486
+ onQueueChange: opts.onQueueChange,
10396
12487
  yolo: opts.yolo,
10397
12488
  getYolo: opts.getYolo,
10398
12489
  getAutonomy: opts.getAutonomy,
@@ -10428,12 +12519,24 @@ async function runTui(opts) {
10428
12519
  getSettings: opts.getSettings,
10429
12520
  saveSettings: opts.saveSettings,
10430
12521
  predictNext: opts.predictNext,
12522
+ onSuggestionsParsed: opts.onSuggestionsParsed,
10431
12523
  chime: opts.chime,
10432
12524
  confirmExit: opts.confirmExit,
12525
+ mouse: mouseEnabled,
10433
12526
  modeLabel: opts.modeLabel,
10434
12527
  getModeLabel: opts.getModeLabel,
10435
12528
  registerDebugStreamCallback: opts.registerDebugStreamCallback,
10436
- restoreDebugStreamCallback: opts.restoreDebugStreamCallback
12529
+ restoreDebugStreamCallback: opts.restoreDebugStreamCallback,
12530
+ restoredMessages: opts.restoredMessages,
12531
+ restoredToolCalls: opts.restoredToolCalls,
12532
+ listSessions: opts.listSessions,
12533
+ onResumeSession: opts.onResumeSession,
12534
+ getProjectPickerItems: opts.getProjectPickerItems,
12535
+ onProjectSelect: opts.onProjectSelect,
12536
+ requestExit: opts.requestExit,
12537
+ getLiveSessions: opts.getLiveSessions,
12538
+ onSwitchToSession: opts.onSwitchToSession,
12539
+ initialAgentsMonitorOpen: opts.initialAgentsMonitorOpen
10437
12540
  }),
10438
12541
  { exitOnCtrlC: false, stdin: inkStdin }
10439
12542
  );
@@ -10464,6 +12567,190 @@ async function runTui(opts) {
10464
12567
  });
10465
12568
  }
10466
12569
 
10467
- export { runTui };
12570
+ // src/components/history/replay.ts
12571
+ function replaySessionEvents(events, startId) {
12572
+ const entries = [];
12573
+ let nextId = startId;
12574
+ const pendingTools = /* @__PURE__ */ new Map();
12575
+ const completedTools = /* @__PURE__ */ new Set();
12576
+ for (const ev of events) {
12577
+ const entry = eventToEntry(ev, pendingTools, completedTools);
12578
+ if (entry) {
12579
+ entries.push({ ...entry, id: nextId++ });
12580
+ }
12581
+ }
12582
+ for (const [, tu] of pendingTools) {
12583
+ entries.push({
12584
+ id: nextId++,
12585
+ kind: "tool",
12586
+ name: tu.name,
12587
+ durationMs: 0,
12588
+ ok: false,
12589
+ input: tu.input
12590
+ });
12591
+ }
12592
+ return entries;
12593
+ }
12594
+ function eventToEntry(ev, pendingTools, completedTools) {
12595
+ switch (ev.type) {
12596
+ case "user_input": {
12597
+ const text = typeof ev.content === "string" ? ev.content : Array.isArray(ev.content) ? ev.content.filter((b) => b.type === "text").map((b) => b.text).join("") : "";
12598
+ if (!text.trim()) return null;
12599
+ return { kind: "user", text };
12600
+ }
12601
+ case "llm_response": {
12602
+ const text = ev.content.filter((b) => b.type === "text").map((b) => b.text).join("");
12603
+ if (!text.trim()) return null;
12604
+ return { kind: "assistant", text };
12605
+ }
12606
+ case "tool_use": {
12607
+ pendingTools.set(ev.id, { name: ev.name, input: ev.input, ts: ev.ts });
12608
+ return null;
12609
+ }
12610
+ case "tool_result": {
12611
+ if (completedTools.has(ev.id)) {
12612
+ completedTools.delete(ev.id);
12613
+ return null;
12614
+ }
12615
+ const tu = pendingTools.get(ev.id);
12616
+ pendingTools.delete(ev.id);
12617
+ return {
12618
+ kind: "tool",
12619
+ name: tu?.name ?? ev.id,
12620
+ durationMs: 0,
12621
+ // duration not available from tool_result alone
12622
+ ok: !ev.isError,
12623
+ input: tu?.input,
12624
+ output: typeof ev.content === "string" ? ev.content.slice(0, 400) : void 0
12625
+ };
12626
+ }
12627
+ case "tool_call_start": {
12628
+ pendingTools.set(ev.id, { name: ev.name, input: ev.input, ts: ev.ts });
12629
+ return null;
12630
+ }
12631
+ case "tool_call_end": {
12632
+ const tu = pendingTools.get(ev.id);
12633
+ pendingTools.delete(ev.id);
12634
+ completedTools.add(ev.id);
12635
+ return {
12636
+ kind: "tool",
12637
+ name: tu?.name ?? ev.name,
12638
+ durationMs: ev.durationMs,
12639
+ ok: ev.ok ?? false,
12640
+ input: tu?.input,
12641
+ outputBytes: ev.outputBytes,
12642
+ outputTokens: ev.outputTokens,
12643
+ outputLines: ev.outputLines
12644
+ };
12645
+ }
12646
+ case "compaction": {
12647
+ const before = (ev.before / 1e3).toFixed(0);
12648
+ const after = (ev.after / 1e3).toFixed(0);
12649
+ const level = ev.level ? ` (${ev.level})` : "";
12650
+ const reductions = ev.reductions && ev.reductions.length > 0 ? ` [${ev.reductions.map((r) => `${r.phase}: \u2212${r.saved}`).join(", ")}]` : "";
12651
+ return {
12652
+ kind: "info",
12653
+ text: `\u27F2 context compacted${level}: ${before}K \u2192 ${after}K tokens${reductions}`
12654
+ };
12655
+ }
12656
+ case "error": {
12657
+ return {
12658
+ kind: "error",
12659
+ text: ev.phase ? `[${ev.phase}] ${ev.message}` : ev.message
12660
+ };
12661
+ }
12662
+ case "provider_retry": {
12663
+ const secs = (ev.delayMs / 1e3).toFixed(ev.delayMs >= 1e3 ? 1 : 2);
12664
+ return {
12665
+ kind: "warn",
12666
+ 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}`
12667
+ };
12668
+ }
12669
+ case "provider_error": {
12670
+ return {
12671
+ kind: "error",
12672
+ text: ev.status ? `provider error (HTTP ${ev.status}, ${ev.retryable ? "retryable" : "fatal"}): ${ev.description}` : `provider error (${ev.retryable ? "retryable" : "fatal"}): ${ev.description}`
12673
+ };
12674
+ }
12675
+ case "checkpoint": {
12676
+ return {
12677
+ kind: "info",
12678
+ text: `\u2713 checkpoint #${ev.promptIndex}: "${ev.promptPreview.slice(0, 60)}"`
12679
+ };
12680
+ }
12681
+ case "agent_spawned": {
12682
+ return {
12683
+ kind: "subagent",
12684
+ agentLabel: ev.agentId.slice(0, 8),
12685
+ agentColor: "magenta",
12686
+ icon: "\u26A1",
12687
+ text: `spawned as ${ev.role}`
12688
+ };
12689
+ }
12690
+ case "agent_stopped": {
12691
+ return {
12692
+ kind: "subagent",
12693
+ agentLabel: ev.agentId.slice(0, 8),
12694
+ agentColor: "gray",
12695
+ icon: "\u2298",
12696
+ text: "stopped"
12697
+ };
12698
+ }
12699
+ case "agent_error": {
12700
+ return {
12701
+ kind: "subagent",
12702
+ agentLabel: ev.agentId.slice(0, 8),
12703
+ agentColor: "red",
12704
+ icon: "\u2717",
12705
+ text: `error: ${ev.error.slice(0, 80)}`
12706
+ };
12707
+ }
12708
+ case "mode_changed": {
12709
+ return {
12710
+ kind: "info",
12711
+ text: `mode: ${ev.from} \u2192 ${ev.to}`
12712
+ };
12713
+ }
12714
+ case "skill_activated": {
12715
+ return {
12716
+ kind: "info",
12717
+ text: `skill activated: ${ev.skillName}`
12718
+ };
12719
+ }
12720
+ case "skill_deactivated": {
12721
+ return {
12722
+ kind: "info",
12723
+ text: `skill deactivated: ${ev.skillName}`
12724
+ };
12725
+ }
12726
+ case "message_truncated": {
12727
+ return {
12728
+ kind: "warn",
12729
+ text: ev.after < ev.before ? `message truncated: ${ev.before} \u2192 ${ev.after} tokens` : `message truncated at ${ev.after} tokens`
12730
+ };
12731
+ }
12732
+ // Skipped — internal markers not relevant for display
12733
+ case "session_start":
12734
+ case "session_resumed":
12735
+ case "session_end":
12736
+ case "in_flight_start":
12737
+ case "in_flight_end":
12738
+ case "llm_request":
12739
+ case "tool_progress":
12740
+ case "rewound":
12741
+ case "file_snapshot":
12742
+ case "task_created":
12743
+ case "task_updated":
12744
+ case "task_completed":
12745
+ case "task_failed":
12746
+ case "spec_parsed":
12747
+ case "spec_analyzed":
12748
+ return null;
12749
+ default:
12750
+ return null;
12751
+ }
12752
+ }
12753
+
12754
+ export { parseInline, replaySessionEvents, runTui };
10468
12755
  //# sourceMappingURL=index.js.map
10469
12756
  //# sourceMappingURL=index.js.map