@wrongstack/tui 0.9.19 → 0.9.20

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
@@ -44,7 +44,7 @@ function ConfirmPrompt({
44
44
  React2.useEffect(() => {
45
45
  process.stdout.write("\x07");
46
46
  }, []);
47
- useInput((input2, key) => {
47
+ useInput((input2, _key) => {
48
48
  if (!input2 || input2 === "\r" || input2 === "\n") return;
49
49
  const ch = input2.toLowerCase();
50
50
  if (ch === "y") {
@@ -159,38 +159,52 @@ function FilePicker({ query, matches, selected }) {
159
159
  ] }, m))
160
160
  ] });
161
161
  }
162
- function highlight(path3, query) {
162
+ function highlight(path3, _query) {
163
163
  return path3;
164
164
  }
165
- function FleetPanel({ entries, totalCost, roster }) {
165
+ function FleetPanel({ entries, totalCost, collabSession }) {
166
166
  const list = Object.values(entries);
167
- if (list.length === 0) return null;
168
- const running = list.filter((e) => e.status === "running");
167
+ if (list.length === 0 && !collabSession) return null;
168
+ const leader = list.find((e) => e.id === "leader");
169
+ const subagents = list.filter((e) => e.id !== "leader");
170
+ const running = subagents.filter((e) => e.status === "running");
169
171
  const runningCount = running.length;
170
- const costLabel = totalCost > 0 ? ` \xB7 $${totalCost.toFixed(3)}` : "";
171
- const summaryLine = runningCount > 0 ? `${runningCount} running${costLabel}` : `idle${costLabel}`;
172
+ const hasCollab = !!collabSession;
173
+ const costLabel = totalCost > 0 ? ` \xB7 ${totalCost.toFixed(3)}` : "";
174
+ const collabLabel = hasCollab && collabSession.sessionId ? ` \xB7 collab(${collabSession.bugCount}b/${collabSession.planCount}p/${collabSession.evalCount}e)` : "";
175
+ const summaryLine = runningCount > 0 ? `${runningCount} running${costLabel}${collabLabel}` : `idle${costLabel}${collabLabel}`;
172
176
  const shown = running.slice(0, 3);
173
177
  const overflow = running.length > 3 ? running.length - 3 : 0;
178
+ const leaderTool = hasCollab ? "waiting for agents" : leader?.currentTool?.name ?? (leader?.status === "running" ? "running" : "\u2014");
174
179
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
175
180
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
176
181
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u26A1 Fleet" }),
177
182
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
178
183
  /* @__PURE__ */ jsx(Text, { children: summaryLine })
179
184
  ] }),
185
+ hasCollab && leader ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
186
+ /* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u25CF" }),
187
+ /* @__PURE__ */ jsx(Text, { children: leader.name.slice(0, 14).padEnd(14) }),
188
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2192" }),
189
+ /* @__PURE__ */ jsx(Text, { color: "yellow", children: leaderTool })
190
+ ] }) : null,
180
191
  shown.map((entry) => {
181
- const name = roster?.[entry.id]?.name ?? entry.name;
192
+ const name = entry.name && entry.name !== entry.id ? entry.name : entry.id.slice(0, 8);
182
193
  const tool = entry.currentTool?.name ?? "\u2014";
183
194
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
184
195
  /* @__PURE__ */ jsx(Text, { color: "green", children: "\u25CF" }),
185
- /* @__PURE__ */ jsx(Text, { children: name.slice(0, 12).padEnd(12) }),
196
+ /* @__PURE__ */ jsx(Text, { children: name.slice(0, 14).padEnd(14) }),
186
197
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2192" }),
187
198
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: tool })
188
199
  ] }, entry.id);
189
200
  }),
190
201
  overflow > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
191
- " +",
202
+ " ",
203
+ "+",
192
204
  overflow,
193
- " more running"
205
+ ": ",
206
+ running[3]?.name?.slice(0, 12) ?? "agent",
207
+ "\u2026"
194
208
  ] }) : null
195
209
  ] });
196
210
  }
@@ -215,8 +229,7 @@ function StatusBar({
215
229
  processCount,
216
230
  hiddenItems,
217
231
  eternalStage,
218
- goalSummary,
219
- autoProceedCountdown
232
+ goalSummary
220
233
  }) {
221
234
  const hiddenSet = new Set(hiddenItems);
222
235
  const usage = tokenCounter?.total();
@@ -612,7 +625,8 @@ function FleetMonitor({
612
625
  totalCost,
613
626
  totalTokens,
614
627
  maxConcurrent = 4,
615
- nowTick
628
+ nowTick,
629
+ collabSession
616
630
  }) {
617
631
  const all = Object.values(entries);
618
632
  const running = all.filter((e) => e.status === "running");
@@ -651,7 +665,12 @@ function FleetMonitor({
651
665
  }
652
666
  }
653
667
  events.sort((a, b) => b.at - a.at);
654
- const timeline = events.slice(0, 8);
668
+ const timeline = events.slice(0, 20);
669
+ const VERDICT_COLOR = {
670
+ approve: "green",
671
+ needs_revision: "yellow",
672
+ reject: "red"
673
+ };
655
674
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
656
675
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
657
676
  /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "FLEET \xB7 ORCHESTRATION" }),
@@ -674,6 +693,39 @@ function FleetMonitor({
674
693
  ] }) : null,
675
694
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 Ctrl+F to close" })
676
695
  ] }),
696
+ collabSession ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
697
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
698
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "magenta", children: "\u26A1 COLLAB SESSION" }),
699
+ collabSession.sessionId ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: collabSession.sessionId.slice(0, 8) }) : null,
700
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
701
+ /* @__PURE__ */ jsxs(Text, { color: "red", children: [
702
+ "\u{1F41B}",
703
+ collabSession.bugCount
704
+ ] }),
705
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
706
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
707
+ "\u{1F4D0}",
708
+ collabSession.planCount
709
+ ] }),
710
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
711
+ /* @__PURE__ */ jsxs(Text, { color: "blue", children: [
712
+ "\u2696\uFE0F",
713
+ collabSession.evalCount
714
+ ] }),
715
+ collabSession.overallVerdict ? /* @__PURE__ */ jsxs(Fragment, { children: [
716
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
717
+ /* @__PURE__ */ jsx(Text, { bold: true, color: VERDICT_COLOR[collabSession.overallVerdict], children: collabSession.overallVerdict })
718
+ ] }) : null
719
+ ] }),
720
+ collabSession.timeline.length > 0 ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 0, children: collabSession.timeline.slice(0, 6).map((ev, i) => (
721
+ // biome-ignore lint/suspicious/noArrayIndexKey: timeline is rebuilt per render
722
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
723
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${fmtElapsed(Math.max(0, nowTick - ev.at))} ago`.padEnd(10) }),
724
+ /* @__PURE__ */ jsx(Text, { color: ev.color, children: ev.icon }),
725
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ev.text })
726
+ ] }, i)
727
+ )) }) : null
728
+ ] }) : null,
677
729
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
678
730
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "concurrency" }),
679
731
  /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
@@ -771,6 +823,26 @@ function snippet(s2, max = 72) {
771
823
  if (oneLine2.length <= max) return oneLine2;
772
824
  return `${oneLine2.slice(0, max - 1)}\u2026`;
773
825
  }
826
+ function ContextBar({
827
+ pct,
828
+ tokens,
829
+ maxTokens
830
+ }) {
831
+ const clamped = Math.max(0, Math.min(2, pct));
832
+ const totalBars = 10;
833
+ const filled = Math.round(clamped * totalBars);
834
+ const empty = totalBars - filled;
835
+ const color = pct < 0.6 ? "green" : pct < 0.75 ? "yellow" : "red";
836
+ const pctText = pct >= 1 ? `${Math.round(pct * 100)}%+` : `${Math.round(pct * 100)}%`;
837
+ const tokenText = tokens ? ` ${fmtTokens2(tokens)}/${fmtTokens2(maxTokens ?? 2e5)}` : "";
838
+ return /* @__PURE__ */ jsxs(Text, { color, children: [
839
+ "\u2588".repeat(filled),
840
+ "\u2591".repeat(Math.max(0, empty)),
841
+ " ",
842
+ pctText,
843
+ tokenText
844
+ ] });
845
+ }
774
846
  function AgentsMonitor({
775
847
  entries,
776
848
  totalCost,
@@ -894,7 +966,11 @@ function AgentsMonitor({
894
966
  "/",
895
967
  e.budgetWarning.limit,
896
968
  " \u2014 extending"
897
- ] }) }) : null
969
+ ] }) }) : null,
970
+ e.ctxPct !== void 0 ? /* @__PURE__ */ jsxs(Box, { paddingLeft: 2, children: [
971
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "ctx " }),
972
+ /* @__PURE__ */ jsx(ContextBar, { pct: e.ctxPct, tokens: e.ctxTokens, maxTokens: e.ctxMaxTokens })
973
+ ] }) : null
898
974
  ] }, e.id);
899
975
  })
900
976
  ] });
@@ -1010,7 +1086,7 @@ var STATUS3 = {
1010
1086
  function s(entry) {
1011
1087
  return STATUS3[entry] ?? { icon: "?", color: "white" };
1012
1088
  }
1013
- function PhasePanel({ phases, runningPhaseIds, nowTick }) {
1089
+ function PhasePanel({ phases, nowTick }) {
1014
1090
  const list = Object.values(phases);
1015
1091
  if (list.length === 0) return null;
1016
1092
  const done = list.filter((p) => p.status === "completed" || p.status === "skipped").length;
@@ -2692,9 +2768,36 @@ var BEGIN = "[200~";
2692
2768
  var END = "[201~";
2693
2769
  var BEGIN_RE = /\x1b?\[200~/g;
2694
2770
  var END_RE = /\x1b?\[201~/g;
2771
+ var PARTIAL_ANSI_RE = /^\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/;
2772
+ var ANSI_RE = new RegExp(
2773
+ [
2774
+ // CSI: ESC [ params* intermediates* final
2775
+ // params 0x30-0x3f (digits, ; : < = > ?)
2776
+ // intermediates 0x20-0x2f (space … /)
2777
+ // final 0x40-0x7e (@ … ~)
2778
+ "\\x1b\\[[\\x30-\\x3f]*[\\x20-\\x2f]*[\\x40-\\x7e]",
2779
+ // OSC: ESC ] … BEL (\x07) or ST (\x1b\\)
2780
+ "\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)",
2781
+ // DCS: ESC P … ST (\x1b\\)
2782
+ "\\x1bP[^\\x1b]*(?:\\x1b\\\\)",
2783
+ // SOS / PM: ESC X / ESC ^
2784
+ "\\x1b[XP][^\\x1b]*(?:\\x1b\\\\)",
2785
+ // Standalone ESC — guard only
2786
+ "\\x1b"
2787
+ ].join("|"),
2788
+ "g"
2789
+ );
2695
2790
  function feedPaste(accum, input) {
2696
- if (accum === null && !input.includes(BEGIN)) return null;
2697
- const piece = input.replace(BEGIN_RE, "").replace(END_RE, "");
2791
+ if (accum === null && !input.includes(BEGIN)) {
2792
+ if (input.startsWith("[") && !input.startsWith(BEGIN) && !input.startsWith(END)) {
2793
+ if (PARTIAL_ANSI_RE.test(input)) {
2794
+ return { accum: "", complete: null };
2795
+ }
2796
+ return null;
2797
+ }
2798
+ return null;
2799
+ }
2800
+ const piece = input.replace(BEGIN_RE, "").replace(END_RE, "").replace(ANSI_RE, "");
2698
2801
  const next = (accum ?? "") + piece;
2699
2802
  if (input.includes(END)) return { accum: null, complete: next };
2700
2803
  return { accum: next, complete: null };
@@ -2966,10 +3069,10 @@ function reducer(state, action) {
2966
3069
  case "clearPlaceholdersOnly":
2967
3070
  return { ...state, placeholders: [], placeholderContents: [] };
2968
3071
  case "clearHistory": {
2969
- const last = state.entries[state.entries.length - 1];
3072
+ const banner = state.entries.find((e) => e.kind === "banner");
2970
3073
  return {
2971
3074
  ...state,
2972
- entries: last ? [last] : state.entries,
3075
+ entries: banner ? [banner] : state.entries,
2973
3076
  queue: [],
2974
3077
  nextQueueId: 1
2975
3078
  };
@@ -2985,7 +3088,7 @@ function reducer(state, action) {
2985
3088
  case "steerStart":
2986
3089
  return { ...state, steeringPending: true, steerSnapshot: action.snapshot };
2987
3090
  case "steerConsume":
2988
- return { ...state, steeringPending: false, steerSnapshot: null };
3091
+ return { ...state, steeringPending: false, steerSnapshot: null, interrupts: 0 };
2989
3092
  case "resetInterrupts":
2990
3093
  return { ...state, interrupts: 0 };
2991
3094
  case "hint":
@@ -3289,7 +3392,7 @@ function reducer(state, action) {
3289
3392
  case "fleetDelta": {
3290
3393
  const cur = state.fleet[action.id];
3291
3394
  if (!cur) return state;
3292
- const appended = (cur.streamingText + action.text).slice(-200);
3395
+ const appended = (cur.streamingText + action.text).slice(-500);
3293
3396
  return {
3294
3397
  ...state,
3295
3398
  fleet: {
@@ -3401,6 +3504,23 @@ function reducer(state, action) {
3401
3504
  }
3402
3505
  };
3403
3506
  }
3507
+ case "fleetCtxPct": {
3508
+ const cur = state.fleet[action.id];
3509
+ if (!cur) return state;
3510
+ return {
3511
+ ...state,
3512
+ fleet: {
3513
+ ...state.fleet,
3514
+ [action.id]: {
3515
+ ...cur,
3516
+ ctxPct: action.load,
3517
+ ctxTokens: action.tokens,
3518
+ ctxMaxTokens: action.maxContext,
3519
+ lastEventAt: Date.now()
3520
+ }
3521
+ }
3522
+ };
3523
+ }
3404
3524
  case "fleetCost": {
3405
3525
  return {
3406
3526
  ...state,
@@ -3452,6 +3572,18 @@ function reducer(state, action) {
3452
3572
  }
3453
3573
  };
3454
3574
  }
3575
+ case "leaderCtxPct": {
3576
+ return {
3577
+ ...state,
3578
+ leader: {
3579
+ ...state.leader,
3580
+ ctxPct: action.load,
3581
+ ctxTokens: action.tokens,
3582
+ ctxMaxTokens: action.maxContext,
3583
+ lastEventAt: Date.now()
3584
+ }
3585
+ };
3586
+ }
3455
3587
  case "setStreamFleet": {
3456
3588
  return { ...state, streamFleet: action.enabled };
3457
3589
  }
@@ -3578,6 +3710,59 @@ function reducer(state, action) {
3578
3710
  case "worktreeMonitorToggle": {
3579
3711
  return { ...state, worktreeMonitorOpen: !state.worktreeMonitorOpen };
3580
3712
  }
3713
+ // --- Collab session ---
3714
+ case "collabSubagentSpawned": {
3715
+ if (state.collabSession) return state;
3716
+ return {
3717
+ ...state,
3718
+ collabSession: {
3719
+ sessionId: null,
3720
+ bugCount: 0,
3721
+ planCount: 0,
3722
+ evalCount: 0,
3723
+ overallVerdict: null,
3724
+ timeline: [{ at: Date.now(), icon: "\u26A1", color: "cyan", text: `${action.role} spawned` }],
3725
+ startedAt: Date.now()
3726
+ }
3727
+ };
3728
+ }
3729
+ case "collabBugFound": {
3730
+ const cs = state.collabSession;
3731
+ if (!cs) {
3732
+ return {
3733
+ ...state,
3734
+ collabSession: {
3735
+ sessionId: action.sessionId,
3736
+ bugCount: 1,
3737
+ planCount: 0,
3738
+ evalCount: 0,
3739
+ overallVerdict: null,
3740
+ timeline: [{ at: Date.now(), icon: "\u{1F41B}", color: "red", text: `bug: ${action.description.slice(0, 60)}\u2026` }],
3741
+ startedAt: Date.now()
3742
+ }
3743
+ };
3744
+ }
3745
+ const entry = { at: Date.now(), icon: "\u{1F41B}", color: "red", text: `bug [${action.severity}]: ${action.description.slice(0, 55)}\u2026` };
3746
+ return { ...state, collabSession: { ...cs, sessionId: action.sessionId, bugCount: cs.bugCount + 1, timeline: [entry, ...cs.timeline].slice(0, 30) } };
3747
+ }
3748
+ case "collabPlanEmitted": {
3749
+ const cs = state.collabSession;
3750
+ if (!cs) return state;
3751
+ const entry = { at: Date.now(), icon: "\u{1F4D0}", color: "yellow", text: `plan [${action.riskScore}]: ${action.phaseCount} phases` };
3752
+ return { ...state, collabSession: { ...cs, sessionId: action.sessionId, planCount: cs.planCount + 1, timeline: [entry, ...cs.timeline].slice(0, 30) } };
3753
+ }
3754
+ case "collabEvalComplete": {
3755
+ const cs = state.collabSession;
3756
+ if (!cs) return state;
3757
+ const entry = { at: Date.now(), icon: "\u2696\uFE0F", color: action.verdict === "approve" ? "green" : action.verdict === "reject" ? "red" : "yellow", text: `eval ${action.score}/10 \u2192 ${action.verdict}` };
3758
+ return { ...state, collabSession: { ...cs, sessionId: action.sessionId, evalCount: cs.evalCount + 1, timeline: [entry, ...cs.timeline].slice(0, 30) } };
3759
+ }
3760
+ case "collabSessionDone": {
3761
+ const cs = state.collabSession;
3762
+ if (!cs) return state;
3763
+ const entry = { at: Date.now(), icon: "\u{1F3C1}", color: "green", text: `session done \u2014 ${action.verdict}` };
3764
+ return { ...state, collabSession: { ...cs, overallVerdict: action.verdict, timeline: [entry, ...cs.timeline].slice(0, 30) } };
3765
+ }
3581
3766
  }
3582
3767
  }
3583
3768
  var PASTE_THRESHOLD_CHARS = 200;
@@ -3747,6 +3932,7 @@ function App({
3747
3932
  streamFleet: true,
3748
3933
  monitorOpen: false,
3749
3934
  agentsMonitorOpen: false,
3935
+ collabSession: null,
3750
3936
  checkpoints: [],
3751
3937
  rewindOverlay: null,
3752
3938
  eternalStage: null,
@@ -3813,7 +3999,6 @@ function App({
3813
3999
  clearInterval(t);
3814
4000
  };
3815
4001
  }, [agent.ctx.cwd]);
3816
- (tokenCounter?.total().input ?? 0) + (tokenCounter?.total().cacheRead ?? 0) + (tokenCounter?.total().cacheWrite ?? 0);
3817
4002
  const maxContext = effectiveMaxContext ?? agent.ctx.provider.capabilities.maxContext;
3818
4003
  const currentContextTokens = (tokenCounter?.currentRequestTokens()?.input ?? 0) + (tokenCounter?.currentRequestTokens()?.cacheRead ?? 0);
3819
4004
  const contextWindow = useMemo(() => {
@@ -3857,7 +4042,10 @@ function App({
3857
4042
  cost: 0,
3858
4043
  startedAt: state.leader.startedAt,
3859
4044
  lastEventAt: state.leader.lastEventAt,
3860
- currentTool: state.leader.currentTool
4045
+ currentTool: state.leader.currentTool,
4046
+ ctxPct: state.leader.ctxPct,
4047
+ ctxTokens: state.leader.ctxTokens,
4048
+ ctxMaxTokens: state.leader.ctxMaxTokens
3861
4049
  };
3862
4050
  return { leader: leaderEntry, ...state.fleet };
3863
4051
  }, [state.fleet, state.leader, state.status, provider, model]);
@@ -3868,9 +4056,8 @@ function App({
3868
4056
  const existing = m.get(id);
3869
4057
  if (existing) return existing;
3870
4058
  const n = m.size + 1;
3871
- const suffix = name && name !== id ? ` ${name}` : "";
3872
4059
  const v = {
3873
- label: `AGENT#${n}${suffix}`,
4060
+ label: name && name !== id ? name : `AGENT#${n}`,
3874
4061
  color: STREAM_COLORS[(n - 1) % STREAM_COLORS.length]
3875
4062
  };
3876
4063
  m.set(id, v);
@@ -4514,6 +4701,23 @@ function App({
4514
4701
  }
4515
4702
  });
4516
4703
  });
4704
+ const offCtxPct = events.on("subagent.ctx_pct", (e) => {
4705
+ dispatch({
4706
+ type: "fleetCtxPct",
4707
+ id: e.subagentId,
4708
+ load: e.load,
4709
+ tokens: e.tokens,
4710
+ maxContext: e.maxContext
4711
+ });
4712
+ });
4713
+ const offLeaderCtxPct = events.on("ctx.pct", (e) => {
4714
+ dispatch({
4715
+ type: "leaderCtxPct",
4716
+ load: e.load,
4717
+ tokens: e.tokens,
4718
+ maxContext: e.maxContext
4719
+ });
4720
+ });
4517
4721
  const offTool = events.on("subagent.tool_executed", (e) => {
4518
4722
  dispatch({
4519
4723
  type: "fleetTool",
@@ -4532,6 +4736,8 @@ function App({
4532
4736
  offBudgetWarning();
4533
4737
  offBudgetExtended();
4534
4738
  offIterationSummary();
4739
+ offCtxPct();
4740
+ offLeaderCtxPct();
4535
4741
  offTool();
4536
4742
  };
4537
4743
  }, [events, director]);
@@ -4941,6 +5147,70 @@ function App({
4941
5147
  }
4942
5148
  break;
4943
5149
  }
5150
+ // --- Collab session events ---
5151
+ case "bug.found": {
5152
+ const role = e.subagentId.includes("bug-hunter") ? "bug-hunter" : e.subagentId.includes("refactor-planner") ? "refactor-planner" : e.subagentId.includes("critic") ? "critic" : null;
5153
+ if (!role && !state.collabSession) {
5154
+ break;
5155
+ }
5156
+ if (!state.collabSession) {
5157
+ dispatch({ type: "collabSubagentSpawned", subagentId: e.subagentId, role: role ?? "unknown" });
5158
+ }
5159
+ const bp = e.payload;
5160
+ if (bp?.finding) {
5161
+ const sessionId = e.subagentId.split("-").slice(1).join("-") || e.subagentId;
5162
+ dispatch({
5163
+ type: "collabBugFound",
5164
+ sessionId,
5165
+ bugId: bp.finding.id ?? "unknown",
5166
+ severity: bp.finding.severity ?? "unknown",
5167
+ description: bp.finding.description ?? ""
5168
+ });
5169
+ }
5170
+ break;
5171
+ }
5172
+ case "refactor.plan": {
5173
+ if (!state.collabSession) break;
5174
+ const pp = e.payload;
5175
+ if (pp?.plan) {
5176
+ const sessionId = e.subagentId.split("-").slice(1).join("-") || e.subagentId;
5177
+ dispatch({
5178
+ type: "collabPlanEmitted",
5179
+ sessionId,
5180
+ planId: pp.plan.id ?? "unknown",
5181
+ riskScore: pp.plan.riskScore ?? "unknown",
5182
+ phaseCount: pp.plan.phases?.length ?? 0
5183
+ });
5184
+ }
5185
+ break;
5186
+ }
5187
+ case "critic.evaluation": {
5188
+ if (!state.collabSession) break;
5189
+ const ep = e.payload;
5190
+ if (ep?.evaluation) {
5191
+ const sessionId = e.subagentId.split("-").slice(1).join("-") || e.subagentId;
5192
+ dispatch({
5193
+ type: "collabEvalComplete",
5194
+ sessionId,
5195
+ evalId: ep.evaluation.id ?? "unknown",
5196
+ verdict: ep.evaluation.verdict ?? "unknown",
5197
+ score: ep.evaluation.score ?? 0
5198
+ });
5199
+ }
5200
+ break;
5201
+ }
5202
+ case "collab.session_done": {
5203
+ if (!state.collabSession) break;
5204
+ const dp = e.payload;
5205
+ if (dp?.report) {
5206
+ dispatch({
5207
+ type: "collabSessionDone",
5208
+ sessionId: dp.report.sessionId ?? state.collabSession.sessionId ?? "unknown",
5209
+ verdict: dp.report.overallVerdict ?? "needs_revision"
5210
+ });
5211
+ }
5212
+ break;
5213
+ }
4944
5214
  }
4945
5215
  });
4946
5216
  const offDone = d.on("task.completed", (payload) => {
@@ -5102,7 +5372,7 @@ function App({
5102
5372
  setDraft(next, cursor + full.length);
5103
5373
  };
5104
5374
  const handleKey = async (input, key) => {
5105
- if (state.status === "aborting" && state.interrupts === 0) return;
5375
+ if (state.status === "aborting" && !state.steeringPending && state.interrupts === 0) return;
5106
5376
  if (state.confirmQueue.length > 0) return;
5107
5377
  if (inputGateRef.current) return;
5108
5378
  if (key.escape) {
@@ -5186,14 +5456,6 @@ function App({
5186
5456
  }
5187
5457
  return;
5188
5458
  }
5189
- if (key.ctrl && input === "p") {
5190
- dispatch({ type: "autoPhaseMonitorToggle" });
5191
- return;
5192
- }
5193
- if (key.ctrl && input === "t") {
5194
- dispatch({ type: "worktreeMonitorToggle" });
5195
- return;
5196
- }
5197
5459
  if (state.autonomyPicker.open) {
5198
5460
  if (key.escape) {
5199
5461
  dispatch({ type: "autonomyPickerClose" });
@@ -5320,20 +5582,47 @@ function App({
5320
5582
  return;
5321
5583
  }
5322
5584
  if (key.ctrl && input === "f") {
5323
- dispatch({ type: "toggleMonitor" });
5585
+ if (state.agentsMonitorOpen) {
5586
+ dispatch({ type: "toggleAgentsMonitor" });
5587
+ dispatch({ type: "toggleMonitor" });
5588
+ } else {
5589
+ dispatch({ type: "toggleMonitor" });
5590
+ }
5324
5591
  return;
5325
5592
  }
5326
5593
  if (key.ctrl && input === "g") {
5327
- dispatch({ type: "toggleAgentsMonitor" });
5594
+ if (state.monitorOpen) {
5595
+ dispatch({ type: "toggleMonitor" });
5596
+ dispatch({ type: "toggleAgentsMonitor" });
5597
+ } else {
5598
+ dispatch({ type: "toggleAgentsMonitor" });
5599
+ }
5328
5600
  return;
5329
5601
  }
5330
- if (key.escape && state.monitorOpen) {
5331
- dispatch({ type: "toggleMonitor" });
5602
+ if (key.ctrl && input === "t") {
5603
+ if (state.worktreeMonitorOpen) {
5604
+ dispatch({ type: "worktreeMonitorToggle" });
5605
+ return;
5606
+ }
5607
+ if (state.agentsMonitorOpen) dispatch({ type: "toggleAgentsMonitor" });
5608
+ if (state.monitorOpen) dispatch({ type: "toggleMonitor" });
5609
+ if (state.autoPhase?.monitorOpen) dispatch({ type: "autoPhaseMonitorToggle" });
5610
+ dispatch({ type: "worktreeMonitorToggle" });
5332
5611
  return;
5333
5612
  }
5334
- if (key.escape && state.agentsMonitorOpen) {
5335
- dispatch({ type: "toggleAgentsMonitor" });
5336
- return;
5613
+ if (key.escape) {
5614
+ if (state.agentsMonitorOpen) {
5615
+ dispatch({ type: "toggleAgentsMonitor" });
5616
+ return;
5617
+ }
5618
+ if (state.monitorOpen) {
5619
+ dispatch({ type: "toggleMonitor" });
5620
+ return;
5621
+ }
5622
+ if (state.worktreeMonitorOpen) {
5623
+ dispatch({ type: "worktreeMonitorToggle" });
5624
+ return;
5625
+ }
5337
5626
  }
5338
5627
  if (isEnter) {
5339
5628
  const now = Date.now();
@@ -5436,18 +5725,6 @@ function App({
5436
5725
  setDraft("", 0);
5437
5726
  return;
5438
5727
  }
5439
- if (key.ctrl && input === "w") {
5440
- if (state.worktreeMonitorOpen) {
5441
- dispatch({ type: "worktreeMonitorToggle" });
5442
- return;
5443
- }
5444
- if (cursor === 0) return;
5445
- const beforeCursor = buffer.slice(0, cursor);
5446
- const lastWordStart = beforeCursor.lastIndexOf(" ") + 1;
5447
- const next2 = buffer.slice(0, lastWordStart) + buffer.slice(cursor);
5448
- setDraft(next2, lastWordStart);
5449
- return;
5450
- }
5451
5728
  if (key.delete || key.ctrl && input === "d") {
5452
5729
  if (cursor >= buffer.length) return;
5453
5730
  const next2 = buffer.slice(0, cursor) + buffer.slice(cursor + 1);
@@ -5817,7 +6094,7 @@ User message:
5817
6094
  value: state.buffer,
5818
6095
  cursor: state.cursor,
5819
6096
  placeholders: state.placeholders,
5820
- disabled: state.status === "aborting" || state.confirmQueue.length > 0,
6097
+ disabled: state.status === "aborting" && !state.steeringPending || state.confirmQueue.length > 0,
5821
6098
  hint: inputHint,
5822
6099
  onKey: handleKey
5823
6100
  }
@@ -5941,9 +6218,10 @@ User message:
5941
6218
  entries: state.fleet,
5942
6219
  totalCost: state.fleetCost,
5943
6220
  totalTokens: state.fleetTokens,
5944
- nowTick
6221
+ nowTick,
6222
+ collabSession: state.collabSession
5945
6223
  }
5946
- ) : director ? /* @__PURE__ */ jsx(FleetPanel, { entries: state.fleet, totalCost: state.fleetCost, roster: fleetRoster }) : null,
6224
+ ) : director ? /* @__PURE__ */ jsx(FleetPanel, { entries: entriesWithLeader, totalCost: state.fleetCost, roster: fleetRoster, collabSession: state.collabSession }) : null,
5947
6225
  state.autoPhase && !state.autoPhase.monitorOpen ? /* @__PURE__ */ jsx(
5948
6226
  PhasePanel,
5949
6227
  {