@wrongstack/tui 0.155.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 * as path3 from 'path';
5
- import React5, { useState, useEffect, useMemo, memo, useRef, useCallback, useReducer } from 'react';
3
+ import { Box as Box$1, useInput, useStdin, useStdout, Text as Text$1, render, useApp, measureElement, Static } from 'ink';
4
+ import * as path4 from 'path';
5
+ import React5, { forwardRef, useState, useEffect, useMemo, memo, useRef, useCallback, useReducer, useLayoutEffect } from 'react';
6
6
  import * as fs2 from 'fs/promises';
7
7
  import { routeImagesForModel } from '@wrongstack/runtime/vision';
8
8
  import { getIndexState, onIndexStateChange, getProcessRegistry } from '@wrongstack/tools';
9
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
10
  import { readClipboardImage } from '@wrongstack/runtime/clipboard';
10
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
11
- import { expectDefined as expectDefined$1 } from '@wrongstack/core/utils';
11
+ import { expectDefined } from '@wrongstack/core/utils';
12
+ import * as v8 from 'v8';
12
13
  import { spawn } from 'child_process';
13
14
 
14
15
  // src/run-tui.ts
16
+
17
+ // src/theme.ts
18
+ var pastel = Object.freeze({
19
+ // Base 8
20
+ black: "#11111b",
21
+ red: "#f38ba8",
22
+ green: "#a6e3a1",
23
+ yellow: "#f9e2af",
24
+ blue: "#89b4fa",
25
+ magenta: "#cba6f7",
26
+ cyan: "#94e2d5",
27
+ white: "#cdd6f4",
28
+ // Greys
29
+ gray: "#7f849c",
30
+ grey: "#7f849c",
31
+ // Bright variants — a touch lighter / shifted within the same family
32
+ blackBright: "#585b70",
33
+ redBright: "#eba0ac",
34
+ greenBright: "#b8e8b0",
35
+ yellowBright: "#f5e6b8",
36
+ blueBright: "#89dceb",
37
+ magentaBright: "#b4befe",
38
+ cyanBright: "#99e6da",
39
+ whiteBright: "#ffffff"
40
+ });
41
+ function softColor(color) {
42
+ if (!color) return color;
43
+ return pastel[color] ?? color;
44
+ }
45
+ var theme = Object.freeze({
46
+ accent: pastel.cyan,
47
+ user: pastel.yellow,
48
+ assistant: pastel.cyan,
49
+ tool: pastel.cyan,
50
+ success: pastel.green,
51
+ warn: pastel.yellow,
52
+ error: pastel.red,
53
+ dim: true,
54
+ // Subtle slate border — present but never harsh.
55
+ borderDefault: pastel.blackBright,
56
+ borderActive: pastel.yellow,
57
+ brand: pastel.magenta,
58
+ monitor: {
59
+ fleet: pastel.cyan,
60
+ agents: pastel.magenta,
61
+ worktree: pastel.green,
62
+ phase: pastel.cyan
63
+ },
64
+ // Diff blocks render dark text on a pastel wash (see DiffBlock).
65
+ diffAddBg: pastel.green,
66
+ diffDelBg: pastel.red
67
+ });
68
+ var colorProps = (color, backgroundColor) => {
69
+ const c = softColor(color);
70
+ const bg = softColor(backgroundColor);
71
+ return { ...c ? { color: c } : {}, ...bg ? { backgroundColor: bg } : {} };
72
+ };
73
+ function Text({ color, backgroundColor, ...rest }) {
74
+ return /* @__PURE__ */ jsx(Text$1, { ...rest, ...colorProps(color, backgroundColor) });
75
+ }
76
+ var Box = forwardRef(function Box2({ borderColor, backgroundColor, ...rest }, ref) {
77
+ const bc = softColor(borderColor);
78
+ const bg = softColor(backgroundColor);
79
+ return /* @__PURE__ */ jsx(
80
+ Box$1,
81
+ {
82
+ ref,
83
+ ...rest,
84
+ ...bc ? { borderColor: bc } : {},
85
+ ...bg ? { backgroundColor: bg } : {}
86
+ }
87
+ );
88
+ });
15
89
  var MODE_ICONS = {
16
90
  teach: "\u{1F9D1}\u200D\u{1F3EB}",
17
91
  brief: "\u26A1",
@@ -46,12 +120,14 @@ function StatusBar({
46
120
  startedAt,
47
121
  todos,
48
122
  plan,
123
+ tasks,
49
124
  fleet,
50
125
  fleetAgents,
51
126
  git,
52
127
  subagentCount = 0,
53
128
  brain,
54
129
  projectName,
130
+ workingDir,
55
131
  processCount,
56
132
  context,
57
133
  hiddenItems,
@@ -61,7 +137,10 @@ function StatusBar({
61
137
  modeLabel,
62
138
  debugStreamStats,
63
139
  enhanceCountdown,
64
- autoProceedCountdown
140
+ nextStepsAutoSubmitCountdown,
141
+ autoProceedCountdown,
142
+ sessionCount,
143
+ mailbox
65
144
  }) {
66
145
  const { stdout } = useStdout();
67
146
  const [termWidth, setTermWidth] = useState(stdout?.columns ?? 90);
@@ -94,17 +173,19 @@ function StatusBar({
94
173
  );
95
174
  return () => clearInterval(t);
96
175
  }, [state]);
97
- const spinner = expectDefined(SPINNER_FRAMES[spinnerIdx]);
176
+ const spinner = expectDefined$1(SPINNER_FRAMES[spinnerIdx]);
98
177
  const { label: stateLabel, color: stateColor } = stateChip(state, fleet?.running ?? 0);
99
178
  const statePrefix = state === "idle" || state === "aborting" ? "\u25CF" : spinner;
100
179
  const thinking = state === "running" || state === "streaming";
101
180
  const hasAutoProceed = autoProceedCountdown != null && autoProceedCountdown > 0;
102
- const hasSecondLine = yolo || autonomy && autonomy !== "off" || startedAt != null || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0 || goalSummary !== null && goalSummary !== void 0 || !!modeLabel || hasAutoProceed;
181
+ const hasSecondLine = yolo || autonomy && autonomy !== "off" || startedAt != null || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0 || workingDir !== void 0 && workingDir.length > 0 || goalSummary !== null && goalSummary !== void 0 || !!modeLabel || hasAutoProceed;
103
182
  const fleetHasActivity = fleet && (fleet.running > 0 || fleet.idle > 0 || fleet.pending > 0 || fleet.completed > 0) || subagentCount > 0;
104
183
  const hasBrainActivity = !!brain && brain.state !== "idle";
105
184
  const hasDebugStream = !!debugStreamStats;
106
185
  const hasEnhanceCountdown = enhanceCountdown != null && enhanceCountdown > 0;
107
- const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity || hasBrainActivity || hasDebugStream || hasEnhanceCountdown;
186
+ const hasNextStepsAutoSubmit = nextStepsAutoSubmitCountdown != null && nextStepsAutoSubmitCountdown > 0;
187
+ const hasTaskActivity = tasks && (tasks.pending > 0 || tasks.inProgress > 0 || tasks.completed > 0 || tasks.blocked > 0 || tasks.failed > 0);
188
+ const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || hasTaskActivity || fleetHasActivity || hasBrainActivity || hasDebugStream || hasEnhanceCountdown || hasNextStepsAutoSubmit;
108
189
  return /* @__PURE__ */ jsxs(
109
190
  Box,
110
191
  {
@@ -148,13 +229,17 @@ function StatusBar({
148
229
  /* @__PURE__ */ jsx(Text, { color: "magenta", children: model }),
149
230
  context && !hiddenSet.has("context") ? /* @__PURE__ */ jsxs(Fragment, { children: [
150
231
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
151
- /* @__PURE__ */ jsxs(Text, { color: context.used / context.max < 0.6 ? "green" : context.used / context.max < 0.75 ? "yellow" : "red", children: [
152
- "ctx ",
153
- renderMeter(context.used / context.max, 8),
154
- " ",
155
- Math.round(context.used / context.max * 100),
156
- "%"
157
- ] })
232
+ (() => {
233
+ const ratio = context.used / context.max;
234
+ const clampedRatio = Math.min(ratio, 1);
235
+ const pctText = `${Math.min(Math.round(ratio * 100), 100)}%`;
236
+ return /* @__PURE__ */ jsxs(Text, { color: clampedRatio < 0.6 ? "green" : clampedRatio < 0.75 ? "yellow" : "red", children: [
237
+ "ctx ",
238
+ renderMeter(clampedRatio, 8),
239
+ " ",
240
+ pctText
241
+ ] });
242
+ })()
158
243
  ] }) : null,
159
244
  usage && isComfortable ? /* @__PURE__ */ jsxs(Fragment, { children: [
160
245
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
@@ -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(path5, _query) {
1471
- return path5;
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" },
@@ -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,7 +3720,7 @@ function Banner({
3408
3720
  entry
3409
3721
  }) {
3410
3722
  const cwdShort = shortenPath(entry.cwd, 48);
3411
- const projectLabel = path3.basename(entry.cwd);
3723
+ const projectLabel = path4.basename(entry.cwd);
3412
3724
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 0, children: [
3413
3725
  /* @__PURE__ */ jsxs(Text, { children: [
3414
3726
  /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: " \u259F\u259B " }),
@@ -3440,7 +3752,7 @@ function Banner({
3440
3752
  ] }),
3441
3753
  /* @__PURE__ */ jsxs(Text, { children: [
3442
3754
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: " hints " }),
3443
- /* @__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" })
3444
3756
  ] })
3445
3757
  ] });
3446
3758
  }
@@ -3468,6 +3780,8 @@ function brainStatusStyle(status) {
3468
3780
  return { icon: "?", color: "yellow" };
3469
3781
  case "denied":
3470
3782
  return { icon: "\xD7", color: "red" };
3783
+ case "intervention":
3784
+ return { icon: "\u26A1", color: "yellow" };
3471
3785
  }
3472
3786
  }
3473
3787
  function brainRiskColor(risk) {
@@ -3618,8 +3932,10 @@ var Entry = React5.memo(function Entry2({
3618
3932
  diff ? /* @__PURE__ */ jsx(DiffBlock, { rows: diff.rows, hidden: diff.hidden }) : null
3619
3933
  ] });
3620
3934
  }
3621
- case "info":
3622
- 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
+ }
3623
3939
  case "warn":
3624
3940
  return /* @__PURE__ */ jsx(
3625
3941
  Box,
@@ -3728,7 +4044,7 @@ var Entry = React5.memo(function Entry2({
3728
4044
  }
3729
4045
  }
3730
4046
  });
3731
- function History({ entries, streamingText, toolStream }) {
4047
+ function History({ entries, generation, streamingText, toolStream }) {
3732
4048
  const { stdout } = useStdout();
3733
4049
  const [termSize, setTermSize] = useState({
3734
4050
  columns: stdout?.columns ?? 80,
@@ -3746,10 +4062,209 @@ function History({ entries, streamingText, toolStream }) {
3746
4062
  const termWidth = termSize.columns;
3747
4063
  const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
3748
4064
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3749
- /* @__PURE__ */ jsx(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth }) }, entry.id) }),
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),
3750
4066
  /* @__PURE__ */ jsx(Box, { flexGrow: 1, children: tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail, termWidth }) : null })
3751
4067
  ] });
3752
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
+ }
3753
4268
 
3754
4269
  // src/fn-keys.ts
3755
4270
  function fnKey(data) {
@@ -3853,6 +4368,45 @@ function layoutInputRows(prompt, value, cursor, width) {
3853
4368
  if (row.length > 0 || rows.length === 0) rows.push(row);
3854
4369
  return rows;
3855
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
+ }
3856
4410
  function renderRow2(cells, rowKey, promptColor) {
3857
4411
  const out = [];
3858
4412
  let run = "";
@@ -3907,14 +4461,6 @@ function isBackspaceOrDelete(data) {
3907
4461
  if (data === "\x1B[3~") return "delete";
3908
4462
  return null;
3909
4463
  }
3910
- function parseMouseWheel(data) {
3911
- const m = data.match(new RegExp(`^${String.fromCharCode(27)}\\[<(\\d+);(\\d+);(\\d+)([Mm])$`, "u"));
3912
- if (!m) return null;
3913
- const cb = Number.parseInt(expectDefined(m[1]), 10);
3914
- if (cb === 64) return 1;
3915
- if (cb === 65) return -1;
3916
- return null;
3917
- }
3918
4464
  var EMPTY_KEY = {
3919
4465
  upArrow: false,
3920
4466
  downArrow: false,
@@ -3948,6 +4494,7 @@ var Input = memo(function Input2({
3948
4494
  const suppressInkDeleteRef = useRef(false);
3949
4495
  useInput((input, key) => {
3950
4496
  if (disabled) return;
4497
+ if (input && isLeakedMouseInput(input)) return;
3951
4498
  if (key.escape && suppressInkEscRef.current) {
3952
4499
  suppressInkEscRef.current = false;
3953
4500
  return;
@@ -4011,9 +4558,15 @@ var Input = memo(function Input2({
4011
4558
  onKey("", { ...EMPTY_KEY, backspace: true, ctrl: true });
4012
4559
  return;
4013
4560
  }
4014
- const wheelDelta = parseMouseWheel(s2);
4015
- if (wheelDelta !== null) {
4016
- 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
+ }
4017
4570
  return;
4018
4571
  }
4019
4572
  const fn = fnKey(s2);
@@ -4319,6 +4872,80 @@ function PhasePanel({ phases, nowTick }) {
4319
4872
  }
4320
4873
  );
4321
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
+ }
4322
4949
  function QueuePanel({ items }) {
4323
4950
  const { stdout } = useStdout();
4324
4951
  const w = stdout?.columns ?? 80;
@@ -4549,6 +5176,200 @@ function renderProgressBar(progress, trend) {
4549
5176
  ] })
4550
5177
  ] });
4551
5178
  }
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
+ ] });
5236
+ }
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
+ }
5250
+ }
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
+ }
5266
+ }
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
+ }
4552
5373
  var DELAY_PRESETS_MS = [0, 15e3, 3e4, 45e3, 6e4, 12e4];
4553
5374
  var SETTINGS_MODES = ["off", "suggest", "auto"];
4554
5375
  var LOG_LEVELS = ["error", "warn", "info", "debug", "trace"];
@@ -4557,6 +5378,7 @@ var COMPACTOR_STRATEGIES = ["hybrid", "intelligent", "selective"];
4557
5378
  var MAX_ITERATIONS_PRESETS = [100, 200, 500, 1e3, 0];
4558
5379
  var AUTO_PROCEED_MAX_PRESETS = [10, 25, 50, 100, 250, 0];
4559
5380
  var ENHANCE_DELAY_PRESETS = [3e4, 45e3, 6e4, 9e4, 12e4];
5381
+ var ENHANCE_LANGUAGES = ["original", "english"];
4560
5382
  function formatSettingsDelay(ms) {
4561
5383
  if (ms === 0) return "disabled";
4562
5384
  if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
@@ -4574,7 +5396,7 @@ var MODE_DESC = {
4574
5396
  suggest: "Shows next-step suggestions after each turn",
4575
5397
  auto: "Self-driving \u2014 agent continues automatically"
4576
5398
  };
4577
- var SETTINGS_FIELD_COUNT = 23;
5399
+ var SETTINGS_FIELD_COUNT = 24;
4578
5400
  var CONFIG_SCOPES = ["global", "project"];
4579
5401
  function SettingsPicker({
4580
5402
  field,
@@ -4599,6 +5421,8 @@ function SettingsPicker({
4599
5421
  maxIterations,
4600
5422
  autoProceedMaxIterations,
4601
5423
  enhanceDelayMs,
5424
+ enhanceEnabled,
5425
+ enhanceLanguage,
4602
5426
  debugStream,
4603
5427
  configScope,
4604
5428
  hint
@@ -4722,6 +5546,16 @@ function SettingsPicker({
4722
5546
  value: formatEnhanceDelay(enhanceDelayMs),
4723
5547
  detail: "Timeout for prompt refinement preview (30s\u2013120s)"
4724
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
+ },
4725
5559
  // ── Debug ──
4726
5560
  { section: "Debug" },
4727
5561
  {
@@ -4883,7 +5717,55 @@ function windowRows(rows, focus, max) {
4883
5717
  }
4884
5718
  return { rows: rows.slice(start, end), start, end, contextHeader };
4885
5719
  }
4886
- function TodosMonitor({ todos }) {
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
+ }
5768
+ function TodosMonitor({ todos }) {
4887
5769
  const { stdout } = useStdout();
4888
5770
  const done = todos.filter((t) => t.status === "completed").length;
4889
5771
  const inProgress = todos.filter((t) => t.status === "in_progress").length;
@@ -5224,7 +6106,7 @@ async function loadIndex(root) {
5224
6106
  async function walk(root, rel, depth, out) {
5225
6107
  if (out.length >= MAX_FILES_INDEXED) return;
5226
6108
  if (depth > MAX_DEPTH) return;
5227
- const dir = rel ? path3.join(root, rel) : root;
6109
+ const dir = rel ? path4.join(root, rel) : root;
5228
6110
  let entries;
5229
6111
  try {
5230
6112
  entries = await fs2.readdir(dir, { withFileTypes: true });
@@ -5796,11 +6678,28 @@ function useBrainEvents(events, dispatch) {
5796
6678
  const offAnswered = events.on("brain.decision_answered", (payload) => addBrainEntry("answered", payload));
5797
6679
  const offAskHuman = events.on("brain.decision_ask_human", (payload) => addBrainEntry("ask_human", payload));
5798
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
+ });
5799
6697
  return () => {
5800
6698
  offRequested();
5801
6699
  offAnswered();
5802
6700
  offAskHuman();
5803
6701
  offDenied();
6702
+ offIntervention();
5804
6703
  };
5805
6704
  }, [events, dispatch]);
5806
6705
  }
@@ -5812,7 +6711,7 @@ function labelFor2(labelsRef, id, name) {
5812
6711
  const n = m.size + 1;
5813
6712
  const v = {
5814
6713
  label: name && name !== id ? name : `AGENT#${n}`,
5815
- color: expectDefined(STREAM_COLORS2[(n - 1) % STREAM_COLORS2.length])
6714
+ color: expectDefined$1(STREAM_COLORS2[(n - 1) % STREAM_COLORS2.length])
5816
6715
  };
5817
6716
  m.set(id, v);
5818
6717
  return v;
@@ -6081,6 +6980,10 @@ function useAutoPhaseEvents(subscribeAutoPhase, dispatch, stateRef) {
6081
6980
  if (!p.kept) dispatch({ type: "worktreeRemove", handleId: p.handleId });
6082
6981
  break;
6083
6982
  }
6983
+ case "countdown.tick": {
6984
+ dispatch({ type: "countdownTick", remainingSeconds: payload.remaining });
6985
+ break;
6986
+ }
6084
6987
  }
6085
6988
  };
6086
6989
  return subscribeAutoPhase(handler);
@@ -6341,6 +7244,52 @@ function buildSteeringPreamble(snapshot, newDirection) {
6341
7244
  lines.push("]");
6342
7245
  return lines.join("\n");
6343
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
+ }
6344
7293
  function reducer(state, action) {
6345
7294
  switch (action.type) {
6346
7295
  case "addEntry": {
@@ -6348,7 +7297,8 @@ function reducer(state, action) {
6348
7297
  if ((e.kind === "user" || e.kind === "assistant" || e.kind === "info" || e.kind === "warn" || e.kind === "error" || e.kind === "turn-summary") && !e.text?.trim()) {
6349
7298
  return state;
6350
7299
  }
6351
- 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 }];
6352
7302
  return { ...state, entries: appended, nextId: state.nextId + 1 };
6353
7303
  }
6354
7304
  case "setBuffer":
@@ -6472,9 +7422,11 @@ function reducer(state, action) {
6472
7422
  case "toolStreamAppend": {
6473
7423
  const cur = state.toolStream;
6474
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;
6475
7427
  return {
6476
7428
  ...state,
6477
- toolStream: { ...cur, text: cur.text + action.text }
7429
+ toolStream: { ...cur, text }
6478
7430
  };
6479
7431
  }
6480
7432
  return {
@@ -6661,6 +7613,35 @@ function reducer(state, action) {
6661
7613
  ...state,
6662
7614
  autonomyPicker: { ...state.autonomyPicker, hint: action.text }
6663
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
+ }
6664
7645
  case "settingsOpen":
6665
7646
  return {
6666
7647
  ...state,
@@ -6688,6 +7669,8 @@ function reducer(state, action) {
6688
7669
  maxIterations: action.maxIterations,
6689
7670
  autoProceedMaxIterations: action.autoProceedMaxIterations,
6690
7671
  enhanceDelayMs: action.enhanceDelayMs,
7672
+ enhanceEnabled: action.enhanceEnabled,
7673
+ enhanceLanguage: action.enhanceLanguage,
6691
7674
  debugStream: action.debugStream,
6692
7675
  configScope: action.configScope,
6693
7676
  hint: void 0
@@ -6716,13 +7699,13 @@ function reducer(state, action) {
6716
7699
  const i = SETTINGS_MODES.indexOf(sp.mode);
6717
7700
  const base = i < 0 ? 0 : i;
6718
7701
  const next = (base + action.delta + SETTINGS_MODES.length) % SETTINGS_MODES.length;
6719
- 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 } };
6720
7703
  }
6721
7704
  if (f === 1) {
6722
7705
  const j = DELAY_PRESETS_MS.indexOf(sp.delayMs);
6723
7706
  const base = j < 0 ? 0 : j;
6724
7707
  const next = (base + action.delta + DELAY_PRESETS_MS.length) % DELAY_PRESETS_MS.length;
6725
- return { ...state, settingsPicker: { ...sp, delayMs: expectDefined(DELAY_PRESETS_MS[next]), hint: void 0 } };
7708
+ return { ...state, settingsPicker: { ...sp, delayMs: expectDefined$1(DELAY_PRESETS_MS[next]), hint: void 0 } };
6726
7709
  }
6727
7710
  if (f === 2) return { ...state, settingsPicker: { ...sp, titleAnimation: !sp.titleAnimation, hint: void 0 } };
6728
7711
  if (f === 3) return { ...state, settingsPicker: { ...sp, yolo: !sp.yolo, hint: void 0 } };
@@ -6740,50 +7723,99 @@ function reducer(state, action) {
6740
7723
  const i = COMPACTOR_STRATEGIES.indexOf(sp.contextStrategy);
6741
7724
  const base = i < 0 ? 0 : i;
6742
7725
  const next = (base + action.delta + COMPACTOR_STRATEGIES.length) % COMPACTOR_STRATEGIES.length;
6743
- 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 } };
6744
7727
  }
6745
7728
  if (f === 15) {
6746
7729
  const i = LOG_LEVELS.indexOf(sp.logLevel);
6747
7730
  const base = i < 0 ? 0 : i;
6748
7731
  const next = (base + action.delta + LOG_LEVELS.length) % LOG_LEVELS.length;
6749
- 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 } };
6750
7733
  }
6751
7734
  if (f === 16) {
6752
7735
  const i = AUDIT_LEVELS.indexOf(sp.auditLevel);
6753
7736
  const base = i < 0 ? 0 : i;
6754
7737
  const next = (base + action.delta + AUDIT_LEVELS.length) % AUDIT_LEVELS.length;
6755
- 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 } };
6756
7739
  }
6757
7740
  if (f === 17) return { ...state, settingsPicker: { ...sp, indexOnStart: !sp.indexOnStart, hint: void 0 } };
6758
7741
  if (f === 18) {
6759
7742
  const j = MAX_ITERATIONS_PRESETS.indexOf(sp.maxIterations);
6760
7743
  const base = j < 0 ? 0 : j;
6761
7744
  const next = (base + action.delta + MAX_ITERATIONS_PRESETS.length) % MAX_ITERATIONS_PRESETS.length;
6762
- return { ...state, settingsPicker: { ...sp, maxIterations: expectDefined(MAX_ITERATIONS_PRESETS[next]), hint: void 0 } };
7745
+ return { ...state, settingsPicker: { ...sp, maxIterations: expectDefined$1(MAX_ITERATIONS_PRESETS[next]), hint: void 0 } };
6763
7746
  }
6764
7747
  if (f === 19) {
6765
7748
  const aj = AUTO_PROCEED_MAX_PRESETS.indexOf(sp.autoProceedMaxIterations);
6766
7749
  const abase = aj < 0 ? 0 : aj;
6767
7750
  const anext = (abase + action.delta + AUTO_PROCEED_MAX_PRESETS.length) % AUTO_PROCEED_MAX_PRESETS.length;
6768
- return { ...state, settingsPicker: { ...sp, autoProceedMaxIterations: expectDefined(AUTO_PROCEED_MAX_PRESETS[anext]), hint: void 0 } };
7751
+ return { ...state, settingsPicker: { ...sp, autoProceedMaxIterations: expectDefined$1(AUTO_PROCEED_MAX_PRESETS[anext]), hint: void 0 } };
6769
7752
  }
6770
7753
  if (f === 20) {
6771
7754
  const ej = ENHANCE_DELAY_PRESETS.indexOf(sp.enhanceDelayMs);
6772
7755
  const ebase = ej < 0 ? 0 : ej;
6773
7756
  const enext = (ebase + action.delta + ENHANCE_DELAY_PRESETS.length) % ENHANCE_DELAY_PRESETS.length;
6774
- return { ...state, settingsPicker: { ...sp, enhanceDelayMs: expectDefined(ENHANCE_DELAY_PRESETS[enext]), hint: void 0 } };
7757
+ return { ...state, settingsPicker: { ...sp, enhanceDelayMs: expectDefined$1(ENHANCE_DELAY_PRESETS[enext]), hint: void 0 } };
6775
7758
  }
6776
- if (f === 21) return { ...state, settingsPicker: { ...sp, debugStream: !sp.debugStream, hint: void 0 } };
6777
- if (f === 22) {
7759
+ if (f === 20) return { ...state, settingsPicker: { ...sp, debugStream: !sp.debugStream, hint: void 0 } };
7760
+ if (f === 21) {
6778
7761
  const i = CONFIG_SCOPES.indexOf(sp.configScope);
6779
7762
  const base = i < 0 ? 0 : i;
6780
7763
  const next = (base + action.delta + CONFIG_SCOPES.length) % CONFIG_SCOPES.length;
6781
- 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 } };
6782
7772
  }
6783
7773
  return state;
6784
7774
  }
6785
7775
  case "settingsHint":
6786
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 } };
6787
7819
  case "confirmOpen":
6788
7820
  return { ...state, confirmQueue: [...state.confirmQueue, action.info] };
6789
7821
  case "confirmClose":
@@ -7459,6 +8491,47 @@ function reducer(state, action) {
7459
8491
  if (state.debugStreamStats === null) return state;
7460
8492
  return { ...state, debugStreamStats: null };
7461
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
+ }
7462
8535
  }
7463
8536
  }
7464
8537
  var INPUT_PROMPT = "\u203A ";
@@ -7467,7 +8540,7 @@ function selectedSlashCommandLine(picker) {
7467
8540
  const picked = picker.matches[picker.selected];
7468
8541
  return picked ? `/${picked.name}` : null;
7469
8542
  }
7470
- function rehydrateHistory(messages, startId) {
8543
+ function rehydrateHistory(messages, startId, toolCalls) {
7471
8544
  const entries = [];
7472
8545
  let nextId = startId;
7473
8546
  for (const msg of messages) {
@@ -7481,6 +8554,20 @@ function rehydrateHistory(messages, startId) {
7481
8554
  entries.push({ id: nextId++, kind: "assistant", text: trimmed });
7482
8555
  }
7483
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
+ }
7484
8571
  return entries;
7485
8572
  }
7486
8573
  var PASTE_THRESHOLD_CHARS = 200;
@@ -7495,9 +8582,11 @@ function App({
7495
8582
  model,
7496
8583
  banner = true,
7497
8584
  queueStore,
8585
+ onQueueChange,
7498
8586
  yolo = false,
7499
8587
  chime = false,
7500
8588
  confirmExit = true,
8589
+ mouse = false,
7501
8590
  enhanceEnabled = true,
7502
8591
  enhanceController,
7503
8592
  enhanceDelayMs = 15e3,
@@ -7519,12 +8608,16 @@ function App({
7519
8608
  getSettings,
7520
8609
  saveSettings,
7521
8610
  predictNext,
8611
+ onSuggestionsParsed,
8612
+ getSuggestions,
7522
8613
  switchAutonomy,
7523
8614
  effectiveMaxContext,
7524
8615
  onExit,
7525
8616
  director,
7526
8617
  fleetRoster,
7527
8618
  onClearHistory,
8619
+ listSessions,
8620
+ onResumeSession,
7528
8621
  fleetStreamController,
7529
8622
  statuslineHiddenItems,
7530
8623
  setStatuslineHiddenItems,
@@ -7535,7 +8628,14 @@ function App({
7535
8628
  modeLabel,
7536
8629
  getModeLabel,
7537
8630
  registerDebugStreamCallback,
7538
- restoreDebugStreamCallback
8631
+ restoreDebugStreamCallback,
8632
+ restoredToolCalls,
8633
+ getProjectPickerItems,
8634
+ onProjectSelect,
8635
+ requestExit,
8636
+ getLiveSessions,
8637
+ onSwitchToSession,
8638
+ initialAgentsMonitorOpen
7539
8639
  }) {
7540
8640
  const { exit } = useApp();
7541
8641
  const { stdout } = useStdout();
@@ -7546,6 +8646,8 @@ function App({
7546
8646
  const [autonomyLive, setAutonomyLive] = useState(getAutonomy?.() ?? "off");
7547
8647
  const [liveModeLabel, setLiveModeLabel] = useState(modeLabel ?? "");
7548
8648
  const [hiddenItems, setHiddenItems] = useState(statuslineHiddenItems);
8649
+ const [sessionCount, setSessionCount] = useState(0);
8650
+ const prevBranchRef = useRef(null);
7549
8651
  const [indexState, setIndexState] = useState(() => getIndexState());
7550
8652
  useEffect(() => {
7551
8653
  setIndexState(getIndexState());
@@ -7596,7 +8698,8 @@ function App({
7596
8698
  return rehydrateHistory(
7597
8699
  visible,
7598
8700
  /* startId */
7599
- 1
8701
+ 1,
8702
+ restoredToolCalls
7600
8703
  );
7601
8704
  })();
7602
8705
  const initialNextId = 1 + restoredEntries.length;
@@ -7616,6 +8719,7 @@ function App({
7616
8719
  ] : [],
7617
8720
  ...restoredEntries
7618
8721
  ],
8722
+ historyGen: 0,
7619
8723
  buffer: "",
7620
8724
  cursor: 0,
7621
8725
  streamingText: "",
@@ -7645,7 +8749,9 @@ function App({
7645
8749
  searchQuery: ""
7646
8750
  },
7647
8751
  autonomyPicker: { open: false, options: [], selected: 0 },
7648
- settingsPicker: { open: false, field: 0, mode: "off", delayMs: 0, titleAnimation: true, yolo: false, streamFleet: true, chime: false, confirmExit: true, nextPrediction: false, featureMcp: true, featurePlugins: true, featureMemory: true, featureSkills: true, featureModelsRegistry: true, contextAutoCompact: true, contextStrategy: "hybrid", logLevel: "info", auditLevel: "standard", indexOnStart: true, maxIterations: 500, autoProceedMaxIterations: 50, enhanceDelayMs: 6e4, debugStream: false, configScope: "global" },
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 },
7649
8755
  confirmQueue: [],
7650
8756
  enhance: null,
7651
8757
  enhanceEnabled,
@@ -7667,12 +8773,15 @@ function App({
7667
8773
  fleetConcurrency: 4,
7668
8774
  streamFleet: true,
7669
8775
  monitorOpen: false,
7670
- agentsMonitorOpen: false,
8776
+ agentsMonitorOpen: initialAgentsMonitorOpen ?? false,
7671
8777
  helpOpen: false,
7672
8778
  todosMonitorOpen: false,
7673
8779
  queuePanelOpen: false,
7674
8780
  processListOpen: false,
7675
8781
  goalPanelOpen: false,
8782
+ sessionsPanelOpen: false,
8783
+ sessionsPanel: { sessions: [], busy: false, selected: -1 },
8784
+ sessionResumeConfirm: null,
7676
8785
  collabSession: null,
7677
8786
  checkpoints: [],
7678
8787
  rewindOverlay: null,
@@ -7685,7 +8794,8 @@ function App({
7685
8794
  totalLines: 0,
7686
8795
  viewportRows: 0,
7687
8796
  pendingNewLines: 0,
7688
- debugStreamStats: null
8797
+ debugStreamStats: null,
8798
+ countdown: null
7689
8799
  });
7690
8800
  const builderRef = useRef(null);
7691
8801
  if (builderRef.current === null) {
@@ -7699,9 +8809,23 @@ function App({
7699
8809
  const lastEnterAtRef = useRef(0);
7700
8810
  const tokenPreviewsRef = useRef(/* @__PURE__ */ new Map());
7701
8811
  const projectName = React5.useMemo(() => {
7702
- const base = path3.basename(projectRoot);
7703
- return base && base !== path3.sep ? base : void 0;
8812
+ const base = path4.basename(projectRoot);
8813
+ return base && base !== path4.sep ? base : void 0;
7704
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]);
7705
8829
  const chimeRef = useRef(chime);
7706
8830
  chimeRef.current = chime;
7707
8831
  const confirmExitRef = useRef(confirmExit);
@@ -7713,6 +8837,48 @@ function App({
7713
8837
  stateRef.current = state;
7714
8838
  const draftRef = useRef({ buffer: state.buffer, cursor: state.cursor });
7715
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
+ });
7716
8882
  const handleKeyRef = useRef(null);
7717
8883
  const handleRewindTo = React5.useCallback(
7718
8884
  async (checkpointIndex) => {
@@ -7738,6 +8904,35 @@ function App({
7738
8904
  const t = setInterval(() => setNowTick(Date.now()), 1e4);
7739
8905
  return () => clearInterval(t);
7740
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]);
7741
8936
  useEffect(() => {
7742
8937
  if (state.goalPanelOpen) refreshGoalSummary();
7743
8938
  }, [state.goalPanelOpen, nowTick, refreshGoalSummary]);
@@ -7748,20 +8943,14 @@ function App({
7748
8943
  return () => clearInterval(t);
7749
8944
  }, [state.enhanceBusy]);
7750
8945
  const todosRef = useRef(JSON.stringify([]));
8946
+ const staleGuardRef = useRef(JSON.stringify({ a: "", y: false, m: "", model: "", provider: "" }));
7751
8947
  useEffect(() => {
7752
8948
  const poll = () => {
7753
- const snap = JSON.stringify(agent.ctx.todos.map((t2) => ({ s: t2.status })));
7754
- if (snap !== todosRef.current) {
7755
- 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;
7756
8952
  setNowTick(Date.now());
7757
8953
  }
7758
- };
7759
- const t = setInterval(poll, 2e3);
7760
- return () => clearInterval(t);
7761
- }, [agent.ctx.todos]);
7762
- const staleGuardRef = useRef(JSON.stringify({ a: "", y: false, m: "", model: "", provider: "" }));
7763
- useEffect(() => {
7764
- const poll = () => {
7765
8954
  const a = getAutonomy?.() ?? "off";
7766
8955
  const y = getYolo?.() ?? false;
7767
8956
  const m = getModeLabel?.() ?? "";
@@ -7793,23 +8982,162 @@ function App({
7793
8982
  liveModel,
7794
8983
  liveProvider,
7795
8984
  agent.ctx.model,
7796
- agent.ctx.provider
8985
+ agent.ctx.provider,
8986
+ agent.ctx.todos
7797
8987
  ]);
7798
8988
  const [gitInfo, setGitInfo] = React5.useState(null);
7799
8989
  useEffect(() => {
7800
8990
  let cancelled = false;
7801
8991
  const refresh = () => {
7802
8992
  readGitInfo(agent.ctx.cwd).then((info) => {
7803
- if (!cancelled) setGitInfo(info);
7804
- }).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
+ });
7805
9020
  };
7806
9021
  refresh();
9022
+ if (gitInfo?.branch && prevBranchRef.current === null) {
9023
+ prevBranchRef.current = gitInfo.branch;
9024
+ }
7807
9025
  const t = setInterval(refresh, 5e3);
7808
9026
  return () => {
7809
9027
  cancelled = true;
7810
9028
  clearInterval(t);
7811
9029
  };
7812
- }, [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]);
7813
9141
  const maxContext = activeMaxContext ?? agent.ctx.provider.capabilities.maxContext;
7814
9142
  const currentContextTokens = (tokenCounter?.currentRequestTokens()?.input ?? 0) + (tokenCounter?.currentRequestTokens()?.cacheRead ?? 0);
7815
9143
  const contextWindow = useMemo(() => {
@@ -7897,6 +9225,53 @@ function App({
7897
9225
  clearInterval(id);
7898
9226
  };
7899
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]);
7900
9275
  const prevAnyOverlayOpen = useRef(false);
7901
9276
  const prevEntriesCount = useRef(0);
7902
9277
  const prevToolStreamLen = useRef(0);
@@ -7907,7 +9282,7 @@ function App({
7907
9282
  }
7908
9283
  }, []);
7909
9284
  React5.useLayoutEffect(() => {
7910
- const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || state.enhanceBusy || state.enhance != null || state.escConfirm != null || state.confirmQueue.length > 0;
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;
7911
9286
  const overlayClosed = prevAnyOverlayOpen.current && !anyOpenNow;
7912
9287
  const newEntryCommitted = state.entries.length > prevEntriesCount.current;
7913
9288
  const curToolStreamLen = state.toolStream?.text.length ?? 0;
@@ -7934,22 +9309,29 @@ function App({
7934
9309
  ]);
7935
9310
  const resizeGateRef = useRef(0);
7936
9311
  const preResizePanelsRef = useRef(null);
9312
+ const resizeRestoreTimerRef = useRef(null);
9313
+ const mountedRef = useRef(true);
7937
9314
  useEffect(() => {
7938
9315
  const handleResize = () => {
7939
9316
  const seq = ++resizeGateRef.current;
7940
9317
  preResizePanelsRef.current = {
7941
9318
  settings: stateRef.current.settingsPicker.open,
9319
+ projectPicker: stateRef.current.projectPicker.open,
7942
9320
  help: stateRef.current.helpOpen,
7943
9321
  monitor: stateRef.current.monitorOpen,
7944
9322
  agents: stateRef.current.agentsMonitorOpen,
7945
9323
  worktree: stateRef.current.worktreeMonitorOpen,
7946
9324
  todos: stateRef.current.todosMonitorOpen,
7947
9325
  queue: stateRef.current.queuePanelOpen,
7948
- processList: stateRef.current.processListOpen
9326
+ processList: stateRef.current.processListOpen,
9327
+ goalPanel: stateRef.current.goalPanelOpen,
9328
+ sessionsPanel: stateRef.current.sessionsPanelOpen
7949
9329
  };
7950
9330
  if (stateRef.current.settingsPicker.open) dispatch({ type: "settingsClose" });
9331
+ if (stateRef.current.projectPicker.open) dispatch({ type: "projectPickerClose" });
7951
9332
  if (stateRef.current.modelPicker.open) dispatch({ type: "modelPickerClose" });
7952
9333
  if (stateRef.current.autonomyPicker.open) dispatch({ type: "autonomyPickerClose" });
9334
+ if (stateRef.current.resumePicker.open) dispatch({ type: "resumePickerClose" });
7953
9335
  if (stateRef.current.slashPicker.open) dispatch({ type: "slashPickerClose" });
7954
9336
  if (stateRef.current.picker.open) dispatch({ type: "pickerClose" });
7955
9337
  if (stateRef.current.rewindOverlay) dispatch({ type: "rewindOverlayClose" });
@@ -7960,8 +9342,11 @@ function App({
7960
9342
  if (stateRef.current.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
7961
9343
  if (stateRef.current.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
7962
9344
  if (stateRef.current.processListOpen) dispatch({ type: "toggleProcessList" });
9345
+ if (stateRef.current.goalPanelOpen) dispatch({ type: "toggleGoalPanel" });
9346
+ if (stateRef.current.sessionsPanelOpen) dispatch({ type: "toggleSessionsPanel" });
7963
9347
  eraseLiveRegion();
7964
- setTimeout(() => {
9348
+ resizeRestoreTimerRef.current = setTimeout(() => {
9349
+ if (!mountedRef.current) return;
7965
9350
  if (resizeGateRef.current !== seq) return;
7966
9351
  const prev = preResizePanelsRef.current;
7967
9352
  if (!prev) return;
@@ -7990,10 +9375,16 @@ function App({
7990
9375
  maxIterations: sp.maxIterations,
7991
9376
  autoProceedMaxIterations: sp.autoProceedMaxIterations,
7992
9377
  enhanceDelayMs: sp.enhanceDelayMs,
9378
+ enhanceEnabled: sp.enhanceEnabled,
9379
+ enhanceLanguage: sp.enhanceLanguage,
7993
9380
  debugStream: sp.debugStream,
7994
9381
  configScope: sp.configScope
7995
9382
  });
7996
9383
  }
9384
+ if (prev.projectPicker) {
9385
+ const pp = stateRef.current.projectPicker;
9386
+ dispatch({ type: "projectPickerOpen", items: pp.allItems });
9387
+ }
7997
9388
  if (prev.help) dispatch({ type: "toggleHelp" });
7998
9389
  if (prev.monitor) dispatch({ type: "toggleMonitor" });
7999
9390
  if (prev.agents) dispatch({ type: "toggleAgentsMonitor" });
@@ -8001,11 +9392,19 @@ function App({
8001
9392
  if (prev.todos) dispatch({ type: "toggleTodosMonitor" });
8002
9393
  if (prev.queue) dispatch({ type: "toggleQueuePanel" });
8003
9394
  if (prev.processList) dispatch({ type: "toggleProcessList" });
9395
+ if (prev.goalPanel) dispatch({ type: "toggleGoalPanel" });
9396
+ if (prev.sessionsPanel) dispatch({ type: "toggleSessionsPanel" });
8004
9397
  preResizePanelsRef.current = null;
9398
+ resizeRestoreTimerRef.current = null;
8005
9399
  }, 300);
8006
9400
  };
8007
9401
  process.stdout.on("resize", handleResize);
8008
9402
  return () => {
9403
+ if (resizeRestoreTimerRef.current) {
9404
+ clearTimeout(resizeRestoreTimerRef.current);
9405
+ resizeRestoreTimerRef.current = null;
9406
+ }
9407
+ mountedRef.current = false;
8009
9408
  process.stdout.off("resize", handleResize);
8010
9409
  };
8011
9410
  }, [eraseLiveRegion]);
@@ -8106,7 +9505,7 @@ function App({
8106
9505
  dispatch({ type: "pickerClose" });
8107
9506
  return;
8108
9507
  }
8109
- const absPath = path3.isAbsolute(picked) ? picked : path3.join(projectRoot, picked);
9508
+ const absPath = path4.isAbsolute(picked) ? picked : path4.join(projectRoot, picked);
8110
9509
  try {
8111
9510
  const data = await fs2.readFile(absPath, "utf8");
8112
9511
  const token = await builder.registerFile({
@@ -8167,6 +9566,9 @@ function App({
8167
9566
  if (!queueStore) return;
8168
9567
  queueStore.write(state.queue.map(({ displayText, blocks }) => ({ displayText, blocks }))).catch(() => void 0);
8169
9568
  }, [state.queue, queueStore]);
9569
+ useEffect(() => {
9570
+ onQueueChange?.(state.queue.map((q) => q.displayText));
9571
+ }, [state.queue, onQueueChange]);
8170
9572
  useEffect(() => {
8171
9573
  const cmd = createQueueSlashCommand({
8172
9574
  getQueue: () => stateRef.current.queue,
@@ -8302,6 +9704,28 @@ function App({
8302
9704
  const providers = await getPickableProviders();
8303
9705
  dispatch({ type: "modelPickerOpen", providers });
8304
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]);
8305
9729
  const openSettings = React5.useCallback(() => {
8306
9730
  if (!getSettings) return;
8307
9731
  const s2 = getSettings();
@@ -8328,38 +9752,87 @@ function App({
8328
9752
  maxIterations: s2.maxIterations ?? 500,
8329
9753
  autoProceedMaxIterations: s2.autoProceedMaxIterations ?? 50,
8330
9754
  enhanceDelayMs: s2.enhanceDelayMs ?? 6e4,
9755
+ enhanceEnabled: s2.enhanceEnabled ?? true,
9756
+ enhanceLanguage: s2.enhanceLanguage ?? "original",
8331
9757
  debugStream: s2.debugStream ?? false,
8332
9758
  configScope: s2.configScope ?? "global"
8333
9759
  });
8334
9760
  }, [getSettings]);
8335
- const [autoProceedCountdown, setAutoProceedCountdown] = useState(null);
8336
- const autoProceedTimerRef = useRef(void 0);
9761
+ const autoSubmitStreakRef = useRef(0);
9762
+ const autoSubmitCapWarnedRef = useRef(false);
9763
+ const [nextStepsRecheck, setNextStepsRecheck] = useState(0);
8337
9764
  useEffect(() => {
8338
- if (autonomyLive !== "auto") {
8339
- clearInterval(autoProceedTimerRef.current);
8340
- autoProceedTimerRef.current = void 0;
8341
- 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) {
8342
9780
  return;
8343
9781
  }
9782
+ const suggestions = getSuggestions?.() ?? [];
9783
+ if (suggestions.length === 0) {
9784
+ const recheck = setTimeout(() => setNextStepsRecheck((t) => t + 1), 1500);
9785
+ return () => clearTimeout(recheck);
9786
+ }
8344
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
+ }
9800
+ return;
9801
+ }
8345
9802
  const delay = cfg?.delayMs ?? 45e3;
9803
+ const top = suggestions[0];
9804
+ if (!top) return;
9805
+ nextStepsAutoSubmitSuggestionRef.current = top;
8346
9806
  const start = Date.now();
8347
- setAutoProceedCountdown(Math.ceil(delay / 1e3));
8348
- autoProceedTimerRef.current = setInterval(() => {
9807
+ setNextStepsAutoSubmitCountdown(Math.ceil(delay / 1e3));
9808
+ nextStepsAutoSubmitTimerRef.current = setInterval(() => {
8349
9809
  const remaining = Math.max(0, Math.ceil((delay - (Date.now() - start)) / 1e3));
8350
9810
  if (remaining <= 0) {
8351
- clearInterval(autoProceedTimerRef.current);
8352
- autoProceedTimerRef.current = void 0;
8353
- 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
+ }
8354
9827
  } else {
8355
- setAutoProceedCountdown(remaining);
9828
+ setNextStepsAutoSubmitCountdown(remaining);
8356
9829
  }
8357
9830
  }, 500);
8358
9831
  return () => {
8359
- clearInterval(autoProceedTimerRef.current);
8360
- autoProceedTimerRef.current = void 0;
9832
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
9833
+ nextStepsAutoSubmitTimerRef.current = void 0;
8361
9834
  };
8362
- }, [autonomyLive, getSettings]);
9835
+ }, [state.status, autonomyLive, state.enhance, state.enhanceBusy, nextStepsRecheck, getSettings, getSuggestions, dispatch]);
8363
9836
  const settingsAutoSaveGateRef = useRef(true);
8364
9837
  useEffect(() => {
8365
9838
  if (state.settingsPicker.open) {
@@ -8396,6 +9869,8 @@ function App({
8396
9869
  maxIterations: sp.maxIterations,
8397
9870
  autoProceedMaxIterations: sp.autoProceedMaxIterations,
8398
9871
  enhanceDelayMs: sp.enhanceDelayMs,
9872
+ enhanceEnabled: sp.enhanceEnabled,
9873
+ enhanceLanguage: sp.enhanceLanguage,
8399
9874
  debugStream: sp.debugStream,
8400
9875
  configScope: sp.configScope
8401
9876
  })).then((err) => {
@@ -8458,6 +9933,21 @@ function App({
8458
9933
  slashRegistry.unregister("settings");
8459
9934
  };
8460
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]);
8461
9951
  useEffect(() => {
8462
9952
  if (!switchAutonomy) return;
8463
9953
  const cmd = {
@@ -8474,6 +9964,34 @@ function App({
8474
9964
  slashRegistry.unregister("autonomy");
8475
9965
  };
8476
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]);
8477
9995
  useEffect(() => {
8478
9996
  const FLUSH_MS2 = 100;
8479
9997
  const flush = () => {
@@ -8666,6 +10184,9 @@ function App({
8666
10184
  }, [state.enhanceEnabled]);
8667
10185
  const enhanceAbortRef = useRef(null);
8668
10186
  const [enhanceCountdown, setEnhanceCountdown] = useState(null);
10187
+ const [nextStepsAutoSubmitCountdown, setNextStepsAutoSubmitCountdown] = useState(null);
10188
+ const nextStepsAutoSubmitSuggestionRef = useRef(null);
10189
+ const nextStepsAutoSubmitTimerRef = useRef(void 0);
8669
10190
  useTuiEventBridge({
8670
10191
  events,
8671
10192
  dispatch,
@@ -8873,7 +10394,28 @@ function App({
8873
10394
  return;
8874
10395
  }
8875
10396
  }
8876
- 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
+ }
8877
10419
  if (state.modelPicker.open) {
8878
10420
  if (key.escape) {
8879
10421
  if (state.modelPicker.step === "model") {
@@ -8883,6 +10425,10 @@ function App({
8883
10425
  }
8884
10426
  return;
8885
10427
  }
10428
+ if (key.mouse?.kind === "wheel") {
10429
+ dispatch({ type: "modelPickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10430
+ return;
10431
+ }
8886
10432
  if (key.upArrow) {
8887
10433
  dispatch({ type: "modelPickerMove", delta: -1 });
8888
10434
  return;
@@ -8948,6 +10494,10 @@ function App({
8948
10494
  dispatch({ type: "autonomyPickerClose" });
8949
10495
  return;
8950
10496
  }
10497
+ if (key.mouse?.kind === "wheel") {
10498
+ dispatch({ type: "autonomyPickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10499
+ return;
10500
+ }
8951
10501
  if (key.upArrow) {
8952
10502
  dispatch({ type: "autonomyPickerMove", delta: -1 });
8953
10503
  return;
@@ -8972,24 +10522,77 @@ function App({
8972
10522
  }
8973
10523
  return;
8974
10524
  }
8975
- if (state.settingsPicker.open) {
8976
- if (key.escape || key.ctrl && input === "s" || key.fn === 5) {
8977
- dispatch({ type: "settingsClose" });
10525
+ if (state.resumePicker.open) {
10526
+ if (key.escape) {
10527
+ dispatch({ type: "resumePickerClose" });
8978
10528
  return;
8979
10529
  }
8980
- if (key.upArrow) {
8981
- dispatch({ type: "settingsFieldMove", delta: -1 });
10530
+ if (key.mouse?.kind === "wheel") {
10531
+ dispatch({ type: "resumePickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
8982
10532
  return;
8983
10533
  }
8984
- if (key.downArrow) {
8985
- dispatch({ type: "settingsFieldMove", delta: 1 });
10534
+ if (key.upArrow) {
10535
+ dispatch({ type: "resumePickerMove", delta: -1 });
8986
10536
  return;
8987
10537
  }
8988
- if (key.leftArrow) {
8989
- dispatch({ type: "settingsValueChange", delta: -1 });
10538
+ if (key.downArrow) {
10539
+ dispatch({ type: "resumePickerMove", delta: 1 });
8990
10540
  return;
8991
10541
  }
8992
- if (key.rightArrow) {
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
+ }
10574
+ if (state.settingsPicker.open) {
10575
+ if (key.escape || key.ctrl && input === "s") {
10576
+ dispatch({ type: "settingsClose" });
10577
+ return;
10578
+ }
10579
+ if (key.mouse?.kind === "wheel") {
10580
+ dispatch({ type: "settingsFieldMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10581
+ return;
10582
+ }
10583
+ if (key.upArrow) {
10584
+ dispatch({ type: "settingsFieldMove", delta: -1 });
10585
+ return;
10586
+ }
10587
+ if (key.downArrow) {
10588
+ dispatch({ type: "settingsFieldMove", delta: 1 });
10589
+ return;
10590
+ }
10591
+ if (key.leftArrow) {
10592
+ dispatch({ type: "settingsValueChange", delta: -1 });
10593
+ return;
10594
+ }
10595
+ if (key.rightArrow) {
8993
10596
  dispatch({ type: "settingsValueChange", delta: 1 });
8994
10597
  return;
8995
10598
  }
@@ -9002,11 +10605,166 @@ function App({
9002
10605
  }
9003
10606
  return;
9004
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
+ }
9005
10759
  if (state.slashPicker.open) {
9006
10760
  if (key.escape) {
9007
10761
  dispatch({ type: "slashPickerClose" });
9008
10762
  return;
9009
10763
  }
10764
+ if (key.mouse?.kind === "wheel") {
10765
+ dispatch({ type: "slashPickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10766
+ return;
10767
+ }
9010
10768
  if (key.upArrow) {
9011
10769
  dispatch({ type: "slashPickerMove", delta: -1 });
9012
10770
  return;
@@ -9043,6 +10801,10 @@ function App({
9043
10801
  dispatch({ type: "pickerClose" });
9044
10802
  return;
9045
10803
  }
10804
+ if (key.mouse?.kind === "wheel") {
10805
+ dispatch({ type: "pickerMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
10806
+ return;
10807
+ }
9046
10808
  if (key.upArrow) {
9047
10809
  dispatch({ type: "pickerMove", delta: -1 });
9048
10810
  return;
@@ -9169,6 +10931,24 @@ function App({
9169
10931
  if (state.helpOpen) dispatch({ type: "toggleHelp" });
9170
10932
  dispatch({ type: "toggleTodosMonitor" });
9171
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
+ }
9172
10952
  if (key.ctrl && input === "f" || key.fn === 2) {
9173
10953
  toggleFleetOverlay();
9174
10954
  return;
@@ -9181,47 +10961,6 @@ function App({
9181
10961
  toggleWorktreeOverlay();
9182
10962
  return;
9183
10963
  }
9184
- if (key.fn === 5) {
9185
- if (state.settingsPicker.open) {
9186
- dispatch({ type: "settingsClose" });
9187
- } else if (getSettings && saveSettings) {
9188
- if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
9189
- if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
9190
- if (state.worktreeMonitorOpen) dispatch({ type: "worktreeMonitorToggle" });
9191
- if (state.todosMonitorOpen) dispatch({ type: "toggleTodosMonitor" });
9192
- if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
9193
- if (state.queuePanelOpen) dispatch({ type: "toggleQueuePanel" });
9194
- if (state.helpOpen) dispatch({ type: "toggleHelp" });
9195
- const cfg = getSettings();
9196
- dispatch({
9197
- type: "settingsOpen",
9198
- mode: cfg.mode,
9199
- delayMs: cfg.delayMs,
9200
- titleAnimation: cfg.titleAnimation ?? true,
9201
- yolo: cfg.yolo ?? false,
9202
- streamFleet: cfg.streamFleet ?? true,
9203
- chime: cfg.chime ?? false,
9204
- confirmExit: cfg.confirmExit ?? true,
9205
- nextPrediction: cfg.nextPrediction ?? false,
9206
- featureMcp: cfg.featureMcp ?? true,
9207
- featurePlugins: cfg.featurePlugins ?? true,
9208
- featureMemory: cfg.featureMemory ?? true,
9209
- featureSkills: cfg.featureSkills ?? true,
9210
- featureModelsRegistry: cfg.featureModelsRegistry ?? true,
9211
- contextAutoCompact: cfg.contextAutoCompact ?? true,
9212
- contextStrategy: cfg.contextStrategy ?? "hybrid",
9213
- logLevel: cfg.logLevel ?? "info",
9214
- auditLevel: cfg.auditLevel ?? "standard",
9215
- indexOnStart: cfg.indexOnStart ?? true,
9216
- maxIterations: cfg.maxIterations ?? 500,
9217
- autoProceedMaxIterations: cfg.autoProceedMaxIterations ?? 50,
9218
- enhanceDelayMs: cfg.enhanceDelayMs ?? 6e4,
9219
- debugStream: cfg.debugStream ?? false,
9220
- configScope: cfg.configScope ?? "global"
9221
- });
9222
- }
9223
- return;
9224
- }
9225
10964
  if (key.fn === 6) {
9226
10965
  toggleTodosOverlay();
9227
10966
  return;
@@ -9274,6 +11013,25 @@ function App({
9274
11013
  }
9275
11014
  return;
9276
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
+ }
9277
11035
  if (key.ctrl && input === "s") {
9278
11036
  if (state.settingsPicker.open) {
9279
11037
  dispatch({ type: "settingsClose" });
@@ -9309,6 +11067,8 @@ function App({
9309
11067
  maxIterations: cfg.maxIterations ?? 500,
9310
11068
  autoProceedMaxIterations: cfg.autoProceedMaxIterations ?? 50,
9311
11069
  enhanceDelayMs: cfg.enhanceDelayMs ?? 6e4,
11070
+ enhanceEnabled: cfg.enhanceEnabled ?? true,
11071
+ enhanceLanguage: cfg.enhanceLanguage ?? "original",
9312
11072
  debugStream: cfg.debugStream ?? false,
9313
11073
  configScope: cfg.configScope ?? "global"
9314
11074
  });
@@ -9332,6 +11092,10 @@ function App({
9332
11092
  dispatch({ type: "settingsClose" });
9333
11093
  return;
9334
11094
  }
11095
+ if (state.projectPicker.open) {
11096
+ dispatch({ type: "projectPickerClose" });
11097
+ return;
11098
+ }
9335
11099
  if (state.queuePanelOpen) {
9336
11100
  dispatch({ type: "toggleQueuePanel" });
9337
11101
  return;
@@ -9344,11 +11108,15 @@ function App({
9344
11108
  dispatch({ type: "toggleGoalPanel" });
9345
11109
  return;
9346
11110
  }
11111
+ if (state.sessionsPanelOpen) {
11112
+ dispatch({ type: "toggleSessionsPanel" });
11113
+ return;
11114
+ }
9347
11115
  }
9348
11116
  if (state.processListOpen) {
9349
11117
  return;
9350
11118
  }
9351
- if (input === "?" && !key.ctrl && !key.meta && draftRef.current.buffer === "" && !state.slashPicker.open && !state.picker.open && !state.modelPicker.open && !state.autonomyPicker.open && !state.settingsPicker.open && !state.rewindOverlay && !state.monitorOpen && !state.agentsMonitorOpen && !state.worktreeMonitorOpen && !state.todosMonitorOpen && !state.autoPhase?.monitorOpen) {
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) {
9352
11120
  dispatch({ type: "toggleHelp" });
9353
11121
  return;
9354
11122
  }
@@ -9382,6 +11150,12 @@ function App({
9382
11150
  }
9383
11151
  if (cursor === 0) return;
9384
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
+ }
9385
11159
  setDraft(next2, cursor - 1);
9386
11160
  return;
9387
11161
  }
@@ -9392,12 +11166,24 @@ function App({
9392
11166
  const nextWordStart = afterCursor.indexOf(" ");
9393
11167
  const end = nextWordStart === -1 ? buffer.length : cursor + nextWordStart + 1;
9394
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
+ }
9395
11175
  setDraft(next3, cursor);
9396
11176
  return;
9397
11177
  }
9398
11178
  if (cursor >= buffer.length) return;
9399
11179
  const span = tokenLengthForward(buffer, cursor) || 1;
9400
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
+ }
9401
11187
  setDraft(next2, cursor);
9402
11188
  return;
9403
11189
  }
@@ -9433,7 +11219,67 @@ function App({
9433
11219
  setDraft(buffer, buffer.length);
9434
11220
  return;
9435
11221
  }
9436
- const overlayOpen = state.monitorOpen || state.agentsMonitorOpen || state.worktreeMonitorOpen || state.todosMonitorOpen || state.queuePanelOpen || state.processListOpen || state.goalPanelOpen || state.helpOpen || (state.autoPhase?.monitorOpen ?? false) || state.rewindOverlay !== null;
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
+ }
9437
11283
  if (key.upArrow) {
9438
11284
  if (!overlayOpen && state.inputHistory.length > 0) {
9439
11285
  dispatch({ type: "historyUp" });
@@ -9465,6 +11311,12 @@ function App({
9465
11311
  return;
9466
11312
  }
9467
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
+ }
9468
11320
  setDraft("", 0);
9469
11321
  return;
9470
11322
  }
@@ -9472,12 +11324,24 @@ function App({
9472
11324
  if (cursor >= buffer.length) return;
9473
11325
  const span = tokenLengthForward(buffer, cursor) || 1;
9474
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
+ }
9475
11333
  setDraft(next2, cursor);
9476
11334
  return;
9477
11335
  }
9478
11336
  if (key.ctrl && input === "k") {
9479
11337
  if (cursor >= buffer.length) return;
9480
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
+ }
9481
11345
  setDraft(next2, cursor);
9482
11346
  return;
9483
11347
  }
@@ -9495,6 +11359,12 @@ function App({
9495
11359
  await commitPaste(input);
9496
11360
  return;
9497
11361
  }
11362
+ if (nextStepsAutoSubmitTimerRef.current != null) {
11363
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11364
+ nextStepsAutoSubmitTimerRef.current = void 0;
11365
+ setNextStepsAutoSubmitCountdown(null);
11366
+ nextStepsAutoSubmitSuggestionRef.current = null;
11367
+ }
9498
11368
  const next = buffer.slice(0, cursor) + input + buffer.slice(cursor);
9499
11369
  setDraft(next, cursor + input.length);
9500
11370
  };
@@ -9561,6 +11431,12 @@ function App({
9561
11431
  } catch {
9562
11432
  }
9563
11433
  }
11434
+ if (result.status === "done" && result.finalText && onSuggestionsParsed) {
11435
+ try {
11436
+ onSuggestionsParsed(result.finalText);
11437
+ } catch {
11438
+ }
11439
+ }
9564
11440
  if (tokenCounter && before) {
9565
11441
  const after = tokenCounter.total();
9566
11442
  const costAfter = tokenCounter.estimateCost().total;
@@ -9715,6 +11591,9 @@ function App({
9715
11591
  return;
9716
11592
  }
9717
11593
  dispatch({ type: "resetInterrupts" });
11594
+ autoSubmitStreakRef.current = 0;
11595
+ autoSubmitCapWarnedRef.current = false;
11596
+ dispatch({ type: "scrollToBottom" });
9718
11597
  const pushSubmittedHistory = () => {
9719
11598
  if (trimmed) dispatch({ type: "historyPush", text: trimmed });
9720
11599
  };
@@ -9755,6 +11634,25 @@ ${content}
9755
11634
  const m = res.metadata.autoPhaseInit;
9756
11635
  dispatch({ type: "autoPhaseInit", title: m.title });
9757
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
+ }
9758
11656
  const ctxModel = agent.ctx.model;
9759
11657
  if (ctxModel && ctxModel !== liveModel) setLiveModel(ctxModel);
9760
11658
  const ctxProviderId = agent.ctx.provider?.id;
@@ -9914,6 +11812,9 @@ User message:
9914
11812
  clearDraft();
9915
11813
  const blocks = await builder.submit();
9916
11814
  if (state.status !== "idle") {
11815
+ if (autonomyLive === "auto" && nextStepsAutoSubmitTimerRef.current != null) {
11816
+ switchAutonomy?.("off");
11817
+ }
9917
11818
  dispatch({
9918
11819
  type: "addEntry",
9919
11820
  entry: { kind: "user", text: displayText, queued: true, pasteContent }
@@ -9922,6 +11823,9 @@ User message:
9922
11823
  return;
9923
11824
  }
9924
11825
  dispatch({ type: "addEntry", entry: { kind: "user", text: displayText, pasteContent } });
11826
+ if (autonomyLive === "auto" && nextStepsAutoSubmitTimerRef.current != null) {
11827
+ switchAutonomy?.("off");
11828
+ }
9925
11829
  await runBlocks(blocks);
9926
11830
  };
9927
11831
  const bootInjectedRef = useRef(false);
@@ -9974,15 +11878,27 @@ User message:
9974
11878
  const inputHeight = Math.max(1, inputCellRows.length);
9975
11879
  const hideInput = enhanceActive || state.helpOpen || state.processListOpen;
9976
11880
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 0, children: [
9977
- /* @__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(
9978
11893
  History,
9979
11894
  {
9980
11895
  entries: state.entries,
11896
+ generation: state.historyGen,
9981
11897
  streamingText: state.streamingText,
9982
11898
  toolStream: state.toolStream
9983
11899
  }
9984
11900
  ),
9985
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [
11901
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, ref: bottomRegionRef, children: [
9986
11902
  /* @__PURE__ */ jsx(
9987
11903
  Input,
9988
11904
  {
@@ -10033,6 +11949,16 @@ User message:
10033
11949
  hint: state.autonomyPicker.hint
10034
11950
  }
10035
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,
10036
11962
  state.settingsPicker.open ? /* @__PURE__ */ jsx(
10037
11963
  SettingsPicker,
10038
11964
  {
@@ -10058,11 +11984,32 @@ User message:
10058
11984
  maxIterations: state.settingsPicker.maxIterations,
10059
11985
  autoProceedMaxIterations: state.settingsPicker.autoProceedMaxIterations,
10060
11986
  enhanceDelayMs: state.settingsPicker.enhanceDelayMs,
11987
+ enhanceEnabled: state.settingsPicker.enhanceEnabled,
11988
+ enhanceLanguage: state.settingsPicker.enhanceLanguage,
10061
11989
  debugStream: state.settingsPicker.debugStream,
10062
11990
  configScope: state.settingsPicker.configScope,
10063
11991
  hint: state.settingsPicker.hint
10064
11992
  }
10065
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,
10066
12013
  state.rewindOverlay ? (() => {
10067
12014
  const overlay = state.rewindOverlay;
10068
12015
  return /* @__PURE__ */ jsx(
@@ -10090,7 +12037,7 @@ User message:
10090
12037
  }
10091
12038
  ) }) : null,
10092
12039
  state.confirmQueue.length > 0 && (() => {
10093
- const head = expectDefined(state.confirmQueue[0]);
12040
+ const head = expectDefined$1(state.confirmQueue[0]);
10094
12041
  let resolved = false;
10095
12042
  const onDecision = (decision) => {
10096
12043
  if (resolved) return;
@@ -10175,7 +12122,7 @@ User message:
10175
12122
  }
10176
12123
  );
10177
12124
  })() : null,
10178
- /* @__PURE__ */ jsx(
12125
+ /* @__PURE__ */ jsx(Box, { ref: statusBarWrapRef, flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx(
10179
12126
  StatusBar,
10180
12127
  {
10181
12128
  model: `${liveProvider}/${liveModel}`,
@@ -10189,11 +12136,13 @@ User message:
10189
12136
  startedAt: startedAtRef.current,
10190
12137
  todos,
10191
12138
  plan: planCounts ?? void 0,
12139
+ tasks: taskCounts ?? void 0,
10192
12140
  fleet: fleetCounts,
10193
12141
  git: gitInfo,
10194
12142
  context: contextWindow,
10195
12143
  brain: state.brain,
10196
12144
  projectName,
12145
+ workingDir: workingDirChip,
10197
12146
  subagentCount: Object.keys(state.fleet).length,
10198
12147
  processCount: getProcessRegistry().activeCount,
10199
12148
  hiddenItems,
@@ -10203,67 +12152,101 @@ User message:
10203
12152
  modeLabel: liveModeLabel || void 0,
10204
12153
  debugStreamStats: state.debugStreamStats,
10205
12154
  enhanceCountdown,
10206
- autoProceedCountdown
12155
+ nextStepsAutoSubmitCountdown,
12156
+ autoProceedCountdown: state.countdown?.remainingSeconds ?? null,
12157
+ sessionCount,
12158
+ mailbox: mailboxStatus
10207
12159
  }
10208
- ),
10209
- state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, {}) : null,
10210
- state.agentsMonitorOpen ? /* @__PURE__ */ jsx(
10211
- AgentsMonitor,
10212
- {
10213
- entries: entriesWithLeader,
10214
- totalCost: state.fleetCost,
10215
- leaderCost: tokenCounter?.estimateCost().total ?? 0,
10216
- totalTokens: state.fleetTokens,
10217
- nowTick
10218
- }
10219
- ) : state.autoPhase?.monitorOpen ? /* @__PURE__ */ jsx(
10220
- PhaseMonitor,
10221
- {
10222
- phases: state.autoPhase.phases,
10223
- runningPhaseIds: state.autoPhase.runningPhaseIds,
10224
- elapsedMs: state.autoPhase.elapsedMs,
10225
- nowTick,
10226
- onClose: () => dispatch({ type: "autoPhaseMonitorToggle" })
10227
- }
10228
- ) : state.worktreeMonitorOpen ? /* @__PURE__ */ jsx(
10229
- WorktreeMonitor,
10230
- {
10231
- worktrees: state.worktrees,
10232
- baseBranch: state.worktreeBase,
10233
- nowTick,
10234
- onClose: () => dispatch({ type: "worktreeMonitorToggle" })
10235
- }
10236
- ) : state.todosMonitorOpen ? /* @__PURE__ */ jsx(TodosMonitor, { todos: agent.ctx.todos }) : state.monitorOpen ? /* @__PURE__ */ jsx(
10237
- FleetMonitor,
10238
- {
10239
- entries: state.fleet,
10240
- totalCost: state.fleetCost,
10241
- totalTokens: state.fleetTokens,
10242
- maxConcurrent: state.fleetConcurrency,
10243
- nowTick,
10244
- collabSession: state.collabSession
10245
- }
10246
- ) : director ? /* @__PURE__ */ jsx(
10247
- FleetPanel,
10248
- {
10249
- entries: entriesWithLeader,
10250
- totalCost: state.fleetCost,
10251
- roster: fleetRoster,
10252
- collabSession: state.collabSession
10253
- }
10254
- ) : null,
10255
- state.autoPhase && !state.autoPhase.monitorOpen ? /* @__PURE__ */ jsx(
10256
- PhasePanel,
12160
+ ) }),
12161
+ /* @__PURE__ */ jsx(
12162
+ MailboxPanel,
10257
12163
  {
10258
- phases: state.autoPhase.phases,
10259
- runningPhaseIds: state.autoPhase.runningPhaseIds,
10260
- nowTick
12164
+ messages: mailboxMessages,
12165
+ agents: mailboxAgents,
12166
+ unreadCount: mailboxStatus.unread,
12167
+ open: mailboxPanelOpen
10261
12168
  }
10262
- ) : null,
10263
- Object.keys(state.worktrees).length > 0 && !state.worktreeMonitorOpen && !state.monitorOpen ? /* @__PURE__ */ jsx(WorktreePanel, { worktrees: state.worktrees, nowTick }) : null,
10264
- state.queuePanelOpen ? /* @__PURE__ */ jsx(QueuePanel, { items: state.queue }) : null,
10265
- state.processListOpen ? /* @__PURE__ */ jsx(ProcessListMonitor, {}) : null,
10266
- state.goalPanelOpen ? /* @__PURE__ */ jsx(GoalPanel, { goal: state.goalSummary }) : null
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
+ ] })
10267
12250
  ] })
10268
12251
  ] }) });
10269
12252
  }
@@ -10365,6 +12348,45 @@ function startTerminalTitle(opts) {
10365
12348
  // src/run-tui.ts
10366
12349
  var BRACKETED_PASTE_ON = "\x1B[?2004h";
10367
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
+ }
10368
12390
  async function runTui(opts) {
10369
12391
  const stdout = process.stdout;
10370
12392
  const stdin = process.stdin;
@@ -10374,14 +12396,16 @@ async function runTui(opts) {
10374
12396
  );
10375
12397
  return 2;
10376
12398
  }
12399
+ silenceTerminal();
10377
12400
  stdout.write(BRACKETED_PASTE_ON);
12401
+ const mouseEnabled = opts.mouse ?? opts.getSettings?.().mouseMode ?? process.env.WRONGSTACK_MOUSE === "1";
10378
12402
  stdout.write("\x1B[2J\x1B[H");
10379
12403
  const inkStdin = stdin;
10380
12404
  const stopTitle = opts.titleAnimation !== false ? startTerminalTitle({
10381
12405
  stdout,
10382
12406
  events: opts.events,
10383
12407
  model: opts.model,
10384
- appName: opts.projectRoot ? path3.basename(opts.projectRoot) : void 0
12408
+ appName: opts.projectRoot ? path4.basename(opts.projectRoot) : void 0
10385
12409
  }) : (() => {
10386
12410
  });
10387
12411
  const swallowSignals = ["SIGTSTP", "SIGQUIT", "SIGTTIN", "SIGTTOU"];
@@ -10397,12 +12421,14 @@ async function runTui(opts) {
10397
12421
  const cleanup = () => {
10398
12422
  if (cleaned) return;
10399
12423
  cleaned = true;
12424
+ unsilenceTerminal();
10400
12425
  try {
10401
12426
  stopTitle();
10402
12427
  } catch {
10403
12428
  }
10404
12429
  try {
10405
12430
  stdout.write(BRACKETED_PASTE_OFF);
12431
+ stdout.write(MOUSE_OFF);
10406
12432
  } catch {
10407
12433
  }
10408
12434
  };
@@ -10423,14 +12449,26 @@ async function runTui(opts) {
10423
12449
  };
10424
12450
  return new Promise((resolve) => {
10425
12451
  let exitCode = 0;
12452
+ let hardExitTimer = null;
10426
12453
  const onExit = (code) => {
10427
12454
  exitCode = code;
10428
12455
  };
10429
12456
  const settle = (code) => {
12457
+ if (hardExitTimer) {
12458
+ clearTimeout(hardExitTimer);
12459
+ hardExitTimer = null;
12460
+ }
10430
12461
  cleanup();
10431
12462
  detachListeners();
10432
12463
  resolve(code);
10433
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;
10434
12472
  let instance;
10435
12473
  try {
10436
12474
  instance = render(
@@ -10445,6 +12483,7 @@ async function runTui(opts) {
10445
12483
  model: opts.model,
10446
12484
  banner: opts.banner ?? true,
10447
12485
  queueStore: opts.queueStore,
12486
+ onQueueChange: opts.onQueueChange,
10448
12487
  yolo: opts.yolo,
10449
12488
  getYolo: opts.getYolo,
10450
12489
  getAutonomy: opts.getAutonomy,
@@ -10480,12 +12519,24 @@ async function runTui(opts) {
10480
12519
  getSettings: opts.getSettings,
10481
12520
  saveSettings: opts.saveSettings,
10482
12521
  predictNext: opts.predictNext,
12522
+ onSuggestionsParsed: opts.onSuggestionsParsed,
10483
12523
  chime: opts.chime,
10484
12524
  confirmExit: opts.confirmExit,
12525
+ mouse: mouseEnabled,
10485
12526
  modeLabel: opts.modeLabel,
10486
12527
  getModeLabel: opts.getModeLabel,
10487
12528
  registerDebugStreamCallback: opts.registerDebugStreamCallback,
10488
- 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
10489
12540
  }),
10490
12541
  { exitOnCtrlC: false, stdin: inkStdin }
10491
12542
  );
@@ -10516,6 +12567,190 @@ async function runTui(opts) {
10516
12567
  });
10517
12568
  }
10518
12569
 
10519
- 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 };
10520
12755
  //# sourceMappingURL=index.js.map
10521
12756
  //# sourceMappingURL=index.js.map