@wrongstack/tui 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
@@ -5,11 +5,11 @@ import { randomUUID } from 'crypto';
5
5
  import * as path4 from 'path';
6
6
  import React5, { forwardRef, useState, useEffect, useMemo, memo, useRef, useCallback, useReducer, useLayoutEffect } from 'react';
7
7
  import * as fs2 from 'fs/promises';
8
+ import { expectDefined, toErrorMessage } from '@wrongstack/core/utils';
8
9
  import { routeImagesForModel } from '@wrongstack/runtime/vision';
9
10
  import { getIndexState, onIndexStateChange, getProcessRegistry } from '@wrongstack/tools';
10
11
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
11
- import { readClipboardImage } from '@wrongstack/runtime/clipboard';
12
- import { expectDefined } from '@wrongstack/core/utils';
12
+ import { readClipboardText, readClipboardImage } from '@wrongstack/runtime/clipboard';
13
13
  import * as v8 from 'v8';
14
14
  import { spawn } from 'child_process';
15
15
 
@@ -160,6 +160,7 @@ function StatusBar({
160
160
  workingDir,
161
161
  processCount,
162
162
  context,
163
+ contextStrategy,
163
164
  hiddenItems,
164
165
  events,
165
166
  eternalStage,
@@ -273,7 +274,12 @@ function StatusBar({
273
274
  "ctx ",
274
275
  renderMeter(clampedRatio, 8),
275
276
  " ",
276
- pctText
277
+ pctText,
278
+ contextStrategy ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
279
+ " [",
280
+ contextStrategy,
281
+ "]"
282
+ ] }) : null
277
283
  ] });
278
284
  })()
279
285
  ] }) : null,
@@ -594,7 +600,8 @@ function StatusBar({
594
600
  ] }),
595
601
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
596
602
  " ",
597
- nextStepsAutoSubmitLabel ? formatSuggestionLabel(nextStepsAutoSubmitLabel) : ""
603
+ nextStepsAutoSubmitLabel ? formatSuggestionLabel(nextStepsAutoSubmitLabel) : "",
604
+ " \xB7 \u21E5 edit"
598
605
  ] })
599
606
  ] }) : null
600
607
  ] }) : /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }),
@@ -3922,13 +3929,14 @@ function parseWithHeading(content, strict) {
3922
3929
  if (steps.length === 0) {
3923
3930
  return { steps: [], texts: [], stripped: content, autoTexts: [] };
3924
3931
  }
3925
- if (strict && !afterHeading.includes("</next_steps>")) {
3932
+ const headingWasXmlTag = headingMatch[0].startsWith("<");
3933
+ if (strict && headingWasXmlTag && !afterHeading.includes("</next_steps>")) {
3926
3934
  return { steps: [], texts: [], stripped: content, autoTexts: [] };
3927
3935
  }
3928
3936
  const texts = steps.map((s2) => s2.text);
3929
3937
  const autoTexts = steps.filter((s2) => s2.auto).map((s2) => s2.text);
3930
3938
  const blockStart = headingMatch.index;
3931
- const blockEnd = headingEnd + findBlockEnd(afterHeading, steps.length);
3939
+ const blockEnd = headingMatch[0].length + findBlockEnd(afterHeading, steps.length);
3932
3940
  const stripped = (content.slice(0, blockStart) + content.slice(blockStart + blockEnd)).replace(/\n{3,}/g, "\n\n").trim();
3933
3941
  return { steps, texts, stripped, autoTexts };
3934
3942
  }
@@ -3937,24 +3945,26 @@ function buildPermissiveHeadingRe() {
3937
3945
  return new RegExp(variants, "i");
3938
3946
  }
3939
3947
  function findBlockEnd(afterHeading, stepCount) {
3948
+ const closeIdx = afterHeading.indexOf("</next_steps>");
3949
+ if (closeIdx !== -1) {
3950
+ let end = closeIdx + "</next_steps>".length;
3951
+ if (afterHeading[end] === "\n") end += 1;
3952
+ return end;
3953
+ }
3940
3954
  const lines = afterHeading.split("\n");
3941
3955
  let consumed = 0;
3942
3956
  let found = 0;
3943
3957
  for (const rawLine of lines) {
3944
3958
  const line = rawLine.trim();
3945
- if (line === "</next_steps>") {
3946
- consumed += rawLine.length + 1;
3947
- break;
3948
- }
3949
- if (!line) {
3950
- consumed += rawLine.length + 1;
3951
- continue;
3952
- }
3959
+ if (!line) break;
3953
3960
  const m = ITEM_RE.exec(line);
3954
3961
  if (!m) break;
3955
3962
  consumed += rawLine.length + 1;
3956
3963
  found++;
3957
- if (found >= stepCount) break;
3964
+ if (found >= stepCount) {
3965
+ consumed -= 1;
3966
+ break;
3967
+ }
3958
3968
  }
3959
3969
  return consumed;
3960
3970
  }
@@ -4936,12 +4946,8 @@ function PhaseMonitor({
4936
4946
  phases,
4937
4947
  runningPhaseIds,
4938
4948
  elapsedMs,
4939
- nowTick,
4940
- onClose
4949
+ nowTick
4941
4950
  }) {
4942
- useInput((_, key) => {
4943
- if (key.escape) onClose();
4944
- });
4945
4951
  const phaseList = Object.values(phases);
4946
4952
  const running = phaseList.filter(
4947
4953
  (p) => runningPhaseIds.includes(Object.keys(phases).find((k) => phases[k] === p) ?? "")
@@ -4973,7 +4979,7 @@ function PhaseMonitor({
4973
4979
  failed.length
4974
4980
  ] })
4975
4981
  ] }) : null,
4976
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 Ctrl+P / Esc to close" })
4982
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 Ctrl+P to close" })
4977
4983
  ] }),
4978
4984
  phaseList.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No phases active. Use /autophase start [title] to begin." }) : phaseList.map((phase, i) => {
4979
4985
  const s2 = fmtPhase(phase.status);
@@ -5002,7 +5008,7 @@ function PhaseMonitor({
5002
5008
  ] })
5003
5009
  ] }) }, phaseKey);
5004
5010
  }),
5005
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate phases \xB7 Esc close" }) })
5011
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate phases" }) })
5006
5012
  ] });
5007
5013
  }
5008
5014
  var fmtElapsed3 = (ms) => {
@@ -5183,7 +5189,7 @@ function QueuePanel({ items }) {
5183
5189
  "+",
5184
5190
  overflow
5185
5191
  ] }) : null,
5186
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 F7 / Esc to close" })
5192
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 F7 to close" })
5187
5193
  ] }) }),
5188
5194
  items.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No queued messages" }) : /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
5189
5195
  visible.map((item, i) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", flexShrink: 0, children: [
@@ -5259,7 +5265,7 @@ function ProcessListMonitor() {
5259
5265
  breakerState,
5260
5266
  b.state !== "closed" ? ` fail=${b.consecutiveFailures}/5 slow=${b.slowCallsInWindow}/3` : ""
5261
5267
  ] }),
5262
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 F8 / Esc close" })
5268
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 F8 to close" })
5263
5269
  ] }),
5264
5270
  all.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No active processes. Bash/exec spawns appear here." }) : null,
5265
5271
  all.map((p, i) => {
@@ -5300,7 +5306,7 @@ function GoalPanel({ goal }) {
5300
5306
  ] }),
5301
5307
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " to create one." })
5302
5308
  ] }),
5303
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Press F9 or Esc to close." }) })
5309
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Press F9 to close." }) })
5304
5310
  ] });
5305
5311
  }
5306
5312
  const displayGoal = goal.refinedGoal || goal.goal;
@@ -5366,7 +5372,7 @@ function GoalPanel({ goal }) {
5366
5372
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Last task: " }),
5367
5373
  /* @__PURE__ */ jsx(Text, { children: goal.lastTask.length > 50 ? goal.lastTask.slice(0, 47) + "\u2026" : goal.lastTask })
5368
5374
  ] }),
5369
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Press F9 or Esc to close." }) })
5375
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Press F9 to close." }) })
5370
5376
  ] })
5371
5377
  ] });
5372
5378
  }
@@ -5391,6 +5397,115 @@ function renderProgressBar(progress, trend) {
5391
5397
  ] })
5392
5398
  ] });
5393
5399
  }
5400
+ var KIND_COLOR = {
5401
+ goal: "cyan",
5402
+ task: "yellow",
5403
+ knowledge: "green",
5404
+ consensus: "magenta",
5405
+ deadlock: "red"
5406
+ };
5407
+ function fmtElapsed4(at, nowTick) {
5408
+ const s2 = Math.floor((nowTick - at) / 1e3);
5409
+ if (s2 < 60) return `${s2}s ago`;
5410
+ const m = Math.floor(s2 / 60);
5411
+ if (m < 60) return `${m}m ago`;
5412
+ return `${Math.floor(m / 60)}h ago`;
5413
+ }
5414
+ function GoalRow({
5415
+ goal
5416
+ }) {
5417
+ const statusColor = {
5418
+ active: "green",
5419
+ paused: "yellow",
5420
+ completed: "gray",
5421
+ failed: "red"
5422
+ };
5423
+ const color = statusColor[goal.status] ?? "gray";
5424
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
5425
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color, bold: true, children: [
5426
+ goal.status === "active" ? "\u25B6" : goal.status === "paused" ? "\u23F8" : goal.status === "completed" ? "\u2713" : "\u2717",
5427
+ " ",
5428
+ goal.title || "(unnamed goal)"
5429
+ ] }) }),
5430
+ goal.tasks.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingLeft: 2, children: goal.tasks.map((task) => {
5431
+ const taskColor = {
5432
+ pending: "gray",
5433
+ running: "yellow",
5434
+ done: "green",
5435
+ failed: "red"
5436
+ };
5437
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: taskColor[task.status] ?? "gray", children: [
5438
+ task.status === "pending" ? "\u25CB" : task.status === "running" ? "\u25B6" : task.status === "done" ? "\u2713" : "\u2717",
5439
+ " ",
5440
+ task.title,
5441
+ task.assignedTo ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5442
+ " \u2192 ",
5443
+ task.assignedTo
5444
+ ] }) : null
5445
+ ] }) }, task.id);
5446
+ }) }),
5447
+ goal.participants.length > 0 && /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
5448
+ "participants: ",
5449
+ goal.participants.join(", ")
5450
+ ] }) })
5451
+ ] }, goal.id);
5452
+ }
5453
+ function CoordinatorPanel({
5454
+ coordinator,
5455
+ nowTick,
5456
+ onClose
5457
+ }) {
5458
+ const handleInput = useCallback(
5459
+ (input, _key) => {
5460
+ if (input === "q" || input === "Q" || input === "\x1B") {
5461
+ onClose();
5462
+ }
5463
+ },
5464
+ [onClose]
5465
+ );
5466
+ useInput(handleInput);
5467
+ const { goals, timeline, knowledgeCount, healthy } = coordinator;
5468
+ return /* @__PURE__ */ jsxs(
5469
+ Box,
5470
+ {
5471
+ flexDirection: "column",
5472
+ borderStyle: "round",
5473
+ borderColor: "cyan",
5474
+ paddingX: 1,
5475
+ height: Math.min(30, Math.max(10, goals.length * 4 + timeline.length + 8)),
5476
+ width: 80,
5477
+ children: [
5478
+ /* @__PURE__ */ jsxs(Box, { borderStyle: "bold", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [
5479
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u{1F916} AutonomousCoordinator" }),
5480
+ /* @__PURE__ */ jsx(Box, { flexGrow: 1 }),
5481
+ /* @__PURE__ */ jsx(Text, { dimColor: !healthy, color: healthy ? "green" : "red", children: healthy ? "\u25CF connected" : "\u25CB disconnected" }),
5482
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 q/esc to close" })
5483
+ ] }),
5484
+ goals.length > 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
5485
+ /* @__PURE__ */ jsxs(Text, { bold: true, children: [
5486
+ "Goals (",
5487
+ goals.length,
5488
+ ")"
5489
+ ] }),
5490
+ goals.map((goal) => /* @__PURE__ */ jsx(GoalRow, { goal }, goal.id))
5491
+ ] }),
5492
+ /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
5493
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Knowledge " }),
5494
+ /* @__PURE__ */ jsx(Text, { color: "green", children: knowledgeCount }),
5495
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " shared facts" })
5496
+ ] }),
5497
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [
5498
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Activity" }),
5499
+ timeline.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: " No activity yet" }) : timeline.slice(0, 10).map((entry, i) => /* @__PURE__ */ jsxs(Box, { alignItems: "flex-start", children: [
5500
+ /* @__PURE__ */ jsx(Text, { color: KIND_COLOR[entry.kind] ?? "gray", dimColor: i > 2, children: entry.icon }),
5501
+ /* @__PURE__ */ jsx(Box, { flexGrow: 1, marginLeft: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: i > 2, children: entry.text }) }),
5502
+ /* @__PURE__ */ jsx(Box, { marginLeft: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtElapsed4(entry.at, nowTick) }) })
5503
+ ] }, i))
5504
+ ] })
5505
+ ]
5506
+ }
5507
+ );
5508
+ }
5394
5509
  function ResumePicker({
5395
5510
  sessions,
5396
5511
  selected,
@@ -5498,7 +5613,7 @@ function SessionsPanel({
5498
5613
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, flexShrink: 0, children: [
5499
5614
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
5500
5615
  /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u29C9 Sessions" }),
5501
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 F10 or Esc to close" }),
5616
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 F10 to close" }),
5502
5617
  busy && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 loading\u2026" })
5503
5618
  ] }),
5504
5619
  resumeConfirm ? /* @__PURE__ */ jsxs(Box, { marginY: 1, borderStyle: "single", borderColor: "yellow", paddingX: 1, children: [
@@ -5611,7 +5726,7 @@ var MODE_DESC = {
5611
5726
  suggest: "Shows next-step suggestions after each turn",
5612
5727
  auto: "Self-driving \u2014 agent continues automatically"
5613
5728
  };
5614
- var SETTINGS_FIELD_COUNT = 26;
5729
+ var SETTINGS_FIELD_COUNT = 27;
5615
5730
  var CONFIG_SCOPES = ["global", "project"];
5616
5731
  function SettingsPicker({
5617
5732
  field,
@@ -5629,6 +5744,7 @@ function SettingsPicker({
5629
5744
  featureSkills,
5630
5745
  featureModelsRegistry,
5631
5746
  featureTokenSaving,
5747
+ allowOutsideProjectRoot,
5632
5748
  contextAutoCompact,
5633
5749
  contextStrategy,
5634
5750
  logLevel,
@@ -5717,6 +5833,11 @@ function SettingsPicker({
5717
5833
  value: boolVal(featureTokenSaving),
5718
5834
  detail: "Omit non-essential tools and trim system prompt to save tokens"
5719
5835
  },
5836
+ {
5837
+ label: "Allow outside project",
5838
+ value: boolVal(allowOutsideProjectRoot),
5839
+ detail: "Allow tools to access paths outside project root"
5840
+ },
5720
5841
  // ── Context ──
5721
5842
  { section: "Context" },
5722
5843
  {
@@ -5822,7 +5943,7 @@ function SettingsPicker({
5822
5943
  };
5823
5944
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
5824
5945
  /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Settings \u2501\u2501" }),
5825
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 field \xB7 \u2190/\u2192 change (instant save) \xB7 Esc / F5 close" }),
5946
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 field \xB7 \u2190/\u2192 change (instant apply) \xB7 F5 to close" }),
5826
5947
  hasAbove ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2191 ${windowStart} field${windowStart === 1 ? "" : "s"} above` }) : null,
5827
5948
  rows.map((row, i) => {
5828
5949
  const fieldAtRow = fieldRowIndex.indexOf(i);
@@ -6072,7 +6193,7 @@ function TodosMonitor({ todos }) {
6072
6193
  done
6073
6194
  ] })
6074
6195
  ] }) : null,
6075
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 F6 / Esc to close" })
6196
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 F6 to close" })
6076
6197
  ] }),
6077
6198
  todos.length === 0 ? /* @__PURE__ */ jsx(Box, { marginY: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No todos. The agent will create them as it plans work." }) }) : twoCols ? (
6078
6199
  /* Two-column layout: split the list in half, render side-by-side.
@@ -6087,7 +6208,7 @@ function TodosMonitor({ todos }) {
6087
6208
  )
6088
6209
  ] });
6089
6210
  }
6090
- var fmtElapsed4 = (ms) => {
6211
+ var fmtElapsed5 = (ms) => {
6091
6212
  const s2 = Math.floor(ms / 1e3);
6092
6213
  const m = Math.floor(s2 / 60);
6093
6214
  const h = Math.floor(m / 60);
@@ -6114,7 +6235,7 @@ function WorktreeMonitor({
6114
6235
  onClose
6115
6236
  }) {
6116
6237
  useInput((input, key) => {
6117
- if (key.escape || key.ctrl && input === "w") onClose();
6238
+ if (key.ctrl && input === "w") onClose();
6118
6239
  });
6119
6240
  const TERMINAL_TTL_MS = 5 * 6e4;
6120
6241
  const list = Object.values(worktrees);
@@ -6151,12 +6272,12 @@ function WorktreeMonitor({
6151
6272
  failed
6152
6273
  ] })
6153
6274
  ] }) : null,
6154
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 Ctrl+T / F4 / Esc to close" })
6275
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 Ctrl+T / F4 to close" })
6155
6276
  ] }),
6156
6277
  recent.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No worktrees. They appear when AutoPhase runs with isolation on." }) : recent.map((w) => {
6157
6278
  const s2 = fmt(w.status);
6158
6279
  const short = w.branch.replace(/^wstack\/ap\//, "");
6159
- const elapsed = w.allocatedAt ? fmtElapsed4(nowTick - w.allocatedAt) : "\u2014";
6280
+ const elapsed = w.allocatedAt ? fmtElapsed5(nowTick - w.allocatedAt) : "\u2014";
6160
6281
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
6161
6282
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
6162
6283
  /* @__PURE__ */ jsx(Text, { color: s2.color, bold: true, children: s2.icon }),
@@ -6201,12 +6322,12 @@ function WorktreeMonitor({
6201
6322
  ] }, w.branch);
6202
6323
  }),
6203
6324
  /* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
6204
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Esc close \xB7 merge conflicts with /worktree merge <branch>" }),
6325
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Ctrl+T to close \xB7 merge conflicts with /worktree merge <branch>" }),
6205
6326
  staleTerminal > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${staleTerminal} terminal pruned` }) : null
6206
6327
  ] })
6207
6328
  ] });
6208
6329
  }
6209
- var fmtElapsed5 = (ms) => {
6330
+ var fmtElapsed6 = (ms) => {
6210
6331
  const s2 = Math.floor(ms / 1e3);
6211
6332
  const m = Math.floor(s2 / 60);
6212
6333
  const h = Math.floor(m / 60);
@@ -6272,7 +6393,7 @@ function WorktreePanel({
6272
6393
  list.map((w) => {
6273
6394
  const s2 = st(w.status);
6274
6395
  const conflict = w.status === "needs-review";
6275
- const elapsed = w.allocatedAt ? fmtElapsed5(nowTick - w.allocatedAt) : "";
6396
+ const elapsed = w.allocatedAt ? fmtElapsed6(nowTick - w.allocatedAt) : "";
6276
6397
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
6277
6398
  /* @__PURE__ */ jsx(Text, { color: s2.color, children: s2.icon }),
6278
6399
  /* @__PURE__ */ jsx(Text, { children: w.branch.replace(/^wstack\/ap\//, "").slice(0, 18).padEnd(18) }),
@@ -6817,6 +6938,21 @@ function handleCollabDone(event, dispatch, stateRef) {
6817
6938
  verdict: payload.report.overallVerdict ?? "needs_revision"
6818
6939
  });
6819
6940
  }
6941
+ function useAutonomousCoordinator(subscribeCoordinatorEvents, dispatch) {
6942
+ const dispatchRef = useRef(dispatch);
6943
+ dispatchRef.current = dispatch;
6944
+ const handler = useCallback((event) => {
6945
+ dispatchRef.current({
6946
+ type: "coordinatorEvent",
6947
+ event
6948
+ });
6949
+ }, []);
6950
+ useEffect(() => {
6951
+ if (!subscribeCoordinatorEvents) return;
6952
+ const unsubscribe = subscribeCoordinatorEvents(handler);
6953
+ return unsubscribe;
6954
+ }, [subscribeCoordinatorEvents, handler]);
6955
+ }
6820
6956
  function useStatuslineState(opts) {
6821
6957
  const [liveModel, setLiveModel] = useState(opts.model);
6822
6958
  const [liveProvider, setLiveProvider] = useState(opts.provider ?? "agent");
@@ -7568,11 +7704,17 @@ function reducer(state, action) {
7568
7704
  const banner = state.entries.find((e) => e.kind === "banner");
7569
7705
  return {
7570
7706
  ...state,
7571
- entries: banner ? [banner] : state.entries,
7707
+ entries: banner ? [banner] : [],
7572
7708
  queue: [],
7573
7709
  nextQueueId: 1,
7574
7710
  scrollOffset: 0,
7575
7711
  pendingNewLines: 0,
7712
+ // Bump the generation so <Static> remounts — without this, Ink's
7713
+ // already-written index exceeds the new (shorter) array and the
7714
+ // committed entries stay on screen even though `state.entries` no
7715
+ // longer references them. /clear would otherwise appear to do
7716
+ // nothing to the visible chat history.
7717
+ historyGen: state.historyGen + 1,
7576
7718
  // Reset fleet state on /clear so old subagent entries don't
7577
7719
  // cause the LiveActivityStrip to render stale spacers, and
7578
7720
  // the fleet cost/tokens chips show zero.
@@ -7914,6 +8056,7 @@ function reducer(state, action) {
7914
8056
  featureSkills: action.featureSkills,
7915
8057
  featureModelsRegistry: action.featureModelsRegistry,
7916
8058
  featureTokenSaving: action.featureTokenSaving,
8059
+ allowOutsideProjectRoot: action.allowOutsideProjectRoot,
7917
8060
  contextAutoCompact: action.contextAutoCompact,
7918
8061
  contextStrategy: action.contextStrategy,
7919
8062
  logLevel: action.logLevel,
@@ -7948,6 +8091,7 @@ function reducer(state, action) {
7948
8091
  case "settingsValueChange": {
7949
8092
  const sp = state.settingsPicker;
7950
8093
  const f = sp.field;
8094
+ const bootHint = "\u21BB Takes effect next session";
7951
8095
  if (f === 0) {
7952
8096
  const i = SETTINGS_MODES.indexOf(sp.mode);
7953
8097
  const base = i < 0 ? 0 : i;
@@ -7966,59 +8110,60 @@ function reducer(state, action) {
7966
8110
  if (f === 5) return { ...state, settingsPicker: { ...sp, chime: !sp.chime, hint: void 0 } };
7967
8111
  if (f === 6) return { ...state, settingsPicker: { ...sp, confirmExit: !sp.confirmExit, hint: void 0 } };
7968
8112
  if (f === 7) return { ...state, settingsPicker: { ...sp, nextPrediction: !sp.nextPrediction, hint: void 0 } };
7969
- if (f === 8) return { ...state, settingsPicker: { ...sp, featureMcp: !sp.featureMcp, hint: void 0 } };
7970
- if (f === 9) return { ...state, settingsPicker: { ...sp, featurePlugins: !sp.featurePlugins, hint: void 0 } };
7971
- if (f === 10) return { ...state, settingsPicker: { ...sp, featureMemory: !sp.featureMemory, hint: void 0 } };
7972
- if (f === 11) return { ...state, settingsPicker: { ...sp, featureSkills: !sp.featureSkills, hint: void 0 } };
7973
- if (f === 12) return { ...state, settingsPicker: { ...sp, featureModelsRegistry: !sp.featureModelsRegistry, hint: void 0 } };
7974
- if (f === 13) return { ...state, settingsPicker: { ...sp, featureTokenSaving: !sp.featureTokenSaving, hint: void 0 } };
7975
- if (f === 14) return { ...state, settingsPicker: { ...sp, contextAutoCompact: !sp.contextAutoCompact, hint: void 0 } };
7976
- if (f === 15) {
8113
+ if (f === 8) return { ...state, settingsPicker: { ...sp, featureMcp: !sp.featureMcp, hint: bootHint } };
8114
+ if (f === 9) return { ...state, settingsPicker: { ...sp, featurePlugins: !sp.featurePlugins, hint: bootHint } };
8115
+ if (f === 10) return { ...state, settingsPicker: { ...sp, featureMemory: !sp.featureMemory, hint: bootHint } };
8116
+ if (f === 11) return { ...state, settingsPicker: { ...sp, featureSkills: !sp.featureSkills, hint: bootHint } };
8117
+ if (f === 12) return { ...state, settingsPicker: { ...sp, featureModelsRegistry: !sp.featureModelsRegistry, hint: bootHint } };
8118
+ if (f === 13) return { ...state, settingsPicker: { ...sp, featureTokenSaving: !sp.featureTokenSaving, hint: bootHint } };
8119
+ if (f === 14) return { ...state, settingsPicker: { ...sp, allowOutsideProjectRoot: !sp.allowOutsideProjectRoot, hint: void 0 } };
8120
+ if (f === 15) return { ...state, settingsPicker: { ...sp, contextAutoCompact: !sp.contextAutoCompact, hint: void 0 } };
8121
+ if (f === 16) {
7977
8122
  const i = COMPACTOR_STRATEGIES.indexOf(sp.contextStrategy);
7978
8123
  const base = i < 0 ? 0 : i;
7979
8124
  const next = (base + action.delta + COMPACTOR_STRATEGIES.length) % COMPACTOR_STRATEGIES.length;
7980
- return { ...state, settingsPicker: { ...sp, contextStrategy: expectDefined$1(COMPACTOR_STRATEGIES[next]), hint: void 0 } };
8125
+ return { ...state, settingsPicker: { ...sp, contextStrategy: expectDefined$1(COMPACTOR_STRATEGIES[next]), hint: bootHint } };
7981
8126
  }
7982
- if (f === 16) {
8127
+ if (f === 17) {
7983
8128
  const i = LOG_LEVELS.indexOf(sp.logLevel);
7984
8129
  const base = i < 0 ? 0 : i;
7985
8130
  const next = (base + action.delta + LOG_LEVELS.length) % LOG_LEVELS.length;
7986
8131
  return { ...state, settingsPicker: { ...sp, logLevel: expectDefined$1(LOG_LEVELS[next]), hint: void 0 } };
7987
8132
  }
7988
- if (f === 17) {
8133
+ if (f === 18) {
7989
8134
  const i = AUDIT_LEVELS.indexOf(sp.auditLevel);
7990
8135
  const base = i < 0 ? 0 : i;
7991
8136
  const next = (base + action.delta + AUDIT_LEVELS.length) % AUDIT_LEVELS.length;
7992
8137
  return { ...state, settingsPicker: { ...sp, auditLevel: expectDefined$1(AUDIT_LEVELS[next]), hint: void 0 } };
7993
8138
  }
7994
- if (f === 18) return { ...state, settingsPicker: { ...sp, indexOnStart: !sp.indexOnStart, hint: void 0 } };
7995
- if (f === 19) {
8139
+ if (f === 19) return { ...state, settingsPicker: { ...sp, indexOnStart: !sp.indexOnStart, hint: bootHint } };
8140
+ if (f === 20) {
7996
8141
  const j = MAX_ITERATIONS_PRESETS.indexOf(sp.maxIterations);
7997
8142
  const base = j < 0 ? 0 : j;
7998
8143
  const next = (base + action.delta + MAX_ITERATIONS_PRESETS.length) % MAX_ITERATIONS_PRESETS.length;
7999
8144
  return { ...state, settingsPicker: { ...sp, maxIterations: expectDefined$1(MAX_ITERATIONS_PRESETS[next]), hint: void 0 } };
8000
8145
  }
8001
- if (f === 20) {
8146
+ if (f === 21) {
8002
8147
  const aj = AUTO_PROCEED_MAX_PRESETS.indexOf(sp.autoProceedMaxIterations);
8003
8148
  const abase = aj < 0 ? 0 : aj;
8004
8149
  const anext = (abase + action.delta + AUTO_PROCEED_MAX_PRESETS.length) % AUTO_PROCEED_MAX_PRESETS.length;
8005
8150
  return { ...state, settingsPicker: { ...sp, autoProceedMaxIterations: expectDefined$1(AUTO_PROCEED_MAX_PRESETS[anext]), hint: void 0 } };
8006
8151
  }
8007
- if (f === 21) {
8152
+ if (f === 22) {
8008
8153
  const ej = ENHANCE_DELAY_PRESETS.indexOf(sp.enhanceDelayMs);
8009
8154
  const ebase = ej < 0 ? 0 : ej;
8010
8155
  const enext = (ebase + action.delta + ENHANCE_DELAY_PRESETS.length) % ENHANCE_DELAY_PRESETS.length;
8011
8156
  return { ...state, settingsPicker: { ...sp, enhanceDelayMs: expectDefined$1(ENHANCE_DELAY_PRESETS[enext]), hint: void 0 } };
8012
8157
  }
8013
- if (f === 22) return { ...state, settingsPicker: { ...sp, enhanceEnabled: !sp.enhanceEnabled, hint: void 0 } };
8014
- if (f === 23) {
8158
+ if (f === 23) return { ...state, settingsPicker: { ...sp, enhanceEnabled: !sp.enhanceEnabled, hint: void 0 } };
8159
+ if (f === 24) {
8015
8160
  const i = ENHANCE_LANGUAGES.indexOf(sp.enhanceLanguage);
8016
8161
  const base = i < 0 ? 0 : i;
8017
8162
  const next = (base + action.delta + ENHANCE_LANGUAGES.length) % ENHANCE_LANGUAGES.length;
8018
8163
  return { ...state, settingsPicker: { ...sp, enhanceLanguage: expectDefined$1(ENHANCE_LANGUAGES[next]), hint: void 0 } };
8019
8164
  }
8020
- if (f === 24) return { ...state, settingsPicker: { ...sp, debugStream: !sp.debugStream, hint: void 0 } };
8021
- if (f === 25) {
8165
+ if (f === 25) return { ...state, settingsPicker: { ...sp, debugStream: !sp.debugStream, hint: void 0 } };
8166
+ if (f === 26) {
8022
8167
  const i = CONFIG_SCOPES.indexOf(sp.configScope);
8023
8168
  const base = i < 0 ? 0 : i;
8024
8169
  const next = (base + action.delta + CONFIG_SCOPES.length) % CONFIG_SCOPES.length;
@@ -8749,9 +8894,10 @@ function reducer(state, action) {
8749
8894
  return { ...state, sessionsPanelOpen: !state.sessionsPanelOpen };
8750
8895
  }
8751
8896
  case "sessionsPanelSet": {
8897
+ const sessions = Array.isArray(action.sessions) ? action.sessions : [];
8752
8898
  return {
8753
8899
  ...state,
8754
- sessionsPanel: { sessions: action.sessions, busy: false, selected: action.sessions.length > 0 ? 0 : -1 }
8900
+ sessionsPanel: { sessions, busy: false, selected: sessions.length > 0 ? 0 : -1 }
8755
8901
  };
8756
8902
  }
8757
8903
  case "sessionsPanelMove": {
@@ -8786,6 +8932,81 @@ function reducer(state, action) {
8786
8932
  if (state.countdown === null) return state;
8787
8933
  return { ...state, countdown: null };
8788
8934
  }
8935
+ // --- AutonomousCoordinator ---
8936
+ case "coordinatorEvent": {
8937
+ const { event } = action;
8938
+ const now = Date.now();
8939
+ let kind;
8940
+ let icon;
8941
+ switch (event.type) {
8942
+ case "goal:added":
8943
+ kind = "goal";
8944
+ icon = "\u{1F3AF}";
8945
+ break;
8946
+ case "goal:completed":
8947
+ kind = "goal";
8948
+ icon = "\u2705";
8949
+ break;
8950
+ case "goal:failed":
8951
+ kind = "goal";
8952
+ icon = "\u274C";
8953
+ break;
8954
+ case "task:ready":
8955
+ kind = "task";
8956
+ icon = "\u26A1";
8957
+ break;
8958
+ case "task:completed":
8959
+ kind = "task";
8960
+ icon = "\u2713";
8961
+ break;
8962
+ case "knowledge:added":
8963
+ kind = "knowledge";
8964
+ icon = "\u{1F4A1}";
8965
+ break;
8966
+ case "consensus:reached":
8967
+ kind = "consensus";
8968
+ icon = "\u{1F91D}";
8969
+ break;
8970
+ case "deadlock:detected":
8971
+ kind = "deadlock";
8972
+ icon = "\u26A0\uFE0F";
8973
+ break;
8974
+ default:
8975
+ kind = "goal";
8976
+ icon = "\u2022";
8977
+ break;
8978
+ }
8979
+ const timelineEntry = {
8980
+ at: now,
8981
+ kind,
8982
+ icon,
8983
+ text: event.text ?? event.type
8984
+ };
8985
+ return {
8986
+ ...state,
8987
+ coordinator: {
8988
+ ...state.coordinator,
8989
+ healthy: true,
8990
+ knowledgeCount: event.type === "knowledge:added" ? state.coordinator.knowledgeCount + 1 : state.coordinator.knowledgeCount,
8991
+ timeline: [timelineEntry, ...state.coordinator.timeline].slice(0, 50)
8992
+ }
8993
+ };
8994
+ }
8995
+ case "toggleCoordinatorMonitor": {
8996
+ const opening = !state.coordinator.monitorOpen;
8997
+ return opening ? {
8998
+ ...state,
8999
+ coordinator: { ...state.coordinator, monitorOpen: true },
9000
+ // Close other monitors when opening coordinator
9001
+ monitorOpen: false,
9002
+ agentsMonitorOpen: false,
9003
+ helpOpen: false,
9004
+ todosMonitorOpen: false,
9005
+ queuePanelOpen: false,
9006
+ processListOpen: false,
9007
+ goalPanelOpen: false
9008
+ } : { ...state, coordinator: { ...state.coordinator, monitorOpen: false } };
9009
+ }
8789
9010
  }
8790
9011
  }
8791
9012
  var INPUT_PROMPT = "\u203A ";
@@ -8840,6 +9061,7 @@ function App({
8840
9061
  yolo = false,
8841
9062
  chime = false,
8842
9063
  confirmExit = true,
9064
+ titleController,
8843
9065
  mouse = false,
8844
9066
  enhanceEnabled = true,
8845
9067
  enhanceController,
@@ -8895,7 +9117,8 @@ function App({
8895
9117
  requestExit,
8896
9118
  getLiveSessions,
8897
9119
  onSwitchToSession,
8898
- initialAgentsMonitorOpen
9120
+ initialAgentsMonitorOpen,
9121
+ subscribeCoordinatorEvents
8899
9122
  }) {
8900
9123
  const { exit } = useApp();
8901
9124
  const { stdout } = useStdout();
@@ -9028,7 +9251,7 @@ function App({
9028
9251
  },
9029
9252
  autonomyPicker: { open: false, options: [], selected: 0 },
9030
9253
  resumePicker: { open: false, sessions: [], selected: 0, busy: false, hint: void 0, error: void 0 },
9031
- settingsPicker: { open: false, field: 0, mode: "off", delayMs: 0, titleAnimation: true, yolo: false, streamFleet: true, chime: false, confirmExit: true, nextPrediction: false, featureMcp: true, featurePlugins: true, featureMemory: true, featureSkills: true, featureModelsRegistry: true, featureTokenSaving: false, contextAutoCompact: true, contextStrategy: "hybrid", logLevel: "info", auditLevel: "standard", indexOnStart: true, maxIterations: 500, autoProceedMaxIterations: 50, enhanceDelayMs: 6e4, enhanceEnabled: true, enhanceLanguage: "original", debugStream: false, configScope: "global" },
9254
+ settingsPicker: { open: false, field: 0, mode: "off", delayMs: 0, titleAnimation: true, yolo: false, streamFleet: true, chime: false, confirmExit: true, nextPrediction: false, featureMcp: true, featurePlugins: true, featureMemory: true, featureSkills: true, featureModelsRegistry: true, featureTokenSaving: false, allowOutsideProjectRoot: true, contextAutoCompact: true, contextStrategy: "hybrid", logLevel: "info", auditLevel: "standard", indexOnStart: true, maxIterations: 500, autoProceedMaxIterations: 50, enhanceDelayMs: 6e4, enhanceEnabled: true, enhanceLanguage: "original", debugStream: false, configScope: "global" },
9032
9255
  projectPicker: { open: false, allItems: [], items: [], selected: 0, filter: "", hint: void 0 },
9033
9256
  confirmQueue: [],
9034
9257
  enhance: null,
@@ -9068,6 +9291,13 @@ function App({
9068
9291
  autoPhase: null,
9069
9292
  worktrees: {},
9070
9293
  worktreeMonitorOpen: false,
9294
+ coordinator: {
9295
+ goals: [],
9296
+ timeline: [],
9297
+ knowledgeCount: 0,
9298
+ monitorOpen: false,
9299
+ healthy: false
9300
+ },
9071
9301
  scrollOffset: 0,
9072
9302
  totalLines: 0,
9073
9303
  viewportRows: 0,
@@ -9075,6 +9305,7 @@ function App({
9075
9305
  debugStreamStats: null,
9076
9306
  countdown: null
9077
9307
  });
9308
+ useAutonomousCoordinator(subscribeCoordinatorEvents, dispatch);
9078
9309
  const builderRef = useRef(null);
9079
9310
  if (builderRef.current === null) {
9080
9311
  builderRef.current = new InputBuilder({ store: attachments });
@@ -9104,10 +9335,16 @@ function App({
9104
9335
  setWorkingDirChip(rel === "." ? void 0 : rel);
9105
9336
  });
9106
9337
  }, [agent.ctx, projectRoot]);
9338
+ const liveSettings = getSettings?.();
9107
9339
  const chimeRef = useRef(chime);
9108
- chimeRef.current = chime;
9340
+ chimeRef.current = liveSettings?.chime ?? chime;
9109
9341
  const confirmExitRef = useRef(confirmExit);
9110
- confirmExitRef.current = confirmExit;
9342
+ confirmExitRef.current = liveSettings?.confirmExit ?? confirmExit;
9343
+ const liveTitleAnimation = liveSettings?.titleAnimation;
9344
+ useEffect(() => {
9345
+ if (!titleController) return;
9346
+ titleController.setEnabled(liveTitleAnimation !== false);
9347
+ }, [titleController, liveTitleAnimation]);
9111
9348
  const streamingTextRef = useRef("");
9112
9349
  const pendingDeltaRef = useRef("");
9113
9350
  const flushTimerRef = useRef(null);
@@ -9575,7 +9812,7 @@ function App({
9575
9812
  }
9576
9813
  }, []);
9577
9814
  React5.useLayoutEffect(() => {
9578
- const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.resumePicker.open || state.settingsPicker.open || state.enhanceBusy || state.enhance != null || state.escConfirm != null || state.confirmQueue.length > 0;
9815
+ const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.resumePicker.open || state.settingsPicker.open || state.enhanceBusy || state.enhance != null || state.coordinator.monitorOpen || state.escConfirm != null || state.confirmQueue.length > 0;
9579
9816
  const overlayClosed = prevAnyOverlayOpen.current && !anyOpenNow;
9580
9817
  const newEntryCommitted = state.entries.length > prevEntriesCount.current;
9581
9818
  const curToolStreamLen = state.toolStream?.text.length ?? 0;
@@ -9594,6 +9831,7 @@ function App({
9594
9831
  state.settingsPicker.open,
9595
9832
  state.enhanceBusy,
9596
9833
  state.enhance,
9834
+ state.coordinator.monitorOpen,
9597
9835
  state.escConfirm,
9598
9836
  state.confirmQueue.length,
9599
9837
  state.entries.length,
@@ -9618,7 +9856,8 @@ function App({
9618
9856
  queue: stateRef.current.queuePanelOpen,
9619
9857
  processList: stateRef.current.processListOpen,
9620
9858
  goalPanel: stateRef.current.goalPanelOpen,
9621
- sessionsPanel: stateRef.current.sessionsPanelOpen
9859
+ sessionsPanel: stateRef.current.sessionsPanelOpen,
9860
+ coordinator: stateRef.current.coordinator.monitorOpen
9622
9861
  };
9623
9862
  if (stateRef.current.settingsPicker.open) dispatch({ type: "settingsClose" });
9624
9863
  if (stateRef.current.projectPicker.open) dispatch({ type: "projectPickerClose" });
@@ -9661,6 +9900,7 @@ function App({
9661
9900
  featureSkills: sp.featureSkills,
9662
9901
  featureModelsRegistry: sp.featureModelsRegistry,
9663
9902
  featureTokenSaving: sp.featureTokenSaving,
9903
+ allowOutsideProjectRoot: sp.allowOutsideProjectRoot,
9664
9904
  contextAutoCompact: sp.contextAutoCompact,
9665
9905
  contextStrategy: sp.contextStrategy,
9666
9906
  logLevel: sp.logLevel,
@@ -9688,6 +9928,7 @@ function App({
9688
9928
  if (prev.processList) dispatch({ type: "toggleProcessList" });
9689
9929
  if (prev.goalPanel) dispatch({ type: "toggleGoalPanel" });
9690
9930
  if (prev.sessionsPanel) dispatch({ type: "toggleSessionsPanel" });
9931
+ if (prev.coordinator) dispatch({ type: "toggleCoordinatorMonitor" });
9691
9932
  preResizePanelsRef.current = null;
9692
9933
  resizeRestoreTimerRef.current = null;
9693
9934
  }, 300);
@@ -9781,7 +10022,28 @@ function App({
9781
10022
  type: "addEntry",
9782
10023
  entry: {
9783
10024
  kind: "error",
9784
- text: `Clipboard image error: ${err instanceof Error ? err.message : String(err)}`
10025
+ text: `Clipboard image error: ${toErrorMessage(err)}`
10026
+ }
10027
+ });
10028
+ }
10029
+ };
10030
+ const pasteClipboardText = async () => {
10031
+ try {
10032
+ const text = await readClipboardText();
10033
+ if (!text) {
10034
+ dispatch({
10035
+ type: "addEntry",
10036
+ entry: { kind: "info", text: "No text on the clipboard." }
10037
+ });
10038
+ return;
10039
+ }
10040
+ await commitPaste(text);
10041
+ } catch (err) {
10042
+ dispatch({
10043
+ type: "addEntry",
10044
+ entry: {
10045
+ kind: "error",
10046
+ text: `Clipboard error: ${toErrorMessage(err)}`
9785
10047
  }
9786
10048
  });
9787
10049
  }
@@ -9818,7 +10080,7 @@ function App({
9818
10080
  type: "addEntry",
9819
10081
  entry: {
9820
10082
  kind: "error",
9821
- text: `Attach failed: ${err instanceof Error ? err.message : String(err)}`
10083
+ text: `Attach failed: ${toErrorMessage(err)}`
9822
10084
  }
9823
10085
  });
9824
10086
  dispatch({ type: "pickerClose" });
@@ -10004,7 +10266,10 @@ function App({
10004
10266
  dispatch({ type: "projectPickerOpen", items });
10005
10267
  }, [getProjectPickerItems]);
10006
10268
  const loadLiveSessions = React5.useCallback(async () => {
10007
- if (!getLiveSessions) return;
10269
+ if (!getLiveSessions) {
10270
+ dispatch({ type: "sessionsPanelSet", sessions: [] });
10271
+ return;
10272
+ }
10008
10273
  dispatch({ type: "sessionsPanelBusy", on: true });
10009
10274
  try {
10010
10275
  const sessions = await getLiveSessions();
@@ -10039,6 +10304,7 @@ function App({
10039
10304
  featureSkills: s2.featureSkills ?? true,
10040
10305
  featureModelsRegistry: s2.featureModelsRegistry ?? true,
10041
10306
  featureTokenSaving: s2.featureTokenSaving ?? false,
10307
+ allowOutsideProjectRoot: s2.allowOutsideProjectRoot ?? true,
10042
10308
  contextAutoCompact: s2.contextAutoCompact ?? true,
10043
10309
  contextStrategy: s2.contextStrategy ?? "hybrid",
10044
10310
  logLevel: s2.logLevel ?? "info",
@@ -10174,6 +10440,7 @@ function App({
10174
10440
  featureSkills: sp.featureSkills,
10175
10441
  featureModelsRegistry: sp.featureModelsRegistry,
10176
10442
  featureTokenSaving: sp.featureTokenSaving,
10443
+ allowOutsideProjectRoot: sp.allowOutsideProjectRoot,
10177
10444
  contextAutoCompact: sp.contextAutoCompact,
10178
10445
  contextStrategy: sp.contextStrategy,
10179
10446
  logLevel: sp.logLevel,
@@ -10204,6 +10471,8 @@ function App({
10204
10471
  state.settingsPicker.featureMemory,
10205
10472
  state.settingsPicker.featureSkills,
10206
10473
  state.settingsPicker.featureModelsRegistry,
10474
+ state.settingsPicker.featureTokenSaving,
10475
+ state.settingsPicker.allowOutsideProjectRoot,
10207
10476
  state.settingsPicker.contextAutoCompact,
10208
10477
  state.settingsPicker.contextStrategy,
10209
10478
  state.settingsPicker.logLevel,
@@ -10212,6 +10481,9 @@ function App({
10212
10481
  state.settingsPicker.maxIterations,
10213
10482
  state.settingsPicker.autoProceedMaxIterations,
10214
10483
  state.settingsPicker.enhanceDelayMs,
10484
+ state.settingsPicker.enhanceEnabled,
10485
+ state.settingsPicker.enhanceLanguage,
10486
+ state.settingsPicker.debugStream,
10215
10487
  saveSettings
10216
10488
  ]);
10217
10489
  useEffect(() => {
@@ -10294,7 +10566,7 @@ function App({
10294
10566
  dispatch({ type: "resumePickerOpen", sessions });
10295
10567
  } catch (err) {
10296
10568
  return {
10297
- message: err instanceof Error ? err.message : String(err)
10569
+ message: toErrorMessage(err)
10298
10570
  };
10299
10571
  }
10300
10572
  return { message: void 0 };
@@ -10897,7 +11169,7 @@ function App({
10897
11169
  }).catch((err) => {
10898
11170
  dispatch({
10899
11171
  type: "resumePickerError",
10900
- text: err instanceof Error ? err.message : String(err)
11172
+ text: toErrorMessage(err)
10901
11173
  });
10902
11174
  });
10903
11175
  return;
@@ -11346,7 +11618,7 @@ function App({
11346
11618
  }
11347
11619
  return;
11348
11620
  }
11349
- if (key.fn === 10) {
11621
+ if (key.fn === 10 || key.escape && state.sessionsPanelOpen) {
11350
11622
  if (state.sessionsPanelOpen) {
11351
11623
  dispatch({ type: "toggleSessionsPanel" });
11352
11624
  } else {
@@ -11365,6 +11637,10 @@ function App({
11365
11637
  }
11366
11638
  return;
11367
11639
  }
11640
+ if (key.fn === 11 || input === "\x1B" && state.coordinator.monitorOpen) {
11641
+ dispatch({ type: "toggleCoordinatorMonitor" });
11642
+ return;
11643
+ }
11368
11644
  if (key.ctrl && input === "s") {
11369
11645
  if (state.settingsPicker.open) {
11370
11646
  dispatch({ type: "settingsClose" });
@@ -11393,6 +11669,7 @@ function App({
11393
11669
  featureSkills: cfg.featureSkills ?? true,
11394
11670
  featureModelsRegistry: cfg.featureModelsRegistry ?? true,
11395
11671
  featureTokenSaving: cfg.featureTokenSaving ?? false,
11672
+ allowOutsideProjectRoot: cfg.allowOutsideProjectRoot ?? true,
11396
11673
  contextAutoCompact: cfg.contextAutoCompact ?? true,
11397
11674
  contextStrategy: cfg.contextStrategy ?? "hybrid",
11398
11675
  logLevel: cfg.logLevel ?? "info",
@@ -11442,10 +11719,18 @@ function App({
11442
11719
  dispatch({ type: "toggleGoalPanel" });
11443
11720
  return;
11444
11721
  }
11722
+ if (state.helpOpen) {
11723
+ dispatch({ type: "toggleHelp" });
11724
+ return;
11725
+ }
11445
11726
  if (state.sessionsPanelOpen) {
11446
11727
  dispatch({ type: "toggleSessionsPanel" });
11447
11728
  return;
11448
11729
  }
11730
+ if (state.coordinator.monitorOpen) {
11731
+ dispatch({ type: "toggleCoordinatorMonitor" });
11732
+ return;
11733
+ }
11449
11734
  }
11450
11735
  if (state.processListOpen) {
11451
11736
  return;
@@ -11468,6 +11753,17 @@ function App({
11468
11753
  return;
11469
11754
  }
11470
11755
  const { buffer, cursor } = draftRef.current;
11756
+ if (key.tab && nextStepsAutoSubmitTimerRef.current != null) {
11757
+ const pending = nextStepsAutoSubmitSuggestionRef.current ?? nextStepsAutoSubmitLabel ?? "";
11758
+ clearInterval(nextStepsAutoSubmitTimerRef.current);
11759
+ nextStepsAutoSubmitTimerRef.current = void 0;
11760
+ setNextStepsAutoSubmitCountdown(null);
11761
+ setNextStepsAutoSubmitLabel(null);
11762
+ nextStepsAutoSubmitSuggestionRef.current = null;
11763
+ const text = pending.trim();
11764
+ if (text) setDraft(text, text.length);
11765
+ return;
11766
+ }
11471
11767
  if (key.backspace) {
11472
11768
  if (key.ctrl) {
11473
11769
  if (cursor === 0) return;
@@ -11555,7 +11851,7 @@ function App({
11555
11851
  setDraft(buffer, buffer.length);
11556
11852
  return;
11557
11853
  }
11558
- const overlayOpen = state.monitorOpen || state.agentsMonitorOpen || state.worktreeMonitorOpen || state.todosMonitorOpen || state.queuePanelOpen || state.processListOpen || state.goalPanelOpen || state.sessionsPanelOpen || state.helpOpen || (state.autoPhase?.monitorOpen ?? false) || state.rewindOverlay !== null;
11854
+ const overlayOpen = state.monitorOpen || state.agentsMonitorOpen || state.worktreeMonitorOpen || state.todosMonitorOpen || state.queuePanelOpen || state.processListOpen || state.goalPanelOpen || state.sessionsPanelOpen || state.coordinator.monitorOpen || state.helpOpen || (state.autoPhase?.monitorOpen ?? false) || state.rewindOverlay !== null;
11559
11855
  if (mouseMode && !overlayOpen) {
11560
11856
  if (key.mouse?.kind === "wheel") {
11561
11857
  if (key.mouse.shift) dispatch({ type: "scrollPage", dir: key.mouse.wheel > 0 ? "up" : "down" });
@@ -11684,6 +11980,10 @@ function App({
11684
11980
  setDraft(next2, cursor);
11685
11981
  return;
11686
11982
  }
11983
+ if (key.ctrl && input === "v") {
11984
+ await pasteClipboardText();
11985
+ return;
11986
+ }
11687
11987
  if (key.meta && input === "v") {
11688
11988
  await pasteClipboardImage();
11689
11989
  return;
@@ -11807,7 +12107,7 @@ function App({
11807
12107
  } catch (err) {
11808
12108
  dispatch({
11809
12109
  type: "addEntry",
11810
- entry: { kind: "error", text: err instanceof Error ? err.message : String(err) }
12110
+ entry: { kind: "error", text: toErrorMessage(err) }
11811
12111
  });
11812
12112
  } finally {
11813
12113
  activeCtrlRef.current = null;
@@ -11849,7 +12149,7 @@ function App({
11849
12149
  type: "addEntry",
11850
12150
  entry: {
11851
12151
  kind: "error",
11852
- text: `[eternal] ${err instanceof Error ? err.message : String(err)}`
12152
+ text: `[eternal] ${toErrorMessage(err)}`
11853
12153
  }
11854
12154
  });
11855
12155
  }
@@ -11885,7 +12185,7 @@ function App({
11885
12185
  type: "addEntry",
11886
12186
  entry: {
11887
12187
  kind: "error",
11888
- text: `[parallel] ${err instanceof Error ? err.message : String(err)}`
12188
+ text: `[parallel] ${toErrorMessage(err)}`
11889
12189
  }
11890
12190
  });
11891
12191
  }
@@ -12047,7 +12347,7 @@ ${content}
12047
12347
  } catch (err) {
12048
12348
  dispatch({
12049
12349
  type: "addEntry",
12050
- entry: { kind: "error", text: err instanceof Error ? err.message : String(err) }
12350
+ entry: { kind: "error", text: toErrorMessage(err) }
12051
12351
  });
12052
12352
  }
12053
12353
  return;
@@ -12327,6 +12627,7 @@ User message:
12327
12627
  featureSkills: state.settingsPicker.featureSkills,
12328
12628
  featureModelsRegistry: state.settingsPicker.featureModelsRegistry,
12329
12629
  featureTokenSaving: state.settingsPicker.featureTokenSaving,
12630
+ allowOutsideProjectRoot: state.settingsPicker.allowOutsideProjectRoot,
12330
12631
  contextAutoCompact: state.settingsPicker.contextAutoCompact,
12331
12632
  contextStrategy: state.settingsPicker.contextStrategy,
12332
12633
  logLevel: state.settingsPicker.logLevel,
@@ -12361,6 +12662,14 @@ User message:
12361
12662
  currentSessionId: agent.ctx.session?.id
12362
12663
  }
12363
12664
  ) : null,
12665
+ state.coordinator.monitorOpen ? /* @__PURE__ */ jsx(
12666
+ CoordinatorPanel,
12667
+ {
12668
+ coordinator: state.coordinator,
12669
+ nowTick,
12670
+ onClose: () => dispatch({ type: "toggleCoordinatorMonitor" })
12671
+ }
12672
+ ) : null,
12364
12673
  state.rewindOverlay ? (() => {
12365
12674
  const overlay = state.rewindOverlay;
12366
12675
  return /* @__PURE__ */ jsx(
@@ -12491,6 +12800,7 @@ User message:
12491
12800
  fleet: fleetCounts,
12492
12801
  git: gitInfo,
12493
12802
  context: contextWindow,
12803
+ contextStrategy: getSettings ? getSettings().contextStrategy : void 0,
12494
12804
  brain: state.brain,
12495
12805
  projectName,
12496
12806
  workingDir: workingDirChip,
@@ -12539,8 +12849,7 @@ User message:
12539
12849
  phases: state.autoPhase.phases,
12540
12850
  runningPhaseIds: state.autoPhase.runningPhaseIds,
12541
12851
  elapsedMs: state.autoPhase.elapsedMs,
12542
- nowTick,
12543
- onClose: () => dispatch({ type: "autoPhaseMonitorToggle" })
12852
+ nowTick
12544
12853
  }
12545
12854
  ) : state.worktreeMonitorOpen ? /* @__PURE__ */ jsx(
12546
12855
  WorktreeMonitor,
@@ -12756,13 +13065,30 @@ async function runTui(opts) {
12756
13065
  const mouseEnabled = opts.mouse ?? opts.getSettings?.().mouseMode ?? process.env.WRONGSTACK_MOUSE === "1";
12757
13066
  stdout.write("\x1B[2J\x1B[H");
12758
13067
  const inkStdin = stdin;
12759
- const stopTitle = opts.titleAnimation !== false ? startTerminalTitle({
12760
- stdout,
12761
- events: opts.events,
12762
- model: opts.model,
12763
- appName: opts.projectRoot ? path4.basename(opts.projectRoot) : void 0
12764
- }) : (() => {
12765
- });
13068
+ let titleStop = null;
13069
+ const startTitle = () => {
13070
+ if (titleStop) return;
13071
+ titleStop = startTerminalTitle({
13072
+ stdout,
13073
+ events: opts.events,
13074
+ model: opts.model,
13075
+ appName: opts.projectRoot ? path4.basename(opts.projectRoot) : void 0
13076
+ });
13077
+ };
13078
+ const stopTitle = () => {
13079
+ try {
13080
+ titleStop?.();
13081
+ } catch {
13082
+ }
13083
+ titleStop = null;
13084
+ };
13085
+ const titleController = {
13086
+ setEnabled(on) {
13087
+ if (on) startTitle();
13088
+ else stopTitle();
13089
+ }
13090
+ };
13091
+ if (opts.titleAnimation !== false) startTitle();
12766
13092
  const swallowSignals = ["SIGTSTP", "SIGQUIT", "SIGTTIN", "SIGTTOU"];
12767
13093
  const swallow = () => {
12768
13094
  };
@@ -12941,6 +13267,7 @@ async function runTui(opts) {
12941
13267
  setSuggestions: opts.setSuggestions,
12942
13268
  chime: opts.chime,
12943
13269
  confirmExit: opts.confirmExit,
13270
+ titleController,
12944
13271
  mouse: mouseEnabled,
12945
13272
  modeLabel: opts.modeLabel,
12946
13273
  tokenSavingMode: opts.tokenSavingMode,
@@ -12957,7 +13284,8 @@ async function runTui(opts) {
12957
13284
  requestExit: opts.requestExit,
12958
13285
  getLiveSessions: opts.getLiveSessions,
12959
13286
  onSwitchToSession: opts.onSwitchToSession,
12960
- initialAgentsMonitorOpen: opts.initialAgentsMonitorOpen
13287
+ initialAgentsMonitorOpen: opts.initialAgentsMonitorOpen,
13288
+ subscribeCoordinatorEvents: opts.subscribeCoordinatorEvents
12961
13289
  }),
12962
13290
  { exitOnCtrlC: false, stdin: inkStdin }
12963
13291
  );