@wrongstack/webui 0.260.0 → 0.264.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
@@ -62,6 +62,20 @@ function installFaviconVisibilityReset() {
62
62
  });
63
63
  }
64
64
 
65
+ // src/lib/ws-client-helpers.ts
66
+ function buildClearModelsMessage(providerId) {
67
+ return { type: "provider.clear_models", payload: { providerId } };
68
+ }
69
+ function buildUndoClearMessage(providerId, previousModels) {
70
+ return {
71
+ type: "provider.undo_clear",
72
+ payload: { providerId, previousModels: [...previousModels] }
73
+ };
74
+ }
75
+ function buildProviderUpdateMessage(payload) {
76
+ return { type: "provider.update", payload };
77
+ }
78
+
65
79
  // src/lib/ws-client.ts
66
80
  function getTokenFromWsUrl(wsUrl) {
67
81
  try {
@@ -338,14 +352,15 @@ var WrongStackWebSocketClient = class {
338
352
  off(eventType, handler) {
339
353
  this.handlers.get(eventType)?.delete(handler);
340
354
  }
341
- sendMessage(content) {
355
+ sendMessage(content, imageBase64) {
342
356
  const id = `msg_${Date.now()}_${crypto.randomUUID().slice(0, 8)}`;
343
357
  this.send({
344
358
  type: "user_message",
345
359
  payload: {
346
360
  id,
347
361
  content,
348
- timestamp: Date.now()
362
+ timestamp: Date.now(),
363
+ ...imageBase64 ? { imageBase64 } : {}
349
364
  }
350
365
  });
351
366
  return id;
@@ -356,6 +371,9 @@ var WrongStackWebSocketClient = class {
356
371
  payload: {}
357
372
  });
358
373
  }
374
+ getGitInfo() {
375
+ this.send({ type: "git.info" });
376
+ }
359
377
  sendConfirm(id, decision) {
360
378
  const pending = this.pendingConfirms.get(id);
361
379
  if (pending) {
@@ -404,6 +422,25 @@ var WrongStackWebSocketClient = class {
404
422
  removeProvider(providerId) {
405
423
  this.send({ type: "provider.remove", payload: { providerId } });
406
424
  }
425
+ /** Run a health probe against a saved provider's `/v1/models`. */
426
+ probeProvider(providerId, timeoutMs) {
427
+ this.send({
428
+ type: "provider.probe",
429
+ payload: timeoutMs !== void 0 ? { providerId, timeoutMs } : { providerId }
430
+ });
431
+ }
432
+ /** Remove the saved model allowlist for a provider. */
433
+ clearProviderModels(providerId) {
434
+ this.send(buildClearModelsMessage(providerId));
435
+ }
436
+ /** Restore a previously-cleared model allowlist (pairs with clear). */
437
+ undoProviderClear(providerId, previousModels) {
438
+ this.send(buildUndoClearMessage(providerId, previousModels));
439
+ }
440
+ /** Update a saved provider's wire config (family / baseUrl / envVars / models). */
441
+ updateProvider(payload) {
442
+ this.send(buildProviderUpdateMessage(payload));
443
+ }
407
444
  clearContext() {
408
445
  this.send({ type: "context.clear" });
409
446
  }
@@ -890,6 +927,8 @@ var useUIStore = create4()(
890
927
  agentsMonitorOpen: false,
891
928
  inspectorOpen: false,
892
929
  inspectorTab: "fleet",
930
+ processMonitorOpen: false,
931
+ queuePanelOpen: false,
893
932
  selectActivity: (activity) => set({ activeActivity: activity }),
894
933
  toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),
895
934
  setSidebarOpen: (open) => set({ sidebarOpen: open }),
@@ -940,7 +979,9 @@ var useUIStore = create4()(
940
979
  setAgentsMonitorOpen: (open) => set({ agentsMonitorOpen: open }),
941
980
  setInspectorOpen: (open) => set({ inspectorOpen: open }),
942
981
  setInspectorTab: (tab) => set({ inspectorTab: tab }),
943
- toggleInspector: () => set((s) => ({ inspectorOpen: !s.inspectorOpen }))
982
+ toggleInspector: () => set((s) => ({ inspectorOpen: !s.inspectorOpen })),
983
+ setProcessMonitorOpen: (open) => set({ processMonitorOpen: open }),
984
+ setQueuePanelOpen: (open) => set({ queuePanelOpen: open })
944
985
  }),
945
986
  {
946
987
  name: "wrongstack-ui",
@@ -990,12 +1031,20 @@ var useMailboxStore = create5()((set) => ({
990
1031
  setAgents: (agents) => set({ agents })
991
1032
  }));
992
1033
  function selectUnreadCount(s) {
993
- return s.messages.filter((m) => m.readByCount === 0 && !m.completed).length;
1034
+ return s.messages.filter((m) => !m.completed && (m.readByCount ?? 0) === 0).length;
994
1035
  }
995
1036
 
996
- // src/stores/history-store.ts
1037
+ // src/stores/git-info-store.ts
997
1038
  import { create as create6 } from "zustand";
998
- var useHistoryStore = create6()((set) => ({
1039
+ var useGitInfoStore = create6()((set) => ({
1040
+ info: null,
1041
+ setInfo: (info) => set({ info }),
1042
+ clear: () => set({ info: null })
1043
+ }));
1044
+
1045
+ // src/stores/history-store.ts
1046
+ import { create as create7 } from "zustand";
1047
+ var useHistoryStore = create7()((set) => ({
999
1048
  entries: [],
1000
1049
  loading: false,
1001
1050
  error: null,
@@ -1008,8 +1057,8 @@ var useHistoryStore = create6()((set) => ({
1008
1057
  }));
1009
1058
 
1010
1059
  // src/stores/worktree-store.ts
1011
- import { create as create7 } from "zustand";
1012
- var useWorktreeStore = create7()((set) => ({
1060
+ import { create as create8 } from "zustand";
1061
+ var useWorktreeStore = create8()((set) => ({
1013
1062
  worktrees: [],
1014
1063
  baseBranch: "",
1015
1064
  activity: [],
@@ -1018,34 +1067,75 @@ var useWorktreeStore = create7()((set) => ({
1018
1067
  }));
1019
1068
 
1020
1069
  // src/stores/fleet-store.ts
1021
- import { create as create8 } from "zustand";
1070
+ import { create as create9 } from "zustand";
1022
1071
 
1023
1072
  // src/components/NextStepsBar.tsx
1024
1073
  import { ArrowRight, Lightbulb, MousePointerClick, Timer } from "lucide-react";
1025
1074
  import { useEffect, useState } from "react";
1026
1075
  import { jsx, jsxs } from "react/jsx-runtime";
1027
- var NEXT_STEPS_RE = /💡\s*Next steps?\s*\n+((?:\d+\.\s+.+(?:\s+auto="true")?\n?)+)/i;
1028
- var ITEM_RE = /^(\d+)\.\s+(.+?)(?:\s+auto="true")?$/;
1029
- function parseNextSteps(content) {
1030
- const match = NEXT_STEPS_RE.exec(content);
1031
- if (match?.[1]) {
1032
- const block = match[1];
1033
- return parseStepLines(block);
1076
+ var STRICT_HEADING_RE = /(?:💡\s*Next steps?|<next_steps>)\s*\n+/i;
1077
+ var ITEM_RE = /^(?:(\d+)[.)]\s*|[-*•]\s*)(.+?)(\s+auto="true")?$/;
1078
+ var MAX_STEPS = 6;
1079
+ function parseNextSteps(content, strict = true) {
1080
+ const headingMatch = strict ? STRICT_HEADING_RE.exec(content) : buildPermissiveHeadingRe().exec(content);
1081
+ if (!headingMatch) {
1082
+ return { steps: [], stripped: content };
1034
1083
  }
1035
- return parseStepLines(content);
1036
- }
1037
- function parseStepLines(block) {
1038
- const lines = block.split("\n").filter(Boolean);
1084
+ const headingEnd = headingMatch.index + headingMatch[0].length;
1085
+ const afterHeading = content.slice(headingEnd);
1086
+ const lines = afterHeading.split("\n");
1039
1087
  const steps = [];
1040
- for (const line of lines) {
1041
- const m = ITEM_RE.exec(line.trim());
1042
- if (m) {
1043
- const text = m[2].trim();
1044
- const hasAuto = line.trim().endsWith('auto="true"');
1045
- steps.push({ index: Number.parseInt(m[1], 10), text, auto: hasAuto });
1088
+ const seenNumbers = /* @__PURE__ */ new Set();
1089
+ let consumed = 0;
1090
+ let found = 0;
1091
+ for (const rawLine of lines) {
1092
+ const line = rawLine.trim();
1093
+ if (line === "</next_steps>") {
1094
+ consumed += rawLine.length + 1;
1095
+ break;
1046
1096
  }
1097
+ if (!line) {
1098
+ consumed += rawLine.length + 1;
1099
+ continue;
1100
+ }
1101
+ const m = ITEM_RE.exec(line);
1102
+ if (!m) break;
1103
+ const numPart = m[1];
1104
+ const text = m[2].trim();
1105
+ const hasAuto = !!m[3];
1106
+ const index = numPart !== void 0 ? Number.parseInt(numPart, 10) : steps.length + 1;
1107
+ if (seenNumbers.has(index)) {
1108
+ consumed += rawLine.length + 1;
1109
+ continue;
1110
+ }
1111
+ seenNumbers.add(index);
1112
+ steps.push(hasAuto ? { index, text, auto: true } : { index, text });
1113
+ consumed += rawLine.length + 1;
1114
+ found++;
1115
+ if (found >= MAX_STEPS) break;
1116
+ }
1117
+ if (steps.length === 0) {
1118
+ return { steps: [], stripped: content };
1119
+ }
1120
+ if (strict && /<next_steps>/i.test(headingMatch[0]) && !afterHeading.includes("</next_steps>")) {
1121
+ return { steps: [], stripped: content };
1047
1122
  }
1048
- return steps.slice(0, 6);
1123
+ const blockStart = headingMatch.index;
1124
+ const blockEnd = headingEnd + consumed;
1125
+ const stripped = (content.slice(0, blockStart) + content.slice(blockEnd)).replace(/\n{3,}/g, "\n\n").trim();
1126
+ return { steps, stripped };
1127
+ }
1128
+ var PERMISSIVE_HEADING_PATTERNS = [
1129
+ { re: /💡\s*Next steps?\s*\n+/i, label: "emoji" },
1130
+ { re: /##?\s*Next steps?\s*\n+/i, label: "markdown" },
1131
+ { re: /\n{1,2}Next steps?\s*\n+/i, label: "plain" },
1132
+ { re: /<next_steps>\s*\n+/i, label: "xml-tag" }
1133
+ ];
1134
+ function buildPermissiveHeadingRe() {
1135
+ const variants = PERMISSIVE_HEADING_PATTERNS.map(
1136
+ ({ re }) => `(?:${re.source})`
1137
+ ).join("|");
1138
+ return new RegExp(variants, "i");
1049
1139
  }
1050
1140
  function stripNextStepsBlock(text) {
1051
1141
  return text.replace(/<next_steps\b[^>]*>[\s\S]*?<\/next_steps>/gi, "").replace(/<next_steps\b[^>]*\/?>/gi, "").replace(/\n{3,}/g, "\n\n").trim();
@@ -1085,10 +1175,11 @@ function NextStepsBar({
1085
1175
  yoloMode = false,
1086
1176
  autoMode = false,
1087
1177
  autoDelayMs = 3e4,
1088
- onAutoSubmit
1178
+ onAutoSubmit,
1179
+ canAutoSubmit: canAutoSubmitProp = true
1089
1180
  }) {
1090
1181
  if (steps.length === 0) return null;
1091
- const showAutoCountdown = yoloMode && autoMode;
1182
+ const showAutoCountdown = yoloMode && autoMode && canAutoSubmitProp;
1092
1183
  const autoStep = showAutoCountdown ? steps.find((s) => s.auto) : void 0;
1093
1184
  return /* @__PURE__ */ jsxs("div", { className: "mt-4 rounded-xl border border-primary/20 bg-primary/[0.03] overflow-hidden animate-message", children: [
1094
1185
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3.5 py-2 border-b border-primary/10 bg-primary/[0.04]", children: [
@@ -1131,7 +1222,7 @@ function NextStepsBar({
1131
1222
  }
1132
1223
  ),
1133
1224
  s.auto && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-[10px] text-primary/70", children: [
1134
- /* @__PURE__ */ jsx(Timer, { className: "h-3 w-3" }),
1225
+ autoMode && s.index === 1 && !showAutoCountdown ? /* @__PURE__ */ jsx("span", { title: "Will auto-submit after countdown", children: "\u23E9" }) : /* @__PURE__ */ jsx(Timer, { className: "h-3 w-3" }),
1135
1226
  "auto"
1136
1227
  ] }),
1137
1228
  /* @__PURE__ */ jsx(
@@ -1179,7 +1270,7 @@ function pushTimeline(timeline, event) {
1179
1270
  function bumpSparkline(bins) {
1180
1271
  return [bins[0] + 1, ...bins.slice(0, SPARKLINE_BINS - 1)];
1181
1272
  }
1182
- var useFleetStore = create8()((set, get) => ({
1273
+ var useFleetStore = create9()((set, get) => ({
1183
1274
  agents: /* @__PURE__ */ new Map(),
1184
1275
  leaderId: void 0,
1185
1276
  fleetTokensIn: 0,
@@ -1378,7 +1469,7 @@ var useFleetStore = create8()((set, get) => ({
1378
1469
  }));
1379
1470
 
1380
1471
  // src/stores/goal-store.ts
1381
- import { create as create9 } from "zustand";
1472
+ import { create as create10 } from "zustand";
1382
1473
 
1383
1474
  // src/lib/goal.ts
1384
1475
  function parseGoalState(raw) {
@@ -1405,7 +1496,7 @@ function parseGoalState(raw) {
1405
1496
  }
1406
1497
 
1407
1498
  // src/stores/goal-store.ts
1408
- var useGoalStore = create9()((set) => ({
1499
+ var useGoalStore = create10()((set) => ({
1409
1500
  goal: null,
1410
1501
  setGoal: (raw) => set({ goal: parseGoalState(raw) }),
1411
1502
  clear: () => set({ goal: null }),
@@ -1418,8 +1509,8 @@ var useGoalStore = create9()((set) => ({
1418
1509
  }));
1419
1510
 
1420
1511
  // src/stores/autophase-store.ts
1421
- import { create as create10 } from "zustand";
1422
- var useAutoPhaseStore = create10()((set) => ({
1512
+ import { create as create11 } from "zustand";
1513
+ var useAutoPhaseStore = create11()((set) => ({
1423
1514
  phases: [],
1424
1515
  activePhaseId: null,
1425
1516
  overallPercent: 0,
@@ -1442,7 +1533,7 @@ var useAutoPhaseStore = create10()((set) => ({
1442
1533
  }));
1443
1534
 
1444
1535
  // src/stores/local-prefs.ts
1445
- import { create as create11 } from "zustand";
1536
+ import { create as create12 } from "zustand";
1446
1537
  import { persist as persist5 } from "zustand/middleware";
1447
1538
  var DEFAULTS = {
1448
1539
  autonomy: "off",
@@ -1466,9 +1557,13 @@ var DEFAULTS = {
1466
1557
  auditLevel: "standard",
1467
1558
  enhanceEnabled: true,
1468
1559
  enhanceDelayMs: 6e4,
1469
- enhanceLanguage: "original"
1560
+ enhanceLanguage: "original",
1561
+ tgConfigured: false,
1562
+ tgSessionEnd: false,
1563
+ tgDelegate: true,
1564
+ tgLongToolMs: 3e4
1470
1565
  };
1471
- var useLocalPrefs = create11()(
1566
+ var useLocalPrefs = create12()(
1472
1567
  persist5(
1473
1568
  (set) => ({
1474
1569
  ...DEFAULTS,
@@ -1477,11 +1572,17 @@ var useLocalPrefs = create11()(
1477
1572
  }),
1478
1573
  {
1479
1574
  name: "wrongstack-local-prefs",
1480
- version: 2,
1575
+ version: 3,
1481
1576
  // v1 stored option values that don't exist in core's config schema —
1482
1577
  // contextStrategy frugal/balanced/deep/archival (context-window modes,
1483
1578
  // a different setting) and auditLevel 'verbose'. Map them onto the
1484
1579
  // canonical values so persisted stores don't resurrect invalid prefs.
1580
+ //
1581
+ // v2 added autoProceedMaxIterations.
1582
+ //
1583
+ // v3 added Telegram notification prefs (tgConfigured, tgSessionEnd,
1584
+ // tgDelegate, tgLongToolMs). Older stores simply get the defaults via
1585
+ // the spread of DEFAULTS; no explicit remap is needed.
1485
1586
  migrate: (persisted) => {
1486
1587
  const p = persisted ?? {};
1487
1588
  const validStrategies = ["hybrid", "intelligent", "selective"];
@@ -1502,8 +1603,8 @@ var useLocalPrefs = create11()(
1502
1603
  );
1503
1604
 
1504
1605
  // src/stores/file-store.ts
1505
- import { create as create12 } from "zustand";
1506
- var useFileStore = create12()((set, get) => ({
1606
+ import { create as create13 } from "zustand";
1607
+ var useFileStore = create13()((set, get) => ({
1507
1608
  projectRoot: "",
1508
1609
  tree: [],
1509
1610
  openFiles: [],
@@ -1566,7 +1667,7 @@ var useFileStore = create12()((set, get) => ({
1566
1667
  }));
1567
1668
 
1568
1669
  // src/stores/viz-store.ts
1569
- import { create as create13 } from "zustand";
1670
+ import { create as create14 } from "zustand";
1570
1671
  var _eventSeq = 0;
1571
1672
  function nextId() {
1572
1673
  return `viz_${Date.now()}_${++_eventSeq}`;
@@ -1594,7 +1695,7 @@ var EDGE_COLORS = {
1594
1695
  "mailbox:send": "hsl(140, 70%, 55%)",
1595
1696
  "default": "hsl(0, 0%, 40%)"
1596
1697
  };
1597
- var useVizStore = create13()((set, get) => ({
1698
+ var useVizStore = create14()((set, get) => ({
1598
1699
  events: [],
1599
1700
  nodes: /* @__PURE__ */ new Map(),
1600
1701
  edges: /* @__PURE__ */ new Map(),
@@ -1971,12 +2072,14 @@ function cn(...inputs) {
1971
2072
  // src/components/Toaster.tsx
1972
2073
  import { AlertTriangle, CheckCircle2, Info, X, XCircle } from "lucide-react";
1973
2074
  import { useEffect as useEffect2 } from "react";
1974
- import { create as create14 } from "zustand";
2075
+ import { randomUUID } from "crypto";
2076
+ import { create as create15 } from "zustand";
1975
2077
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1976
- var useToastStore = create14((set) => ({
2078
+ var ACTION_TTL_MS = 8e3;
2079
+ var useToastStore = create15((set) => ({
1977
2080
  toasts: [],
1978
2081
  push: (t) => {
1979
- const id = `toast_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
2082
+ const id = `toast_${randomUUID()}`;
1980
2083
  set((state) => ({ toasts: [...state.toasts, { ...t, id }] }));
1981
2084
  return id;
1982
2085
  },
@@ -1987,6 +2090,12 @@ var toast = {
1987
2090
  error: (msg, ttl = 6e3) => useToastStore.getState().push({ message: msg, variant: "error", ttl }),
1988
2091
  warn: (msg, ttl = 4500) => useToastStore.getState().push({ message: msg, variant: "warn", ttl }),
1989
2092
  info: (msg, ttl = 3500) => useToastStore.getState().push({ message: msg, variant: "info", ttl }),
2093
+ /**
2094
+ * Fire a toast carrying an "Undo" action button. The toast lingers
2095
+ * for {@link ACTION_TTL_MS} so the user has time to react; letting it
2096
+ * expire is the same as not undoing.
2097
+ */
2098
+ undoable: (msg, onUndo, label = "Undo", ttl = ACTION_TTL_MS) => useToastStore.getState().push({ message: msg, variant: "info", ttl, action: { label, onClick: onUndo } }),
1990
2099
  dismiss: (id) => useToastStore.getState().dismiss(id)
1991
2100
  };
1992
2101
  function Icon({ variant }) {
@@ -2014,6 +2123,18 @@ function ToastItem({ entry }) {
2014
2123
  children: [
2015
2124
  /* @__PURE__ */ jsx2(Icon, { variant: entry.variant }),
2016
2125
  /* @__PURE__ */ jsx2("div", { className: "flex-1 min-w-0 whitespace-pre-wrap break-words leading-snug", children: entry.message }),
2126
+ entry.action && /* @__PURE__ */ jsx2(
2127
+ "button",
2128
+ {
2129
+ type: "button",
2130
+ onClick: () => {
2131
+ entry.action?.onClick();
2132
+ dismiss(entry.id);
2133
+ },
2134
+ className: "shrink-0 font-medium text-primary hover:underline",
2135
+ children: entry.action.label
2136
+ }
2137
+ ),
2017
2138
  /* @__PURE__ */ jsx2(
2018
2139
  "button",
2019
2140
  {
@@ -2670,8 +2791,8 @@ function handleFilesWritten(msg) {
2670
2791
  }
2671
2792
  }
2672
2793
  function queryMailbox() {
2673
- const ws = getWSClient(useConfigStore.getState().wsUrl);
2674
- ws?.send?.({ type: "mailbox.messages", payload: { limit: 30 } });
2794
+ const ws = getWSClient();
2795
+ ws?.send?.({ type: "mailbox.messages", payload: { limit: 30, incompleteOnly: true } });
2675
2796
  ws?.send?.({ type: "mailbox.agents", payload: {} });
2676
2797
  }
2677
2798
  var WS_HANDLERS = {
@@ -2755,6 +2876,9 @@ var WS_HANDLERS = {
2755
2876
  useMailboxStore.getState().setMessages([]);
2756
2877
  queryMailbox();
2757
2878
  },
2879
+ "mailbox.purged": (msg) => {
2880
+ queryMailbox();
2881
+ },
2758
2882
  "brain.status": (msg) => {
2759
2883
  const p = msg.payload;
2760
2884
  const lines = [
@@ -2840,6 +2964,10 @@ _${p.decision.rationale}_` : "";
2840
2964
  refined: p.refined,
2841
2965
  english: p.english
2842
2966
  });
2967
+ },
2968
+ "git.info": (msg) => {
2969
+ const p = msg.payload;
2970
+ useGitInfoStore.getState().setInfo({ ...p, fetchedAt: Date.now() });
2843
2971
  }
2844
2972
  };
2845
2973
 
@@ -2896,8 +3024,8 @@ function useWebSocket() {
2896
3024
  const { wsUrl } = useConfigStore();
2897
3025
  const client2 = getWSClient(wsUrl);
2898
3026
  const sendMessage = useCallback(
2899
- (content) => {
2900
- if (client2.isConnected) return client2.sendMessage(content);
3027
+ (content, imageBase64) => {
3028
+ if (client2.isConnected) return client2.sendMessage(content, imageBase64);
2901
3029
  return null;
2902
3030
  },
2903
3031
  [client2]
@@ -3060,7 +3188,7 @@ function useWebSocket() {
3060
3188
  }
3061
3189
 
3062
3190
  // src/App.tsx
3063
- import { useEffect as useEffect44 } from "react";
3191
+ import { useEffect as useEffect46 } from "react";
3064
3192
 
3065
3193
  // src/components/ActivityBar.tsx
3066
3194
  import {
@@ -4036,7 +4164,7 @@ import {
4036
4164
  Terminal as Terminal4,
4037
4165
  Zap as Zap4
4038
4166
  } from "lucide-react";
4039
- import { memo as memo5, useCallback as useCallback9, useEffect as useEffect18, useMemo as useMemo6, useRef as useRef13, useState as useState24 } from "react";
4167
+ import { memo as memo5, useCallback as useCallback10, useEffect as useEffect19, useMemo as useMemo7, useRef as useRef14, useState as useState24 } from "react";
4040
4168
  import { VList } from "virtua";
4041
4169
 
4042
4170
  // src/components/AutonomyPicker.tsx
@@ -4156,9 +4284,68 @@ function AutonomyPicker({
4156
4284
  }
4157
4285
 
4158
4286
  // src/components/ChatInput.tsx
4159
- import { expectDefined as expectDefined6 } from "@wrongstack/core";
4287
+ import { expectDefined as expectDefined6, toErrorMessage } from "@wrongstack/core";
4288
+
4289
+ // src/stores/auto-submit-streak.ts
4290
+ import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef3 } from "react";
4291
+ var _streak = 0;
4292
+ var _capWarned = false;
4293
+ function useAutoSubmitStreak() {
4294
+ const autoProceedMaxIterations = useLocalPrefs((s) => s.autoProceedMaxIterations);
4295
+ const autonomy = useLocalPrefs((s) => s.autonomy);
4296
+ const streakRef = useRef3(_streak);
4297
+ const capWarnedRef = useRef3(_capWarned);
4298
+ const prevAutonomyRef = useRef3(autonomy);
4299
+ useEffect6(() => {
4300
+ streakRef.current = _streak;
4301
+ capWarnedRef.current = _capWarned;
4302
+ });
4303
+ useEffect6(() => {
4304
+ if (prevAutonomyRef.current !== "auto" && autonomy === "auto") {
4305
+ _capWarned = false;
4306
+ capWarnedRef.current = false;
4307
+ }
4308
+ prevAutonomyRef.current = autonomy;
4309
+ }, [autonomy]);
4310
+ const canAutoSubmit = useCallback4(() => {
4311
+ if (autoProceedMaxIterations <= 0) return true;
4312
+ return streakRef.current < autoProceedMaxIterations;
4313
+ }, [autoProceedMaxIterations]);
4314
+ const recordAutoSubmit = useCallback4(() => {
4315
+ const max = autoProceedMaxIterations;
4316
+ if (max > 0 && streakRef.current >= max) {
4317
+ return false;
4318
+ }
4319
+ _streak = ++streakRef.current;
4320
+ if (max > 0 && _streak >= max) {
4321
+ _capWarned = true;
4322
+ capWarnedRef.current = true;
4323
+ }
4324
+ return true;
4325
+ }, [autoProceedMaxIterations]);
4326
+ const reset = useCallback4(() => {
4327
+ _streak = 0;
4328
+ streakRef.current = 0;
4329
+ _capWarned = false;
4330
+ capWarnedRef.current = false;
4331
+ }, []);
4332
+ const resetCapWarned = useCallback4(() => {
4333
+ _capWarned = false;
4334
+ capWarnedRef.current = false;
4335
+ }, []);
4336
+ return {
4337
+ streak: streakRef.current,
4338
+ capWarned: capWarnedRef.current,
4339
+ canAutoSubmit,
4340
+ recordAutoSubmit,
4341
+ reset,
4342
+ resetCapWarned
4343
+ };
4344
+ }
4345
+
4346
+ // src/components/ChatInput.tsx
4160
4347
  import { Pencil, Send, Square as Square2, Sparkles as Sparkles2 } from "lucide-react";
4161
- import { useCallback as useCallback4, useEffect as useEffect9, useRef as useRef6, useState as useState8 } from "react";
4348
+ import { useCallback as useCallback5, useEffect as useEffect10, useRef as useRef7, useState as useState8 } from "react";
4162
4349
 
4163
4350
  // src/components/CommandPalette/index.tsx
4164
4351
  import {
@@ -4188,7 +4375,7 @@ import {
4188
4375
  VolumeX,
4189
4376
  Wrench
4190
4377
  } from "lucide-react";
4191
- import { useEffect as useEffect6, useMemo, useRef as useRef3, useState as useState5 } from "react";
4378
+ import { useEffect as useEffect7, useMemo, useRef as useRef4, useState as useState5 } from "react";
4192
4379
 
4193
4380
  // src/components/CommandPalette/export-utils.ts
4194
4381
  function downloadChatAsMarkdown() {
@@ -4327,15 +4514,15 @@ function CommandPalette() {
4327
4514
  const ws = useWebSocket();
4328
4515
  const [query, setQuery] = useState5("");
4329
4516
  const [index, setIndex] = useState5(0);
4330
- const inputRef = useRef3(null);
4331
- useEffect6(() => {
4517
+ const inputRef = useRef4(null);
4518
+ useEffect7(() => {
4332
4519
  if (open) {
4333
4520
  setQuery("");
4334
4521
  setIndex(0);
4335
4522
  requestAnimationFrame(() => inputRef.current?.focus());
4336
4523
  }
4337
4524
  }, [open]);
4338
- useEffect6(() => {
4525
+ useEffect7(() => {
4339
4526
  const onKey = (e) => {
4340
4527
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") {
4341
4528
  e.preventDefault();
@@ -4564,7 +4751,7 @@ function CommandPalette() {
4564
4751
  return hay.includes(q);
4565
4752
  });
4566
4753
  }, [items, query]);
4567
- useEffect6(() => {
4754
+ useEffect7(() => {
4568
4755
  if (index >= filtered.length) setIndex(0);
4569
4756
  }, [filtered.length, index]);
4570
4757
  if (!open) return null;
@@ -4669,16 +4856,16 @@ function renderGroupedList(filtered, index, dispatch, setIndex) {
4669
4856
  // src/components/FilePicker.tsx
4670
4857
  import { expectDefined as expectDefined4 } from "@wrongstack/core";
4671
4858
  import { FileText, Folder } from "lucide-react";
4672
- import { useEffect as useEffect7, useRef as useRef4, useState as useState6 } from "react";
4859
+ import { useEffect as useEffect8, useRef as useRef5, useState as useState6 } from "react";
4673
4860
  import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
4674
4861
  function FilePicker({ query, onPick, onClose }) {
4675
4862
  const ws = useWebSocket();
4676
4863
  const wsUrl = useConfigStore((s) => s.wsUrl);
4677
4864
  const [files, setFiles] = useState6([]);
4678
4865
  const [index, setIndex] = useState6(0);
4679
- const debounceRef = useRef4(null);
4680
- const wantHandle = useRef4(null);
4681
- useEffect7(() => {
4866
+ const debounceRef = useRef5(null);
4867
+ const wantHandle = useRef5(null);
4868
+ useEffect8(() => {
4682
4869
  const client2 = getWSClient(wsUrl);
4683
4870
  const off = client2.on("files.list", (msg) => {
4684
4871
  const p = msg.payload;
@@ -4689,7 +4876,7 @@ function FilePicker({ query, onPick, onClose }) {
4689
4876
  });
4690
4877
  return () => off();
4691
4878
  }, [wsUrl]);
4692
- useEffect7(() => {
4879
+ useEffect8(() => {
4693
4880
  if (debounceRef.current) clearTimeout(debounceRef.current);
4694
4881
  debounceRef.current = setTimeout(() => {
4695
4882
  ws.client.listFiles(query, 50);
@@ -4698,7 +4885,7 @@ function FilePicker({ query, onPick, onClose }) {
4698
4885
  if (debounceRef.current) clearTimeout(debounceRef.current);
4699
4886
  };
4700
4887
  }, [query, ws.client]);
4701
- useEffect7(() => {
4888
+ useEffect8(() => {
4702
4889
  const onKey = (e) => {
4703
4890
  if (e.key === "ArrowDown") {
4704
4891
  e.preventDefault();
@@ -4987,7 +5174,7 @@ ${text}
4987
5174
  }
4988
5175
 
4989
5176
  // src/components/RefinePanel.tsx
4990
- import { useEffect as useEffect8, useRef as useRef5, useState as useState7 } from "react";
5177
+ import { useEffect as useEffect9, useRef as useRef6, useState as useState7 } from "react";
4991
5178
  import { Check, Edit3, Globe, X as X3 } from "lucide-react";
4992
5179
  import { Fragment, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
4993
5180
  function RefinePanel({
@@ -5001,9 +5188,9 @@ function RefinePanel({
5001
5188
  const [countdown, setCountdown] = useState7(autoSendDelayMs > 0 ? Math.ceil(autoSendDelayMs / 1e3) : null);
5002
5189
  const [editText, setEditText] = useState7(refined);
5003
5190
  const [isEditing, setIsEditing] = useState7(false);
5004
- const countdownRef = useRef5(null);
5005
- const panelRef = useRef5(null);
5006
- useEffect8(() => {
5191
+ const countdownRef = useRef6(null);
5192
+ const panelRef = useRef6(null);
5193
+ useEffect9(() => {
5007
5194
  if (autoSendDelayMs <= 0 || isEditing) return;
5008
5195
  countdownRef.current = setInterval(() => {
5009
5196
  setCountdown((prev) => {
@@ -5019,7 +5206,7 @@ function RefinePanel({
5019
5206
  if (countdownRef.current) clearInterval(countdownRef.current);
5020
5207
  };
5021
5208
  }, [autoSendDelayMs, isEditing, onDecision]);
5022
- useEffect8(() => {
5209
+ useEffect9(() => {
5023
5210
  const handleKeyDown = (e) => {
5024
5211
  if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
5025
5212
  switch (e.key) {
@@ -5065,7 +5252,7 @@ function RefinePanel({
5065
5252
  window.addEventListener("keydown", handleKeyDown);
5066
5253
  return () => window.removeEventListener("keydown", handleKeyDown);
5067
5254
  }, [isEditing, onDecision, refined, setRefinePanel]);
5068
- useEffect8(() => {
5255
+ useEffect9(() => {
5069
5256
  if (isEditing && panelRef.current) {
5070
5257
  const textarea = panelRef.current.querySelector("textarea");
5071
5258
  textarea?.focus();
@@ -5261,6 +5448,9 @@ function ChatInput({
5261
5448
  const refinePanel = useUIStore((s) => s.refinePanel);
5262
5449
  const toggleRefineEnabled = useUIStore((s) => s.toggleRefineEnabled);
5263
5450
  const setRefinePanel = useUIStore((s) => s.setRefinePanel);
5451
+ const setProcessMonitorOpen = useUIStore((s) => s.setProcessMonitorOpen);
5452
+ const setQueuePanelOpen = useUIStore((s) => s.setQueuePanelOpen);
5453
+ const { reset: resetAutoSubmitStreak } = useAutoSubmitStreak();
5264
5454
  const lastInputTokens = useSessionStore((s) => s.lastInputTokens);
5265
5455
  const maxContext = useSessionStore((s) => s.maxContext);
5266
5456
  const [input, setInput] = useState8("");
@@ -5269,8 +5459,8 @@ function ChatInput({
5269
5459
  const [atMention, setAtMention] = useState8(null);
5270
5460
  const [pasteHint, setPasteHint] = useState8(null);
5271
5461
  const [draggingOver, setDraggingOver] = useState8(false);
5272
- const textareaRef = useRef6(null);
5273
- const runSlashCommand = useCallback4(
5462
+ const textareaRef = useRef7(null);
5463
+ const runSlashCommand = useCallback5(
5274
5464
  (raw) => {
5275
5465
  const trimmed = raw.trim();
5276
5466
  const sp = trimmed.indexOf(" ");
@@ -5415,6 +5605,32 @@ function ChatInput({
5415
5605
  case "/next-steps":
5416
5606
  sendMsg("What are the next steps I should take? Be specific and actionable.");
5417
5607
  return true;
5608
+ case "/kill":
5609
+ case "/ps": {
5610
+ setProcessMonitorOpen(true);
5611
+ return true;
5612
+ }
5613
+ case "/queue": {
5614
+ const q = queue;
5615
+ if (q.length === 0) {
5616
+ addMessage({
5617
+ role: "assistant",
5618
+ content: "\u{1F4CB} **Message Queue** \u2014 empty.\n\nType while the agent is running to queue messages; they are sent automatically when the agent finishes."
5619
+ });
5620
+ } else {
5621
+ const lines = [`\u{1F4CB} **Message Queue** (${q.length} queued)`, ""];
5622
+ q.forEach((item, i) => {
5623
+ const preview = item.length > 80 ? `${item.slice(0, 77)}\u2026` : item;
5624
+ lines.push(`${i + 1}. ${preview}`);
5625
+ });
5626
+ lines.push("", "_Use `/queue open` to manage, or `/queue clear` to wipe._");
5627
+ addMessage({ role: "assistant", content: lines.join("\n") });
5628
+ }
5629
+ if (args.toLowerCase() === "open") {
5630
+ setQueuePanelOpen(true);
5631
+ }
5632
+ return true;
5633
+ }
5418
5634
  case "/next": {
5419
5635
  const narg = args.trim().toLowerCase();
5420
5636
  if (!narg || narg === "list" || narg === "ls" || narg === "show") return handleNextList();
@@ -5432,17 +5648,20 @@ function ChatInput({
5432
5648
  addMessage,
5433
5649
  clearMessages,
5434
5650
  client2,
5651
+ queue,
5435
5652
  sendAbort,
5436
5653
  setLoading,
5437
5654
  setCurrentView,
5438
5655
  toggleRefineEnabled,
5656
+ setProcessMonitorOpen,
5657
+ setQueuePanelOpen,
5439
5658
  ws,
5440
5659
  onOpenBreakdown
5441
5660
  ]
5442
5661
  );
5443
- const NEXT_STEPS_RE2 = /💡\s*Next steps?\s*\n+((?:\d+\.\s+.+\n?)+)/i;
5662
+ const NEXT_STEPS_RE = /💡\s*Next steps?\s*\n+((?:\d+\.\s+.+\n?)+)/i;
5444
5663
  function parseNextStepsFromContent(content) {
5445
- const match = NEXT_STEPS_RE2.exec(content);
5664
+ const match = NEXT_STEPS_RE.exec(content);
5446
5665
  if (!match?.[1]) return [];
5447
5666
  const steps = [];
5448
5667
  for (const line of match[1].split("\n").filter(Boolean)) {
@@ -5515,10 +5734,10 @@ function ChatInput({
5515
5734
  return true;
5516
5735
  }
5517
5736
  const slashSuggestions = input.startsWith("/") && !input.includes(" ") ? matchSlash(input) : [];
5518
- useEffect9(() => {
5737
+ useEffect10(() => {
5519
5738
  if (slashIndex >= slashSuggestions.length) setSlashIndex(0);
5520
5739
  }, [slashSuggestions.length, slashIndex]);
5521
- const _clearTextarea = useCallback4(() => {
5740
+ const _clearTextarea = useCallback5(() => {
5522
5741
  const ta = textareaRef.current;
5523
5742
  if (ta) {
5524
5743
  ta.value = "";
@@ -5529,10 +5748,13 @@ function ChatInput({
5529
5748
  }
5530
5749
  }
5531
5750
  }, [isLoading]);
5532
- const handleSubmit = useCallback4(
5751
+ const handleSubmit = useCallback5(
5533
5752
  async (e) => {
5534
5753
  e.preventDefault();
5535
- if (!input.trim()) return;
5754
+ resetAutoSubmitStreak();
5755
+ if (!input.trim() && !pendingImageRef.current) return;
5756
+ const pendingImage = pendingImageRef.current;
5757
+ pendingImageRef.current = null;
5536
5758
  const content = input.trim();
5537
5759
  if (content.startsWith("/") && runSlashCommand(content)) {
5538
5760
  pushPrompt(content);
@@ -5547,7 +5769,10 @@ function ChatInput({
5547
5769
  pushPrompt(content);
5548
5770
  _clearTextarea();
5549
5771
  if (isLoading) {
5550
- enqueue(content);
5772
+ const queued = pendingImage ? `![pasted image](${pendingImage})
5773
+
5774
+ ${content}` : content;
5775
+ enqueue(queued);
5551
5776
  return;
5552
5777
  }
5553
5778
  try {
@@ -5563,15 +5788,18 @@ function ChatInput({
5563
5788
  });
5564
5789
  refineModel(content);
5565
5790
  } else {
5566
- addMessage({ role: "user", content });
5791
+ const fullContent = pendingImage ? `![pasted image](${pendingImage})
5792
+
5793
+ ${content}` : content;
5794
+ addMessage({ role: "user", content: fullContent });
5567
5795
  setLoading(true);
5568
- sendMessage(content);
5796
+ sendMessage(content, pendingImage ?? void 0);
5569
5797
  }
5570
5798
  } else {
5571
5799
  console.warn(JSON.stringify({ level: "warn", event: "ws_send_failed", reason: "not_connected", timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
5572
5800
  }
5573
5801
  } catch (err) {
5574
- console.warn(JSON.stringify({ level: "warn", event: "ws_send_error", error: err instanceof Error ? err.message : String(err), timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
5802
+ console.warn(JSON.stringify({ level: "warn", event: "ws_send_error", error: toErrorMessage(err), timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
5575
5803
  setLoading(false);
5576
5804
  }
5577
5805
  },
@@ -5588,14 +5816,15 @@ function ChatInput({
5588
5816
  setLoading,
5589
5817
  runSlashCommand,
5590
5818
  pushPrompt,
5591
- _clearTextarea
5819
+ _clearTextarea,
5820
+ resetAutoSubmitStreak
5592
5821
  ]
5593
5822
  );
5594
- const handleAbort = useCallback4(() => {
5823
+ const handleAbort = useCallback5(() => {
5595
5824
  sendAbort();
5596
5825
  setLoading(false);
5597
5826
  }, [sendAbort, setLoading]);
5598
- const handleStopAndEdit = useCallback4(() => {
5827
+ const handleStopAndEdit = useCallback5(() => {
5599
5828
  sendAbort();
5600
5829
  setLoading(false);
5601
5830
  const all = useChatStore.getState().messages;
@@ -5616,7 +5845,7 @@ function ChatInput({
5616
5845
  }
5617
5846
  }
5618
5847
  }, [sendAbort, setLoading]);
5619
- const handleKeyDown = useCallback4(
5848
+ const handleKeyDown = useCallback5(
5620
5849
  (e) => {
5621
5850
  if (slashSuggestions.length === 0 && !atMention && promptHistory.length > 0) {
5622
5851
  if (e.key === "ArrowUp") {
@@ -5703,6 +5932,33 @@ function ChatInput({
5703
5932
  },
5704
5933
  [slashSuggestions, slashIndex, atMention, promptHistory, historyIdx, input, runSlashCommand, handleSubmit]
5705
5934
  );
5935
+ const pendingImageRef = useRef7(null);
5936
+ useEffect10(() => {
5937
+ const ta = textareaRef.current;
5938
+ if (!ta) return;
5939
+ const onPaste = async (e) => {
5940
+ const items = e.clipboardData?.items;
5941
+ if (!items) return;
5942
+ for (const item of items) {
5943
+ if (item.type.startsWith("image/")) {
5944
+ e.preventDefault();
5945
+ try {
5946
+ const blob = item.getAsFile();
5947
+ if (!blob) continue;
5948
+ const reader = new FileReader();
5949
+ reader.onload = () => {
5950
+ pendingImageRef.current = reader.result;
5951
+ };
5952
+ reader.readAsDataURL(blob);
5953
+ } catch {
5954
+ }
5955
+ return;
5956
+ }
5957
+ }
5958
+ };
5959
+ ta.addEventListener("paste", onPaste);
5960
+ return () => ta.removeEventListener("paste", onPaste);
5961
+ }, []);
5706
5962
  const adjustTextareaHeight = () => {
5707
5963
  const textarea = textareaRef.current;
5708
5964
  if (textarea) {
@@ -6130,7 +6386,7 @@ function ChatInput({
6130
6386
 
6131
6387
  // src/components/CheckpointTimeline.tsx
6132
6388
  import { Clock as Clock4, History, Rewind, X as X4 } from "lucide-react";
6133
- import { useCallback as useCallback5, useEffect as useEffect10, useRef as useRef7, useState as useState9 } from "react";
6389
+ import { useCallback as useCallback6, useEffect as useEffect11, useRef as useRef8, useState as useState9 } from "react";
6134
6390
  import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
6135
6391
  function CheckpointTimeline({
6136
6392
  open,
@@ -6140,8 +6396,8 @@ function CheckpointTimeline({
6140
6396
  const [checkpoints, setCheckpoints] = useState9([]);
6141
6397
  const [rewinding, setRewinding] = useState9(false);
6142
6398
  const ws = useWebSocket();
6143
- const offRef = useRef7(null);
6144
- useEffect10(() => {
6399
+ const offRef = useRef8(null);
6400
+ useEffect11(() => {
6145
6401
  if (!open || !ws.client?.isConnected) return;
6146
6402
  ws.client.send?.({ type: "session.checkpoints" });
6147
6403
  offRef.current = ws.client.on?.("session.checkpoints", (msg) => {
@@ -6152,7 +6408,7 @@ function CheckpointTimeline({
6152
6408
  offRef.current?.();
6153
6409
  };
6154
6410
  }, [open, ws.client]);
6155
- const handleRewind = useCallback5(
6411
+ const handleRewind = useCallback6(
6156
6412
  async (index) => {
6157
6413
  setRewinding(true);
6158
6414
  ws.client.send?.({ type: "session.rewind", payload: { checkpointIndex: index } });
@@ -6276,7 +6532,7 @@ function CheckpointTimeline({
6276
6532
  // src/components/ContextModePicker.tsx
6277
6533
  import { expectDefined as expectDefined7 } from "@wrongstack/core";
6278
6534
  import { Check as Check2, ChevronDown as ChevronDown2, Gauge, Wrench as Wrench2, Zap as Zap2, FileSearch } from "lucide-react";
6279
- import { useCallback as useCallback6, useEffect as useEffect11, useRef as useRef8, useState as useState10 } from "react";
6535
+ import { useCallback as useCallback7, useEffect as useEffect12, useRef as useRef9, useState as useState10 } from "react";
6280
6536
  import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
6281
6537
  var FALLBACK_MODES = [
6282
6538
  {
@@ -6294,12 +6550,12 @@ function ContextModePicker() {
6294
6550
  const { listContextModes, switchContextMode, client: client2 } = useWebSocket();
6295
6551
  const [open, setOpen] = useState10(false);
6296
6552
  const [opsOpen, setOpsOpen] = useState10(false);
6297
- const rootRef = useRef8(null);
6298
- const opsRef = useRef8(null);
6299
- useEffect11(() => {
6553
+ const rootRef = useRef9(null);
6554
+ const opsRef = useRef9(null);
6555
+ useEffect12(() => {
6300
6556
  if (open) listContextModes();
6301
6557
  }, [open, listContextModes]);
6302
- useEffect11(() => {
6558
+ useEffect12(() => {
6303
6559
  if (!open) return;
6304
6560
  const onClick = (e) => {
6305
6561
  if (!rootRef.current?.contains(e.target)) setOpen(false);
@@ -6314,7 +6570,7 @@ function ContextModePicker() {
6314
6570
  document.removeEventListener("keydown", onKey);
6315
6571
  };
6316
6572
  }, [open]);
6317
- useEffect11(() => {
6573
+ useEffect12(() => {
6318
6574
  if (!opsOpen) return;
6319
6575
  const onClick = (e) => {
6320
6576
  if (!opsRef.current?.contains(e.target)) setOpsOpen(false);
@@ -6322,12 +6578,12 @@ function ContextModePicker() {
6322
6578
  document.addEventListener("mousedown", onClick);
6323
6579
  return () => document.removeEventListener("mousedown", onClick);
6324
6580
  }, [opsOpen]);
6325
- const compact = useCallback6((aggressive) => {
6581
+ const compact = useCallback7((aggressive) => {
6326
6582
  client2?.compactContext?.(aggressive);
6327
6583
  setOpsOpen(false);
6328
6584
  setOpen(false);
6329
6585
  }, [client2]);
6330
- const repair = useCallback6(() => {
6586
+ const repair = useCallback7(() => {
6331
6587
  client2?.repairContext?.();
6332
6588
  setOpsOpen(false);
6333
6589
  setOpen(false);
@@ -6731,7 +6987,7 @@ function ContextFillBar({
6731
6987
 
6732
6988
  // src/components/ContextBreakdownModal.tsx
6733
6989
  import { AlertTriangle as AlertTriangle2, FileText as FileText2, MessageSquare as MessageSquare2, RefreshCw, Wrench as Wrench3, X as X5 } from "lucide-react";
6734
- import { useEffect as useEffect12, useState as useState11 } from "react";
6990
+ import { useEffect as useEffect13, useState as useState11 } from "react";
6735
6991
  import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
6736
6992
  function ContextBreakdownModal({ open, onClose }) {
6737
6993
  const wsUrl = useConfigStore((s) => s.wsUrl);
@@ -6739,7 +6995,7 @@ function ContextBreakdownModal({ open, onClose }) {
6739
6995
  const [data, setData] = useState11(null);
6740
6996
  const [loading, setLoading] = useState11(false);
6741
6997
  const [error, setError] = useState11(null);
6742
- useEffect12(() => {
6998
+ useEffect13(() => {
6743
6999
  if (!open) {
6744
7000
  setData(null);
6745
7001
  setError(null);
@@ -6773,7 +7029,7 @@ function ContextBreakdownModal({ open, onClose }) {
6773
7029
  unsubscribe();
6774
7030
  };
6775
7031
  }, [open, wsUrl]);
6776
- useEffect12(() => {
7032
+ useEffect13(() => {
6777
7033
  if (!open) return;
6778
7034
  const onKey = (e) => {
6779
7035
  if (e.key === "Escape") onClose();
@@ -6931,7 +7187,7 @@ function ContextBreakdownModal({ open, onClose }) {
6931
7187
  }
6932
7188
 
6933
7189
  // src/components/CostChip.tsx
6934
- import { useEffect as useEffect13, useRef as useRef9, useState as useState12 } from "react";
7190
+ import { useEffect as useEffect14, useRef as useRef10, useState as useState12 } from "react";
6935
7191
  import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
6936
7192
  function CostChip() {
6937
7193
  const cost = useSessionStore((s) => s.cost);
@@ -6940,8 +7196,8 @@ function CostChip() {
6940
7196
  const cacheReadCost = useSessionStore((s) => s.cacheReadCost);
6941
7197
  const messages = useChatStore((s) => s.messages);
6942
7198
  const [open, setOpen] = useState12(false);
6943
- const rootRef = useRef9(null);
6944
- useEffect13(() => {
7199
+ const rootRef = useRef10(null);
7200
+ useEffect14(() => {
6945
7201
  if (!open) return;
6946
7202
  const onClick = (e) => {
6947
7203
  if (!rootRef.current?.contains(e.target)) setOpen(false);
@@ -7190,7 +7446,7 @@ import {
7190
7446
  User,
7191
7447
  XCircle as XCircle4
7192
7448
  } from "lucide-react";
7193
- import { memo as memo3, useState as useState18 } from "react";
7449
+ import { memo as memo3, useMemo as useMemo5, useState as useState18 } from "react";
7194
7450
  import ReactMarkdown from "react-markdown";
7195
7451
  import remarkGfm from "remark-gfm";
7196
7452
 
@@ -7547,7 +7803,7 @@ import { useState as useState15 } from "react";
7547
7803
 
7548
7804
  // src/components/MessageBubble/utils.tsx
7549
7805
  import { Check as Check3, Copy, FileCode2 } from "lucide-react";
7550
- import { useCallback as useCallback7, useMemo as useMemo4, useState as useState14 } from "react";
7806
+ import { useCallback as useCallback8, useMemo as useMemo4, useState as useState14 } from "react";
7551
7807
  import rehypeHighlight from "rehype-highlight";
7552
7808
  import { jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
7553
7809
  async function copyToClipboard(text) {
@@ -7600,7 +7856,7 @@ function formatToolDuration(ms) {
7600
7856
  var rehypePlugins = [rehypeHighlight];
7601
7857
  function CodeCopyButton({ text }) {
7602
7858
  const [copied, setCopied] = useState14(false);
7603
- const handleCopy = useCallback7(async () => {
7859
+ const handleCopy = useCallback8(async () => {
7604
7860
  const ok = await copyToClipboard(text);
7605
7861
  if (ok) {
7606
7862
  setCopied(true);
@@ -7626,25 +7882,17 @@ function CodeCopyButton({ text }) {
7626
7882
  );
7627
7883
  }
7628
7884
  var markdownComponents = {
7629
- next_steps({ children }) {
7630
- const rawText = typeof children === "string" ? children.trim() : "";
7631
- if (!rawText) return null;
7632
- const steps = parseNextSteps(rawText);
7633
- if (steps.length === 0) return null;
7634
- return /* @__PURE__ */ jsx23(
7635
- NextStepsBar,
7636
- {
7637
- steps,
7638
- onAutoSubmit: (text) => {
7639
- fillInput(text);
7640
- const form = document.querySelector('form[class*="flex items-end gap-2"]');
7641
- if (form) {
7642
- form.dispatchEvent(new Event("submit", { bubbles: true, cancelable: true }));
7643
- }
7644
- }
7645
- }
7646
- );
7647
- },
7885
+ // NOTE: <next_steps> is parsed and rendered in MessageBubble/index.tsx
7886
+ // (post-render, via parseNextSteps + NextStepsBar). We do NOT register a
7887
+ // custom component here for two reasons:
7888
+ // 1. react-markdown v10's micromark parser doesn't actually dispatch
7889
+ // <next_steps> (or any underscored tag) to the components map — they
7890
+ // fall through as raw HTML, which is exactly what the previous code
7891
+ // was trying (and failing) to catch.
7892
+ // 2. The MessageBubble path strips the block before passing content to
7893
+ // react-markdown, so this handler is unreachable in practice.
7894
+ // Leaving the comment here so future contributors know this was a
7895
+ // deliberate decision, not an oversight.
7648
7896
  code({
7649
7897
  inline,
7650
7898
  className,
@@ -7859,7 +8107,20 @@ var MessageBubble = memo3(function MessageBubble2({
7859
8107
  const cacheReadCost = useSessionStore((s) => s.cacheReadCost);
7860
8108
  const localPrefs = useLocalPrefs();
7861
8109
  const { autonomy, yolo } = localPrefs;
8110
+ const { canAutoSubmit, recordAutoSubmit, capWarned } = useAutoSubmitStreak();
8111
+ const autoProceedMaxIterations = localPrefs.autoProceedMaxIterations;
8112
+ const canAutoSubmitNow = autoProceedMaxIterations <= 0 || canAutoSubmit();
7862
8113
  const handleAutoSubmit = (text) => {
8114
+ if (!canAutoSubmit()) {
8115
+ if (!capWarned) {
8116
+ addMessage({
8117
+ role: "assistant",
8118
+ content: "\u26A0\uFE0F _Auto-proceed paused \u2014 maximum consecutive automatic turns reached. Type anything to continue (autonomy stays on)._"
8119
+ });
8120
+ }
8121
+ return;
8122
+ }
8123
+ recordAutoSubmit();
7863
8124
  addMessage({ role: "user", content: text });
7864
8125
  setLoading(true);
7865
8126
  const client2 = getWSClient(wsUrl);
@@ -7875,6 +8136,10 @@ var MessageBubble = memo3(function MessageBubble2({
7875
8136
  }
7876
8137
  return false;
7877
8138
  })();
8139
+ const nextStepsResult = useMemo5(
8140
+ () => isLatestAssistant && message.content ? parseNextSteps(message.content) : null,
8141
+ [isLatestAssistant, message.content]
8142
+ );
7878
8143
  const regenerate = () => {
7879
8144
  const all = useChatStore.getState().messages;
7880
8145
  const idx = all.findIndex((m) => m.id === message.id);
@@ -8034,21 +8299,21 @@ var MessageBubble = memo3(function MessageBubble2({
8034
8299
  /* @__PURE__ */ jsx27("button", { type: "button", onClick: saveEdit, disabled: !editValue.trim(), className: "text-xs px-2 py-0.5 rounded bg-primary-foreground text-primary disabled:opacity-50", children: "Save & resend" })
8035
8300
  ] })
8036
8301
  ] })
8037
- ] }) : /* @__PURE__ */ jsx27("div", { className: cn("text-sm leading-relaxed markdown-content", message.streaming && "streaming-cursor"), children: message.content ? showRaw && message.role === "assistant" ? /* @__PURE__ */ jsx27("pre", { className: "whitespace-pre-wrap break-words font-mono text-xs leading-relaxed text-foreground/90 max-h-[40rem] overflow-auto", children: message.content }) : message.role === "assistant" && message.isError ? /* @__PURE__ */ jsx27(ErrorBodyWithStack, { text: message.content }) : /* @__PURE__ */ jsx27(ReactMarkdown, { remarkPlugins: [remarkGfm], rehypePlugins, components: markdownComponents, children: message.content }) : message.streaming ? /* @__PURE__ */ jsx27("span", { className: "inline-block animate-pulse text-muted-foreground", children: "Typing..." }) : /* @__PURE__ */ jsx27("span", { className: "text-muted-foreground italic", children: "No content" }) }) }),
8038
- isLatestAssistant && message.content && (() => {
8039
- const steps = parseNextSteps(message.content);
8040
- if (steps.length === 0) return null;
8041
- return /* @__PURE__ */ jsx27(
8042
- NextStepsBar,
8043
- {
8044
- steps,
8045
- yoloMode: yolo,
8046
- autoMode: autonomy === "auto",
8047
- autoDelayMs: localPrefs.autonomyDelayMs,
8048
- onAutoSubmit: handleAutoSubmit
8049
- }
8050
- );
8051
- })(),
8302
+ ] }) : (() => {
8303
+ const renderedContent = nextStepsResult ? nextStepsResult.stripped : message.content;
8304
+ return /* @__PURE__ */ jsx27("div", { className: cn("text-sm leading-relaxed markdown-content", message.streaming && "streaming-cursor"), children: renderedContent ? showRaw && message.role === "assistant" ? /* @__PURE__ */ jsx27("pre", { className: "whitespace-pre-wrap break-words font-mono text-xs leading-relaxed text-foreground/90 max-h-[40rem] overflow-auto", children: message.content }) : message.role === "assistant" && message.isError ? /* @__PURE__ */ jsx27(ErrorBodyWithStack, { text: message.content }) : /* @__PURE__ */ jsx27(ReactMarkdown, { remarkPlugins: [remarkGfm], rehypePlugins, components: markdownComponents, children: renderedContent }) : message.streaming ? /* @__PURE__ */ jsx27("span", { className: "inline-block animate-pulse text-muted-foreground", children: "Typing..." }) : /* @__PURE__ */ jsx27("span", { className: "text-muted-foreground italic", children: "No content" }) });
8305
+ })() }),
8306
+ nextStepsResult && nextStepsResult.steps.length > 0 && /* @__PURE__ */ jsx27(
8307
+ NextStepsBar,
8308
+ {
8309
+ steps: nextStepsResult.steps,
8310
+ yoloMode: yolo,
8311
+ autoMode: autonomy === "auto",
8312
+ autoDelayMs: localPrefs.autonomyDelayMs,
8313
+ onAutoSubmit: handleAutoSubmit,
8314
+ canAutoSubmit: canAutoSubmitNow
8315
+ }
8316
+ ),
8052
8317
  /* @__PURE__ */ jsxs25("div", { className: cn("flex items-center gap-2 px-1", isUser ? "flex-row-reverse" : "flex-row"), children: [
8053
8318
  /* @__PURE__ */ jsx27("span", { className: "text-xs text-muted-foreground/50", children: new Date(message.timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) }),
8054
8319
  /* @__PURE__ */ jsx27("span", { className: "w-px h-3 bg-border/60 shrink-0", "aria-hidden": true }),
@@ -8124,18 +8389,18 @@ var MessageBubble = memo3(function MessageBubble2({
8124
8389
 
8125
8390
  // src/components/ModePicker.tsx
8126
8391
  import { Check as Check5, ChevronDown as ChevronDown5 } from "lucide-react";
8127
- import { useEffect as useEffect14, useRef as useRef10, useState as useState19 } from "react";
8392
+ import { useEffect as useEffect15, useRef as useRef11, useState as useState19 } from "react";
8128
8393
  import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
8129
8394
  function ModePicker() {
8130
8395
  const mode = useSessionStore((s) => s.mode);
8131
8396
  const modes = useSessionStore((s) => s.modes);
8132
8397
  const { listModes, switchMode } = useWebSocket();
8133
8398
  const [open, setOpen] = useState19(false);
8134
- const rootRef = useRef10(null);
8135
- useEffect14(() => {
8399
+ const rootRef = useRef11(null);
8400
+ useEffect15(() => {
8136
8401
  if (open) listModes();
8137
8402
  }, [open, listModes]);
8138
- useEffect14(() => {
8403
+ useEffect15(() => {
8139
8404
  if (!open) return;
8140
8405
  const onClick = (e) => {
8141
8406
  if (!rootRef.current?.contains(e.target)) setOpen(false);
@@ -8207,7 +8472,7 @@ function ModePicker() {
8207
8472
 
8208
8473
  // src/components/ProcessMonitor.tsx
8209
8474
  import { Shield, Square as Square3, Terminal as Terminal2, X as X6 } from "lucide-react";
8210
- import { useCallback as useCallback8, useEffect as useEffect15, useRef as useRef11, useState as useState20 } from "react";
8475
+ import { useCallback as useCallback9, useEffect as useEffect16, useRef as useRef12, useState as useState20 } from "react";
8211
8476
  import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
8212
8477
  function ProcessMonitor({
8213
8478
  open,
@@ -8216,9 +8481,9 @@ function ProcessMonitor({
8216
8481
  }) {
8217
8482
  const [processes, setProcesses] = useState20([]);
8218
8483
  const ws = useWebSocket();
8219
- const offRef = useRef11(null);
8220
- const pollRef = useRef11(null);
8221
- useEffect15(() => {
8484
+ const offRef = useRef12(null);
8485
+ const pollRef = useRef12(null);
8486
+ useEffect16(() => {
8222
8487
  if (!open || !ws.client?.isConnected) return;
8223
8488
  ws.client.send?.({ type: "process.list" });
8224
8489
  offRef.current = ws.client.on?.("process.list", (msg) => {
@@ -8233,13 +8498,13 @@ function ProcessMonitor({
8233
8498
  if (pollRef.current) clearInterval(pollRef.current);
8234
8499
  };
8235
8500
  }, [open, ws.client]);
8236
- const handleKill = useCallback8(
8501
+ const handleKill = useCallback9(
8237
8502
  (pid) => {
8238
8503
  ws.client.send?.({ type: "process.kill", payload: { pid } });
8239
8504
  },
8240
8505
  [ws.client]
8241
8506
  );
8242
- const handleKillAll = useCallback8(() => {
8507
+ const handleKillAll = useCallback9(() => {
8243
8508
  ws.client.send?.({ type: "process.killAll" });
8244
8509
  }, [ws.client]);
8245
8510
  const running = processes.filter((p) => p.status === "running");
@@ -8365,7 +8630,7 @@ function ProcessMonitor({
8365
8630
 
8366
8631
  // src/components/SearchOverlay.tsx
8367
8632
  import { ArrowDown, ArrowUp, Search as Search2, X as X7 } from "lucide-react";
8368
- import { useEffect as useEffect16, useMemo as useMemo5, useRef as useRef12, useState as useState21 } from "react";
8633
+ import { useEffect as useEffect17, useMemo as useMemo6, useRef as useRef13, useState as useState21 } from "react";
8369
8634
  import { jsx as jsx30, jsxs as jsxs28 } from "react/jsx-runtime";
8370
8635
  var HIGHLIGHT_STYLES = `
8371
8636
  ::highlight(chat-search) { background-color: hsl(var(--primary) / 0.3); color: inherit; }
@@ -8378,13 +8643,13 @@ function SearchOverlay() {
8378
8643
  const setQuery = useUIStore((s) => s.setSearchQuery);
8379
8644
  const messages = useChatStore((s) => s.messages);
8380
8645
  const requestScrollToMessage = useUIStore((s) => s.requestScrollToMessage);
8381
- const inputRef = useRef12(null);
8646
+ const inputRef = useRef13(null);
8382
8647
  const [activeHit, setActiveHit] = useState21(0);
8383
8648
  const [repaintNonce, setRepaintNonce] = useState21(0);
8384
- useEffect16(() => {
8649
+ useEffect17(() => {
8385
8650
  if (open) requestAnimationFrame(() => inputRef.current?.focus());
8386
8651
  }, [open]);
8387
- useEffect16(() => {
8652
+ useEffect17(() => {
8388
8653
  const style = document.createElement("style");
8389
8654
  style.textContent = HIGHLIGHT_STYLES;
8390
8655
  document.head.appendChild(style);
@@ -8392,7 +8657,7 @@ function SearchOverlay() {
8392
8657
  document.head.removeChild(style);
8393
8658
  };
8394
8659
  }, []);
8395
- const hits = useMemo5(() => {
8660
+ const hits = useMemo6(() => {
8396
8661
  const q = query.trim().toLowerCase();
8397
8662
  if (!q) return [];
8398
8663
  return messages.filter((m) => {
@@ -8402,10 +8667,10 @@ function SearchOverlay() {
8402
8667
  return m.content.toLowerCase().includes(q);
8403
8668
  }).map((m) => m.id);
8404
8669
  }, [messages, query]);
8405
- useEffect16(() => {
8670
+ useEffect17(() => {
8406
8671
  if (activeHit >= hits.length) setActiveHit(0);
8407
8672
  }, [hits, activeHit]);
8408
- useEffect16(() => {
8673
+ useEffect17(() => {
8409
8674
  const win = window;
8410
8675
  const highlights = win.CSS?.highlights;
8411
8676
  const HighlightCtor = win.Highlight;
@@ -8459,7 +8724,7 @@ function SearchOverlay() {
8459
8724
  }
8460
8725
  return clear;
8461
8726
  }, [query, hits, activeHit, open, repaintNonce]);
8462
- useEffect16(() => {
8727
+ useEffect17(() => {
8463
8728
  const id = hits[activeHit];
8464
8729
  if (!id) return;
8465
8730
  requestScrollToMessage(id);
@@ -8618,7 +8883,7 @@ import {
8618
8883
  Sparkles as Sparkles3,
8619
8884
  Zap as Zap3
8620
8885
  } from "lucide-react";
8621
- import { useEffect as useEffect17, useState as useState23 } from "react";
8886
+ import { useEffect as useEffect18, useState as useState23 } from "react";
8622
8887
  import { Fragment as Fragment7, jsx as jsx32, jsxs as jsxs30 } from "react/jsx-runtime";
8623
8888
  var CARDS = [
8624
8889
  {
@@ -8694,7 +8959,7 @@ function WelcomeScreen() {
8694
8959
  const wsUrl = useConfigStore((s) => s.wsUrl);
8695
8960
  const setCurrentView = useUIStore((s) => s.setCurrentView);
8696
8961
  const [savedCount, setSavedCount] = useState23(void 0);
8697
- useEffect17(() => {
8962
+ useEffect18(() => {
8698
8963
  if (!wsConnected) return;
8699
8964
  const client2 = getWSClient(wsUrl);
8700
8965
  const off = client2.on("providers.saved", (msg) => {
@@ -8710,7 +8975,7 @@ function WelcomeScreen() {
8710
8975
  const recentPrompts = promptHistory.slice(0, 6);
8711
8976
  const { listSessions, resumeSession } = useWebSocket();
8712
8977
  const historyEntries = useHistoryStore((s) => s.entries);
8713
- useEffect17(() => {
8978
+ useEffect18(() => {
8714
8979
  if (wsConnected && historyEntries.length === 0) listSessions(10);
8715
8980
  }, [wsConnected]);
8716
8981
  const sessionNicknames = useUIStore((s) => s.sessionNicknames);
@@ -8958,8 +9223,8 @@ function ChatView() {
8958
9223
  const [titleDraft, setTitleDraft] = useState24("");
8959
9224
  const historyEntries = useHistoryStore((s) => s.entries);
8960
9225
  const [switcherOpen, setSwitcherOpen] = useState24(false);
8961
- const switcherRef = useRef13(null);
8962
- useEffect18(() => {
9226
+ const switcherRef = useRef14(null);
9227
+ useEffect19(() => {
8963
9228
  if (!switcherOpen) return;
8964
9229
  const onClick = (e) => {
8965
9230
  if (!switcherRef.current?.contains(e.target)) setSwitcherOpen(false);
@@ -8975,11 +9240,11 @@ function ChatView() {
8975
9240
  };
8976
9241
  }, [switcherOpen]);
8977
9242
  const { provider, model } = useConfigStore();
8978
- const vlistRef = useRef13(null);
8979
- const rows = useMemo6(() => buildChatRows(messages), [messages]);
8980
- const childCountRef = useRef13(0);
9243
+ const vlistRef = useRef14(null);
9244
+ const rows = useMemo7(() => buildChatRows(messages), [messages]);
9245
+ const childCountRef = useRef14(0);
8981
9246
  childCountRef.current = rows.length + 1;
8982
- const rowIndexById = useMemo6(() => {
9247
+ const rowIndexById = useMemo7(() => {
8983
9248
  const map = /* @__PURE__ */ new Map();
8984
9249
  rows.forEach((row, i) => {
8985
9250
  if (row.kind === "user") map.set(row.message.id, i);
@@ -8994,7 +9259,7 @@ function ChatView() {
8994
9259
  }, [rows]);
8995
9260
  const scrollTarget = useUIStore((s) => s.scrollTarget);
8996
9261
  const autonomy = useLocalPrefs((s) => s.autonomy);
8997
- const handleAutonomyChange = useCallback9((mode) => {
9262
+ const handleAutonomyChange = useCallback10((mode) => {
8998
9263
  useLocalPrefs.getState().set({ autonomy: mode });
8999
9264
  const ws = getWSClient();
9000
9265
  ws?.send?.({ type: "autonomy.switch", payload: { mode } });
@@ -9002,7 +9267,7 @@ function ChatView() {
9002
9267
  const [processOpen, setProcessOpen] = useState24(false);
9003
9268
  const [checkpointOpen, setCheckpointOpen] = useState24(false);
9004
9269
  const [breakdownOpen, setBreakdownOpen] = useState24(false);
9005
- useEffect18(() => {
9270
+ useEffect19(() => {
9006
9271
  const handler = () => setBreakdownOpen(true);
9007
9272
  document.addEventListener("open:context-breakdown", handler);
9008
9273
  return () => document.removeEventListener("open:context-breakdown", handler);
@@ -9012,8 +9277,8 @@ function ChatView() {
9012
9277
  const [pinnedToBottom, setPinnedToBottom] = useState24(true);
9013
9278
  const [unreadCount, setUnreadCount] = useState24(0);
9014
9279
  const [scrolledDeep, setScrolledDeep] = useState24(false);
9015
- const lastSeenCount = useRef13(messages.length);
9016
- const handleScroll = useCallback9(() => {
9280
+ const lastSeenCount = useRef14(messages.length);
9281
+ const handleScroll = useCallback10(() => {
9017
9282
  const h = vlistRef.current;
9018
9283
  if (!h) return;
9019
9284
  const dist = h.scrollSize - h.scrollOffset - h.viewportSize;
@@ -9025,7 +9290,7 @@ function ChatView() {
9025
9290
  }
9026
9291
  setScrolledDeep(h.scrollOffset > h.viewportSize && h.scrollSize > h.viewportSize * 2.5);
9027
9292
  }, []);
9028
- const handleHistorySelect = useCallback9(
9293
+ const handleHistorySelect = useCallback10(
9029
9294
  (sessionId2) => {
9030
9295
  const ws = getWSClient();
9031
9296
  ws?.resumeSession?.(sessionId2);
@@ -9033,7 +9298,7 @@ function ChatView() {
9033
9298
  },
9034
9299
  []
9035
9300
  );
9036
- useEffect18(() => {
9301
+ useEffect19(() => {
9037
9302
  const h = vlistRef.current;
9038
9303
  if (!h) return;
9039
9304
  if (pinnedToBottom) {
@@ -9044,7 +9309,7 @@ function ChatView() {
9044
9309
  if (delta > 0) setUnreadCount(delta);
9045
9310
  }
9046
9311
  }, [messages, pinnedToBottom]);
9047
- useEffect18(() => {
9312
+ useEffect19(() => {
9048
9313
  setPinnedToBottom(true);
9049
9314
  setUnreadCount(0);
9050
9315
  lastSeenCount.current = useChatStore.getState().messages.length;
@@ -9052,25 +9317,25 @@ function ChatView() {
9052
9317
  vlistRef.current?.scrollToIndex(childCountRef.current - 1, { align: "end" });
9053
9318
  });
9054
9319
  }, [sessionId]);
9055
- useEffect18(() => {
9320
+ useEffect19(() => {
9056
9321
  if (!scrollTarget) return;
9057
9322
  const idx = rowIndexById.get(scrollTarget.id);
9058
9323
  if (idx === void 0) return;
9059
9324
  vlistRef.current?.scrollToIndex(idx, { align: "center", smooth: true });
9060
9325
  }, [scrollTarget, rowIndexById]);
9061
- const scrollToBottom = useCallback9(() => {
9326
+ const scrollToBottom = useCallback10(() => {
9062
9327
  vlistRef.current?.scrollToIndex(childCountRef.current - 1, { align: "end", smooth: true });
9063
9328
  setPinnedToBottom(true);
9064
9329
  setUnreadCount(0);
9065
9330
  lastSeenCount.current = useChatStore.getState().messages.length;
9066
9331
  }, []);
9067
- const scrollToTop = useCallback9(() => {
9332
+ const scrollToTop = useCallback10(() => {
9068
9333
  vlistRef.current?.scrollToIndex(0, { align: "start", smooth: true });
9069
9334
  }, []);
9070
9335
  const [runStartedAt, setRunStartedAt] = useState24(null);
9071
9336
  const [nowTick, setNowTick] = useState24(() => Date.now());
9072
- const streamAnchor = useRef13(null);
9073
- const runningStatus = useMemo6(() => {
9337
+ const streamAnchor = useRef14(null);
9338
+ const runningStatus = useMemo7(() => {
9074
9339
  const last = messages[messages.length - 1];
9075
9340
  const runningTools = messages.filter((m) => m.role === "tool" && m.toolResult === void 0);
9076
9341
  let label = "Thinking\u2026";
@@ -9105,11 +9370,11 @@ function ChatView() {
9105
9370
  }
9106
9371
  return { label, elapsed, speedLabel };
9107
9372
  }, [messages, nowTick, runStartedAt]);
9108
- useEffect18(() => {
9373
+ useEffect19(() => {
9109
9374
  if (isLoading && runStartedAt === null) setRunStartedAt(Date.now());
9110
9375
  if (!isLoading && runStartedAt !== null) setRunStartedAt(null);
9111
9376
  }, [isLoading, runStartedAt]);
9112
- useEffect18(() => {
9377
+ useEffect19(() => {
9113
9378
  if (!isLoading) return;
9114
9379
  const t = setInterval(() => setNowTick(Date.now()), 500);
9115
9380
  return () => clearInterval(t);
@@ -9495,7 +9760,7 @@ function ChatView() {
9495
9760
 
9496
9761
  // src/components/CodeEditor.tsx
9497
9762
  import { X as X8, Circle as Circle3 } from "lucide-react";
9498
- import { useCallback as useCallback10, useEffect as useEffect19, useMemo as useMemo7, useRef as useRef14 } from "react";
9763
+ import { useCallback as useCallback11, useEffect as useEffect20, useMemo as useMemo8, useRef as useRef15 } from "react";
9499
9764
  import Editor, { loader } from "@monaco-editor/react";
9500
9765
  import * as monaco2 from "monaco-editor";
9501
9766
 
@@ -9941,22 +10206,22 @@ function CodeEditor() {
9941
10206
  const activeFilePath = useFileStore((s) => s.activeFilePath);
9942
10207
  const updateContent = useFileStore((s) => s.updateContent);
9943
10208
  const { theme: appTheme } = useTheme();
9944
- const editorRef = useRef14(null);
9945
- const activeFile = useMemo7(
10209
+ const editorRef = useRef15(null);
10210
+ const activeFile = useMemo8(
9946
10211
  () => openFiles.find((f) => f.path === activeFilePath) ?? null,
9947
10212
  [openFiles, activeFilePath]
9948
10213
  );
9949
10214
  const language = activeFilePath ? getLanguage(activeFilePath) : "plaintext";
9950
10215
  const monacoTheme = getMonacoTheme();
9951
- useEffect19(() => {
10216
+ useEffect20(() => {
9952
10217
  const resolved = getMonacoTheme();
9953
10218
  monaco2.editor.setTheme(resolved);
9954
10219
  }, [appTheme]);
9955
- const handleMount = useCallback10((editor3) => {
10220
+ const handleMount = useCallback11((editor3) => {
9956
10221
  editorRef.current = editor3;
9957
10222
  monaco2.editor.setTheme(getMonacoTheme());
9958
10223
  }, []);
9959
- const handleChange = useCallback10(
10224
+ const handleChange = useCallback11(
9960
10225
  (value) => {
9961
10226
  if (activeFilePath && value !== void 0) {
9962
10227
  updateContent(activeFilePath, value);
@@ -9964,7 +10229,7 @@ function CodeEditor() {
9964
10229
  },
9965
10230
  [activeFilePath, updateContent]
9966
10231
  );
9967
- useEffect19(() => {
10232
+ useEffect20(() => {
9968
10233
  const onKeyDown = (e) => {
9969
10234
  const mod = e.ctrlKey || e.metaKey;
9970
10235
  const tag = e.target?.tagName?.toLowerCase();
@@ -10030,7 +10295,7 @@ function CodeEditor() {
10030
10295
 
10031
10296
  // src/components/ConfirmDialog.tsx
10032
10297
  import { AlertTriangle as AlertTriangle3, FileEdit, Globe as Globe2, ShieldAlert, Terminal as Terminal5, Wrench as Wrench4 } from "lucide-react";
10033
- import { useEffect as useEffect20, useRef as useRef15 } from "react";
10298
+ import { useEffect as useEffect21, useRef as useRef16 } from "react";
10034
10299
 
10035
10300
  // src/components/ui/dialog.tsx
10036
10301
  import * as DialogPrimitive from "@radix-ui/react-dialog";
@@ -10159,14 +10424,14 @@ function SmartInputPreview({
10159
10424
  function ConfirmDialog() {
10160
10425
  const { showConfirmDialog, confirmInfo, hideConfirm } = useUIStore();
10161
10426
  const { sendConfirm } = useWebSocket();
10162
- const dialogRef = useRef15(null);
10427
+ const dialogRef = useRef16(null);
10163
10428
  const handleConfirm = (decision) => {
10164
10429
  if (confirmInfo) {
10165
10430
  sendConfirm(confirmInfo.id, decision);
10166
10431
  }
10167
10432
  hideConfirm();
10168
10433
  };
10169
- useEffect20(() => {
10434
+ useEffect21(() => {
10170
10435
  if (!showConfirmDialog) return;
10171
10436
  const onKey = (e) => {
10172
10437
  const target = e.target;
@@ -10290,10 +10555,10 @@ function ConfirmDialog() {
10290
10555
  }
10291
10556
 
10292
10557
  // src/components/ConfirmModal.tsx
10293
- import { useEffect as useEffect21 } from "react";
10294
- import { create as create15 } from "zustand";
10558
+ import { useEffect as useEffect22 } from "react";
10559
+ import { create as create16 } from "zustand";
10295
10560
  import { jsx as jsx38, jsxs as jsxs36 } from "react/jsx-runtime";
10296
- var useConfirmModalStore = create15()((set, get) => ({
10561
+ var useConfirmModalStore = create16()((set, get) => ({
10297
10562
  request: null,
10298
10563
  open: (request) => {
10299
10564
  get().request?.resolve(false);
@@ -10312,7 +10577,7 @@ function confirmModal(options) {
10312
10577
  function ConfirmModalHost() {
10313
10578
  const request = useConfirmModalStore((s) => s.request);
10314
10579
  const settle = useConfirmModalStore((s) => s.settle);
10315
- useEffect21(() => {
10580
+ useEffect22(() => {
10316
10581
  if (!request) return;
10317
10582
  const onKey = (e) => {
10318
10583
  if (e.key === "Enter") {
@@ -10355,19 +10620,19 @@ function ConfirmModalHost() {
10355
10620
 
10356
10621
  // src/components/ConnectionBanner.tsx
10357
10622
  import { Loader2 as Loader22, RotateCcw as RotateCcw4, WifiOff, X as X10 } from "lucide-react";
10358
- import { useEffect as useEffect22, useState as useState25 } from "react";
10623
+ import { useEffect as useEffect23, useState as useState25 } from "react";
10359
10624
  import { jsx as jsx39, jsxs as jsxs37 } from "react/jsx-runtime";
10360
10625
  function ConnectionBanner() {
10361
10626
  const wsStatus = useConfigStore((s) => s.wsStatus);
10362
10627
  const wsUrl = useConfigStore((s) => s.wsUrl);
10363
10628
  const [dismissed, setDismissed] = useState25(false);
10364
10629
  const [now, setNow] = useState25(Date.now());
10365
- useEffect22(() => {
10630
+ useEffect23(() => {
10366
10631
  if (wsStatus.state !== "reconnecting") return;
10367
10632
  const id = setInterval(() => setNow(Date.now()), 500);
10368
10633
  return () => clearInterval(id);
10369
10634
  }, [wsStatus.state]);
10370
- useEffect22(() => {
10635
+ useEffect23(() => {
10371
10636
  if (wsStatus.state === "open") setDismissed(false);
10372
10637
  }, [wsStatus.state]);
10373
10638
  if (wsStatus.state === "open" || wsStatus.state === "connecting") return null;
@@ -10459,7 +10724,7 @@ var ErrorBoundary = class extends Component {
10459
10724
 
10460
10725
  // src/components/QuickModelSwitcher.tsx
10461
10726
  import { ArrowRight as ArrowRight3, Cpu as Cpu5, Search as Search4 } from "lucide-react";
10462
- import { useEffect as useEffect23, useMemo as useMemo8, useRef as useRef16, useState as useState26 } from "react";
10727
+ import { useEffect as useEffect24, useMemo as useMemo9, useRef as useRef17, useState as useState26 } from "react";
10463
10728
 
10464
10729
  // src/components/QuickModelSwitcher.filter.ts
10465
10730
  function buildModelCandidates(saved, modelsByProvider, query, currentProvider, currentModel) {
@@ -10495,13 +10760,13 @@ function QuickModelSwitcher() {
10495
10760
  const [selected, setSelected] = useState26(0);
10496
10761
  const [saved, setSaved] = useState26([]);
10497
10762
  const [modelsByProvider, setModelsByProvider] = useState26({});
10498
- const inputRef = useRef16(null);
10763
+ const inputRef = useRef17(null);
10499
10764
  const wsUrl = useConfigStore((s) => s.wsUrl);
10500
10765
  const currentProvider = useConfigStore((s) => s.provider);
10501
10766
  const currentModel = useConfigStore((s) => s.model);
10502
10767
  const paletteOpen = useUIStore((s) => s.paletteOpen);
10503
10768
  const { listSavedProviders, listProviderModels, switchModel } = useWebSocket();
10504
- useEffect23(() => {
10769
+ useEffect24(() => {
10505
10770
  const onKey = (e) => {
10506
10771
  const mod = e.ctrlKey || e.metaKey;
10507
10772
  if (mod && e.key.toLowerCase() === "m" && !e.shiftKey && !e.altKey) {
@@ -10518,7 +10783,7 @@ function QuickModelSwitcher() {
10518
10783
  window.addEventListener("keydown", onKey);
10519
10784
  return () => window.removeEventListener("keydown", onKey);
10520
10785
  }, [open, paletteOpen]);
10521
- useEffect23(() => {
10786
+ useEffect24(() => {
10522
10787
  const client2 = getWSClient(wsUrl);
10523
10788
  const offSaved = client2.on("providers.saved", (msg) => {
10524
10789
  const p = msg.payload;
@@ -10533,14 +10798,14 @@ function QuickModelSwitcher() {
10533
10798
  offModels();
10534
10799
  };
10535
10800
  }, [wsUrl]);
10536
- useEffect23(() => {
10801
+ useEffect24(() => {
10537
10802
  if (!open) return;
10538
10803
  setQuery("");
10539
10804
  setSelected(0);
10540
10805
  listSavedProviders();
10541
10806
  requestAnimationFrame(() => inputRef.current?.focus());
10542
10807
  }, [open, listSavedProviders]);
10543
- useEffect23(() => {
10808
+ useEffect24(() => {
10544
10809
  if (!open) return;
10545
10810
  for (const sp of saved) {
10546
10811
  if (!modelsByProvider[sp.id]) {
@@ -10548,7 +10813,7 @@ function QuickModelSwitcher() {
10548
10813
  }
10549
10814
  }
10550
10815
  }, [open, saved, modelsByProvider, listProviderModels]);
10551
- const candidates = useMemo8(
10816
+ const candidates = useMemo9(
10552
10817
  () => buildModelCandidates(
10553
10818
  saved,
10554
10819
  modelsByProvider,
@@ -10558,7 +10823,7 @@ function QuickModelSwitcher() {
10558
10823
  ),
10559
10824
  [saved, modelsByProvider, query, currentProvider, currentModel]
10560
10825
  );
10561
- useEffect23(() => {
10826
+ useEffect24(() => {
10562
10827
  if (selected >= candidates.length) setSelected(0);
10563
10828
  }, [candidates.length, selected]);
10564
10829
  const commit = (idx) => {
@@ -10666,12 +10931,13 @@ import {
10666
10931
  Network,
10667
10932
  Palette,
10668
10933
  Puzzle,
10934
+ Send as Send2,
10669
10935
  Shield as Shield2,
10670
10936
  Sun as Sun3,
10671
10937
  X as X11,
10672
10938
  Zap as Zap5
10673
10939
  } from "lucide-react";
10674
- import { useState as useState28, useEffect as useEffect24, useCallback as useCallback12 } from "react";
10940
+ import { useState as useState28, useEffect as useEffect25, useCallback as useCallback13 } from "react";
10675
10941
 
10676
10942
  // src/components/ui/input.tsx
10677
10943
  import * as React3 from "react";
@@ -10900,7 +11166,7 @@ import {
10900
11166
  Plus,
10901
11167
  Trash2 as Trash22
10902
11168
  } from "lucide-react";
10903
- import { useState as useState27, useCallback as useCallback11 } from "react";
11169
+ import { useState as useState27, useCallback as useCallback12 } from "react";
10904
11170
  import { Fragment as Fragment9, jsx as jsx47, jsxs as jsxs43 } from "react/jsx-runtime";
10905
11171
  var PROVIDER_FAMILIES = ["anthropic", "openai", "google", "openai-compatible"];
10906
11172
  function ProviderSection({
@@ -10929,7 +11195,7 @@ function ProviderSection({
10929
11195
  const [newProviderFamily, setNewProviderFamily] = useState27("openai-compatible");
10930
11196
  const [newProviderBaseUrl, setNewProviderBaseUrl] = useState27("");
10931
11197
  const [newProviderApiKey, setNewProviderApiKey] = useState27("");
10932
- const handleAddKey = useCallback11(
11198
+ const handleAddKey = useCallback12(
10933
11199
  (providerId) => {
10934
11200
  if (!newKeyLabel.trim() || !newKeyValue.trim()) return;
10935
11201
  onAddKey(providerId, newKeyLabel.trim(), newKeyValue.trim());
@@ -10939,7 +11205,7 @@ function ProviderSection({
10939
11205
  },
10940
11206
  [onAddKey, newKeyLabel, newKeyValue]
10941
11207
  );
10942
- const handleAddProvider = useCallback11(() => {
11208
+ const handleAddProvider = useCallback12(() => {
10943
11209
  if (!newProviderId.trim()) return;
10944
11210
  onAddProvider(newProviderId.trim(), newProviderFamily, newProviderBaseUrl || void 0, newProviderApiKey || void 0);
10945
11211
  setNewProviderId("");
@@ -11296,7 +11562,7 @@ function ModelSection({
11296
11562
  }
11297
11563
 
11298
11564
  // src/components/SettingsPanel/index.tsx
11299
- import { jsx as jsx49, jsxs as jsxs45 } from "react/jsx-runtime";
11565
+ import { Fragment as Fragment11, jsx as jsx49, jsxs as jsxs45 } from "react/jsx-runtime";
11300
11566
  function SettingsPanel() {
11301
11567
  const { setCurrentView } = useUIStore();
11302
11568
  const { provider, model, setProvider, setModel, wsConnected } = useConfigStore();
@@ -11305,7 +11571,7 @@ function SettingsPanel() {
11305
11571
  const wsClient = ws.client;
11306
11572
  const { updatePrefs, switchAutonomy } = ws;
11307
11573
  const localPrefs = useLocalPrefs();
11308
- const syncPref = useCallback12(
11574
+ const syncPref = useCallback13(
11309
11575
  (key, value) => {
11310
11576
  localPrefs.set({ [key]: value });
11311
11577
  updatePrefs({ [key]: value });
@@ -11321,7 +11587,7 @@ function SettingsPanel() {
11321
11587
  const [providerTab, setProviderTab] = useState28("catalog");
11322
11588
  const [catalogQuery, setCatalogQuery] = useState28("");
11323
11589
  const currentCatalogProvider = catalogProviders.find((p) => p.id === provider);
11324
- useEffect24(() => {
11590
+ useEffect25(() => {
11325
11591
  const handleProviderCatalog = (msg) => {
11326
11592
  if (msg.type === "provider.catalog") {
11327
11593
  const payload = msg.payload;
@@ -11359,7 +11625,7 @@ function SettingsPanel() {
11359
11625
  off3?.();
11360
11626
  };
11361
11627
  }, [wsConnected, wsClient]);
11362
- const handleProviderSelect = useCallback12(
11628
+ const handleProviderSelect = useCallback13(
11363
11629
  (providerId) => {
11364
11630
  setProvider(providerId);
11365
11631
  if (!catalogModels[providerId]) {
@@ -11369,7 +11635,7 @@ function SettingsPanel() {
11369
11635
  },
11370
11636
  [catalogModels, setProvider, ws]
11371
11637
  );
11372
- const handleModelSelect = useCallback12(
11638
+ const handleModelSelect = useCallback13(
11373
11639
  (modelId) => {
11374
11640
  setModel(modelId);
11375
11641
  const currentProvider = useConfigStore.getState().provider;
@@ -11378,31 +11644,31 @@ function SettingsPanel() {
11378
11644
  },
11379
11645
  [setModel, ws]
11380
11646
  );
11381
- const handleAddKey = useCallback12(
11647
+ const handleAddKey = useCallback13(
11382
11648
  (providerId, label, value) => {
11383
11649
  ws.addKey?.(providerId, label, value);
11384
11650
  },
11385
11651
  [ws]
11386
11652
  );
11387
- const handleDeleteKey = useCallback12(
11653
+ const handleDeleteKey = useCallback13(
11388
11654
  (providerId, label) => {
11389
11655
  ws.deleteKey?.(providerId, label);
11390
11656
  },
11391
11657
  [ws]
11392
11658
  );
11393
- const handleSetActiveKey = useCallback12(
11659
+ const handleSetActiveKey = useCallback13(
11394
11660
  (providerId, label) => {
11395
11661
  ws.setActiveKey?.(providerId, label);
11396
11662
  },
11397
11663
  [ws]
11398
11664
  );
11399
- const handleAddProvider = useCallback12(
11665
+ const handleAddProvider = useCallback13(
11400
11666
  (id, family, baseUrl, apiKey) => {
11401
11667
  ws.addProvider?.(id, family, baseUrl, apiKey);
11402
11668
  },
11403
11669
  [ws]
11404
11670
  );
11405
- const handleRemoveProvider = useCallback12(
11671
+ const handleRemoveProvider = useCallback13(
11406
11672
  (providerId) => {
11407
11673
  ws.removeProvider?.(providerId);
11408
11674
  },
@@ -11742,6 +12008,47 @@ function SettingsPanel() {
11742
12008
  onChange: () => syncPref("nextPrediction", !localPrefs.nextPrediction)
11743
12009
  }
11744
12010
  )
12011
+ ] }),
12012
+ /* @__PURE__ */ jsxs45("div", { className: "pt-2 border-t", children: [
12013
+ /* @__PURE__ */ jsxs45("h3", { className: "text-sm font-semibold mb-3 mt-3 flex items-center gap-2", children: [
12014
+ /* @__PURE__ */ jsx49(Send2, { className: "h-4 w-4 text-muted-foreground" }),
12015
+ "Telegram Notifications"
12016
+ ] }),
12017
+ localPrefs.tgConfigured ? /* @__PURE__ */ jsxs45(Fragment11, { children: [
12018
+ /* @__PURE__ */ jsx49(
12019
+ PreferenceToggle,
12020
+ {
12021
+ label: "Session end",
12022
+ hint: "Send a summary when a session ends.",
12023
+ value: localPrefs.tgSessionEnd,
12024
+ onChange: () => syncPref("tgSessionEnd", !localPrefs.tgSessionEnd)
12025
+ }
12026
+ ),
12027
+ /* @__PURE__ */ jsx49(
12028
+ PreferenceToggle,
12029
+ {
12030
+ label: "Delegate finished",
12031
+ hint: "Send a humanized note when a subagent completes.",
12032
+ value: localPrefs.tgDelegate,
12033
+ onChange: () => syncPref("tgDelegate", !localPrefs.tgDelegate)
12034
+ }
12035
+ ),
12036
+ /* @__PURE__ */ jsx49(
12037
+ PreferenceToggle,
12038
+ {
12039
+ label: "Long-running tools",
12040
+ hint: `Notify when a tool exceeds ${localPrefs.tgLongToolMs}ms. Set 0 to disable.`,
12041
+ value: localPrefs.tgLongToolMs > 0,
12042
+ onChange: () => syncPref("tgLongToolMs", localPrefs.tgLongToolMs > 0 ? 0 : 3e4)
12043
+ }
12044
+ ),
12045
+ /* @__PURE__ */ jsx49("p", { className: "text-xs text-muted-foreground mt-2", children: "Changes apply immediately." })
12046
+ ] }) : /* @__PURE__ */ jsxs45("p", { className: "text-xs text-muted-foreground", children: [
12047
+ "Telegram is not configured. Run",
12048
+ " ",
12049
+ /* @__PURE__ */ jsx49("code", { className: "bg-muted px-1 py-0.5 rounded", children: "/telegram-setup" }),
12050
+ " in the CLI to connect a bot first."
12051
+ ] })
11745
12052
  ] })
11746
12053
  ] }),
11747
12054
  /* @__PURE__ */ jsxs45(TabsContent, { value: "features", className: "space-y-4", children: [
@@ -11880,7 +12187,7 @@ import {
11880
12187
  Network as Network2,
11881
12188
  Zap as Zap6
11882
12189
  } from "lucide-react";
11883
- import { useCallback as useCallback13, useEffect as useEffect25, useState as useState29 } from "react";
12190
+ import { useCallback as useCallback14, useEffect as useEffect26, useState as useState29 } from "react";
11884
12191
  import { jsx as jsx50, jsxs as jsxs46 } from "react/jsx-runtime";
11885
12192
  function ProviderCard({
11886
12193
  provider,
@@ -12029,7 +12336,7 @@ function SetupScreen() {
12029
12336
  const [isLoadingModels, setIsLoadingModels] = useState29(false);
12030
12337
  const [selectedProvider, setSelectedProvider] = useState29(null);
12031
12338
  const [selectedModel, setSelectedModel] = useState29(null);
12032
- useEffect25(() => {
12339
+ useEffect26(() => {
12033
12340
  if (!wsConnected) return;
12034
12341
  const wsClient = getWSClient(wsUrl);
12035
12342
  const off1 = wsClient.on("provider.catalog", (msg) => {
@@ -12071,7 +12378,7 @@ function SetupScreen() {
12071
12378
  off3?.();
12072
12379
  };
12073
12380
  }, [wsConnected, wsUrl, provider, model]);
12074
- useEffect25(() => {
12381
+ useEffect26(() => {
12075
12382
  if (!selectedProvider || !wsConnected) return;
12076
12383
  const wsClient = getWSClient(wsUrl);
12077
12384
  if (!catalogModels[selectedProvider]) {
@@ -12082,12 +12389,12 @@ function SetupScreen() {
12082
12389
  const selectedProviderData = catalogProviders.find((p) => p.id === selectedProvider);
12083
12390
  const savedProviderData = savedProviders.find((p) => p.id === selectedProvider);
12084
12391
  const hasActiveKey = savedProviderData?.apiKeys.some((k) => k.isActive) || selectedProviderData?.hasApiKey;
12085
- const handleProviderSelect = useCallback13((providerId) => {
12392
+ const handleProviderSelect = useCallback14((providerId) => {
12086
12393
  setSelectedProvider(providerId);
12087
12394
  setSelectedModel(null);
12088
12395
  setStep("model");
12089
12396
  }, []);
12090
- const handleModelSelect = useCallback13(
12397
+ const handleModelSelect = useCallback14(
12091
12398
  (modelId) => {
12092
12399
  setSelectedModel(modelId);
12093
12400
  if (!hasActiveKey) {
@@ -12098,10 +12405,10 @@ function SetupScreen() {
12098
12405
  },
12099
12406
  [hasActiveKey]
12100
12407
  );
12101
- const handleApiKeySaved = useCallback13(() => {
12408
+ const handleApiKeySaved = useCallback14(() => {
12102
12409
  setStep("done");
12103
12410
  }, []);
12104
- const handleStartSession = useCallback13(() => {
12411
+ const handleStartSession = useCallback14(() => {
12105
12412
  if (!selectedProvider || !selectedModel) return;
12106
12413
  setProvider(selectedProvider);
12107
12414
  setModel(selectedModel);
@@ -12277,7 +12584,7 @@ function SetupScreen() {
12277
12584
 
12278
12585
  // src/components/SessionsDashboard.tsx
12279
12586
  import { Clock as Clock8, Cpu as Cpu9, FolderGit2, Wifi, WifiOff as WifiOff2, Loader2 as Loader26 } from "lucide-react";
12280
- import { useCallback as useCallback14, useEffect as useEffect26, useState as useState30 } from "react";
12587
+ import { useCallback as useCallback15, useEffect as useEffect27, useState as useState30 } from "react";
12281
12588
  import { jsx as jsx51, jsxs as jsxs47 } from "react/jsx-runtime";
12282
12589
  function agentIcon(status) {
12283
12590
  switch (status) {
@@ -12343,7 +12650,7 @@ function SessionsDashboard() {
12343
12650
  const [sessions, setSessions] = useState30([]);
12344
12651
  const [loading, setLoading] = useState30(true);
12345
12652
  const [error, setError] = useState30(null);
12346
- const fetchSessions = useCallback14(async () => {
12653
+ const fetchSessions = useCallback15(async () => {
12347
12654
  try {
12348
12655
  const res = await fetch("/api/sessions");
12349
12656
  if (!res.ok) {
@@ -12363,7 +12670,7 @@ function SessionsDashboard() {
12363
12670
  setLoading(false);
12364
12671
  }
12365
12672
  }, []);
12366
- useEffect26(() => {
12673
+ useEffect27(() => {
12367
12674
  void fetchSessions();
12368
12675
  const t = setInterval(fetchSessions, 5e3);
12369
12676
  return () => clearInterval(t);
@@ -12456,7 +12763,7 @@ function SessionsDashboard() {
12456
12763
 
12457
12764
  // src/components/ShortcutsOverlay.tsx
12458
12765
  import { Keyboard as Keyboard3, X as X12 } from "lucide-react";
12459
- import { useEffect as useEffect27 } from "react";
12766
+ import { useEffect as useEffect28 } from "react";
12460
12767
  import { jsx as jsx52, jsxs as jsxs48 } from "react/jsx-runtime";
12461
12768
  var SHORTCUTS = [
12462
12769
  {
@@ -12518,7 +12825,7 @@ var SHORTCUTS = [
12518
12825
  function ShortcutsOverlay() {
12519
12826
  const open = useUIStore((s) => s.shortcutsOpen);
12520
12827
  const setOpen = useUIStore((s) => s.setShortcutsOpen);
12521
- useEffect27(() => {
12828
+ useEffect28(() => {
12522
12829
  const onKey = (e) => {
12523
12830
  const target = e.target;
12524
12831
  const tag = target?.tagName?.toLowerCase();
@@ -12612,7 +12919,7 @@ import {
12612
12919
  ReactFlowProvider
12613
12920
  } from "@xyflow/react";
12614
12921
  import "@xyflow/react/dist/style.css";
12615
- import { useCallback as useCallback15, useEffect as useEffect28, useState as useState31 } from "react";
12922
+ import { useCallback as useCallback16, useEffect as useEffect29, useState as useState31 } from "react";
12616
12923
  import {
12617
12924
  Bot as Bot7,
12618
12925
  Cpu as Cpu10,
@@ -12693,7 +13000,7 @@ function AgentFlowGraphCSS() {
12693
13000
  }
12694
13001
 
12695
13002
  // src/components/AgentFlowGraph/AgentFlowCanvas.tsx
12696
- import { Fragment as Fragment11, jsx as jsx54, jsxs as jsxs49 } from "react/jsx-runtime";
13003
+ import { Fragment as Fragment12, jsx as jsx54, jsxs as jsxs49 } from "react/jsx-runtime";
12697
13004
  var TOOL_ICONS = {
12698
13005
  read: /* @__PURE__ */ jsx54(FileText3, { className: "h-3 w-3" }),
12699
13006
  write: /* @__PURE__ */ jsx54(FileText3, { className: "h-3 w-3" }),
@@ -12820,7 +13127,7 @@ function ContextNode({ data }) {
12820
13127
  /* @__PURE__ */ jsx54(Database2, { className: "h-3 w-3", style: { color: colors } }),
12821
13128
  /* @__PURE__ */ jsx54("span", { className: "text-[10px] font-medium", style: { color: colors }, children: "Context" })
12822
13129
  ] }),
12823
- data.stats?.ctxPct !== void 0 && /* @__PURE__ */ jsxs49(Fragment11, { children: [
13130
+ data.stats?.ctxPct !== void 0 && /* @__PURE__ */ jsxs49(Fragment12, { children: [
12824
13131
  /* @__PURE__ */ jsxs49("div", { className: "mt-1 text-[9px] text-center text-gray-400", children: [
12825
13132
  data.stats.ctxPct,
12826
13133
  "%"
@@ -12859,7 +13166,7 @@ function FlowEdge({
12859
13166
  status: { icon: "\u{1F4CA}", bg: "bg-pink-500/20", text: "text-pink-400" }
12860
13167
  };
12861
13168
  const meta2 = flowMeta[flowType] || flowMeta.response;
12862
- return /* @__PURE__ */ jsxs49(Fragment11, { children: [
13169
+ return /* @__PURE__ */ jsxs49(Fragment12, { children: [
12863
13170
  /* @__PURE__ */ jsx54(
12864
13171
  "path",
12865
13172
  {
@@ -12959,7 +13266,7 @@ function AgentFlowCanvas({ containerRef }) {
12959
13266
  const [nodes, setNodes, onNodesChange] = useNodesState([]);
12960
13267
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
12961
13268
  const [selectedNode, setSelectedNode] = useState31(null);
12962
- useEffect28(() => {
13269
+ useEffect29(() => {
12963
13270
  const rfNodes = [];
12964
13271
  const rfEdges = [];
12965
13272
  rfNodes.push({
@@ -13133,7 +13440,7 @@ function AgentFlowCanvas({ containerRef }) {
13133
13440
  setEdges(rfEdges);
13134
13441
  setTimeout(() => fitView({ padding: 0.2, duration: 300 }), 50);
13135
13442
  }, [session, projectName, iteration, fleetAgents, cost, lastInputTokens, maxContext, setNodes, setEdges, fitView]);
13136
- useEffect28(() => {
13443
+ useEffect29(() => {
13137
13444
  if (vizEvents.length === 0) return;
13138
13445
  const latestEvent = vizEvents[0];
13139
13446
  if (!latestEvent) return;
@@ -13155,14 +13462,14 @@ function AgentFlowCanvas({ containerRef }) {
13155
13462
  );
13156
13463
  }
13157
13464
  }, [vizEvents, setNodes]);
13158
- const onConnect = useCallback15(
13465
+ const onConnect = useCallback16(
13159
13466
  (params) => setEdges((eds) => addEdge({ ...params, type: "flow" }, eds)),
13160
13467
  [setEdges]
13161
13468
  );
13162
- const onNodeClick = useCallback15((_, node) => {
13469
+ const onNodeClick = useCallback16((_, node) => {
13163
13470
  setSelectedNode(node);
13164
13471
  }, []);
13165
- const onPaneClick = useCallback15(() => {
13472
+ const onPaneClick = useCallback16(() => {
13166
13473
  setSelectedNode(null);
13167
13474
  }, []);
13168
13475
  return /* @__PURE__ */ jsxs49("div", { className: "w-full h-full bg-background relative", children: [
@@ -13238,7 +13545,7 @@ function AgentFlowCanvas({ containerRef }) {
13238
13545
  /* @__PURE__ */ jsx54("span", { className: "text-muted-foreground", children: "Tool" }),
13239
13546
  /* @__PURE__ */ jsx54("span", { className: "font-mono text-amber-400", children: String(selectedNode.data.currentTool) })
13240
13547
  ] }),
13241
- selectedNode.data.stats && /* @__PURE__ */ jsxs49(Fragment11, { children: [
13548
+ selectedNode.data.stats && /* @__PURE__ */ jsxs49(Fragment12, { children: [
13242
13549
  selectedNode.data.stats.iterations !== void 0 && /* @__PURE__ */ jsxs49("div", { className: "flex justify-between", children: [
13243
13550
  /* @__PURE__ */ jsx54("span", { className: "text-muted-foreground", children: "Iterations" }),
13244
13551
  /* @__PURE__ */ jsx54("span", { className: "font-mono", children: selectedNode.data.stats.iterations })
@@ -13339,7 +13646,7 @@ function AgentFlowCanvasWithProvider(props) {
13339
13646
 
13340
13647
  // src/components/SidePanel/index.tsx
13341
13648
  import { PanelLeftClose } from "lucide-react";
13342
- import { useEffect as useEffect35 } from "react";
13649
+ import { useEffect as useEffect36 } from "react";
13343
13650
 
13344
13651
  // src/components/FileExplorer.tsx
13345
13652
  import {
@@ -13360,7 +13667,7 @@ import {
13360
13667
  Loader2 as Loader27,
13361
13668
  Minimize2
13362
13669
  } from "lucide-react";
13363
- import { useCallback as useCallback16, useEffect as useEffect29, useMemo as useMemo10, useRef as useRef18, useState as useState32 } from "react";
13670
+ import { useCallback as useCallback17, useEffect as useEffect30, useMemo as useMemo11, useRef as useRef19, useState as useState32 } from "react";
13364
13671
  import { jsx as jsx55, jsxs as jsxs50 } from "react/jsx-runtime";
13365
13672
  var EXT_ICONS = {
13366
13673
  // ── Code ──
@@ -13484,7 +13791,7 @@ function TreeNodeItem({
13484
13791
  const [expanded, setExpanded] = useState32(depth < 1);
13485
13792
  const activeFilePath = useFileStore((s) => s.activeFilePath);
13486
13793
  const isActive = node.type === "file" && node.path === activeFilePath;
13487
- useEffect29(() => {
13794
+ useEffect30(() => {
13488
13795
  if (forceExpand !== null) setExpanded(forceExpand);
13489
13796
  }, [forceExpand]);
13490
13797
  if (node.type === "directory") {
@@ -13586,7 +13893,7 @@ function FileExplorer() {
13586
13893
  const segments = cwd.replace(/\\/g, "/").split("/").filter(Boolean);
13587
13894
  return (segments[segments.length - 1] ?? "") === projectName;
13588
13895
  })();
13589
- const breadcrumbs = useMemo10(() => {
13896
+ const breadcrumbs = useMemo11(() => {
13590
13897
  if (!cwd || !projectName) return [];
13591
13898
  const norm = cwd.replace(/\\/g, "/");
13592
13899
  const segments = norm.split("/").filter(Boolean);
@@ -13612,18 +13919,18 @@ function FileExplorer() {
13612
13919
  isLast: i === rel.length - 1
13613
13920
  }));
13614
13921
  }, [cwd, projectName]);
13615
- const bcRef = useRef18(null);
13616
- useEffect29(() => {
13922
+ const bcRef = useRef19(null);
13923
+ useEffect30(() => {
13617
13924
  const el = bcRef.current;
13618
13925
  if (el && breadcrumbs.length > 1) {
13619
13926
  el.scrollLeft = el.scrollWidth;
13620
13927
  }
13621
13928
  }, [breadcrumbs]);
13622
- const handleBreadcrumbClick = useCallback16((crumbPath) => {
13929
+ const handleBreadcrumbClick = useCallback17((crumbPath) => {
13623
13930
  getWSClient().send({ type: "working_dir.set", payload: { path: crumbPath } });
13624
13931
  }, []);
13625
13932
  const [contextMenu, setContextMenu] = useState32(null);
13626
- useEffect29(() => {
13933
+ useEffect30(() => {
13627
13934
  if (!contextMenu) return;
13628
13935
  const close = () => setContextMenu(null);
13629
13936
  const onKey = (e) => {
@@ -13636,7 +13943,7 @@ function FileExplorer() {
13636
13943
  window.removeEventListener("keydown", onKey);
13637
13944
  };
13638
13945
  }, [contextMenu]);
13639
- const handleBreadcrumbContext = useCallback16(
13946
+ const handleBreadcrumbContext = useCallback17(
13640
13947
  (e, crumb) => {
13641
13948
  e.preventDefault();
13642
13949
  e.stopPropagation();
@@ -13644,29 +13951,29 @@ function FileExplorer() {
13644
13951
  },
13645
13952
  []
13646
13953
  );
13647
- const copyToClipboard2 = useCallback16((text) => {
13954
+ const copyToClipboard2 = useCallback17((text) => {
13648
13955
  void navigator.clipboard.writeText(text);
13649
13956
  setContextMenu(null);
13650
13957
  }, []);
13651
- const handleFileIndicatorClick = useCallback16(() => {
13958
+ const handleFileIndicatorClick = useCallback17(() => {
13652
13959
  if (!activeFilePath) return;
13653
13960
  const norm = activeFilePath.replace(/\\/g, "/");
13654
13961
  const parent = norm.split("/").slice(0, -1).join("/") || ".";
13655
13962
  getWSClient().send({ type: "working_dir.set", payload: { path: parent } });
13656
13963
  }, [activeFilePath]);
13657
- const handleShellOpen = useCallback16((dirPath, target) => {
13964
+ const handleShellOpen = useCallback17((dirPath, target) => {
13658
13965
  getWSClient().send({ type: "shell.open", payload: { path: dirPath, target } });
13659
13966
  setContextMenu(null);
13660
13967
  }, []);
13661
- const handleGoUp = useCallback16(() => {
13968
+ const handleGoUp = useCallback17(() => {
13662
13969
  if (!cwd) return;
13663
13970
  const norm = cwd.replace(/\\/g, "/");
13664
13971
  const parent = norm.split("/").slice(0, -1).join("/") || norm;
13665
13972
  getWSClient().send({ type: "working_dir.set", payload: { path: parent } });
13666
13973
  }, [cwd]);
13667
13974
  const [showSpinner, setShowSpinner] = useState32(false);
13668
- const spinnerTimer = useRef18(null);
13669
- useEffect29(() => {
13975
+ const spinnerTimer = useRef19(null);
13976
+ useEffect30(() => {
13670
13977
  if (treeLoading) {
13671
13978
  spinnerTimer.current = setTimeout(() => setShowSpinner(true), 150);
13672
13979
  } else {
@@ -13679,7 +13986,7 @@ function FileExplorer() {
13679
13986
  }, [treeLoading]);
13680
13987
  const [selectedPath, setSelectedPath] = useState32(null);
13681
13988
  const [globalExpand, setGlobalExpand] = useState32(null);
13682
- const dirCount = useMemo10(() => {
13989
+ const dirCount = useMemo11(() => {
13683
13990
  let count = 0;
13684
13991
  const walk = (nodes) => {
13685
13992
  for (const n of nodes) {
@@ -13690,7 +13997,7 @@ function FileExplorer() {
13690
13997
  walk(tree);
13691
13998
  return count;
13692
13999
  }, [tree]);
13693
- const cwdStats = useMemo10(() => {
14000
+ const cwdStats = useMemo11(() => {
13694
14001
  let files = 0;
13695
14002
  let dirs = 0;
13696
14003
  for (const n of tree) {
@@ -13700,21 +14007,21 @@ function FileExplorer() {
13700
14007
  return { files, dirs };
13701
14008
  }, [tree]);
13702
14009
  const userInteractedRef = { value: false };
13703
- const handleGlobalCollapse = useCallback16(() => {
14010
+ const handleGlobalCollapse = useCallback17(() => {
13704
14011
  userInteractedRef.value = true;
13705
14012
  setGlobalExpand(false);
13706
14013
  setTimeout(() => {
13707
14014
  userInteractedRef.value = false;
13708
14015
  }, 400);
13709
14016
  }, []);
13710
- const handleGlobalExpand = useCallback16(() => {
14017
+ const handleGlobalExpand = useCallback17(() => {
13711
14018
  userInteractedRef.value = true;
13712
14019
  setGlobalExpand(true);
13713
14020
  setTimeout(() => {
13714
14021
  userInteractedRef.value = false;
13715
14022
  }, 400);
13716
14023
  }, []);
13717
- const handleSelect = useCallback16(
14024
+ const handleSelect = useCallback17(
13718
14025
  (filePath) => {
13719
14026
  const existing = openFiles.find((f) => f.path === filePath);
13720
14027
  if (existing) {
@@ -13725,13 +14032,13 @@ function FileExplorer() {
13725
14032
  },
13726
14033
  [openFiles]
13727
14034
  );
13728
- const handleOpen = useCallback16((filePath) => {
14035
+ const handleOpen = useCallback17((filePath) => {
13729
14036
  window.dispatchEvent(
13730
14037
  new CustomEvent("wrongstack:open-file", { detail: { filePath } })
13731
14038
  );
13732
14039
  setSelectedPath(null);
13733
14040
  }, []);
13734
- useEffect29(() => {
14041
+ useEffect30(() => {
13735
14042
  if (activeFilePath) setSelectedPath(null);
13736
14043
  }, [activeFilePath]);
13737
14044
  if (showSpinner) {
@@ -13939,21 +14246,22 @@ import {
13939
14246
  Circle as Circle4,
13940
14247
  FileText as FileText5,
13941
14248
  HelpCircle,
13942
- Send as Send2,
14249
+ Send as Send3,
13943
14250
  Bell,
13944
14251
  RotateCw,
13945
14252
  UserCheck,
13946
- Trash2 as Trash23
14253
+ Trash2 as Trash23,
14254
+ Sparkles as Sparkles4
13947
14255
  } from "lucide-react";
13948
- import { useEffect as useEffect30, useState as useState33 } from "react";
14256
+ import { useEffect as useEffect31, useState as useState33 } from "react";
13949
14257
  import { jsx as jsx56, jsxs as jsxs51 } from "react/jsx-runtime";
13950
14258
  var TYPE_ICONS = {
13951
14259
  note: FileText5,
13952
14260
  ask: HelpCircle,
13953
- assign: Send2,
14261
+ assign: Send3,
13954
14262
  steer: RotateCw,
13955
14263
  btw: Bell,
13956
- broadcast: Send2,
14264
+ broadcast: Send3,
13957
14265
  status: Circle4,
13958
14266
  result: CheckCircle210
13959
14267
  };
@@ -13971,13 +14279,14 @@ function MailboxPanel({ className }) {
13971
14279
  const agents = useMailboxStore((s) => s.agents);
13972
14280
  const [collapsed, setCollapsed] = useState33(false);
13973
14281
  const [deleting, setDeleting] = useState33(false);
14282
+ const [purging, setPurging] = useState33(false);
13974
14283
  const { client: client2 } = useWebSocket();
13975
14284
  const [ready, setReady] = useState33(client2.status.state === "open");
13976
- useEffect30(() => {
14285
+ useEffect31(() => {
13977
14286
  const off = client2.onStatus((s) => setReady(s.state === "open"));
13978
14287
  return () => off();
13979
14288
  }, [client2]);
13980
- useEffect30(() => {
14289
+ useEffect31(() => {
13981
14290
  if (!ready) return;
13982
14291
  client2.send({ type: "mailbox.messages", payload: { limit: 30 } });
13983
14292
  client2.send({ type: "mailbox.agents", payload: {} });
@@ -13996,9 +14305,26 @@ function MailboxPanel({ className }) {
13996
14305
  setDeleting(true);
13997
14306
  client2.send({ type: "mailbox.clear" });
13998
14307
  }
13999
- useEffect30(() => {
14308
+ useEffect31(() => {
14000
14309
  if (deleting && messages.length === 0) setDeleting(false);
14001
14310
  }, [deleting, messages.length]);
14311
+ async function handlePurge() {
14312
+ const ok = await confirmModal({
14313
+ title: "Purge stale messages?",
14314
+ message: "Removes completed messages older than 1 day and incomplete messages older than 7 days. Active messages are preserved.",
14315
+ confirmLabel: "Purge",
14316
+ danger: false
14317
+ });
14318
+ if (!ok) return;
14319
+ setPurging(true);
14320
+ client2.send({ type: "mailbox.purge" });
14321
+ }
14322
+ useEffect31(() => {
14323
+ if (purging) {
14324
+ const t = setTimeout(() => setPurging(false), 3e3);
14325
+ return () => clearTimeout(t);
14326
+ }
14327
+ }, [purging]);
14002
14328
  return /* @__PURE__ */ jsxs51("div", { className: cn("rounded-lg border border-border bg-card/60 backdrop-blur-sm", className), children: [
14003
14329
  /* @__PURE__ */ jsxs51(
14004
14330
  "button",
@@ -14034,6 +14360,20 @@ function MailboxPanel({ className }) {
14034
14360
  deleting ? "Deleting\u2026" : "Delete all"
14035
14361
  ]
14036
14362
  }
14363
+ ),
14364
+ /* @__PURE__ */ jsxs51(
14365
+ "button",
14366
+ {
14367
+ type: "button",
14368
+ onClick: handlePurge,
14369
+ disabled: purging,
14370
+ className: "flex items-center gap-1 text-[10px] text-muted-foreground hover:text-cyan-500 disabled:opacity-40 transition-colors",
14371
+ title: "Purge stale/orphaned messages",
14372
+ children: [
14373
+ /* @__PURE__ */ jsx56(Sparkles4, { className: "h-3 w-3" }),
14374
+ purging ? "Purging\u2026" : "Purge"
14375
+ ]
14376
+ }
14037
14377
  )
14038
14378
  ] }),
14039
14379
  messages.slice(0, 8).map((m) => {
@@ -14105,7 +14445,7 @@ function MailboxPanel({ className }) {
14105
14445
 
14106
14446
  // src/components/ProjectsPanel.tsx
14107
14447
  import { ExternalLink, Folder as Folder3, FolderPlus, History as History3, Loader2 as Loader28 } from "lucide-react";
14108
- import { useCallback as useCallback17, useEffect as useEffect31, useRef as useRef19, useState as useState34 } from "react";
14448
+ import { useCallback as useCallback18, useEffect as useEffect32, useRef as useRef20, useState as useState34 } from "react";
14109
14449
  import { jsx as jsx57, jsxs as jsxs52 } from "react/jsx-runtime";
14110
14450
  function ProjectsPanel({ fullView }) {
14111
14451
  const [projects, setProjects] = useState34([]);
@@ -14114,13 +14454,13 @@ function ProjectsPanel({ fullView }) {
14114
14454
  const [folderPath, setFolderPath] = useState34("");
14115
14455
  const [projectName, setProjectName] = useState34("");
14116
14456
  const [dialogOpen, setDialogOpen] = useState34(false);
14117
- const projectNameRef = useRef19(null);
14457
+ const projectNameRef = useRef20(null);
14118
14458
  const [confirmSwitch, setConfirmSwitch] = useState34({ open: false, project: null, agentCount: 0 });
14119
- const fetchProjects = useCallback17(() => {
14459
+ const fetchProjects = useCallback18(() => {
14120
14460
  const ws = getWSClient();
14121
14461
  ws.send({ type: "projects.list" });
14122
14462
  }, []);
14123
- useEffect31(() => {
14463
+ useEffect32(() => {
14124
14464
  setLoading(true);
14125
14465
  const ws = getWSClient();
14126
14466
  const offList = ws.on("projects.list", (msg) => {
@@ -14157,7 +14497,7 @@ function ProjectsPanel({ fullView }) {
14157
14497
  if (days < 365) return `${Math.floor(days / 30)}mo ago`;
14158
14498
  return `${Math.floor(days / 365)}y ago`;
14159
14499
  };
14160
- const handleSelect = useCallback17((p) => {
14500
+ const handleSelect = useCallback18((p) => {
14161
14501
  const sessionId = useSessionStore.getState().session?.id;
14162
14502
  if (sessionId) {
14163
14503
  const fleet = useFleetStore.getState();
@@ -14170,7 +14510,7 @@ function ProjectsPanel({ fullView }) {
14170
14510
  }
14171
14511
  doSwitch(p);
14172
14512
  }, []);
14173
- const doSwitch = useCallback17((p) => {
14513
+ const doSwitch = useCallback18((p) => {
14174
14514
  const ws = getWSClient();
14175
14515
  ws.send({ type: "projects.select", payload: { root: p.root, name: p.name } });
14176
14516
  const off = ws.on("projects.selected", (msg) => {
@@ -14185,7 +14525,7 @@ function ProjectsPanel({ fullView }) {
14185
14525
  }
14186
14526
  });
14187
14527
  }, []);
14188
- const handleAdd = useCallback17(() => {
14528
+ const handleAdd = useCallback18(() => {
14189
14529
  const trimmed = folderPath.trim();
14190
14530
  if (!trimmed) {
14191
14531
  toast.error("Please enter a folder path.");
@@ -14394,11 +14734,11 @@ function ProjectsPanel({ fullView }) {
14394
14734
 
14395
14735
  // src/components/SidePanel/AgentsPanel.tsx
14396
14736
  import { Bot as Bot9, LayoutGrid as LayoutGrid2, Wrench as Wrench7 } from "lucide-react";
14397
- import { useMemo as useMemo12, useState as useState36 } from "react";
14737
+ import { useMemo as useMemo13, useState as useState36 } from "react";
14398
14738
 
14399
14739
  // src/components/FleetPanel.tsx
14400
14740
  import { Bot as Bot8, Check as Check7, ChevronDown as ChevronDown8, ChevronRight as ChevronRight7, Clock as Clock10, Copy as Copy3, Cpu as Cpu11, Crown, Wrench as Wrench6, X as X14, Zap as Zap8 } from "lucide-react";
14401
- import { useCallback as useCallback18, useMemo as useMemo11, useState as useState35 } from "react";
14741
+ import { useCallback as useCallback19, useMemo as useMemo12, useState as useState35 } from "react";
14402
14742
 
14403
14743
  // src/components/ui/sparkline.tsx
14404
14744
  import { jsx as jsx58 } from "react/jsx-runtime";
@@ -14442,7 +14782,7 @@ function SparklineChart({ bins, className, width = NUM_BINS }) {
14442
14782
  }
14443
14783
 
14444
14784
  // src/components/FleetPanel.tsx
14445
- import { Fragment as Fragment12, jsx as jsx59, jsxs as jsxs53 } from "react/jsx-runtime";
14785
+ import { Fragment as Fragment13, jsx as jsx59, jsxs as jsxs53 } from "react/jsx-runtime";
14446
14786
  var STATUS_META2 = {
14447
14787
  running: { led: "text-[hsl(var(--success))]", label: "running", pulse: true },
14448
14788
  completed: { led: "text-[hsl(var(--success))]", label: "done", pulse: false },
@@ -14477,7 +14817,7 @@ function AgentDetail({
14477
14817
  const [copied, setCopied] = useState35(false);
14478
14818
  const leaderId = useFleetStore((s) => s.leaderId);
14479
14819
  const isLeader = agent.id === leaderId;
14480
- const handleCopy = useCallback18(async (text) => {
14820
+ const handleCopy = useCallback19(async (text) => {
14481
14821
  try {
14482
14822
  await navigator.clipboard.writeText(text);
14483
14823
  setCopied(true);
@@ -14777,7 +15117,7 @@ function FleetPanel({
14777
15117
  const agents = useFleetStore((s) => s.agents);
14778
15118
  const [collapsed, setCollapsed] = useState35(true);
14779
15119
  const [selectedId, setSelectedId] = useState35(null);
14780
- const list = useMemo11(() => {
15120
+ const list = useMemo12(() => {
14781
15121
  const arr = Array.from(agents.values());
14782
15122
  arr.sort((x, y) => {
14783
15123
  const xa = x.status === "running" ? 0 : 1;
@@ -14794,7 +15134,7 @@ function FleetPanel({
14794
15134
  const failed = list.filter((a) => a.status === "failed" || a.status === "timeout");
14795
15135
  const totalCost = list.reduce((sum, a) => sum + (a.costUsd ?? 0), 0);
14796
15136
  const effectiveCollapsed = collapsed && list.length >= 3;
14797
- return /* @__PURE__ */ jsxs53(Fragment12, { children: [
15137
+ return /* @__PURE__ */ jsxs53(Fragment13, { children: [
14798
15138
  /* @__PURE__ */ jsxs53(
14799
15139
  "div",
14800
15140
  {
@@ -14840,7 +15180,7 @@ function FleetPanel({
14840
15180
  }
14841
15181
 
14842
15182
  // src/components/SidePanel/AgentsPanel.tsx
14843
- import { Fragment as Fragment13, jsx as jsx60, jsxs as jsxs54 } from "react/jsx-runtime";
15183
+ import { Fragment as Fragment14, jsx as jsx60, jsxs as jsxs54 } from "react/jsx-runtime";
14844
15184
  var STATUS_META3 = {
14845
15185
  running: { led: "text-[hsl(var(--success))]", label: "running", pulse: true },
14846
15186
  completed: { led: "text-[hsl(var(--success))]", label: "done", pulse: false },
@@ -14891,7 +15231,7 @@ function AgentsPanel() {
14891
15231
  const fleetAgents = useFleetStore((s) => s.agents);
14892
15232
  const setCurrentView = useUIStore((s) => s.setCurrentView);
14893
15233
  const [selectedAgentId, setSelectedAgentId] = useState36(null);
14894
- const fleetList = useMemo12(() => {
15234
+ const fleetList = useMemo13(() => {
14895
15235
  const arr = Array.from(fleetAgents.values());
14896
15236
  arr.sort((x, y) => {
14897
15237
  const xa = x.status === "running" ? 0 : 1;
@@ -14910,7 +15250,7 @@ function AgentsPanel() {
14910
15250
  /* @__PURE__ */ jsx60("p", { className: "text-xs mt-1", children: "Subagents appear here when the fleet is active." })
14911
15251
  ] }) });
14912
15252
  }
14913
- return /* @__PURE__ */ jsxs54(Fragment13, { children: [
15253
+ return /* @__PURE__ */ jsxs54(Fragment14, { children: [
14914
15254
  /* @__PURE__ */ jsxs54("div", { className: "px-3 py-2 border-b text-[10px] text-muted-foreground flex items-center gap-2", children: [
14915
15255
  /* @__PURE__ */ jsx60("span", { className: "font-semibold uppercase tracking-wider", children: "Fleet" }),
14916
15256
  /* @__PURE__ */ jsxs54("span", { className: "ml-auto tabular-nums", children: [
@@ -14938,7 +15278,7 @@ function AgentsPanel() {
14938
15278
 
14939
15279
  // src/components/SidePanel/HistoryPanel.tsx
14940
15280
  import { LayoutGrid as LayoutGrid3 } from "lucide-react";
14941
- import { useEffect as useEffect33, useState as useState38 } from "react";
15281
+ import { useEffect as useEffect34, useState as useState38 } from "react";
14942
15282
 
14943
15283
  // src/components/SidePanel/SessionList.tsx
14944
15284
  import {
@@ -14950,8 +15290,8 @@ import {
14950
15290
  Trash2 as Trash24,
14951
15291
  X as X15
14952
15292
  } from "lucide-react";
14953
- import { useCallback as useCallback19, useEffect as useEffect32, useMemo as useMemo13, useState as useState37 } from "react";
14954
- import { Fragment as Fragment14, jsx as jsx61, jsxs as jsxs55 } from "react/jsx-runtime";
15293
+ import { useCallback as useCallback20, useEffect as useEffect33, useMemo as useMemo14, useState as useState37 } from "react";
15294
+ import { Fragment as Fragment15, jsx as jsx61, jsxs as jsxs55 } from "react/jsx-runtime";
14955
15295
  var formatRelative = (iso) => {
14956
15296
  const ts = Date.parse(iso);
14957
15297
  if (Number.isNaN(ts)) return "";
@@ -14984,12 +15324,12 @@ function SessionList({
14984
15324
  const [renamingId, setRenamingId] = useState37(null);
14985
15325
  const [renameDraft, setRenameDraft] = useState37("");
14986
15326
  const [resumingId, setResumingId] = useState37(null);
14987
- useEffect32(() => {
15327
+ useEffect33(() => {
14988
15328
  if (resumingId && historyEntries.some((e) => e.id === resumingId && e.isCurrent)) {
14989
15329
  setResumingId(null);
14990
15330
  }
14991
15331
  }, [resumingId, historyEntries]);
14992
- const handleResume = useCallback19(
15332
+ const handleResume = useCallback20(
14993
15333
  (id) => {
14994
15334
  setResumingId(id);
14995
15335
  resumeSession(id);
@@ -14997,11 +15337,11 @@ function SessionList({
14997
15337
  },
14998
15338
  [resumeSession]
14999
15339
  );
15000
- const emptySessionIds = useMemo13(
15340
+ const emptySessionIds = useMemo14(
15001
15341
  () => getEmptySessionIds(historyEntries),
15002
15342
  [historyEntries]
15003
15343
  );
15004
- const handleDeleteEmpty = useCallback19(async () => {
15344
+ const handleDeleteEmpty = useCallback20(async () => {
15005
15345
  if (emptySessionIds.length === 0) return;
15006
15346
  const ok = await confirmModal({
15007
15347
  title: emptySessionIds.length === 1 ? "Delete 1 empty session?" : `Delete ${emptySessionIds.length} empty sessions?`,
@@ -15050,7 +15390,7 @@ function SessionList({
15050
15390
  if (older.length) out.push({ label: "Earlier", rows: older });
15051
15391
  return out;
15052
15392
  })();
15053
- return /* @__PURE__ */ jsxs55(Fragment14, { children: [
15393
+ return /* @__PURE__ */ jsxs55(Fragment15, { children: [
15054
15394
  /* @__PURE__ */ jsxs55("div", { className: "flex items-center justify-between px-4 py-2 border-b", children: [
15055
15395
  /* @__PURE__ */ jsx61("span", { className: "text-xs uppercase tracking-wider text-muted-foreground", children: "Recent sessions" }),
15056
15396
  /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-1", children: [
@@ -15162,14 +15502,14 @@ Double-click to rename`, children: sessionNicknames[entry.id] || entry.title ||
15162
15502
  /* @__PURE__ */ jsx61(Loader29, { className: "h-3 w-3 animate-spin" }),
15163
15503
  "resuming\u2026"
15164
15504
  ] }) : /* @__PURE__ */ jsx61("span", { children: formatRelative(entry.startedAt) }),
15165
- entry.tokenTotal > 0 && /* @__PURE__ */ jsxs55(Fragment14, { children: [
15505
+ entry.tokenTotal > 0 && /* @__PURE__ */ jsxs55(Fragment15, { children: [
15166
15506
  /* @__PURE__ */ jsx61("span", { className: "opacity-50", children: "\xB7" }),
15167
15507
  /* @__PURE__ */ jsxs55("span", { className: "tabular-nums", children: [
15168
15508
  entry.tokenTotal.toLocaleString(),
15169
15509
  " tok"
15170
15510
  ] })
15171
15511
  ] }),
15172
- entry.isCurrent && /* @__PURE__ */ jsxs55(Fragment14, { children: [
15512
+ entry.isCurrent && /* @__PURE__ */ jsxs55(Fragment15, { children: [
15173
15513
  /* @__PURE__ */ jsx61("span", { className: "opacity-50", children: "\xB7" }),
15174
15514
  /* @__PURE__ */ jsx61("span", { className: "text-primary font-medium", children: "active" })
15175
15515
  ] })
@@ -15212,7 +15552,7 @@ function HistoryPanel() {
15212
15552
  const setCurrentView = useUIStore((s) => s.setCurrentView);
15213
15553
  const activeSessionId = useSessionStore((s) => s.session?.id);
15214
15554
  const [query, setQuery] = useState38("");
15215
- useEffect33(() => {
15555
+ useEffect34(() => {
15216
15556
  void activeSessionId;
15217
15557
  if (wsConnected) listSessions(50);
15218
15558
  }, [wsConnected, activeSessionId, listSessions]);
@@ -15263,7 +15603,7 @@ import {
15263
15603
  Wifi as Wifi2,
15264
15604
  WifiOff as WifiOff3
15265
15605
  } from "lucide-react";
15266
- import { useCallback as useCallback20, useEffect as useEffect34, useMemo as useMemo14, useState as useState39 } from "react";
15606
+ import { useCallback as useCallback21, useEffect as useEffect35, useMemo as useMemo15, useState as useState39 } from "react";
15267
15607
  import { jsx as jsx63, jsxs as jsxs57 } from "react/jsx-runtime";
15268
15608
  function fmtCost3(v) {
15269
15609
  if (v <= 0) return "$0.000";
@@ -15383,7 +15723,7 @@ function SessionPanel() {
15383
15723
  const unpinAll = useUIStore((s) => s.unpinAll);
15384
15724
  const setModelSwitcherOpen = useUIStore((s) => s.setModelSwitcherOpen);
15385
15725
  const localPrefs = useLocalPrefs();
15386
- const syncPref = useCallback20(
15726
+ const syncPref = useCallback21(
15387
15727
  (key, value) => {
15388
15728
  localPrefs.set({ [key]: value });
15389
15729
  updatePrefs({ [key]: value });
@@ -15393,12 +15733,12 @@ function SessionPanel() {
15393
15733
  const [breakdownOpen, setBreakdownOpen] = useState39(false);
15394
15734
  const startedAt = session?.startedAt ?? null;
15395
15735
  const [now, setNow] = useState39(() => Date.now());
15396
- useEffect34(() => {
15736
+ useEffect35(() => {
15397
15737
  if (!startedAt) return;
15398
15738
  const t = setInterval(() => setNow(Date.now()), 1e3);
15399
15739
  return () => clearInterval(t);
15400
15740
  }, [startedAt]);
15401
- const runningAgents = useMemo14(
15741
+ const runningAgents = useMemo15(
15402
15742
  () => Array.from(fleetAgents.values()).filter((a) => a.status === "running").length,
15403
15743
  [fleetAgents]
15404
15744
  );
@@ -15767,7 +16107,7 @@ function SidePanel() {
15767
16107
  const setSidebarWidth = useUIStore((s) => s.setSidebarWidth);
15768
16108
  const wsConnected = useConfigStore((s) => s.wsConnected);
15769
16109
  const { client: client2 } = useWebSocket();
15770
- useEffect35(() => {
16110
+ useEffect36(() => {
15771
16111
  if (activeActivity !== "files" || !wsConnected) return;
15772
16112
  useFileStore.getState().setTreeLoading(true);
15773
16113
  const cwd = useSessionStore.getState().cwd;
@@ -15834,11 +16174,30 @@ function SidePanel() {
15834
16174
 
15835
16175
  // src/components/WorkspaceDock.tsx
15836
16176
  import { Bot as Bot10, GitBranch as GitBranch3, ListTodo as ListTodo2, Maximize2 as Maximize22, Rocket as Rocket4, Target as Target3, Users as Users3 } from "lucide-react";
15837
- import { useCallback as useCallback25, useEffect as useEffect41, useMemo as useMemo15, useRef as useRef23, useState as useState46 } from "react";
16177
+ import { useCallback as useCallback27, useEffect as useEffect43, useMemo as useMemo16, useRef as useRef25, useState as useState46 } from "react";
16178
+
16179
+ // src/hooks/useGitInfo.ts
16180
+ import { useCallback as useCallback22, useEffect as useEffect37, useRef as useRef21 } from "react";
16181
+ function useGitInfo() {
16182
+ const info = useGitInfoStore((s) => s.info);
16183
+ const { client: client2 } = useWebSocket();
16184
+ const intervalRef = useRef21(null);
16185
+ const fetch2 = useCallback22(() => {
16186
+ client2?.getGitInfo?.();
16187
+ }, [client2]);
16188
+ useEffect37(() => {
16189
+ fetch2();
16190
+ intervalRef.current = setInterval(fetch2, 3e4);
16191
+ return () => {
16192
+ if (intervalRef.current) clearInterval(intervalRef.current);
16193
+ };
16194
+ }, [fetch2]);
16195
+ return info;
16196
+ }
15838
16197
 
15839
16198
  // src/components/CollabPanel.tsx
15840
16199
  import { Eye as Eye2, LogIn, LogOut, MessageSquareWarning, Pause as Pause5, Play as Play6, Users as Users2 } from "lucide-react";
15841
- import { useEffect as useEffect36, useState as useState40 } from "react";
16200
+ import { useEffect as useEffect38, useState as useState40 } from "react";
15842
16201
  import { jsx as jsx65, jsxs as jsxs59 } from "react/jsx-runtime";
15843
16202
  function CollabPanel({ sessionId, className }) {
15844
16203
  const [participants, setParticipants] = useState40([]);
@@ -15849,7 +16208,7 @@ function CollabPanel({ sessionId, className }) {
15849
16208
  const [paused, setPaused] = useState40(false);
15850
16209
  const wsUrl = useConfigStore((s) => s.wsUrl);
15851
16210
  const client2 = getWSClient(wsUrl);
15852
- useEffect36(() => {
16211
+ useEffect38(() => {
15853
16212
  const offs = [];
15854
16213
  offs.push(
15855
16214
  client2.on("collab.state", (msg) => {
@@ -16137,7 +16496,7 @@ import {
16137
16496
  TrendingUp,
16138
16497
  Minus
16139
16498
  } from "lucide-react";
16140
- import { useEffect as useEffect37, useState as useState41 } from "react";
16499
+ import { useEffect as useEffect39, useState as useState41 } from "react";
16141
16500
  import { jsx as jsx66, jsxs as jsxs60 } from "react/jsx-runtime";
16142
16501
  var TREND_ICON = {
16143
16502
  up: /* @__PURE__ */ jsx66(TrendingUp, { className: "h-3.5 w-3.5 text-emerald-500" }),
@@ -16173,7 +16532,7 @@ var STATE_CONFIG = {
16173
16532
  };
16174
16533
  function GoalPanel({ goal, className }) {
16175
16534
  const [collapsed, setCollapsed] = useState41(false);
16176
- useEffect37(() => {
16535
+ useEffect39(() => {
16177
16536
  const ws = getWSClient();
16178
16537
  ws?.send?.({ type: "goal.get" });
16179
16538
  const timer = setInterval(() => {
@@ -16181,7 +16540,7 @@ function GoalPanel({ goal, className }) {
16181
16540
  }, 1e4);
16182
16541
  return () => clearInterval(timer);
16183
16542
  }, []);
16184
- useEffect37(() => {
16543
+ useEffect39(() => {
16185
16544
  if (!goal || goal.goalState === "completed" || goal.goalState === "failed" || goal.goalState === "abandoned") {
16186
16545
  setCollapsed(true);
16187
16546
  }
@@ -16305,7 +16664,7 @@ import { useState as useState45 } from "react";
16305
16664
 
16306
16665
  // src/components/TodosPanel.tsx
16307
16666
  import { CheckCircle2 as CheckCircle213, Circle as Circle7, Clock as Clock12, Trash2 as Trash25 } from "lucide-react";
16308
- import { useCallback as useCallback22, useEffect as useEffect38, useRef as useRef20, useState as useState42 } from "react";
16667
+ import { useCallback as useCallback24, useEffect as useEffect40, useRef as useRef22, useState as useState42 } from "react";
16309
16668
  import { jsx as jsx67, jsxs as jsxs61 } from "react/jsx-runtime";
16310
16669
  var STATUS_ORDER = {
16311
16670
  in_progress: 0,
@@ -16316,8 +16675,8 @@ function TodosPanel() {
16316
16675
  const [todos, setTodos] = useState42([]);
16317
16676
  const [collapsedSections, setCollapsedSections] = useState42(/* @__PURE__ */ new Set());
16318
16677
  const ws = getWSClient();
16319
- const offRef = useRef20(null);
16320
- useEffect38(() => {
16678
+ const offRef = useRef22(null);
16679
+ useEffect40(() => {
16321
16680
  ws.send({ type: "todos.get" });
16322
16681
  offRef.current = ws.on("todos.updated", (msg) => {
16323
16682
  const payload = msg?.payload;
@@ -16327,10 +16686,10 @@ function TodosPanel() {
16327
16686
  offRef.current?.();
16328
16687
  };
16329
16688
  }, [ws]);
16330
- const handleRemove = useCallback22((id) => {
16689
+ const handleRemove = useCallback24((id) => {
16331
16690
  ws.removeTodo(id);
16332
16691
  }, [ws]);
16333
- const handleToggle = useCallback22(
16692
+ const handleToggle = useCallback24(
16334
16693
  (t) => {
16335
16694
  if (t.status === "in_progress") return;
16336
16695
  const next = t.status === "completed" ? "pending" : "completed";
@@ -16450,7 +16809,7 @@ function TodosPanel() {
16450
16809
 
16451
16810
  // src/components/TasksPanel.tsx
16452
16811
  import { CheckCircle2 as CheckCircle214, Circle as Circle8, Clock as Clock13, Pause as Pause6, XCircle as XCircle7, RotateCcw as RotateCcw5 } from "lucide-react";
16453
- import { useCallback as useCallback23, useEffect as useEffect39, useRef as useRef21, useState as useState43 } from "react";
16812
+ import { useCallback as useCallback25, useEffect as useEffect41, useRef as useRef23, useState as useState43 } from "react";
16454
16813
  import { jsx as jsx68, jsxs as jsxs62 } from "react/jsx-runtime";
16455
16814
  var STATUS_CONFIG2 = {
16456
16815
  pending: { icon: /* @__PURE__ */ jsx68(Circle8, { className: "w-3.5 h-3.5" }), label: "Pending", color: "text-muted-foreground/50" },
@@ -16478,8 +16837,8 @@ function TasksPanel() {
16478
16837
  const [tasks, setTasks] = useState43([]);
16479
16838
  const [collapsed, setCollapsed] = useState43(/* @__PURE__ */ new Set());
16480
16839
  const ws = getWSClient();
16481
- const offRef = useRef21(null);
16482
- useEffect39(() => {
16840
+ const offRef = useRef23(null);
16841
+ useEffect41(() => {
16483
16842
  ws.getTasks();
16484
16843
  offRef.current = ws.on("tasks.updated", (msg) => {
16485
16844
  const payload = msg?.payload;
@@ -16489,7 +16848,7 @@ function TasksPanel() {
16489
16848
  offRef.current?.();
16490
16849
  };
16491
16850
  }, [ws]);
16492
- const toggle = useCallback23((key) => {
16851
+ const toggle = useCallback25((key) => {
16493
16852
  setCollapsed((prev) => {
16494
16853
  const next = new Set(prev);
16495
16854
  if (next.has(key)) next.delete(key);
@@ -16497,7 +16856,7 @@ function TasksPanel() {
16497
16856
  return next;
16498
16857
  });
16499
16858
  }, []);
16500
- const handleStatusChange = useCallback23(
16859
+ const handleStatusChange = useCallback25(
16501
16860
  (taskId, status) => {
16502
16861
  ws.updateTaskStatus(taskId, status);
16503
16862
  },
@@ -16619,7 +16978,7 @@ function TasksPanel() {
16619
16978
 
16620
16979
  // src/components/PlanPanel.tsx
16621
16980
  import { CheckCircle2 as CheckCircle215, Circle as Circle9, Clock as Clock14 } from "lucide-react";
16622
- import { useCallback as useCallback24, useEffect as useEffect40, useRef as useRef22, useState as useState44 } from "react";
16981
+ import { useCallback as useCallback26, useEffect as useEffect42, useRef as useRef24, useState as useState44 } from "react";
16623
16982
  import { jsx as jsx69, jsxs as jsxs63 } from "react/jsx-runtime";
16624
16983
  var STATUS_CONFIG3 = {
16625
16984
  open: { icon: /* @__PURE__ */ jsx69(Circle9, { className: "w-3.5 h-3.5" }), label: "Open", color: "text-muted-foreground/50" },
@@ -16630,8 +16989,8 @@ function PlanPanel() {
16630
16989
  const [items, setItems] = useState44([]);
16631
16990
  const [collapsed, setCollapsed] = useState44(/* @__PURE__ */ new Set());
16632
16991
  const ws = getWSClient();
16633
- const offRef = useRef22(null);
16634
- useEffect40(() => {
16992
+ const offRef = useRef24(null);
16993
+ useEffect42(() => {
16635
16994
  ws.getPlan();
16636
16995
  offRef.current = ws.on("plan.updated", (msg) => {
16637
16996
  const payload = msg?.payload;
@@ -16641,7 +17000,7 @@ function PlanPanel() {
16641
17000
  offRef.current?.();
16642
17001
  };
16643
17002
  }, [ws]);
16644
- const toggle = useCallback24((key) => {
17003
+ const toggle = useCallback26((key) => {
16645
17004
  setCollapsed((prev) => {
16646
17005
  const next = new Set(prev);
16647
17006
  if (next.has(key)) next.delete(key);
@@ -16649,7 +17008,7 @@ function PlanPanel() {
16649
17008
  return next;
16650
17009
  });
16651
17010
  }, []);
16652
- const handleStatusChange = useCallback24(
17011
+ const handleStatusChange = useCallback26(
16653
17012
  (item, status) => {
16654
17013
  ws.updatePlanItem(item.id, status);
16655
17014
  },
@@ -16857,25 +17216,26 @@ function WorkspaceDock({ sessionId }) {
16857
17216
  const baseBranch = useWorktreeStore((s) => s.baseBranch);
16858
17217
  const todos = useSessionStore((s) => s.todos);
16859
17218
  const fleetAgents = useFleetStore((s) => s.agents);
16860
- const handlePhaseClick = useCallback25(
17219
+ const gitInfo = useGitInfo();
17220
+ const handlePhaseClick = useCallback27(
16861
17221
  (phaseId) => selectAutoPhase(phaseId),
16862
17222
  [selectAutoPhase]
16863
17223
  );
16864
- const handleToggleAutonomous = useCallback25(
17224
+ const handleToggleAutonomous = useCallback27(
16865
17225
  () => toggleAutoPhaseAutonomous(!autoPhase.autonomous),
16866
17226
  [toggleAutoPhaseAutonomous, autoPhase.autonomous]
16867
17227
  );
16868
17228
  const [worktreeView, setWorktreeView] = useState46("graph");
16869
17229
  const fleetTotal = fleetAgents.size;
16870
- const fleetRunning = useMemo15(
17230
+ const fleetRunning = useMemo16(
16871
17231
  () => Array.from(fleetAgents.values()).filter((a) => a.status === "running").length,
16872
17232
  [fleetAgents]
16873
17233
  );
16874
17234
  const todosDone = todos.filter((t) => t.status === "completed").length;
16875
17235
  const todosActive = todos.some((t) => t.status === "in_progress");
16876
17236
  const phasesActive = autoPhase.phases.length > 0;
16877
- const prevPhaseCount = useRef23(autoPhase.phases.length);
16878
- useEffect41(() => {
17237
+ const prevPhaseCount = useRef25(autoPhase.phases.length);
17238
+ useEffect43(() => {
16879
17239
  const ui = useUIStore.getState();
16880
17240
  if (prevPhaseCount.current === 0 && autoPhase.phases.length > 0 && ui.dockSection === null) {
16881
17241
  ui.setDockSection("autophase");
@@ -16955,6 +17315,39 @@ function WorkspaceDock({ sessionId }) {
16955
17315
  onClick: () => toggleDockSection("worktrees")
16956
17316
  }
16957
17317
  ),
17318
+ gitInfo && /* @__PURE__ */ jsxs65(
17319
+ "button",
17320
+ {
17321
+ type: "button",
17322
+ onClick: () => {
17323
+ },
17324
+ className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-[11px] font-mono border border-transparent hover:border-border transition-colors",
17325
+ children: [
17326
+ /* @__PURE__ */ jsx71(GitBranch3, { className: "h-3 w-3 shrink-0 text-muted-foreground" }),
17327
+ /* @__PURE__ */ jsx71("span", { className: "font-semibold text-foreground", children: gitInfo.branch }),
17328
+ gitInfo.ahead > 0 && /* @__PURE__ */ jsxs65("span", { className: "text-emerald-600 dark:text-emerald-400", title: `${gitInfo.ahead} ahead`, children: [
17329
+ "\u2191",
17330
+ gitInfo.ahead
17331
+ ] }),
17332
+ gitInfo.behind > 0 && /* @__PURE__ */ jsxs65("span", { className: "text-amber-600 dark:text-amber-400", title: `${gitInfo.behind} behind`, children: [
17333
+ "\u2193",
17334
+ gitInfo.behind
17335
+ ] }),
17336
+ gitInfo.added > 0 && /* @__PURE__ */ jsxs65("span", { className: "text-emerald-600 dark:text-emerald-400", title: `${gitInfo.added} lines added`, children: [
17337
+ "+",
17338
+ gitInfo.added
17339
+ ] }),
17340
+ gitInfo.deleted > 0 && /* @__PURE__ */ jsxs65("span", { className: "text-red-600 dark:text-red-400", title: `${gitInfo.deleted} lines deleted`, children: [
17341
+ "-",
17342
+ gitInfo.deleted
17343
+ ] }),
17344
+ gitInfo.untracked > 0 && /* @__PURE__ */ jsxs65("span", { className: "text-muted-foreground", title: `${gitInfo.untracked} untracked files`, children: [
17345
+ gitInfo.untracked,
17346
+ "?"
17347
+ ] })
17348
+ ]
17349
+ }
17350
+ ),
16958
17351
  /* @__PURE__ */ jsx71(
16959
17352
  DockChip,
16960
17353
  {
@@ -17018,8 +17411,8 @@ function WorkspaceDock({ sessionId }) {
17018
17411
 
17019
17412
  // src/components/AgentsMonitor.tsx
17020
17413
  import { Bot as Bot11, ChevronLeft, ChevronRight as ChevronRight9, Cpu as Cpu13, Crown as Crown2, Loader2 as Loader210, Wrench as Wrench8, X as X16, Zap as Zap9 } from "lucide-react";
17021
- import { useCallback as useCallback26, useEffect as useEffect42, useMemo as useMemo16, useState as useState47 } from "react";
17022
- import { Fragment as Fragment15, jsx as jsx72, jsxs as jsxs66 } from "react/jsx-runtime";
17414
+ import { useCallback as useCallback28, useEffect as useEffect44, useMemo as useMemo17, useState as useState47 } from "react";
17415
+ import { Fragment as Fragment16, jsx as jsx72, jsxs as jsxs66 } from "react/jsx-runtime";
17023
17416
  function fmtCost4(v) {
17024
17417
  if (v <= 0) return "$0";
17025
17418
  if (v >= 0.01) return `$${v.toFixed(3)}`;
@@ -17144,7 +17537,7 @@ function AgentsMonitor({ onClose }) {
17144
17537
  const fleetAgents = useFleetStore((s) => s.agents);
17145
17538
  const leaderId = useFleetStore((s) => s.leaderId);
17146
17539
  const [selectedIdx, setSelectedIdx] = useState47(0);
17147
- const fleetList = useMemo16(() => {
17540
+ const fleetList = useMemo17(() => {
17148
17541
  const arr = Array.from(fleetAgents.values());
17149
17542
  arr.sort((x, y) => {
17150
17543
  if (x.id === leaderId) return -1;
@@ -17156,7 +17549,7 @@ function AgentsMonitor({ onClose }) {
17156
17549
  });
17157
17550
  return arr;
17158
17551
  }, [fleetAgents, leaderId]);
17159
- const handleKeyDown = useCallback26(
17552
+ const handleKeyDown = useCallback28(
17160
17553
  (e) => {
17161
17554
  if (e.key === "Escape") {
17162
17555
  onClose();
@@ -17175,7 +17568,7 @@ function AgentsMonitor({ onClose }) {
17175
17568
  },
17176
17569
  [fleetList.length, onClose]
17177
17570
  );
17178
- useEffect42(() => {
17571
+ useEffect44(() => {
17179
17572
  const handleGlobal = (e) => {
17180
17573
  if (e.key === "Escape") onClose();
17181
17574
  };
@@ -17183,7 +17576,7 @@ function AgentsMonitor({ onClose }) {
17183
17576
  return () => window.removeEventListener("keydown", handleGlobal);
17184
17577
  }, [onClose]);
17185
17578
  const selectedAgent = fleetList[selectedIdx] ?? null;
17186
- return /* @__PURE__ */ jsxs66(Fragment15, { children: [
17579
+ return /* @__PURE__ */ jsxs66(Fragment16, { children: [
17187
17580
  /* @__PURE__ */ jsx72(
17188
17581
  "div",
17189
17582
  {
@@ -17297,7 +17690,7 @@ import {
17297
17690
  XCircle as XCircle8,
17298
17691
  Zap as Zap10
17299
17692
  } from "lucide-react";
17300
- import { useCallback as useCallback27, useEffect as useEffect43, useMemo as useMemo17, useState as useState48 } from "react";
17693
+ import { useCallback as useCallback29, useEffect as useEffect45, useMemo as useMemo18, useState as useState48 } from "react";
17301
17694
 
17302
17695
  // src/components/ui/dropdown-menu.tsx
17303
17696
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
@@ -17486,7 +17879,7 @@ function EventTimeline({ events, max = 20, className }) {
17486
17879
  }
17487
17880
 
17488
17881
  // src/components/FleetMonitor.tsx
17489
- import { Fragment as Fragment16, jsx as jsx76, jsxs as jsxs70 } from "react/jsx-runtime";
17882
+ import { Fragment as Fragment17, jsx as jsx76, jsxs as jsxs70 } from "react/jsx-runtime";
17490
17883
  function fmtCost5(v) {
17491
17884
  if (v <= 0) return "$0";
17492
17885
  if (v >= 0.01) return `$${v.toFixed(3)}`;
@@ -17520,7 +17913,7 @@ function FleetAgentDetailPanel({
17520
17913
  const [showFullToolLog, setShowFullToolLog] = useState48(false);
17521
17914
  const meta2 = STATUS_META5[agent.status];
17522
17915
  const active = agent.status === "running";
17523
- const handleCopy = useCallback27(async (text) => {
17916
+ const handleCopy = useCallback29(async (text) => {
17524
17917
  try {
17525
17918
  await navigator.clipboard.writeText(text);
17526
17919
  setCopied(true);
@@ -17530,9 +17923,9 @@ function FleetAgentDetailPanel({
17530
17923
  }, []);
17531
17924
  const totalToolDuration = agent.toolLog.reduce((sum, t) => sum + t.durationMs, 0);
17532
17925
  const avgToolDuration = agent.toolLog.length > 0 ? Math.round(totalToolDuration / agent.toolLog.length) : 0;
17533
- const uniqueTools = useMemo17(() => {
17926
+ const uniqueTools = useMemo18(() => {
17534
17927
  const tools = /* @__PURE__ */ new Set();
17535
- agent.toolLog.forEach((t) => tools.add(t.name));
17928
+ for (const t of agent.toolLog) tools.add(t.name);
17536
17929
  return tools.size;
17537
17930
  }, [agent.toolLog]);
17538
17931
  const outputText = agent.partialText || agent.finalText || void 0;
@@ -17663,10 +18056,10 @@ function FleetAgentDetailPanel({
17663
18056
  ] }),
17664
18057
  outputText ? /* @__PURE__ */ jsxs70("div", { className: "rounded-lg border bg-card", children: [
17665
18058
  /* @__PURE__ */ jsxs70("div", { className: "flex items-center justify-between px-4 py-2 border-b bg-muted/30", children: [
17666
- /* @__PURE__ */ jsx76("span", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider flex items-center gap-2", children: isStream ? /* @__PURE__ */ jsxs70(Fragment16, { children: [
18059
+ /* @__PURE__ */ jsx76("span", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider flex items-center gap-2", children: isStream ? /* @__PURE__ */ jsxs70(Fragment17, { children: [
17667
18060
  /* @__PURE__ */ jsx76("span", { className: "w-2 h-2 rounded-full bg-blue-500 animate-pulse" }),
17668
18061
  "Live Output"
17669
- ] }) : /* @__PURE__ */ jsxs70(Fragment16, { children: [
18062
+ ] }) : /* @__PURE__ */ jsxs70(Fragment17, { children: [
17670
18063
  /* @__PURE__ */ jsx76(FolderOpen4, { className: "h-3 w-3" }),
17671
18064
  "Final Output"
17672
18065
  ] }) }),
@@ -17840,11 +18233,11 @@ function FleetMonitor({
17840
18233
  const eventTimeline = useFleetStore((s) => s.eventTimeline);
17841
18234
  const [selectedIdx, setSelectedIdx] = useState48(null);
17842
18235
  const [nowTick, setNowTick] = useState48(Date.now());
17843
- useEffect43(() => {
18236
+ useEffect45(() => {
17844
18237
  const t = setInterval(() => setNowTick(Date.now()), 1e3);
17845
18238
  return () => clearInterval(t);
17846
18239
  }, []);
17847
- const fleetList = useMemo17(() => {
18240
+ const fleetList = useMemo18(() => {
17848
18241
  const arr = Array.from(fleetAgents.values());
17849
18242
  arr.sort((x, y) => {
17850
18243
  if (x.id === leaderId) return -1;
@@ -17856,17 +18249,17 @@ function FleetMonitor({
17856
18249
  });
17857
18250
  return arr;
17858
18251
  }, [fleetAgents, leaderId]);
17859
- const totalCost = useMemo17(
18252
+ const totalCost = useMemo18(
17860
18253
  () => Array.from(fleetAgents.values()).reduce((sum, a) => sum + a.costUsd, 0),
17861
18254
  [fleetAgents]
17862
18255
  );
17863
18256
  const runningCount = fleetList.filter((a) => a.status === "running").length;
17864
18257
  const selectedAgent = selectedIdx !== null ? fleetList[selectedIdx] : null;
17865
- const handleAgentClick = useCallback27(
18258
+ const handleAgentClick = useCallback29(
17866
18259
  (i) => setSelectedIdx((prev) => prev === i ? null : i),
17867
18260
  []
17868
18261
  );
17869
- const handleKeyDown = useCallback27(
18262
+ const handleKeyDown = useCallback29(
17870
18263
  (e) => {
17871
18264
  if (e.key === "Escape") {
17872
18265
  if (selectedIdx !== null) {
@@ -17892,14 +18285,14 @@ function FleetMonitor({
17892
18285
  },
17893
18286
  [fleetList, selectedIdx, onClose, onSelectAgent]
17894
18287
  );
17895
- useEffect43(() => {
18288
+ useEffect45(() => {
17896
18289
  const handleGlobal = (e) => {
17897
18290
  if (e.key === "Escape") onClose();
17898
18291
  };
17899
18292
  window.addEventListener("keydown", handleGlobal);
17900
18293
  return () => window.removeEventListener("keydown", handleGlobal);
17901
18294
  }, [onClose]);
17902
- return /* @__PURE__ */ jsxs70(Fragment16, { children: [
18295
+ return /* @__PURE__ */ jsxs70(Fragment17, { children: [
17903
18296
  /* @__PURE__ */ jsx76(
17904
18297
  "div",
17905
18298
  {
@@ -18052,8 +18445,8 @@ function FleetMonitor({
18052
18445
 
18053
18446
  // src/components/InspectorPanel.tsx
18054
18447
  import { Bot as Bot13, ChevronDown as ChevronDown10, ChevronUp, Users as Users5 } from "lucide-react";
18055
- import { useMemo as useMemo18, useState as useState49 } from "react";
18056
- import { Fragment as Fragment17, jsx as jsx77, jsxs as jsxs71 } from "react/jsx-runtime";
18448
+ import { useMemo as useMemo19, useState as useState49 } from "react";
18449
+ import { Fragment as Fragment18, jsx as jsx77, jsxs as jsxs71 } from "react/jsx-runtime";
18057
18450
  var PANEL_HEIGHT = 320;
18058
18451
  function fmtCost6(v) {
18059
18452
  if (v <= 0) return "$0";
@@ -18090,7 +18483,7 @@ function InspectorPanel() {
18090
18483
  const fleetConcurrency = useFleetStore((s) => s.fleetConcurrency);
18091
18484
  const fleetConcurrencyMax = useFleetStore((s) => s.fleetConcurrencyMax);
18092
18485
  const eventTimeline = useFleetStore((s) => s.eventTimeline);
18093
- const fleetList = useMemo18(
18486
+ const fleetList = useMemo19(
18094
18487
  () => sortFleet(fleetAgents, leaderId),
18095
18488
  [fleetAgents, leaderId]
18096
18489
  );
@@ -18098,7 +18491,7 @@ function InspectorPanel() {
18098
18491
  const totalCost = fleetList.reduce((sum, a) => sum + a.costUsd, 0);
18099
18492
  const fleetTotal = fleetList.length;
18100
18493
  const [selectedAgentId, setSelectedAgentId] = useState49(null);
18101
- const selectedAgent = useMemo18(() => {
18494
+ const selectedAgent = useMemo19(() => {
18102
18495
  if (!selectedAgentId) return fleetList[0] ?? null;
18103
18496
  return fleetAgents.get(selectedAgentId) ?? fleetList[0] ?? null;
18104
18497
  }, [selectedAgentId, fleetList, fleetAgents]);
@@ -18123,7 +18516,7 @@ function InspectorPanel() {
18123
18516
  /* @__PURE__ */ jsxs71("span", { className: "flex items-center gap-2 min-w-0", children: [
18124
18517
  inspectorOpen ? /* @__PURE__ */ jsx77(ChevronDown10, { className: "h-3.5 w-3.5 shrink-0" }) : /* @__PURE__ */ jsx77(ChevronUp, { className: "h-3.5 w-3.5 shrink-0" }),
18125
18518
  /* @__PURE__ */ jsx77("span", { className: "font-medium uppercase tracking-wider", children: "Inspector" }),
18126
- fleetTotal > 0 && /* @__PURE__ */ jsxs71(Fragment17, { children: [
18519
+ fleetTotal > 0 && /* @__PURE__ */ jsxs71(Fragment18, { children: [
18127
18520
  /* @__PURE__ */ jsx77("span", { className: "opacity-40", children: "\xB7" }),
18128
18521
  /* @__PURE__ */ jsxs71("span", { className: "flex items-center gap-1", children: [
18129
18522
  /* @__PURE__ */ jsx77(
@@ -18364,8 +18757,106 @@ function AgentsTabContent({
18364
18757
  ] });
18365
18758
  }
18366
18759
 
18760
+ // src/components/QueuePanel.tsx
18761
+ import { ListOrdered, Trash2 as Trash26, X as X17 } from "lucide-react";
18762
+ import { useCallback as useCallback30 } from "react";
18763
+ import { jsx as jsx78, jsxs as jsxs72 } from "react/jsx-runtime";
18764
+ function QueuePanel({ open, onClose, className }) {
18765
+ const queue = useChatStore((s) => s.queue);
18766
+ const removeQueued = useChatStore((s) => s.removeQueued);
18767
+ const clearQueue = useChatStore((s) => s.clearQueue);
18768
+ const handleRemove = useCallback30(
18769
+ (index) => {
18770
+ removeQueued(index);
18771
+ },
18772
+ [removeQueued]
18773
+ );
18774
+ if (!open) return null;
18775
+ return /* @__PURE__ */ jsx78(
18776
+ "div",
18777
+ {
18778
+ className: cn(
18779
+ "fixed inset-0 z-50 flex items-start justify-center pt-[10vh] bg-black/40 backdrop-blur-sm",
18780
+ className
18781
+ ),
18782
+ onClick: (e) => {
18783
+ if (e.target === e.currentTarget) onClose();
18784
+ },
18785
+ children: /* @__PURE__ */ jsxs72("div", { className: "w-full max-w-lg rounded-xl border bg-card shadow-2xl max-h-[70vh] flex flex-col animate-in fade-in zoom-in-95", children: [
18786
+ /* @__PURE__ */ jsxs72("div", { className: "flex items-center justify-between px-4 py-3 border-b shrink-0", children: [
18787
+ /* @__PURE__ */ jsxs72("div", { className: "flex items-center gap-2.5", children: [
18788
+ /* @__PURE__ */ jsx78("span", { className: "flex items-center justify-center w-8 h-8 rounded-lg bg-indigo-500/10 text-indigo-600 dark:text-indigo-400", children: /* @__PURE__ */ jsx78(ListOrdered, { className: "h-4 w-4" }) }),
18789
+ /* @__PURE__ */ jsxs72("div", { children: [
18790
+ /* @__PURE__ */ jsx78("h2", { className: "text-sm font-semibold", children: "Message Queue" }),
18791
+ /* @__PURE__ */ jsxs72("span", { className: "text-[10px] text-muted-foreground tabular-nums", children: [
18792
+ queue.length,
18793
+ " queued \xB7 messages sent before the agent finishes"
18794
+ ] })
18795
+ ] })
18796
+ ] }),
18797
+ /* @__PURE__ */ jsxs72("div", { className: "flex items-center gap-1", children: [
18798
+ queue.length > 0 && /* @__PURE__ */ jsxs72(
18799
+ "button",
18800
+ {
18801
+ type: "button",
18802
+ onClick: () => clearQueue(),
18803
+ className: "flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-xs text-destructive hover:bg-destructive/10 transition-colors font-medium",
18804
+ title: "Clear all queued messages",
18805
+ children: [
18806
+ /* @__PURE__ */ jsx78(Trash26, { className: "h-3 w-3" }),
18807
+ "Clear"
18808
+ ]
18809
+ }
18810
+ ),
18811
+ /* @__PURE__ */ jsx78(
18812
+ "button",
18813
+ {
18814
+ type: "button",
18815
+ onClick: onClose,
18816
+ className: "p-1.5 rounded-md hover:bg-muted transition-colors",
18817
+ children: /* @__PURE__ */ jsx78(X17, { className: "h-4 w-4" })
18818
+ }
18819
+ )
18820
+ ] })
18821
+ ] }),
18822
+ /* @__PURE__ */ jsx78("div", { className: "flex-1 overflow-y-auto", children: queue.length === 0 ? /* @__PURE__ */ jsxs72("div", { className: "flex flex-col items-center justify-center py-16 gap-3 text-muted-foreground", children: [
18823
+ /* @__PURE__ */ jsx78(ListOrdered, { className: "h-10 w-10 opacity-15" }),
18824
+ /* @__PURE__ */ jsx78("p", { className: "text-sm font-medium", children: "Queue is empty" }),
18825
+ /* @__PURE__ */ jsx78("p", { className: "text-xs text-center max-w-xs", children: "Type messages while the agent is running to queue them. Queued messages are sent automatically when the agent finishes." })
18826
+ ] }) : /* @__PURE__ */ jsx78("div", { className: "divide-y", children: queue.map((item, idx) => /* @__PURE__ */ jsxs72(
18827
+ "div",
18828
+ {
18829
+ className: "flex items-start justify-between px-4 py-3 text-xs hover:bg-muted/30 transition-colors",
18830
+ children: [
18831
+ /* @__PURE__ */ jsxs72("div", { className: "flex items-start gap-3 min-w-0 flex-1", children: [
18832
+ /* @__PURE__ */ jsxs72("span", { className: "mt-0.5 text-[10px] font-mono text-muted-foreground shrink-0 w-5 text-right", children: [
18833
+ idx + 1,
18834
+ "."
18835
+ ] }),
18836
+ /* @__PURE__ */ jsx78("p", { className: "text-sm text-foreground leading-relaxed min-w-0 break-words", children: item.length > 120 ? `${item.slice(0, 117)}\u2026` : item })
18837
+ ] }),
18838
+ /* @__PURE__ */ jsx78(
18839
+ "button",
18840
+ {
18841
+ type: "button",
18842
+ onClick: () => handleRemove(idx),
18843
+ className: "ml-3 p-1.5 rounded-md shrink-0 hover:bg-destructive/10 hover:text-destructive transition-colors",
18844
+ title: "Remove from queue",
18845
+ children: /* @__PURE__ */ jsx78(X17, { className: "h-3.5 w-3.5" })
18846
+ }
18847
+ )
18848
+ ]
18849
+ },
18850
+ idx
18851
+ )) }) }),
18852
+ queue.length > 0 && /* @__PURE__ */ jsx78("div", { className: "px-4 py-2.5 border-t shrink-0", children: /* @__PURE__ */ jsx78("p", { className: "text-[10px] text-muted-foreground text-center", children: "Messages are sent in order when the current agent run completes." }) })
18853
+ ] })
18854
+ }
18855
+ );
18856
+ }
18857
+
18367
18858
  // src/App.tsx
18368
- import { Fragment as Fragment18, jsx as jsx78, jsxs as jsxs72 } from "react/jsx-runtime";
18859
+ import { Fragment as Fragment19, jsx as jsx79, jsxs as jsxs73 } from "react/jsx-runtime";
18369
18860
  function AppInner() {
18370
18861
  const { theme } = useTheme();
18371
18862
  const {
@@ -18380,7 +18871,11 @@ function AppInner() {
18380
18871
  fleetMonitorOpen,
18381
18872
  agentsMonitorOpen,
18382
18873
  setFleetMonitorOpen,
18383
- setAgentsMonitorOpen
18874
+ setAgentsMonitorOpen,
18875
+ processMonitorOpen,
18876
+ setProcessMonitorOpen,
18877
+ queuePanelOpen,
18878
+ setQueuePanelOpen
18384
18879
  } = useUIStore();
18385
18880
  const isLoading = useChatStore((s) => s.isLoading);
18386
18881
  const iteration = useSessionStore((s) => s.iteration);
@@ -18388,7 +18883,7 @@ function AppInner() {
18388
18883
  const sessionTitle = useSessionStore((s) => s.session?.title);
18389
18884
  const sessionId = useSessionStore((s) => s.session?.id);
18390
18885
  const nickname = useUIStore((s) => sessionId ? s.sessionNicknames[sessionId] : void 0);
18391
- useEffect44(() => {
18886
+ useEffect46(() => {
18392
18887
  const onOpenFile = (e) => {
18393
18888
  const { filePath } = e.detail;
18394
18889
  const ws = getWSClient(useConfigStore.getState().wsUrl);
@@ -18399,7 +18894,7 @@ function AppInner() {
18399
18894
  window.addEventListener("wrongstack:open-file", onOpenFile);
18400
18895
  return () => window.removeEventListener("wrongstack:open-file", onOpenFile);
18401
18896
  }, []);
18402
- useEffect44(() => {
18897
+ useEffect46(() => {
18403
18898
  const onSaveFile = (e) => {
18404
18899
  const { filePath } = e.detail;
18405
18900
  const file = useFileStore.getState().openFiles.find((f) => f.path === filePath);
@@ -18415,7 +18910,7 @@ function AppInner() {
18415
18910
  window.addEventListener("wrongstack:save-file", onSaveFile);
18416
18911
  return () => window.removeEventListener("wrongstack:save-file", onSaveFile);
18417
18912
  }, []);
18418
- useEffect44(() => {
18913
+ useEffect46(() => {
18419
18914
  if (typeof window === "undefined") return;
18420
18915
  const mq = window.matchMedia("(max-width: 768px)");
18421
18916
  const apply = () => {
@@ -18428,7 +18923,7 @@ function AppInner() {
18428
18923
  return () => mq.removeEventListener("change", apply);
18429
18924
  }, [setSidebarOpen]);
18430
18925
  useWebSocketBootstrap();
18431
- useEffect44(() => {
18926
+ useEffect46(() => {
18432
18927
  const parts = [];
18433
18928
  if (isLoading) {
18434
18929
  const it = iteration ? ` iter ${iteration.index}${iteration.max ? `/${iteration.max}` : ""}` : "";
@@ -18445,7 +18940,7 @@ function AppInner() {
18445
18940
  document.title = projectName || "AI Agent";
18446
18941
  };
18447
18942
  }, [isLoading, iteration, projectName, sessionTitle, nickname]);
18448
- useEffect44(() => {
18943
+ useEffect46(() => {
18449
18944
  const onKey = (e) => {
18450
18945
  const t = e.target;
18451
18946
  const tag = t?.tagName?.toLowerCase();
@@ -18573,41 +19068,43 @@ function AppInner() {
18573
19068
  window.addEventListener("keydown", onKey);
18574
19069
  return () => window.removeEventListener("keydown", onKey);
18575
19070
  }, [toggleSidebar, setSearchOpen]);
18576
- return /* @__PURE__ */ jsxs72("div", { className: cn("flex h-screen", theme), children: [
18577
- currentView !== "setup" && /* @__PURE__ */ jsx78(ActivityBar, {}),
18578
- sidebarOpen && currentView !== "setup" && /* @__PURE__ */ jsx78(SidePanel, {}),
18579
- /* @__PURE__ */ jsxs72("main", { className: "flex-1 flex flex-col overflow-hidden", children: [
18580
- currentView !== "setup" && /* @__PURE__ */ jsx78(ConnectionBanner, {}),
18581
- currentView === "chat" && /* @__PURE__ */ jsxs72(Fragment18, { children: [
18582
- sessionId && /* @__PURE__ */ jsx78("div", { className: "px-4 pt-2", children: /* @__PURE__ */ jsx78(WorkspaceDock, { sessionId }) }),
18583
- /* @__PURE__ */ jsx78(ChatView, {}),
18584
- /* @__PURE__ */ jsx78(InspectorPanel, {})
19071
+ return /* @__PURE__ */ jsxs73("div", { className: cn("flex h-screen", theme), children: [
19072
+ currentView !== "setup" && /* @__PURE__ */ jsx79(ActivityBar, {}),
19073
+ sidebarOpen && currentView !== "setup" && /* @__PURE__ */ jsx79(SidePanel, {}),
19074
+ /* @__PURE__ */ jsxs73("main", { className: "flex-1 flex flex-col overflow-hidden", children: [
19075
+ currentView !== "setup" && /* @__PURE__ */ jsx79(ConnectionBanner, {}),
19076
+ currentView === "chat" && /* @__PURE__ */ jsxs73(Fragment19, { children: [
19077
+ sessionId && /* @__PURE__ */ jsx79("div", { className: "px-4 pt-2", children: /* @__PURE__ */ jsx79(WorkspaceDock, { sessionId }) }),
19078
+ /* @__PURE__ */ jsx79(ChatView, {}),
19079
+ /* @__PURE__ */ jsx79(InspectorPanel, {})
18585
19080
  ] }),
18586
- currentView === "settings" && /* @__PURE__ */ jsx78(SettingsPanel, {}),
18587
- currentView === "setup" && /* @__PURE__ */ jsx78(SetupScreen, {}),
18588
- currentView === "autophase" && /* @__PURE__ */ jsx78(AutoPhaseView, { onClose: () => setCurrentView("chat") }),
18589
- currentView === "agentflow" && /* @__PURE__ */ jsx78("div", { className: "flex-1 flex flex-col overflow-hidden", children: /* @__PURE__ */ jsx78(AgentFlowCanvasWithProvider, {}) }),
18590
- currentView === "sessions" && /* @__PURE__ */ jsx78("div", { className: "flex-1 overflow-y-auto", children: /* @__PURE__ */ jsx78(SessionsDashboard, {}) }),
18591
- currentView === "files" && /* @__PURE__ */ jsx78(CodeEditor, {})
19081
+ currentView === "settings" && /* @__PURE__ */ jsx79(SettingsPanel, {}),
19082
+ currentView === "setup" && /* @__PURE__ */ jsx79(SetupScreen, {}),
19083
+ currentView === "autophase" && /* @__PURE__ */ jsx79(AutoPhaseView, { onClose: () => setCurrentView("chat") }),
19084
+ currentView === "agentflow" && /* @__PURE__ */ jsx79("div", { className: "flex-1 flex flex-col overflow-hidden", children: /* @__PURE__ */ jsx79(AgentFlowCanvasWithProvider, {}) }),
19085
+ currentView === "sessions" && /* @__PURE__ */ jsx79("div", { className: "flex-1 overflow-y-auto", children: /* @__PURE__ */ jsx79(SessionsDashboard, {}) }),
19086
+ currentView === "files" && /* @__PURE__ */ jsx79(CodeEditor, {})
18592
19087
  ] }),
18593
- fleetMonitorOpen && /* @__PURE__ */ jsx78(FleetMonitor, { onClose: () => setFleetMonitorOpen(false) }),
18594
- agentsMonitorOpen && /* @__PURE__ */ jsx78(AgentsMonitor, { onClose: () => setAgentsMonitorOpen(false) }),
18595
- /* @__PURE__ */ jsx78(ConfirmDialog, {}),
18596
- /* @__PURE__ */ jsx78(ConfirmModalHost, {}),
18597
- /* @__PURE__ */ jsx78(CommandPalette, {}),
18598
- /* @__PURE__ */ jsx78(ShortcutsOverlay, {}),
18599
- /* @__PURE__ */ jsx78(QuickModelSwitcher, {}),
18600
- /* @__PURE__ */ jsx78(Toaster, {})
19088
+ fleetMonitorOpen && /* @__PURE__ */ jsx79(FleetMonitor, { onClose: () => setFleetMonitorOpen(false) }),
19089
+ agentsMonitorOpen && /* @__PURE__ */ jsx79(AgentsMonitor, { onClose: () => setAgentsMonitorOpen(false) }),
19090
+ processMonitorOpen && /* @__PURE__ */ jsx79(ProcessMonitor, { open: processMonitorOpen, onClose: () => setProcessMonitorOpen(false) }),
19091
+ queuePanelOpen && /* @__PURE__ */ jsx79(QueuePanel, { open: queuePanelOpen, onClose: () => setQueuePanelOpen(false) }),
19092
+ /* @__PURE__ */ jsx79(ConfirmDialog, {}),
19093
+ /* @__PURE__ */ jsx79(ConfirmModalHost, {}),
19094
+ /* @__PURE__ */ jsx79(CommandPalette, {}),
19095
+ /* @__PURE__ */ jsx79(ShortcutsOverlay, {}),
19096
+ /* @__PURE__ */ jsx79(QuickModelSwitcher, {}),
19097
+ /* @__PURE__ */ jsx79(Toaster, {})
18601
19098
  ] });
18602
19099
  }
18603
19100
  function App() {
18604
- return /* @__PURE__ */ jsx78(ErrorBoundary, { children: /* @__PURE__ */ jsx78(ThemeProvider, { defaultTheme: "system", children: /* @__PURE__ */ jsx78(AppInner, {}) }) });
19101
+ return /* @__PURE__ */ jsx79(ErrorBoundary, { children: /* @__PURE__ */ jsx79(ThemeProvider, { defaultTheme: "system", children: /* @__PURE__ */ jsx79(AppInner, {}) }) });
18605
19102
  }
18606
19103
 
18607
19104
  // src/main.tsx
18608
19105
  import "./index.css";
18609
- import { jsx as jsx79 } from "react/jsx-runtime";
19106
+ import { jsx as jsx80 } from "react/jsx-runtime";
18610
19107
  ReactDOM.createRoot(expectDefined14(document.getElementById("root"))).render(
18611
- /* @__PURE__ */ jsx79(React7.StrictMode, { children: /* @__PURE__ */ jsx79(App, {}) })
19108
+ /* @__PURE__ */ jsx80(React7.StrictMode, { children: /* @__PURE__ */ jsx80(App, {}) })
18612
19109
  );
18613
19110
  //# sourceMappingURL=index.js.map