@wrongstack/cli 0.257.2 → 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
@@ -1,18 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  import * as fsp5 from 'fs/promises';
3
- import * as path38 from 'path';
3
+ import * as path39 from 'path';
4
4
  import { join } from 'path';
5
- import { color, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, DefaultSecretScrubber, resolveProjectDir, GlobalMailbox, TOKENS, ToolRegistry, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, SlashCommandRegistry, attachDepWatcherBridge, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, wstackGlobalRoot, DEFAULT_CONTEXT_WINDOW_MODE_ID, recentTextTurns, enhanceUserPrompt, projectSlug, DefaultSystemPromptBuilder, mutateTasks, loadTasks, resolveContextWindowPolicy, repairToolUseAdjacency, mutatePlan, setPlanItemStatus, getPlanTemplate, loadPlan, emptyPlan, addPlanItem, savePlan, DefaultLogger, DefaultModelsRegistry, isStdinTTY, DefaultPathResolver, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, makeAutonomyPromptContributor, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, createDefaultPipelines, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, makeFleetStatusTool, resolveModelMatrix, DEFAULT_SUBAGENT_BASELINE, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, writeOut, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, CHIMERA_REVIEW_PROMPT, noOpVault, decryptConfigSecrets, encryptConfigSecrets, atomicWrite, setQueuedMessagesSnapshot, DefaultSessionRewinder, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, resolveWstackPaths, ToolAuditLog, DefaultSessionStore as DefaultSessionStore$1, ProviderRegistry, StreamHangError, ProviderError, makeAgentSubagentRunner, NULL_FLEET_BUS, buildChildEnv, formatContextWindowModeList, getContextWindowMode, AGENT_CATALOG, dispatchAgent, formatTodosList, formatTaskList, formatTaskProgress, formatPlan, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, formatGoal, emptyGoal, buildGoalPreamble, pendingBtwCount, setBtwNote, MATRIX_PHASE_KEYS, matrixKeyKind, phaseForRole, onResize, ERROR_CODES, FsError, ConfigError, InputBuilder, truncate, estimateMessageTokens, AGENTS_BY_PHASE, validateAgainstSchema, resolveMailboxIdentity, isSecretField as isSecretField$1 } from '@wrongstack/core';
5
+ import { color, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, DefaultSecretScrubber, resolveProjectDir, GlobalMailbox, TOKENS, ToolRegistry, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, SlashCommandRegistry, attachDepWatcherBridge, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, wstackGlobalRoot, DEFAULT_CONTEXT_WINDOW_MODE_ID, recentTextTurns, enhanceUserPrompt, projectSlug, DefaultSystemPromptBuilder, mutateTasks, loadTasks, resolveContextWindowPolicy, repairToolUseAdjacency, mutatePlan, setPlanItemStatus, getPlanTemplate, loadPlan, emptyPlan, addPlanItem, savePlan, DefaultLogger, DefaultModelsRegistry, isStdinTTY, DefaultPathResolver, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, makeAutonomyPromptContributor, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore, Context, QueueStore, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, createDefaultPipelines, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, makeFleetStatusTool, resolveModelMatrix, DEFAULT_SUBAGENT_BASELINE, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, writeOut, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, CHIMERA_REVIEW_PROMPT, AutonomousCoordinator, noOpVault, decryptConfigSecrets, encryptConfigSecrets, atomicWrite, setQueuedMessagesSnapshot, DefaultSessionRewinder, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, resolveWstackPaths, ToolAuditLog, DefaultSessionStore as DefaultSessionStore$1, ProviderRegistry, StreamHangError, ProviderError, makeAgentSubagentRunner, NULL_FLEET_BUS, buildChildEnv, formatContextWindowModeList, getContextWindowMode, AGENT_CATALOG, dispatchAgent, formatTodosList, formatTaskList, formatTaskProgress, formatPlan, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, formatGoal, emptyGoal, buildGoalPreamble, pendingBtwCount, setBtwNote, MATRIX_PHASE_KEYS, matrixKeyKind, phaseForRole, onResize, ERROR_CODES, FsError, ConfigError, InputBuilder, truncate, estimateMessageTokens, AGENTS_BY_PHASE, validateAgainstSchema, resolveMailboxIdentity, isSecretField as isSecretField$1 } from '@wrongstack/core';
6
6
  import { DefaultSecretVault, encryptConfigSecrets as encryptConfigSecrets$1, decryptConfigSecrets as decryptConfigSecrets$1, isSecretField } from '@wrongstack/core/security';
7
7
  import * as crypto3 from 'crypto';
8
- import { createHash, randomUUID } from 'crypto';
8
+ import { createHash, randomBytes, randomUUID } from 'crypto';
9
9
  import { createRequire } from 'module';
10
10
  import * as os from 'os';
11
11
  import os__default from 'os';
12
12
  import { findFreePort, AutoPhaseWebSocketHandler, handleShellOpen, handleMemoryForget, handleMemoryRemember, handleMemoryList, handleFilesWrite, handleFilesRead, handleFilesTree, handleFilesList, createEternalSubscription, createHttpServer, registerInstance, openBrowser, unregisterInstance, estimateTokens as estimateTokens$1, stringifyContent, messagePreview, messageTokens, createCustomModeStore } from '@wrongstack/webui/server';
13
13
  import { makeProviderFromConfig, capabilitiesFor, buildProviderFactoriesFromRegistry } from '@wrongstack/providers';
14
+ import { toErrorMessage } from '@wrongstack/core/utils';
14
15
  import { getProcessRegistry, builtinToolsPack, TIER1_TOOLS, rememberTool, forgetTool, searchMemoryTool, relatedMemoryTool, runStartupIndex, isIndexableFile, enqueueReindex, cancelPendingReindexes, shutdownCodebaseIndexHost, resetIndexCircuitBreaker } from '@wrongstack/tools';
15
16
  import { DefaultSessionStore } from '@wrongstack/core/storage';
17
+ import { probeLocalLlm } from '@wrongstack/runtime/probe';
16
18
  import { WebSocketServer, WebSocket } from 'ws';
17
19
  import { spawn, execFile, execFileSync } from 'child_process';
18
20
  import { MCPRegistry, MCPServer, serveHttp, serveStdio } from '@wrongstack/mcp';
@@ -21,10 +23,11 @@ import { writeFileSync, existsSync, readFileSync } from 'fs';
21
23
  import { fileURLToPath } from 'url';
22
24
  import { createDefaultContainer, routeImagesForModel, readClipboardImage } from '@wrongstack/runtime';
23
25
  import * as readline from 'readline';
26
+ import { ACP_AGENT_COMMANDS, makeACPSubagentRunner, runEnsemble, renderEnsembleText, EnsembleRegistry, makeACPSubagentRunnerWithStop, findAgentDescriptor } from '@wrongstack/acp';
24
27
  import { parseNextSteps } from '@wrongstack/tui';
25
- import { ACP_AGENT_COMMANDS, makeACPSubagentRunner, makeACPSubagentRunnerWithStop } from '@wrongstack/acp';
26
28
  import { WrongStackACPServer } from '@wrongstack/acp/agent';
27
- import { ACP_AGENTS, SubagentBudget } from '@wrongstack/core/coordination';
29
+ import { SubagentBudget } from '@wrongstack/core/coordination';
30
+ import { loadBenchConfig, reportHeaderLine, readSummary, renderMarkdownReport, createPolyglotSuite, createSwebenchSuite, runBenchmark, writeJsonArtifacts, collectCellPredictions, writePredictionsJsonl, gradePolyglot, gradeSwebench } from '@wrongstack/bench';
28
31
  import { allServers } from '@wrongstack/core/infrastructure';
29
32
  import { ToolExecutor } from '@wrongstack/core/execution';
30
33
  import { createToolVisionAdapters } from '@wrongstack/runtime/vision';
@@ -70,10 +73,10 @@ async function detectPackageManager2(root, declared) {
70
73
  const name = declared.split("@")[0];
71
74
  if (name) return name;
72
75
  }
73
- if (await pathExists(path38.join(root, "pnpm-lock.yaml"))) return "pnpm";
74
- if (await pathExists(path38.join(root, "bun.lockb"))) return "bun";
75
- if (await pathExists(path38.join(root, "bun.lock"))) return "bun";
76
- if (await pathExists(path38.join(root, "yarn.lock"))) return "yarn";
76
+ if (await pathExists(path39.join(root, "pnpm-lock.yaml"))) return "pnpm";
77
+ if (await pathExists(path39.join(root, "bun.lockb"))) return "bun";
78
+ if (await pathExists(path39.join(root, "bun.lock"))) return "bun";
79
+ if (await pathExists(path39.join(root, "yarn.lock"))) return "yarn";
77
80
  return "npm";
78
81
  }
79
82
  function hasUsableScript(scripts, name) {
@@ -94,7 +97,7 @@ function parseMakeTargets(makefile) {
94
97
  async function detectProjectFacts(root) {
95
98
  const facts = { hints: [] };
96
99
  try {
97
- const pkg = JSON.parse(await fsp5.readFile(path38.join(root, "package.json"), "utf8"));
100
+ const pkg = JSON.parse(await fsp5.readFile(path39.join(root, "package.json"), "utf8"));
98
101
  const scripts = pkg.scripts ?? {};
99
102
  const pm = await detectPackageManager2(root, pkg.packageManager);
100
103
  if (hasUsableScript(scripts, "build")) facts.build = `${pm} run build`;
@@ -108,14 +111,14 @@ async function detectProjectFacts(root) {
108
111
  } catch {
109
112
  }
110
113
  try {
111
- if (!await pathExists(path38.join(root, "pyproject.toml"))) throw new Error("not python");
114
+ if (!await pathExists(path39.join(root, "pyproject.toml"))) throw new Error("not python");
112
115
  facts.test ??= "pytest";
113
116
  facts.lint ??= "ruff check .";
114
117
  facts.hints.push("pyproject.toml");
115
118
  } catch {
116
119
  }
117
120
  try {
118
- if (!await pathExists(path38.join(root, "go.mod"))) throw new Error("not go");
121
+ if (!await pathExists(path39.join(root, "go.mod"))) throw new Error("not go");
119
122
  facts.build ??= "go build ./...";
120
123
  facts.test ??= "go test ./...";
121
124
  facts.run ??= "go run .";
@@ -123,7 +126,7 @@ async function detectProjectFacts(root) {
123
126
  } catch {
124
127
  }
125
128
  try {
126
- if (!await pathExists(path38.join(root, "Cargo.toml"))) throw new Error("not rust");
129
+ if (!await pathExists(path39.join(root, "Cargo.toml"))) throw new Error("not rust");
127
130
  facts.build ??= "cargo build";
128
131
  facts.test ??= "cargo test";
129
132
  facts.lint ??= "cargo clippy";
@@ -132,7 +135,7 @@ async function detectProjectFacts(root) {
132
135
  } catch {
133
136
  }
134
137
  try {
135
- const makefile = await fsp5.readFile(path38.join(root, "Makefile"), "utf8");
138
+ const makefile = await fsp5.readFile(path39.join(root, "Makefile"), "utf8");
136
139
  const targets = parseMakeTargets(makefile);
137
140
  facts.build ??= targets.has("build") ? "make build" : "make";
138
141
  if (targets.has("test")) facts.test ??= "make test";
@@ -371,26 +374,26 @@ function fmtDuration(ms) {
371
374
  const remMin = m - h * 60;
372
375
  return `${h}h${remMin}m`;
373
376
  }
374
- function fmtTaskResultLine(r, color71) {
377
+ function fmtTaskResultLine(r, color74) {
375
378
  const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
376
379
  const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
377
380
  const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
378
381
  const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
379
- const errKindChip = errKind ? color71.dim(` [${errKind}]`) : "";
380
- const errSnip = errMsg || errKind ? `${errKindChip}${color71.dim(errTail)}` : "";
382
+ const errKindChip = errKind ? color74.dim(` [${errKind}]`) : "";
383
+ const errSnip = errMsg || errKind ? `${errKindChip}${color74.dim(errTail)}` : "";
381
384
  switch (r.status) {
382
385
  case "success":
383
- return { mark: color71.green("\u2713"), stats, tail: "" };
386
+ return { mark: color74.green("\u2713"), stats, tail: "" };
384
387
  case "timeout":
385
388
  return {
386
- mark: color71.yellow("\u23F1"),
387
- stats: `${color71.yellow("timeout")} ${stats}`,
389
+ mark: color74.yellow("\u23F1"),
390
+ stats: `${color74.yellow("timeout")} ${stats}`,
388
391
  tail: errSnip
389
392
  };
390
393
  case "stopped":
391
- return { mark: color71.dim("\u2298"), stats: `${color71.dim("stopped")} ${stats}`, tail: errSnip };
394
+ return { mark: color74.dim("\u2298"), stats: `${color74.dim("stopped")} ${stats}`, tail: errSnip };
392
395
  case "failed":
393
- return { mark: color71.red("\u2717"), stats: `${color71.red("failed")} ${stats}`, tail: errSnip };
396
+ return { mark: color74.red("\u2717"), stats: `${color74.red("failed")} ${stats}`, tail: errSnip };
394
397
  }
395
398
  }
396
399
  var init_utils = __esm({
@@ -675,7 +678,7 @@ async function findSpec(store, idOrTitle) {
675
678
  async function gatherProjectContext2(projectRoot) {
676
679
  const parts = [];
677
680
  try {
678
- const pkgPath = path38.join(projectRoot, "package.json");
681
+ const pkgPath = path39.join(projectRoot, "package.json");
679
682
  const pkgRaw = await fsp5.readFile(pkgPath, "utf8");
680
683
  const pkg = JSON.parse(pkgRaw);
681
684
  parts.push(`Project: ${String(pkg.name ?? "unknown")}`);
@@ -693,13 +696,13 @@ async function gatherProjectContext2(projectRoot) {
693
696
  } catch {
694
697
  }
695
698
  try {
696
- const tsconfigPath = path38.join(projectRoot, "tsconfig.json");
699
+ const tsconfigPath = path39.join(projectRoot, "tsconfig.json");
697
700
  await fsp5.access(tsconfigPath);
698
701
  parts.push("Language: TypeScript");
699
702
  } catch {
700
703
  }
701
704
  try {
702
- const srcDir = path38.join(projectRoot, "src");
705
+ const srcDir = path39.join(projectRoot, "src");
703
706
  const entries = await fsp5.readdir(srcDir, { withFileTypes: true });
704
707
  const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
705
708
  if (dirs.length > 0) parts.push(`Source structure: src/${dirs.join(", src/")}`);
@@ -2124,12 +2127,12 @@ __export(project_utils_exports, {
2124
2127
  touchProjectInManifest: () => touchProjectInManifest
2125
2128
  });
2126
2129
  function projectsJsonPath(globalConfigPath) {
2127
- const base = globalConfigPath ? path38.dirname(globalConfigPath) : wstackGlobalRoot();
2128
- return path38.join(base, "projects.json");
2130
+ const base = globalConfigPath ? path39.dirname(globalConfigPath) : wstackGlobalRoot();
2131
+ return path39.join(base, "projects.json");
2129
2132
  }
2130
2133
  function projectsDataDir(globalConfigPath) {
2131
- const base = globalConfigPath ? path38.dirname(globalConfigPath) : wstackGlobalRoot();
2132
- return path38.join(base, "projects");
2134
+ const base = globalConfigPath ? path39.dirname(globalConfigPath) : wstackGlobalRoot();
2135
+ return path39.join(base, "projects");
2133
2136
  }
2134
2137
  async function loadManifest(globalConfigPath) {
2135
2138
  const file = projectsJsonPath(globalConfigPath);
@@ -2143,12 +2146,12 @@ async function loadManifest(globalConfigPath) {
2143
2146
  }
2144
2147
  async function saveManifest(manifest, globalConfigPath) {
2145
2148
  const file = projectsJsonPath(globalConfigPath);
2146
- await fsp5.mkdir(path38.dirname(file), { recursive: true });
2149
+ await fsp5.mkdir(path39.dirname(file), { recursive: true });
2147
2150
  await fsp5.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
2148
2151
  }
2149
2152
  function generateSlug(root) {
2150
- const base = path38.basename(root).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
2151
- const hash = createHash("sha256").update(path38.resolve(root)).digest("hex").slice(0, 6);
2153
+ const base = path39.basename(root).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
2154
+ const hash = createHash("sha256").update(path39.resolve(root)).digest("hex").slice(0, 6);
2152
2155
  return `${base}-${hash}`;
2153
2156
  }
2154
2157
  function findProject(manifest, query) {
@@ -2163,29 +2166,29 @@ function findProject(manifest, query) {
2163
2166
  return found;
2164
2167
  }
2165
2168
  async function ensureProjectDataDir(slug, globalConfigPath) {
2166
- const dir = path38.join(projectsDataDir(globalConfigPath), slug);
2169
+ const dir = path39.join(projectsDataDir(globalConfigPath), slug);
2167
2170
  await fsp5.mkdir(dir, { recursive: true });
2168
2171
  return dir;
2169
2172
  }
2170
2173
  async function touchProjectInManifest(opts) {
2171
- const root = path38.resolve(opts.projectRoot);
2174
+ const root = path39.resolve(opts.projectRoot);
2172
2175
  const file = projectsJsonPath(opts.globalConfigPath);
2173
2176
  let entry;
2174
2177
  await withFileLock(file, async () => {
2175
2178
  const manifest = await loadManifest(opts.globalConfigPath);
2176
2179
  const now = (/* @__PURE__ */ new Date()).toISOString();
2177
- entry = manifest.projects.find((p) => path38.resolve(p.root) === root);
2180
+ entry = manifest.projects.find((p) => path39.resolve(p.root) === root);
2178
2181
  if (entry) {
2179
2182
  entry.lastSeen = now;
2180
- if (opts.workingDir) entry.lastWorkingDir = path38.resolve(opts.workingDir);
2183
+ if (opts.workingDir) entry.lastWorkingDir = path39.resolve(opts.workingDir);
2181
2184
  } else {
2182
2185
  entry = {
2183
- name: opts.name ?? path38.basename(root),
2186
+ name: opts.name ?? path39.basename(root),
2184
2187
  root,
2185
2188
  slug: generateSlug(root),
2186
2189
  createdAt: now,
2187
2190
  lastSeen: now,
2188
- lastWorkingDir: opts.workingDir ? path38.resolve(opts.workingDir) : void 0
2191
+ lastWorkingDir: opts.workingDir ? path39.resolve(opts.workingDir) : void 0
2189
2192
  };
2190
2193
  manifest.projects.push(entry);
2191
2194
  }
@@ -2379,7 +2382,7 @@ async function runProjectPicker(opts) {
2379
2382
  const reservedBottom = 3;
2380
2383
  const headerHeight = reservedTop + reservedBottom;
2381
2384
  const baseVisibleHeight = Math.max(5, terminalHeight() - headerHeight);
2382
- return new Promise((resolve10) => {
2385
+ return new Promise((resolve11) => {
2383
2386
  const wasRaw = stdin.isRaw;
2384
2387
  const wasPaused = stdin.isPaused();
2385
2388
  let filter = "";
@@ -2503,7 +2506,7 @@ async function runProjectPicker(opts) {
2503
2506
  cleanup();
2504
2507
  out.write(CURSOR_SHOW);
2505
2508
  out.write("\n");
2506
- resolve10(void 0);
2509
+ resolve11(void 0);
2507
2510
  return;
2508
2511
  }
2509
2512
  if (ch === ESC) {
@@ -2516,7 +2519,7 @@ async function runProjectPicker(opts) {
2516
2519
  cleanup();
2517
2520
  out.write(CURSOR_SHOW);
2518
2521
  out.write("\n");
2519
- resolve10(void 0);
2522
+ resolve11(void 0);
2520
2523
  return;
2521
2524
  }
2522
2525
  if (ch === BS || ch === "\b") {
@@ -2533,22 +2536,22 @@ async function runProjectPicker(opts) {
2533
2536
  out.write(CURSOR_SHOW);
2534
2537
  out.write("\n");
2535
2538
  if (!item || item.key === "__divider__") {
2536
- resolve10(void 0);
2539
+ resolve11(void 0);
2537
2540
  return;
2538
2541
  }
2539
2542
  if (item.key === "quit") {
2540
- resolve10(void 0);
2543
+ resolve11(void 0);
2541
2544
  return;
2542
2545
  }
2543
2546
  if (item.key === "new-session") {
2544
- resolve10({ kind: "action", key: "new-session", action: "new-session" });
2547
+ resolve11({ kind: "action", key: "new-session", action: "new-session" });
2545
2548
  return;
2546
2549
  }
2547
2550
  if (item.key === "prev-sessions") {
2548
- resolve10({ kind: "action", key: "prev-sessions", action: "prev-sessions" });
2551
+ resolve11({ kind: "action", key: "prev-sessions", action: "prev-sessions" });
2549
2552
  return;
2550
2553
  }
2551
- resolve10({ kind: "project", key: item.key });
2554
+ resolve11({ kind: "project", key: item.key });
2552
2555
  return;
2553
2556
  }
2554
2557
  if (filter.length === 0) {
@@ -2556,7 +2559,7 @@ async function runProjectPicker(opts) {
2556
2559
  cleanup();
2557
2560
  out.write(CURSOR_SHOW);
2558
2561
  out.write("\n");
2559
- resolve10(void 0);
2562
+ resolve11(void 0);
2560
2563
  return;
2561
2564
  }
2562
2565
  if (ch === "j") {
@@ -2585,7 +2588,7 @@ async function runProjectPicker(opts) {
2585
2588
  try {
2586
2589
  stdin.setRawMode(true);
2587
2590
  } catch {
2588
- resolve10(void 0);
2591
+ resolve11(void 0);
2589
2592
  return;
2590
2593
  }
2591
2594
  stdin.resume();
@@ -2596,7 +2599,7 @@ async function runProjectPicker(opts) {
2596
2599
  stdin.once("close", () => {
2597
2600
  cleanup();
2598
2601
  out.write(CURSOR_SHOW);
2599
- resolve10(void 0);
2602
+ resolve11(void 0);
2600
2603
  });
2601
2604
  });
2602
2605
  }
@@ -2631,7 +2634,7 @@ __export(update_check_exports, {
2631
2634
  getUpdateNotification: () => getUpdateNotification
2632
2635
  });
2633
2636
  function cachePath(homeFn = defaultHomeDir2) {
2634
- return path38.join(homeFn(), ".wrongstack", "update-cache.json");
2637
+ return path39.join(homeFn(), ".wrongstack", "update-cache.json");
2635
2638
  }
2636
2639
  function currentVersion() {
2637
2640
  const req2 = createRequire(import.meta.url);
@@ -2668,7 +2671,7 @@ async function readCache(homeFn = defaultHomeDir2) {
2668
2671
  }
2669
2672
  async function writeCache(entry, homeFn = defaultHomeDir2) {
2670
2673
  try {
2671
- const dir = path38.dirname(cachePath(homeFn));
2674
+ const dir = path39.dirname(cachePath(homeFn));
2672
2675
  await fsp5.mkdir(dir, { recursive: true });
2673
2676
  await fsp5.writeFile(cachePath(homeFn), JSON.stringify(entry, null, 2), "utf8");
2674
2677
  } catch {
@@ -2754,7 +2757,7 @@ function registerWebuiInstance(p, deps = {}) {
2754
2757
  wsPort: p.wsPort,
2755
2758
  host: p.host,
2756
2759
  projectRoot: p.projectRoot,
2757
- projectName: path38.basename(p.projectRoot) || p.projectRoot,
2760
+ projectName: path39.basename(p.projectRoot) || p.projectRoot,
2758
2761
  startedAt: p.startedAt,
2759
2762
  url: `http://${p.host}:${p.httpPort}`
2760
2763
  },
@@ -2818,7 +2821,7 @@ var init_lifecycle = __esm({
2818
2821
  }
2819
2822
  });
2820
2823
  function getVault(globalConfigPath) {
2821
- const keyFile = path38.join(path38.dirname(globalConfigPath ?? ""), ".key");
2824
+ const keyFile = path39.join(path39.dirname(globalConfigPath ?? ""), ".key");
2822
2825
  return new DefaultSecretVault({ keyFile });
2823
2826
  }
2824
2827
  async function loadSavedProviders(globalConfigPath) {
@@ -2852,7 +2855,7 @@ function resolveDistDir() {
2852
2855
  try {
2853
2856
  const requireFromHere = createRequire(import.meta.url);
2854
2857
  const serverEntry = requireFromHere.resolve("@wrongstack/webui/server");
2855
- return path38.resolve(path38.dirname(serverEntry), "..");
2858
+ return path39.resolve(path39.dirname(serverEntry), "..");
2856
2859
  } catch {
2857
2860
  return null;
2858
2861
  }
@@ -2907,7 +2910,7 @@ async function handleModesList(ctx, ws) {
2907
2910
  payload: {
2908
2911
  modes: [],
2909
2912
  activeId: "default",
2910
- error: err instanceof Error ? err.message : String(err)
2913
+ error: toErrorMessage(err)
2911
2914
  }
2912
2915
  });
2913
2916
  }
@@ -2930,7 +2933,7 @@ async function handleModeSwitch(ctx, ws, id) {
2930
2933
  const payload = await ctx.buildSessionStart({ mode: id });
2931
2934
  ctx.broadcast({ type: "session.start", payload });
2932
2935
  } catch (err) {
2933
- sendResult(ctx, ws, false, err instanceof Error ? err.message : String(err));
2936
+ sendResult(ctx, ws, false, toErrorMessage(err));
2934
2937
  }
2935
2938
  }
2936
2939
  async function handleModelSwitch(ctx, ws, payload) {
@@ -2949,7 +2952,7 @@ async function handleModelSwitch(ctx, ws, payload) {
2949
2952
  ctx,
2950
2953
  ws,
2951
2954
  false,
2952
- `Switch failed: ${err instanceof Error ? err.message : String(err)}`
2955
+ `Switch failed: ${toErrorMessage(err)}`
2953
2956
  );
2954
2957
  }
2955
2958
  }
@@ -2998,7 +3001,7 @@ async function handleModelRefine(ctx, ws, text) {
2998
3001
  payload: {
2999
3002
  refined: text,
3000
3003
  english: text,
3001
- error: err instanceof Error ? err.message : String(err)
3004
+ error: toErrorMessage(err)
3002
3005
  }
3003
3006
  });
3004
3007
  }
@@ -3008,8 +3011,6 @@ var init_agent_config = __esm({
3008
3011
  init_provider_config();
3009
3012
  }
3010
3013
  });
3011
-
3012
- // src/webui-server/ws-handlers/brain.ts
3013
3014
  function sendResult2(ctx, ws, success, message) {
3014
3015
  ctx.send(ws, { type: "key.operation_result", payload: { success, message } });
3015
3016
  }
@@ -3063,7 +3064,7 @@ async function handleBrainAsk(ctx, ws, question) {
3063
3064
  ctx,
3064
3065
  ws,
3065
3066
  false,
3066
- `Brain consultation failed: ${err instanceof Error ? err.message : String(err)}`
3067
+ `Brain consultation failed: ${toErrorMessage(err)}`
3067
3068
  );
3068
3069
  }
3069
3070
  }
@@ -3071,8 +3072,6 @@ var init_brain = __esm({
3071
3072
  "src/webui-server/ws-handlers/brain.ts"() {
3072
3073
  }
3073
3074
  });
3074
-
3075
- // src/webui-server/ws-handlers/connection.ts
3076
3075
  async function handleUserMessage(ctx, ws, content) {
3077
3076
  if (ctx.abortControllers.has(ws)) {
3078
3077
  ctx.send(ws, {
@@ -3105,7 +3104,7 @@ async function handleUserMessage(ctx, ws, content) {
3105
3104
  type: "error",
3106
3105
  payload: {
3107
3106
  phase: "agent.run",
3108
- message: err instanceof Error ? err.message : String(err)
3107
+ message: toErrorMessage(err)
3109
3108
  }
3110
3109
  });
3111
3110
  } finally {
@@ -3124,10 +3123,10 @@ function handlePing(ctx, ws) {
3124
3123
  ctx.send(ws, { type: "pong", payload: {} });
3125
3124
  }
3126
3125
  function handleToolConfirmResult(ctx, id, decision) {
3127
- const resolve10 = ctx.pendingConfirms.get(id);
3128
- if (resolve10) {
3126
+ const resolve11 = ctx.pendingConfirms.get(id);
3127
+ if (resolve11) {
3129
3128
  ctx.pendingConfirms.delete(id);
3130
- resolve10(decision);
3129
+ resolve11(decision);
3131
3130
  }
3132
3131
  }
3133
3132
  var init_connection = __esm({
@@ -3354,8 +3353,6 @@ var init_cost_helpers = __esm({
3354
3353
  "src/webui-server/cost-helpers.ts"() {
3355
3354
  }
3356
3355
  });
3357
-
3358
- // src/webui-server/ws-handlers/introspection.ts
3359
3356
  async function handleSkillsList(ctx, ws) {
3360
3357
  if (!ctx.skillLoader) {
3361
3358
  ctx.send(ws, { type: "skills.list", payload: { skills: [], enabled: false } });
@@ -3386,7 +3383,7 @@ async function handleSkillsList(ctx, ws) {
3386
3383
  payload: {
3387
3384
  skills: [],
3388
3385
  enabled: true,
3389
- error: err instanceof Error ? err.message : String(err)
3386
+ error: toErrorMessage(err)
3390
3387
  }
3391
3388
  });
3392
3389
  }
@@ -3543,8 +3540,8 @@ function sendResult6(ctx, ws, success, message) {
3543
3540
  ctx.send(ws, { type: "key.operation_result", payload: { success, message } });
3544
3541
  }
3545
3542
  async function handleProjectsList(ctx, ws) {
3546
- const projectsBase = ctx.opts.globalConfigPath ? path38.resolve(path38.dirname(ctx.opts.globalConfigPath)) : wstackGlobalRoot();
3547
- const manifestPath = path38.join(projectsBase, "projects.json");
3543
+ const projectsBase = ctx.opts.globalConfigPath ? path39.resolve(path39.dirname(ctx.opts.globalConfigPath)) : wstackGlobalRoot();
3544
+ const manifestPath = path39.join(projectsBase, "projects.json");
3548
3545
  try {
3549
3546
  const raw = await fsp5.readFile(manifestPath, "utf8");
3550
3547
  const manifest = JSON.parse(raw);
@@ -3557,22 +3554,22 @@ async function handleProjectsSelect(ctx, ws, payload) {
3557
3554
  const { opts } = ctx;
3558
3555
  const { root, name: projectName } = payload;
3559
3556
  try {
3560
- const resolved = path38.resolve(root);
3557
+ const resolved = path39.resolve(root);
3561
3558
  const stat7 = await fsp5.stat(resolved).catch(() => null);
3562
3559
  if (!stat7?.isDirectory()) {
3563
3560
  ctx.send(ws, {
3564
3561
  type: "projects.selected",
3565
3562
  payload: {
3566
3563
  root,
3567
- name: projectName ?? path38.basename(root),
3564
+ name: projectName ?? path39.basename(root),
3568
3565
  message: `Cannot switch: not a directory: ${resolved}`
3569
3566
  }
3570
3567
  });
3571
3568
  return;
3572
3569
  }
3573
3570
  const manifest = await loadManifest(opts.globalConfigPath);
3574
- const entry = manifest.projects.find((p) => path38.resolve(p.root) === resolved);
3575
- const displayName = projectName?.trim() || entry?.name || path38.basename(resolved);
3571
+ const entry = manifest.projects.find((p) => path39.resolve(p.root) === resolved);
3572
+ const displayName = projectName?.trim() || entry?.name || path39.basename(resolved);
3576
3573
  if (entry) {
3577
3574
  entry.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
3578
3575
  } else {
@@ -3618,8 +3615,8 @@ async function handleProjectsSelect(ctx, ws, payload) {
3618
3615
  });
3619
3616
  } catch {
3620
3617
  }
3621
- const globalRoot = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : wstackGlobalRoot();
3622
- const newSessionsDir = path38.join(resolveProjectDir(resolved, globalRoot), "sessions");
3618
+ const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : wstackGlobalRoot();
3619
+ const newSessionsDir = path39.join(resolveProjectDir(resolved, globalRoot), "sessions");
3623
3620
  await fsp5.mkdir(newSessionsDir, { recursive: true });
3624
3621
  const newStore = new DefaultSessionStore({ dir: newSessionsDir });
3625
3622
  opts.sessionStore = newStore;
@@ -3643,17 +3640,17 @@ async function handleProjectsSelect(ctx, ws, payload) {
3643
3640
  const switchedP = await ctx.buildSessionStart({ reset: true, clearedSessionId: oldSessionId });
3644
3641
  ctx.broadcast({ type: "session.start", payload: switchedP });
3645
3642
  } catch (err) {
3646
- sendResult6(ctx, ws, false, err instanceof Error ? err.message : String(err));
3643
+ sendResult6(ctx, ws, false, toErrorMessage(err));
3647
3644
  }
3648
3645
  }
3649
3646
  async function handleProjectsAdd(ctx, ws, payload) {
3650
3647
  const { root: addRoot, name: addName } = payload;
3651
3648
  try {
3652
- const resolved = path38.resolve(addRoot);
3649
+ const resolved = path39.resolve(addRoot);
3653
3650
  const stat7 = await fsp5.stat(resolved).catch(() => null);
3654
3651
  if (!stat7?.isDirectory()) throw new Error(`Not a directory: ${resolved}`);
3655
3652
  const manifest = await loadManifest(ctx.opts.globalConfigPath);
3656
- const existing = manifest.projects.find((p) => path38.resolve(p.root) === resolved);
3653
+ const existing = manifest.projects.find((p) => path39.resolve(p.root) === resolved);
3657
3654
  if (existing) {
3658
3655
  ctx.send(ws, {
3659
3656
  type: "projects.added",
@@ -3666,7 +3663,7 @@ async function handleProjectsAdd(ctx, ws, payload) {
3666
3663
  });
3667
3664
  return;
3668
3665
  }
3669
- const name = addName?.trim() || path38.basename(resolved);
3666
+ const name = addName?.trim() || path39.basename(resolved);
3670
3667
  const slug = projectSlug(resolved);
3671
3668
  await ensureProjectDataDir(slug, ctx.opts.globalConfigPath);
3672
3669
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -3680,10 +3677,10 @@ async function handleProjectsAdd(ctx, ws, payload) {
3680
3677
  ctx.send(ws, {
3681
3678
  type: "projects.added",
3682
3679
  payload: {
3683
- name: path38.basename(addRoot),
3680
+ name: path39.basename(addRoot),
3684
3681
  root: addRoot,
3685
3682
  slug: "",
3686
- message: err instanceof Error ? err.message : String(err)
3683
+ message: toErrorMessage(err)
3687
3684
  }
3688
3685
  });
3689
3686
  }
@@ -3691,8 +3688,8 @@ async function handleProjectsAdd(ctx, ws, payload) {
3691
3688
  async function handleWorkingDirSet(ctx, ws, newPath) {
3692
3689
  try {
3693
3690
  const wdRoot = ctx.opts.projectRoot ?? ctx.opts.agent.ctx.projectRoot;
3694
- const resolved = path38.resolve(wdRoot, newPath);
3695
- if (!resolved.startsWith(wdRoot + path38.sep) && resolved !== wdRoot) {
3691
+ const resolved = path39.resolve(wdRoot, newPath);
3692
+ if (!resolved.startsWith(wdRoot + path39.sep) && resolved !== wdRoot) {
3696
3693
  sendResult6(ctx, ws, false, `Path must stay inside the project root: ${wdRoot}`);
3697
3694
  return;
3698
3695
  }
@@ -3705,7 +3702,7 @@ async function handleWorkingDirSet(ctx, ws, newPath) {
3705
3702
  ctx.broadcast({ type: "working_dir.changed", payload: { cwd: resolved, projectRoot: wdRoot } });
3706
3703
  sendResult6(ctx, ws, true, `Working directory set to ${resolved}`);
3707
3704
  } catch (err) {
3708
- sendResult6(ctx, ws, false, err instanceof Error ? err.message : String(err));
3705
+ sendResult6(ctx, ws, false, toErrorMessage(err));
3709
3706
  }
3710
3707
  }
3711
3708
  var init_projects = __esm({
@@ -3713,11 +3710,27 @@ var init_projects = __esm({
3713
3710
  init_project_utils();
3714
3711
  }
3715
3712
  });
3716
-
3717
- // src/webui-server/ws-handlers/providers.ts
3718
3713
  function sendResult7(ctx, ws, success, message) {
3719
3714
  ctx.send(ws, { type: "key.operation_result", payload: { success, message } });
3720
3715
  }
3716
+ function broadcastSaved(ctx, providers) {
3717
+ ctx.broadcast({
3718
+ type: "providers.saved",
3719
+ payload: {
3720
+ providers: Object.entries(providers).map(([id, cfg]) => ({
3721
+ id,
3722
+ family: cfg.family,
3723
+ baseUrl: cfg.baseUrl,
3724
+ apiKeys: normalizeKeys(cfg).map((k) => ({
3725
+ label: k.label,
3726
+ maskedKey: maskedKey(k.apiKey),
3727
+ isActive: k.label === cfg.activeKey,
3728
+ createdAt: k.createdAt
3729
+ }))
3730
+ }))
3731
+ }
3732
+ });
3733
+ }
3721
3734
  async function handleProvidersList(ctx, ws) {
3722
3735
  if (!ctx.modelsRegistry) {
3723
3736
  sendResult7(ctx, ws, false, "Models registry not available");
@@ -3742,7 +3755,7 @@ async function handleProvidersList(ctx, ws) {
3742
3755
  }
3743
3756
  });
3744
3757
  } catch (err) {
3745
- sendResult7(ctx, ws, false, err instanceof Error ? err.message : String(err));
3758
+ sendResult7(ctx, ws, false, toErrorMessage(err));
3746
3759
  }
3747
3760
  }
3748
3761
  async function handleProviderModels(ctx, ws, providerId) {
@@ -3777,7 +3790,7 @@ async function handleProviderModels(ctx, ws, providerId) {
3777
3790
  }
3778
3791
  });
3779
3792
  } catch (err) {
3780
- sendResult7(ctx, ws, false, err instanceof Error ? err.message : String(err));
3793
+ sendResult7(ctx, ws, false, toErrorMessage(err));
3781
3794
  }
3782
3795
  }
3783
3796
  async function handleProvidersSaved(ctx, ws) {
@@ -3800,7 +3813,7 @@ async function handleProvidersSaved(ctx, ws) {
3800
3813
  }
3801
3814
  });
3802
3815
  } catch (err) {
3803
- sendResult7(ctx, ws, false, err instanceof Error ? err.message : String(err));
3816
+ sendResult7(ctx, ws, false, toErrorMessage(err));
3804
3817
  }
3805
3818
  }
3806
3819
  async function handleKeyUpsert(ctx, ws, providerId, label, apiKey) {
@@ -3820,7 +3833,7 @@ async function handleKeyUpsert(ctx, ws, providerId, label, apiKey) {
3820
3833
  await ctx.providerStore.save(providers);
3821
3834
  sendResult7(ctx, ws, true, `Key "${label}" saved for ${providerId}`);
3822
3835
  } catch (err) {
3823
- sendResult7(ctx, ws, false, err instanceof Error ? err.message : String(err));
3836
+ sendResult7(ctx, ws, false, toErrorMessage(err));
3824
3837
  }
3825
3838
  }
3826
3839
  async function handleKeyDelete(ctx, ws, providerId, label) {
@@ -3844,7 +3857,7 @@ async function handleKeyDelete(ctx, ws, providerId, label) {
3844
3857
  await ctx.providerStore.save(providers);
3845
3858
  sendResult7(ctx, ws, true, `Key "${label}" deleted from ${providerId}`);
3846
3859
  } catch (err) {
3847
- sendResult7(ctx, ws, false, err instanceof Error ? err.message : String(err));
3860
+ sendResult7(ctx, ws, false, toErrorMessage(err));
3848
3861
  }
3849
3862
  }
3850
3863
  async function handleKeySetActive(ctx, ws, providerId, label) {
@@ -3861,7 +3874,7 @@ async function handleKeySetActive(ctx, ws, providerId, label) {
3861
3874
  await ctx.providerStore.save(providers);
3862
3875
  sendResult7(ctx, ws, true, `Active key for ${providerId} set to "${label}"`);
3863
3876
  } catch (err) {
3864
- sendResult7(ctx, ws, false, err instanceof Error ? err.message : String(err));
3877
+ sendResult7(ctx, ws, false, toErrorMessage(err));
3865
3878
  }
3866
3879
  }
3867
3880
  async function handleProviderAdd(ctx, ws, payload) {
@@ -3906,7 +3919,7 @@ async function handleProviderAdd(ctx, ws, payload) {
3906
3919
  }
3907
3920
  });
3908
3921
  } catch (err) {
3909
- sendResult7(ctx, ws, false, err instanceof Error ? err.message : String(err));
3922
+ sendResult7(ctx, ws, false, toErrorMessage(err));
3910
3923
  }
3911
3924
  }
3912
3925
  async function handleProviderRemove(ctx, ws, providerId) {
@@ -3920,12 +3933,92 @@ async function handleProviderRemove(ctx, ws, providerId) {
3920
3933
  await ctx.providerStore.save(providers);
3921
3934
  sendResult7(ctx, ws, true, `Provider "${providerId}" removed`);
3922
3935
  } catch (err) {
3923
- sendResult7(ctx, ws, false, err instanceof Error ? err.message : String(err));
3936
+ sendResult7(ctx, ws, false, toErrorMessage(err));
3937
+ }
3938
+ }
3939
+ async function handleProviderClearModels(ctx, ws, providerId) {
3940
+ try {
3941
+ const providers = await ctx.providerStore.load();
3942
+ const cfg = providers[providerId];
3943
+ if (!cfg) {
3944
+ sendResult7(ctx, ws, false, `Unknown provider "${providerId}"`);
3945
+ return;
3946
+ }
3947
+ delete cfg.models;
3948
+ await ctx.providerStore.save(providers);
3949
+ sendResult7(ctx, ws, true, `Cleared model allowlist for ${providerId}`);
3950
+ broadcastSaved(ctx, providers);
3951
+ } catch (err) {
3952
+ sendResult7(ctx, ws, false, toErrorMessage(err));
3924
3953
  }
3925
3954
  }
3955
+ async function handleProviderUndoClear(ctx, ws, providerId, previousModels) {
3956
+ try {
3957
+ const providers = await ctx.providerStore.load();
3958
+ const cfg = providers[providerId];
3959
+ if (!cfg) {
3960
+ sendResult7(ctx, ws, false, `Unknown provider "${providerId}"`);
3961
+ return;
3962
+ }
3963
+ cfg.models = [...previousModels];
3964
+ await ctx.providerStore.save(providers);
3965
+ sendResult7(ctx, ws, true, `Restored ${previousModels.length} model(s) for ${providerId}`);
3966
+ broadcastSaved(ctx, providers);
3967
+ } catch (err) {
3968
+ sendResult7(ctx, ws, false, toErrorMessage(err));
3969
+ }
3970
+ }
3971
+ async function handleProviderUpdate(ctx, ws, payload) {
3972
+ try {
3973
+ const providers = await ctx.providerStore.load();
3974
+ const cfg = providers[payload.id];
3975
+ if (!cfg) {
3976
+ sendResult7(ctx, ws, false, `Unknown provider "${payload.id}"`);
3977
+ return;
3978
+ }
3979
+ if (payload.family !== void 0) cfg.family = payload.family;
3980
+ if (payload.baseUrl !== void 0) cfg.baseUrl = payload.baseUrl;
3981
+ if (payload.envVars !== void 0) cfg.envVars = payload.envVars;
3982
+ if (payload.models !== void 0) cfg.models = payload.models;
3983
+ await ctx.providerStore.save(providers);
3984
+ sendResult7(ctx, ws, true, `Updated ${payload.id}`);
3985
+ broadcastSaved(ctx, providers);
3986
+ } catch (err) {
3987
+ sendResult7(ctx, ws, false, toErrorMessage(err));
3988
+ }
3989
+ }
3990
+ async function handleProviderProbe(ctx, ws, providerId, timeoutMs) {
3991
+ const reply = (payload) => ctx.send(ws, { type: "provider.probe", payload: { providerId, ...payload } });
3992
+ try {
3993
+ const providers = await ctx.providerStore.load();
3994
+ const cfg = providers[providerId];
3995
+ if (!cfg) {
3996
+ reply({ ok: false, status: "no_provider" });
3997
+ return;
3998
+ }
3999
+ if (!cfg.baseUrl) {
4000
+ reply({ ok: false, status: "no_base_url" });
4001
+ return;
4002
+ }
4003
+ const keys = normalizeKeys(cfg);
4004
+ const active = keys.find((k) => k.label === cfg.activeKey) ?? keys[0];
4005
+ const result = await probeLocalLlm({
4006
+ baseUrl: cfg.baseUrl,
4007
+ apiKey: active?.apiKey,
4008
+ noAuth: false,
4009
+ scrubber: probeScrubber,
4010
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
4011
+ });
4012
+ reply(result);
4013
+ } catch (err) {
4014
+ reply({ ok: false, status: "unreachable", detail: toErrorMessage(err) });
4015
+ }
4016
+ }
4017
+ var probeScrubber;
3926
4018
  var init_providers = __esm({
3927
4019
  "src/webui-server/ws-handlers/providers.ts"() {
3928
4020
  init_provider_config();
4021
+ probeScrubber = new DefaultSecretScrubber();
3929
4022
  }
3930
4023
  });
3931
4024
  function sendResult8(ctx, ws, success, message) {
@@ -3933,13 +4026,13 @@ function sendResult8(ctx, ws, success, message) {
3933
4026
  }
3934
4027
  function storeFor(opts) {
3935
4028
  return opts.sessionStore ?? new DefaultSessionStore({
3936
- dir: path38.join(opts.projectRoot ?? opts.agent.ctx.projectRoot, ".wrongstack", "sessions")
4029
+ dir: path39.join(opts.projectRoot ?? opts.agent.ctx.projectRoot, ".wrongstack", "sessions")
3937
4030
  });
3938
4031
  }
3939
4032
  async function handleGoalGet(ctx, _ws) {
3940
4033
  const projectRoot = ctx.opts.projectRoot ?? ctx.opts.agent.ctx.projectRoot;
3941
4034
  try {
3942
- const goalPath = path38.join(projectRoot, ".wrongstack", "goal.json");
4035
+ const goalPath = path39.join(projectRoot, ".wrongstack", "goal.json");
3943
4036
  const raw = await fsp5.readFile(goalPath, "utf8");
3944
4037
  ctx.broadcast({ type: "goal.updated", payload: JSON.parse(raw) });
3945
4038
  } catch {
@@ -3967,7 +4060,7 @@ async function handleSessionsList(ctx, ws, limit) {
3967
4060
  } catch (err) {
3968
4061
  ctx.send(ws, {
3969
4062
  type: "sessions.list",
3970
- payload: { sessions: [], error: err instanceof Error ? err.message : String(err) }
4063
+ payload: { sessions: [], error: toErrorMessage(err) }
3971
4064
  });
3972
4065
  }
3973
4066
  }
@@ -3999,7 +4092,7 @@ async function handleSessionNew(ctx, _ws) {
3999
4092
  JSON.stringify({
4000
4093
  level: "warn",
4001
4094
  event: "webui.session_new_store_failed",
4002
- message: err instanceof Error ? err.message : String(err),
4095
+ message: toErrorMessage(err),
4003
4096
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
4004
4097
  })
4005
4098
  );
@@ -4015,7 +4108,7 @@ async function handleSessionNew(ctx, _ws) {
4015
4108
  function rewinderFor(opts) {
4016
4109
  const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot;
4017
4110
  return new DefaultSessionRewinder(
4018
- opts.sessionsDir ?? path38.join(projectRoot, ".wrongstack", "sessions"),
4111
+ opts.sessionsDir ?? path39.join(projectRoot, ".wrongstack", "sessions"),
4019
4112
  projectRoot
4020
4113
  );
4021
4114
  }
@@ -4039,7 +4132,7 @@ async function handleSessionRewind(ctx, ws, checkpointIndex) {
4039
4132
  const payload = await ctx.buildSessionStart({ reset: true });
4040
4133
  ctx.broadcast({ type: "session.start", payload });
4041
4134
  } catch (err) {
4042
- sendResult8(ctx, ws, false, err instanceof Error ? err.message : String(err));
4135
+ sendResult8(ctx, ws, false, toErrorMessage(err));
4043
4136
  }
4044
4137
  }
4045
4138
  async function handleSessionDelete(ctx, ws, id) {
@@ -4051,7 +4144,7 @@ async function handleSessionDelete(ctx, ws, id) {
4051
4144
  await storeFor(ctx.opts).delete(id);
4052
4145
  sendResult8(ctx, ws, true, `Session ${id} deleted`);
4053
4146
  } catch (err) {
4054
- sendResult8(ctx, ws, false, err instanceof Error ? err.message : String(err));
4147
+ sendResult8(ctx, ws, false, toErrorMessage(err));
4055
4148
  }
4056
4149
  }
4057
4150
  function handleSessionSave(ctx, ws) {
@@ -4094,7 +4187,7 @@ async function handleSessionResume(ctx, ws, id) {
4094
4187
  ctx.broadcast({ type: "session.start", payload });
4095
4188
  sendResult8(ctx, ws, true, `Resumed session ${id}`);
4096
4189
  } catch (err) {
4097
- sendResult8(ctx, ws, false, err instanceof Error ? err.message : String(err));
4190
+ sendResult8(ctx, ws, false, toErrorMessage(err));
4098
4191
  }
4099
4192
  }
4100
4193
  var init_sessions = __esm({
@@ -4349,14 +4442,14 @@ async function runWebUI(opts) {
4349
4442
  let customModeStoreP = null;
4350
4443
  const getCustomModeStore = () => {
4351
4444
  customModeStoreP ??= (async () => {
4352
- const dir = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : wstackGlobalRoot();
4445
+ const dir = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : wstackGlobalRoot();
4353
4446
  const store = createCustomModeStore(dir);
4354
4447
  await store.load();
4355
4448
  return store;
4356
4449
  })();
4357
4450
  return customModeStoreP;
4358
4451
  };
4359
- const autoPhaseStoreDir = opts.projectRoot ? path38.join(opts.projectRoot, ".wrongstack", "autophase") : path38.join(os.tmpdir(), ".wrongstack", "autophase");
4452
+ const autoPhaseStoreDir = opts.projectRoot ? path39.join(opts.projectRoot, ".wrongstack", "autophase") : path39.join(os.tmpdir(), ".wrongstack", "autophase");
4360
4453
  const autoPhaseHandler = new AutoPhaseWebSocketHandler(
4361
4454
  opts.agent,
4362
4455
  opts.agent.ctx,
@@ -4447,7 +4540,7 @@ async function runWebUI(opts) {
4447
4540
  return;
4448
4541
  }
4449
4542
  const vault = new DefaultSecretVault({
4450
- keyFile: path38.join(path38.dirname(configPath2), ".key")
4543
+ keyFile: path39.join(path39.dirname(configPath2), ".key")
4451
4544
  });
4452
4545
  const decrypted = decryptConfigSecrets$1(parsed, vault);
4453
4546
  const autonomyCfg = decrypted.autonomy ?? {};
@@ -4570,7 +4663,7 @@ async function runWebUI(opts) {
4570
4663
  model: opts.agent.ctx.model,
4571
4664
  provider: opts.agent.ctx.provider.id,
4572
4665
  mode: opts.modeId ?? "default",
4573
- projectName: opts.projectRoot ? path38.basename(opts.projectRoot) : void 0,
4666
+ projectName: opts.projectRoot ? path39.basename(opts.projectRoot) : void 0,
4574
4667
  // Frontend reads `projectRoot` from session.start (ws-handlers setEnv) —
4575
4668
  // omitting it left the store's projectRoot empty after a project switch.
4576
4669
  projectRoot: opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "",
@@ -4596,7 +4689,7 @@ async function runWebUI(opts) {
4596
4689
  const projectDir = resolveProjectDir(opts.projectRoot, wstackGlobalRoot());
4597
4690
  const mailbox = new GlobalMailbox(projectDir, opts.events);
4598
4691
  webuiClientId = `webui@${crypto3.randomUUID().slice(0, 8)}`;
4599
- const projectName = opts.projectRoot ? path38.basename(opts.projectRoot) : "unknown";
4692
+ const projectName = opts.projectRoot ? path39.basename(opts.projectRoot) : "unknown";
4600
4693
  await mailbox.registerClient({
4601
4694
  clientId: webuiClientId,
4602
4695
  sessionId: opts.projectRoot,
@@ -4627,7 +4720,7 @@ async function runWebUI(opts) {
4627
4720
  host,
4628
4721
  httpPort,
4629
4722
  wsPort,
4630
- globalRoot: path38.dirname(opts.globalConfigPath ?? "")
4723
+ globalRoot: path39.dirname(opts.globalConfigPath ?? "")
4631
4724
  });
4632
4725
  if (httpServer) {
4633
4726
  announceWebuiReady({
@@ -4642,7 +4735,7 @@ async function runWebUI(opts) {
4642
4735
  `[WebUI] Frontend not served (run \`pnpm --filter @wrongstack/webui build\`). WS bridge still active on ws://${host}:${wsPort}.`
4643
4736
  );
4644
4737
  }
4645
- const registryBaseDir = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : void 0;
4738
+ const registryBaseDir = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : void 0;
4646
4739
  if (opts.projectRoot) {
4647
4740
  registerWebuiInstance({
4648
4741
  pid: process.pid,
@@ -4999,12 +5092,12 @@ async function runWebUI(opts) {
4999
5092
  broadcast,
5000
5093
  log: (m) => console.log(m)
5001
5094
  };
5002
- return new Promise((resolve10) => {
5095
+ return new Promise((resolve11) => {
5003
5096
  wss.on("listening", () => {
5004
5097
  console.log(`[WebUI] WebSocket server running on ws://${host}:${port}`);
5005
5098
  setupEvents();
5006
5099
  opts.onListening?.({ httpPort, wsPort, host });
5007
- const globalRoot = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : void 0;
5100
+ const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : void 0;
5008
5101
  if (globalRoot) {
5009
5102
  const statusInterval = setInterval(async () => {
5010
5103
  try {
@@ -5123,8 +5216,8 @@ async function runWebUI(opts) {
5123
5216
  clients.delete(ws);
5124
5217
  abortControllers.delete(ws);
5125
5218
  if (clients.size === 0 && pendingConfirms.size > 0) {
5126
- for (const [id, resolve11] of pendingConfirms) {
5127
- resolve11("no");
5219
+ for (const [id, resolve12] of pendingConfirms) {
5220
+ resolve12("no");
5128
5221
  pendingConfirms.delete(id);
5129
5222
  }
5130
5223
  }
@@ -5163,7 +5256,7 @@ async function runWebUI(opts) {
5163
5256
  wss,
5164
5257
  pid: process.pid,
5165
5258
  registryBaseDir,
5166
- onStopped: resolve10
5259
+ onStopped: resolve11
5167
5260
  });
5168
5261
  registerWebuiSignalHandlers(signalShutdown);
5169
5262
  });
@@ -5226,6 +5319,31 @@ async function runWebUI(opts) {
5226
5319
  await handleProviderRemove(wsHandlerCtx, ws, m.payload.providerId);
5227
5320
  break;
5228
5321
  }
5322
+ case "provider.clear_models": {
5323
+ const m = msg;
5324
+ await handleProviderClearModels(wsHandlerCtx, ws, m.payload.providerId);
5325
+ break;
5326
+ }
5327
+ case "provider.undo_clear": {
5328
+ const m = msg;
5329
+ await handleProviderUndoClear(
5330
+ wsHandlerCtx,
5331
+ ws,
5332
+ m.payload.providerId,
5333
+ m.payload.previousModels
5334
+ );
5335
+ break;
5336
+ }
5337
+ case "provider.update": {
5338
+ const m = msg;
5339
+ await handleProviderUpdate(wsHandlerCtx, ws, m.payload);
5340
+ break;
5341
+ }
5342
+ case "provider.probe": {
5343
+ const m = msg;
5344
+ await handleProviderProbe(wsHandlerCtx, ws, m.payload.providerId, m.payload.timeoutMs);
5345
+ break;
5346
+ }
5229
5347
  case "todos.get": {
5230
5348
  handleTodosGet(worklistCtx, ws);
5231
5349
  break;
@@ -5500,11 +5618,16 @@ async function runWebUI(opts) {
5500
5618
  break;
5501
5619
  }
5502
5620
  // Collaboration messages — the CLI webui-server doesn't run a
5503
- // full collab hub; silently acknowledge and ignore.
5621
+ // full collab hub; silently acknowledge and ignore. request_pause /
5622
+ // resume are included so the CollabPanel's pause/resume buttons don't
5623
+ // trip the "Unhandled message type" warning (the standalone webui
5624
+ // server is the one that wires the real CollaborationWebSocketHandler).
5504
5625
  case "collab.join":
5505
5626
  case "collab.leave":
5506
5627
  case "collab.annotate":
5507
5628
  case "collab.resolve":
5629
+ case "collab.request_pause":
5630
+ case "collab.resume":
5508
5631
  break;
5509
5632
  case "projects.list": {
5510
5633
  await handleProjectsList(projectsCtx, ws);
@@ -5553,7 +5676,7 @@ async function runWebUI(opts) {
5553
5676
  // ── Mailbox operations — project-level inter-agent messaging ────
5554
5677
  case "mailbox.messages": {
5555
5678
  const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
5556
- const globalRoot = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : "";
5679
+ const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
5557
5680
  if (!projectRoot || !globalRoot) {
5558
5681
  send(ws, {
5559
5682
  type: "mailbox.messages",
@@ -5602,7 +5725,7 @@ async function runWebUI(opts) {
5602
5725
  }
5603
5726
  case "mailbox.agents": {
5604
5727
  const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
5605
- const globalRoot = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : "";
5728
+ const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
5606
5729
  if (!projectRoot || !globalRoot) {
5607
5730
  send(ws, {
5608
5731
  type: "mailbox.agents",
@@ -5645,7 +5768,7 @@ async function runWebUI(opts) {
5645
5768
  }
5646
5769
  case "mailbox.clear": {
5647
5770
  const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
5648
- const globalRoot = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : "";
5771
+ const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
5649
5772
  if (!projectRoot || !globalRoot) {
5650
5773
  send(ws, { type: "mailbox.cleared", payload: { error: "No project root available" } });
5651
5774
  break;
@@ -5663,6 +5786,57 @@ async function runWebUI(opts) {
5663
5786
  }
5664
5787
  break;
5665
5788
  }
5789
+ case "mailbox.purge": {
5790
+ const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
5791
+ const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
5792
+ if (!projectRoot || !globalRoot) {
5793
+ send(ws, { type: "mailbox.purged", payload: { error: "No project root available" } });
5794
+ break;
5795
+ }
5796
+ try {
5797
+ const mbDir = resolveProjectDir(projectRoot, globalRoot);
5798
+ const mb = new GlobalMailbox(mbDir);
5799
+ const payload = msg;
5800
+ const result = await mb.purgeStale(payload.payload);
5801
+ send(ws, { type: "mailbox.purged", payload: result });
5802
+ } catch (err) {
5803
+ send(ws, {
5804
+ type: "mailbox.purged",
5805
+ payload: { error: err instanceof Error ? err.message : String(err) }
5806
+ });
5807
+ }
5808
+ break;
5809
+ }
5810
+ case "git.info": {
5811
+ const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
5812
+ const cwd = projectRoot || void 0;
5813
+ const { execFile: ef } = await import('child_process');
5814
+ const git = (args) => new Promise((resolve11) => {
5815
+ ef("git", args, { cwd, timeout: 3e3 }, (err, stdout) => {
5816
+ resolve11(err ? "" : stdout.trim());
5817
+ });
5818
+ });
5819
+ const [branchRaw, diffRaw, statusRaw, upstreamRaw] = await Promise.all([
5820
+ git(["branch", "--show-current"]),
5821
+ git(["diff", "--stat"]),
5822
+ git(["status", "--porcelain"]),
5823
+ git(["rev-list", "--left-right", "--count", "@{upstream}...HEAD"])
5824
+ ]);
5825
+ const branch = branchRaw || "(detached)";
5826
+ const addMatch = /(\d+)\s+insertion/i.exec(diffRaw);
5827
+ const delMatch = /(\d+)\s+deletion/i.exec(diffRaw);
5828
+ const added = addMatch ? Number(addMatch[1]) : 0;
5829
+ const deleted = delMatch ? Number(delMatch[1]) : 0;
5830
+ const untracked = statusRaw.split("\n").filter((l) => l.startsWith("??")).length;
5831
+ const [behindRaw, aheadRaw] = (upstreamRaw || "0 0").split(" ");
5832
+ const behind = Number(behindRaw) || 0;
5833
+ const ahead = Number(aheadRaw) || 0;
5834
+ send(ws, {
5835
+ type: "git.info",
5836
+ payload: { branch, added, deleted, untracked, ahead, behind }
5837
+ });
5838
+ break;
5839
+ }
5666
5840
  default: {
5667
5841
  const msgType = msg.type;
5668
5842
  if (msgType.startsWith("autophase.")) {
@@ -5715,7 +5889,7 @@ var init_webui_server = __esm({
5715
5889
  var WORKTREE_PHASE_CONCURRENCY = 4;
5716
5890
  var MAX_CMD_OUTPUT = 2e5;
5717
5891
  function gitText(args, cwd) {
5718
- return new Promise((resolve10, reject) => {
5892
+ return new Promise((resolve11, reject) => {
5719
5893
  let child;
5720
5894
  try {
5721
5895
  child = spawn("git", args, {
@@ -5735,8 +5909,8 @@ function gitText(args, cwd) {
5735
5909
  };
5736
5910
  child.stdout?.on("data", emit);
5737
5911
  child.stderr?.on("data", emit);
5738
- child.on("error", () => resolve10({ code: 1, out: chunks.join("") }));
5739
- child.on("close", (code) => resolve10({ code: code ?? 1, out: chunks.join("").trim() }));
5912
+ child.on("error", () => resolve11({ code: 1, out: chunks.join("") }));
5913
+ child.on("close", (code) => resolve11({ code: code ?? 1, out: chunks.join("").trim() }));
5740
5914
  });
5741
5915
  }
5742
5916
  async function isGitRepo(cwd) {
@@ -5772,7 +5946,7 @@ function runCmd(cmd, args, cwd, shell = false) {
5772
5946
  });
5773
5947
  }
5774
5948
  }
5775
- return new Promise((resolve10, reject) => {
5949
+ return new Promise((resolve11, reject) => {
5776
5950
  const chunks = [];
5777
5951
  let child;
5778
5952
  try {
@@ -5795,11 +5969,11 @@ function runCmd(cmd, args, cwd, shell = false) {
5795
5969
  };
5796
5970
  child.stdout?.on("data", append);
5797
5971
  child.stderr?.on("data", append);
5798
- child.on("error", (e) => resolve10({ code: 1, out: `${chunks.join("")}${String(e)}` }));
5972
+ child.on("error", (e) => resolve11({ code: 1, out: `${chunks.join("")}${String(e)}` }));
5799
5973
  child.on("close", (code) => {
5800
5974
  let out = chunks.join("");
5801
5975
  if (out.length > MAX_CMD_OUTPUT) out = out.slice(-MAX_CMD_OUTPUT);
5802
- resolve10({ code: code ?? 1, out: out.trim() });
5976
+ resolve11({ code: code ?? 1, out: out.trim() });
5803
5977
  });
5804
5978
  });
5805
5979
  }
@@ -6275,7 +6449,7 @@ var ReadlineInputReader = class {
6275
6449
  history = [];
6276
6450
  pending = false;
6277
6451
  constructor(opts = {}) {
6278
- this.historyFile = opts.historyFile ?? path38.join(wstackGlobalRoot(), "history");
6452
+ this.historyFile = opts.historyFile ?? path39.join(wstackGlobalRoot(), "history");
6279
6453
  }
6280
6454
  async loadHistory() {
6281
6455
  try {
@@ -6287,7 +6461,7 @@ var ReadlineInputReader = class {
6287
6461
  }
6288
6462
  async saveHistory() {
6289
6463
  try {
6290
- await fsp5.mkdir(path38.dirname(this.historyFile), { recursive: true });
6464
+ await fsp5.mkdir(path39.dirname(this.historyFile), { recursive: true });
6291
6465
  await fsp5.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
6292
6466
  } catch {
6293
6467
  }
@@ -6306,31 +6480,31 @@ var ReadlineInputReader = class {
6306
6480
  async readLine(prompt) {
6307
6481
  if (this.history.length === 0) await this.loadHistory();
6308
6482
  while (this.pending) {
6309
- await new Promise((resolve10) => setTimeout(resolve10, 50));
6483
+ await new Promise((resolve11) => setTimeout(resolve11, 50));
6310
6484
  }
6311
6485
  this.pending = true;
6312
6486
  try {
6313
6487
  if (this.rl) {
6314
6488
  const old = this.rl;
6315
6489
  this.rl = void 0;
6316
- await new Promise((resolve10) => {
6490
+ await new Promise((resolve11) => {
6317
6491
  if (old.closed) {
6318
- resolve10();
6492
+ resolve11();
6319
6493
  } else {
6320
- old.once("close", resolve10);
6494
+ old.once("close", resolve11);
6321
6495
  old.close();
6322
6496
  }
6323
6497
  });
6324
6498
  }
6325
6499
  const fresh = this.ensure();
6326
6500
  this.installPromptGuard(fresh);
6327
- return new Promise((resolve10) => {
6501
+ return new Promise((resolve11) => {
6328
6502
  let settled = false;
6329
6503
  const settle = (line) => {
6330
6504
  if (settled) return;
6331
6505
  settled = true;
6332
6506
  setOutputLineGuard(null);
6333
- resolve10(line);
6507
+ resolve11(line);
6334
6508
  };
6335
6509
  fresh.question(prompt ?? "> ", (line) => {
6336
6510
  if (line.trim()) {
@@ -6383,7 +6557,7 @@ var ReadlineInputReader = class {
6383
6557
  async readKey(prompt, options) {
6384
6558
  setOutputLineGuard(null);
6385
6559
  writeOut(prompt);
6386
- return new Promise((resolve10) => {
6560
+ return new Promise((resolve11) => {
6387
6561
  const stdin = process.stdin;
6388
6562
  const wasRaw = stdin.isRaw;
6389
6563
  const wasPaused = stdin.isPaused();
@@ -6394,7 +6568,7 @@ var ReadlineInputReader = class {
6394
6568
  if (key === "") {
6395
6569
  cleanup();
6396
6570
  writeOut("\n");
6397
- resolve10("");
6571
+ resolve11("");
6398
6572
  return;
6399
6573
  }
6400
6574
  const opt = options.find(
@@ -6404,12 +6578,12 @@ var ReadlineInputReader = class {
6404
6578
  cleanup();
6405
6579
  writeOut(`${opt.key}
6406
6580
  `);
6407
- resolve10(opt.value);
6581
+ resolve11(opt.value);
6408
6582
  }
6409
6583
  };
6410
6584
  const onClose = () => {
6411
6585
  cleanup();
6412
- resolve10("");
6586
+ resolve11("");
6413
6587
  };
6414
6588
  const cleanup = () => {
6415
6589
  stdin.off("data", onData);
@@ -6438,7 +6612,7 @@ var ReadlineInputReader = class {
6438
6612
  this.rl?.close();
6439
6613
  this.rl = void 0;
6440
6614
  writeOut(prompt);
6441
- return new Promise((resolve10) => {
6615
+ return new Promise((resolve11) => {
6442
6616
  let buf = "";
6443
6617
  const wasRaw = stdin.isRaw;
6444
6618
  setRawMode(stdin, true);
@@ -6456,7 +6630,7 @@ var ReadlineInputReader = class {
6456
6630
  cleanup();
6457
6631
  writeOut(` ${dim(`[${buf.length} chars]`)}
6458
6632
  `);
6459
- resolve10(buf);
6633
+ resolve11(buf);
6460
6634
  return;
6461
6635
  }
6462
6636
  if (ch === "") {
@@ -6619,7 +6793,7 @@ function pickGroupIndex(opts) {
6619
6793
  if (Number.isFinite(parsed)) current = wrap(parsed);
6620
6794
  } catch {
6621
6795
  }
6622
- fs2.mkdirSync(path38.dirname(opts.cursorFile), { recursive: true });
6796
+ fs2.mkdirSync(path39.dirname(opts.cursorFile), { recursive: true });
6623
6797
  fs2.writeFileSync(opts.cursorFile, String(wrap(current + 1)));
6624
6798
  return current;
6625
6799
  } catch {
@@ -6672,11 +6846,11 @@ function assertSafeToDelete(filename, parentDir) {
6672
6846
  throw new FsError({
6673
6847
  message: `Refusing to delete protected file: ${filename}`,
6674
6848
  code: ERROR_CODES.FS_DELETE_FAILED,
6675
- path: path38.join(parentDir, filename),
6849
+ path: path39.join(parentDir, filename),
6676
6850
  context: { reason: "protected_basename" }
6677
6851
  });
6678
6852
  }
6679
- if (filename !== path38.basename(filename)) {
6853
+ if (filename !== path39.basename(filename)) {
6680
6854
  throw new FsError({
6681
6855
  message: `Refusing to delete path with traversal: ${filename}`,
6682
6856
  code: ERROR_CODES.FS_DELETE_FAILED,
@@ -6688,11 +6862,11 @@ function assertSafeToDelete(filename, parentDir) {
6688
6862
  throw new FsError({
6689
6863
  message: `Refusing to delete unknown file: ${filename}`,
6690
6864
  code: ERROR_CODES.FS_DELETE_FAILED,
6691
- path: path38.join(parentDir, filename),
6865
+ path: path39.join(parentDir, filename),
6692
6866
  context: { reason: "unknown_file_pattern" }
6693
6867
  });
6694
6868
  }
6695
- const resolvedParent = path38.resolve(parentDir);
6869
+ const resolvedParent = path39.resolve(parentDir);
6696
6870
  if (!resolvedParent.endsWith(".wrongstack")) {
6697
6871
  throw new FsError({
6698
6872
  message: `Unexpected parent directory for bak prune: ${resolvedParent}`,
@@ -6703,8 +6877,8 @@ function assertSafeToDelete(filename, parentDir) {
6703
6877
  }
6704
6878
  }
6705
6879
  async function safeDelete(filePath) {
6706
- const dir = path38.dirname(filePath);
6707
- const filename = path38.basename(filePath);
6880
+ const dir = path39.dirname(filePath);
6881
+ const filename = path39.basename(filePath);
6708
6882
  try {
6709
6883
  assertSafeToDelete(filename, dir);
6710
6884
  await fsp5.unlink(filePath);
@@ -6749,16 +6923,16 @@ function diffSummary(oldCfg, newCfg) {
6749
6923
  }
6750
6924
  var defaultHomeDir = () => os__default.homedir();
6751
6925
  function historyDir(homeFn = defaultHomeDir) {
6752
- return path38.join(homeFn(), ".wrongstack", "config.history", "entries");
6926
+ return path39.join(homeFn(), ".wrongstack", "config.history", "entries");
6753
6927
  }
6754
6928
  function historyIndexPath(homeFn = defaultHomeDir) {
6755
- return path38.join(homeFn(), ".wrongstack", "config.history", "index.json");
6929
+ return path39.join(homeFn(), ".wrongstack", "config.history", "index.json");
6756
6930
  }
6757
6931
  function configPath(homeFn = defaultHomeDir) {
6758
- return path38.join(homeFn(), ".wrongstack", "config.json");
6932
+ return path39.join(homeFn(), ".wrongstack", "config.json");
6759
6933
  }
6760
6934
  function backupLastPath(homeFn = defaultHomeDir) {
6761
- return path38.join(homeFn(), ".wrongstack", "config.json.last");
6935
+ return path39.join(homeFn(), ".wrongstack", "config.json.last");
6762
6936
  }
6763
6937
  function entryId(ts) {
6764
6938
  return ts.replace(/[:.]/g, "-").slice(0, 19);
@@ -6768,7 +6942,7 @@ async function ensureHistoryDir(homeFn = defaultHomeDir) {
6768
6942
  await fsp5.mkdir(historyDir(homeFn), { recursive: true });
6769
6943
  } catch (err) {
6770
6944
  throw new FsError({
6771
- message: err instanceof Error ? err.message : String(err),
6945
+ message: toErrorMessage(err),
6772
6946
  code: ERROR_CODES.FS_MKDIR_FAILED,
6773
6947
  path: historyDir(homeFn),
6774
6948
  cause: err
@@ -6789,7 +6963,7 @@ async function writeIndex(idx, homeFn = defaultHomeDir) {
6789
6963
  await atomicWrite(historyIndexPath(homeFn), JSON.stringify(idx, null, 2));
6790
6964
  } catch (err) {
6791
6965
  throw new FsError({
6792
- message: err instanceof Error ? err.message : String(err),
6966
+ message: toErrorMessage(err),
6793
6967
  code: ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
6794
6968
  path: historyIndexPath(homeFn),
6795
6969
  cause: err
@@ -6810,30 +6984,30 @@ async function backupCurrent(homeFn = defaultHomeDir) {
6810
6984
  await atomicWrite(last, content);
6811
6985
  } catch (err) {
6812
6986
  writeErr(
6813
- `[config-history] .last backup failed: ${err instanceof Error ? err.message : String(err)}`
6987
+ `[config-history] .last backup failed: ${toErrorMessage(err)}`
6814
6988
  );
6815
6989
  }
6816
6990
  }
6817
6991
  if (content !== void 0) {
6818
6992
  try {
6819
- const bakPath = path38.join(homeFn(), ".wrongstack", `config.json.${ts}.bak`);
6993
+ const bakPath = path39.join(homeFn(), ".wrongstack", `config.json.${ts}.bak`);
6820
6994
  await atomicWrite(bakPath, content);
6821
6995
  } catch (err) {
6822
6996
  writeErr(
6823
- `[config-history] timestamped backup failed: ${err instanceof Error ? err.message : String(err)}`
6997
+ `[config-history] timestamped backup failed: ${toErrorMessage(err)}`
6824
6998
  );
6825
6999
  }
6826
7000
  }
6827
7001
  try {
6828
- const dir = path38.join(homeFn(), ".wrongstack");
7002
+ const dir = path39.join(homeFn(), ".wrongstack");
6829
7003
  const files = await fsp5.readdir(dir);
6830
7004
  const baks = files.filter((f) => f.startsWith("config.json.") && f.endsWith(".bak")).sort().reverse();
6831
7005
  for (const f of baks.slice(10)) {
6832
- await safeDelete(path38.join(dir, f));
7006
+ await safeDelete(path39.join(dir, f));
6833
7007
  }
6834
7008
  } catch (err) {
6835
7009
  writeErr(
6836
- `[config-history] backup prune failed: ${err instanceof Error ? err.message : String(err)}`
7010
+ `[config-history] backup prune failed: ${toErrorMessage(err)}`
6837
7011
  );
6838
7012
  }
6839
7013
  }
@@ -6850,15 +7024,15 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
6850
7024
  };
6851
7025
  try {
6852
7026
  await fsp5.writeFile(
6853
- path38.join(historyDir(homeFn), `${id}.json`),
7027
+ path39.join(historyDir(homeFn), `${id}.json`),
6854
7028
  JSON.stringify(entry, null, 2),
6855
7029
  "utf8"
6856
7030
  );
6857
7031
  } catch (err) {
6858
7032
  throw new FsError({
6859
- message: err instanceof Error ? err.message : String(err),
7033
+ message: toErrorMessage(err),
6860
7034
  code: ERROR_CODES.FS_WRITE_FAILED,
6861
- path: path38.join(historyDir(homeFn), `${id}.json`),
7035
+ path: path39.join(historyDir(homeFn), `${id}.json`),
6862
7036
  cause: err
6863
7037
  });
6864
7038
  }
@@ -6873,7 +7047,7 @@ async function listHistory(homeFn = defaultHomeDir) {
6873
7047
  }
6874
7048
  async function getHistoryEntry(id, homeFn = defaultHomeDir) {
6875
7049
  try {
6876
- const raw = await fsp5.readFile(path38.join(historyDir(homeFn), `${id}.json`), "utf8");
7050
+ const raw = await fsp5.readFile(path39.join(historyDir(homeFn), `${id}.json`), "utf8");
6877
7051
  return JSON.parse(raw);
6878
7052
  } catch {
6879
7053
  return null;
@@ -6984,16 +7158,14 @@ async function buildPickableProviders(modelsRegistry, config) {
6984
7158
  }
6985
7159
  return out;
6986
7160
  }
6987
-
6988
- // src/picker.ts
6989
7161
  var theme = { primary: color.amber };
6990
7162
  async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? os__default.homedir()) {
6991
7163
  try {
6992
7164
  const { atomicWrite: atomicWrite17 } = await import('@wrongstack/core');
6993
- const fs38 = await import('fs/promises');
7165
+ const fs39 = await import('fs/promises');
6994
7166
  let existing = {};
6995
7167
  try {
6996
- const raw = await fs38.readFile(configPath2, "utf8");
7168
+ const raw = await fs39.readFile(configPath2, "utf8");
6997
7169
  existing = JSON.parse(raw);
6998
7170
  } catch {
6999
7171
  }
@@ -7007,7 +7179,7 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
7007
7179
  JSON.stringify({
7008
7180
  level: "warn",
7009
7181
  event: "picker.backup_failed",
7010
- message: err instanceof Error ? err.message : String(err),
7182
+ message: toErrorMessage(err),
7011
7183
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
7012
7184
  })
7013
7185
  );
@@ -7028,7 +7200,7 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
7028
7200
  JSON.stringify({
7029
7201
  level: "warn",
7030
7202
  event: "picker.save_failed",
7031
- message: err instanceof Error ? err.message : String(err),
7203
+ message: toErrorMessage(err),
7032
7204
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
7033
7205
  })
7034
7206
  );
@@ -7486,7 +7658,7 @@ function buildAutonomyCommand(opts) {
7486
7658
  const current = opts.onAutonomy();
7487
7659
  const lines = [`Autonomy mode: ${MODE_LABELS[current] ?? current}`];
7488
7660
  try {
7489
- const goal = await loadGoal(goalFilePath(opts.projectRoot));
7661
+ const goal = await loadGoal(goalFilePath(opts.projectRoot), opts.events);
7490
7662
  if (goal) {
7491
7663
  const u = summarizeUsage(goal);
7492
7664
  lines.push(
@@ -7532,7 +7704,7 @@ function buildAutonomyCommand(opts) {
7532
7704
  opts.onAutonomy("off");
7533
7705
  let summaryLine = "";
7534
7706
  try {
7535
- const goal = await loadGoal(goalFilePath(opts.projectRoot));
7707
+ const goal = await loadGoal(goalFilePath(opts.projectRoot), opts.events);
7536
7708
  if (goal) {
7537
7709
  const u = summarizeUsage(goal);
7538
7710
  if (u.iterationsWithUsage > 0) {
@@ -7572,7 +7744,7 @@ function buildAutonomyCommand(opts) {
7572
7744
  if (newMode === "eternal" || newMode === "eternal-parallel") {
7573
7745
  const wantKeep = modifiers.includes("--keep") || modifiers.includes("keep");
7574
7746
  const wantNew = modifiers.includes("--new") || modifiers.includes("new");
7575
- const goal = await loadGoal(goalFilePath(opts.projectRoot));
7747
+ const goal = await loadGoal(goalFilePath(opts.projectRoot), opts.events);
7576
7748
  if (!goal) {
7577
7749
  const msg3 = `${color.red("Eternal/parallel mode requires a goal.")} Run \`/goal set <mission>\` first.`;
7578
7750
  opts.renderer.writeWarning(msg3);
@@ -7675,7 +7847,7 @@ function formatPhaseList(graph) {
7675
7847
  }
7676
7848
  async function gatherProjectContext(projectRoot) {
7677
7849
  try {
7678
- const raw = await fsp5.readFile(path38.join(projectRoot, "package.json"), "utf8");
7850
+ const raw = await fsp5.readFile(path39.join(projectRoot, "package.json"), "utf8");
7679
7851
  const pkg = JSON.parse(raw);
7680
7852
  const parts = [
7681
7853
  `Project: ${String(pkg.name ?? "unknown")}`,
@@ -8026,7 +8198,7 @@ function buildCodebaseReindexCommand(opts) {
8026
8198
  ${color.yellow(` ${r.errors.length} file(s) had errors`)}` : "");
8027
8199
  return { message: summary };
8028
8200
  } catch (err) {
8029
- const msg = `${color.red("Codebase reindex failed:")} ${err instanceof Error ? err.message : String(err)}`;
8201
+ const msg = `${color.red("Codebase reindex failed:")} ${toErrorMessage(err)}`;
8030
8202
  return { message: msg };
8031
8203
  }
8032
8204
  }
@@ -8113,7 +8285,7 @@ async function historyCommand(opts, sessionId, args) {
8113
8285
  } catch (err) {
8114
8286
  return {
8115
8287
  message: color.yellow(
8116
- `Failed to read session: ${err instanceof Error ? err.message : String(err)}`
8288
+ `Failed to read session: ${toErrorMessage(err)}`
8117
8289
  )
8118
8290
  };
8119
8291
  }
@@ -8146,7 +8318,9 @@ async function annotationsCommand(opts, sessionId) {
8146
8318
  )
8147
8319
  };
8148
8320
  }
8149
- const annotations = new AnnotationsStore({ dir: storeDir });
8321
+ const annotationsOpts = { dir: storeDir, events: opts.events };
8322
+ if (opts.context?.traceId !== void 0) annotationsOpts.traceId = opts.context.traceId;
8323
+ const annotations = new AnnotationsStore(annotationsOpts);
8150
8324
  const open = await annotations.listOpen(sessionId);
8151
8325
  if (open.length === 0) {
8152
8326
  return {
@@ -8613,7 +8787,7 @@ async function spawnAgent(opts, role, _task, _name, header) {
8613
8787
  opts.renderer.write(msg);
8614
8788
  return { message: msg };
8615
8789
  } catch (err) {
8616
- const msg = `${color.red("\u2717 Spawn failed")}: ${err instanceof Error ? err.message : String(err)}`;
8790
+ const msg = `${color.red("\u2717 Spawn failed")}: ${toErrorMessage(err)}`;
8617
8791
  opts.renderer.writeWarning(msg);
8618
8792
  return { message: msg };
8619
8793
  }
@@ -8664,7 +8838,7 @@ The following characters are not allowed: ; & | < > ^ $ , ( ) { } [ ] ! # % ' "
8664
8838
  }
8665
8839
  }
8666
8840
  function runCommand(cmd, cwd, timeout) {
8667
- return new Promise((resolve10) => {
8841
+ return new Promise((resolve11) => {
8668
8842
  validateCommand(cmd);
8669
8843
  const opts = {
8670
8844
  cwd,
@@ -8677,7 +8851,7 @@ function runCommand(cmd, cwd, timeout) {
8677
8851
  shell: process.platform === "win32" ? true : false
8678
8852
  };
8679
8853
  execFile(cmd, [], opts, (error, stdout, stderr) => {
8680
- resolve10({
8854
+ resolve11({
8681
8855
  stdout,
8682
8856
  stderr,
8683
8857
  exitCode: typeof error?.code === "number" ? error.code : 0,
@@ -9099,17 +9273,17 @@ function diagnoseConfig(cfg, plugins = []) {
9099
9273
  function scanPlaintextSecrets(node, prefix, findings) {
9100
9274
  if (!isPlainObject(node)) return;
9101
9275
  for (const [key, value] of Object.entries(node)) {
9102
- const path39 = prefix ? `${prefix}.${key}` : key;
9276
+ const path40 = prefix ? `${prefix}.${key}` : key;
9103
9277
  if (typeof value === "string") {
9104
9278
  if (value.length > 0 && isSecretField$1(key) && !value.startsWith(ENC_PREFIX)) {
9105
9279
  findings.push({
9106
- path: path39,
9280
+ path: path40,
9107
9281
  problem: "looks like a plaintext secret (not vault-encrypted) \u2014 it will be encrypted on next boot",
9108
9282
  severity: "warning"
9109
9283
  });
9110
9284
  }
9111
9285
  } else if (isPlainObject(value)) {
9112
- scanPlaintextSecrets(value, path39, findings);
9286
+ scanPlaintextSecrets(value, path40, findings);
9113
9287
  }
9114
9288
  }
9115
9289
  }
@@ -9130,7 +9304,7 @@ function resolvePersistPath(deps) {
9130
9304
  return deps.globalConfigPath;
9131
9305
  }
9132
9306
  async function ensureProjectDir(filePath) {
9133
- const dir = path38.dirname(filePath);
9307
+ const dir = path39.dirname(filePath);
9134
9308
  try {
9135
9309
  await fsp5.mkdir(dir, { recursive: true });
9136
9310
  } catch {
@@ -9334,8 +9508,8 @@ function buildDoctorCommand(opts) {
9334
9508
  return ` ${icon} ${color.cyan(f.path)} \u2014 ${f.problem}${fix}`;
9335
9509
  }
9336
9510
  async function findParsableBackup(file) {
9337
- const dir = path38.dirname(file);
9338
- const base = path38.basename(file);
9511
+ const dir = path39.dirname(file);
9512
+ const base = path39.basename(file);
9339
9513
  const candidates = [`${base}.last`];
9340
9514
  try {
9341
9515
  const siblings = await fsp5.readdir(dir);
@@ -9346,7 +9520,7 @@ function buildDoctorCommand(opts) {
9346
9520
  }
9347
9521
  for (const name of candidates) {
9348
9522
  try {
9349
- const raw = await fsp5.readFile(path38.join(dir, name), "utf8");
9523
+ const raw = await fsp5.readFile(path39.join(dir, name), "utf8");
9350
9524
  JSON.parse(raw);
9351
9525
  return { name, raw };
9352
9526
  } catch {
@@ -9412,7 +9586,7 @@ function buildDoctorCommand(opts) {
9412
9586
  parsed = JSON.parse(raw);
9413
9587
  } catch (err) {
9414
9588
  errorCount++;
9415
- const msg = err instanceof Error ? err.message : String(err);
9589
+ const msg = toErrorMessage(err);
9416
9590
  lines.push(` ${color.red("\u2717")} invalid JSON \u2014 ${msg}`);
9417
9591
  if (!applyFixes) {
9418
9592
  fixableCount++;
@@ -9463,7 +9637,7 @@ function buildDoctorCommand(opts) {
9463
9637
  await atomicWrite(target.file, JSON.stringify(report.fixed, null, 2));
9464
9638
  if (!target.isProject) {
9465
9639
  try {
9466
- const homeFn = () => path38.dirname(path38.dirname(target.file));
9640
+ const homeFn = () => path39.dirname(path39.dirname(target.file));
9467
9641
  await appendHistory(parsed, report.fixed, "config doctor auto-fix", homeFn);
9468
9642
  } catch {
9469
9643
  }
@@ -9551,6 +9725,65 @@ function buildEnhanceCommand(opts) {
9551
9725
  }
9552
9726
  };
9553
9727
  }
9728
+ function buildEnsembleCommand(_opts) {
9729
+ return {
9730
+ name: "ensemble",
9731
+ category: "Agent",
9732
+ description: "Fan a task out to multiple ACP agents in parallel (claude-code, gemini-cli, codex-cli, etc.).",
9733
+ argsHint: "<agent-ids-csv> <task description>",
9734
+ help: [
9735
+ "Fan a single task out to multiple ACP-supporting agents in parallel.",
9736
+ "",
9737
+ "Usage:",
9738
+ " /ensemble <agent-ids-csv> <task description>",
9739
+ "",
9740
+ "Examples:",
9741
+ ' /ensemble claude-code,gemini-cli "review this diff"',
9742
+ ' /ensemble claude-code,codex-cli "refactor auth/session.ts"',
9743
+ ' /ensemble claude-code,gemini-cli,codex-cli "explain the v1 protocol"',
9744
+ "",
9745
+ "Each agent runs in its own process. Agents not installed on the host",
9746
+ "are skipped with a warning. The command waits for all agents to finish",
9747
+ "and reports per-agent outcomes (success / failed / skipped / cancelled).",
9748
+ "",
9749
+ "Use /acp list (or wstack acp list) to see which agents are detected."
9750
+ ].join("\n"),
9751
+ async run(args) {
9752
+ const trimmed = args.trim();
9753
+ if (!trimmed) {
9754
+ return {
9755
+ message: 'Usage: /ensemble <agent-ids-csv> <task description>\n\nExamples:\n /ensemble claude-code,gemini-cli "review this diff"\n /ensemble claude-code,codex-cli "refactor auth/session.ts"\n\nRun `wstack acp list` to see which agents are detected on this host.'
9756
+ };
9757
+ }
9758
+ const spaceIdx = trimmed.search(/\s/);
9759
+ if (spaceIdx === -1) {
9760
+ return {
9761
+ message: 'Task description is required.\n\nUsage: /ensemble <agent-ids-csv> <task description>\nExample: /ensemble claude-code,gemini-cli "explain this code"'
9762
+ };
9763
+ }
9764
+ const agentIds = trimmed.slice(0, spaceIdx);
9765
+ const task = stripSurroundingQuotes(trimmed.slice(spaceIdx + 1).trim());
9766
+ if (!task) {
9767
+ return { message: "Task description is required." };
9768
+ }
9769
+ try {
9770
+ const result = await runEnsemble({ agentIds, task });
9771
+ return { message: renderEnsembleText(result) };
9772
+ } catch (err) {
9773
+ return { message: `Ensemble failed: ${toErrorMessage(err)}` };
9774
+ }
9775
+ }
9776
+ };
9777
+ }
9778
+ function stripSurroundingQuotes(s) {
9779
+ if (s.length < 2) return s;
9780
+ const first = s[0];
9781
+ const last = s[s.length - 1];
9782
+ if ((first === '"' || first === "'") && first === last) {
9783
+ return s.slice(1, -1);
9784
+ }
9785
+ return s;
9786
+ }
9554
9787
  function parseModelRef(ref) {
9555
9788
  const trimmed = ref.trim();
9556
9789
  const slash = trimmed.indexOf("/");
@@ -9851,7 +10084,7 @@ function buildFallbackCommand(opts) {
9851
10084
  };
9852
10085
  } catch (err) {
9853
10086
  return {
9854
- message: `${color.red("fallback error")}: ${err instanceof Error ? err.message : String(err)}`
10087
+ message: `${color.red("fallback error")}: ${toErrorMessage(err)}`
9855
10088
  };
9856
10089
  }
9857
10090
  }
@@ -11068,7 +11301,7 @@ async function handleSpawn(opts, subargs) {
11068
11301
  const id = await opts.onFleetSpawn(role);
11069
11302
  spawned.push(id);
11070
11303
  } catch (err) {
11071
- const warnMsg = `${color.red("\u2717 Spawn failed")} for slot ${i + 1}: ${err instanceof Error ? err.message : String(err)}`;
11304
+ const warnMsg = `${color.red("\u2717 Spawn failed")} for slot ${i + 1}: ${toErrorMessage(err)}`;
11072
11305
  opts.renderer.writeWarning(warnMsg);
11073
11306
  }
11074
11307
  }
@@ -11122,7 +11355,7 @@ async function handleDispatch(opts, subargs) {
11122
11355
  lines.push(` ${color.green("\u2713 spawned")} ${color.bold(decision.role)} as ${color.dim(id)}`);
11123
11356
  } catch (err) {
11124
11357
  lines.push(
11125
- ` ${color.amber("\u26A0 spawn failed:")} ${err instanceof Error ? err.message : String(err)}`
11358
+ ` ${color.amber("\u26A0 spawn failed:")} ${toErrorMessage(err)}`
11126
11359
  );
11127
11360
  }
11128
11361
  } else {
@@ -11324,7 +11557,7 @@ function buildGoalCommand(opts) {
11324
11557
  case "":
11325
11558
  case "show":
11326
11559
  case "status": {
11327
- const current = await loadGoal(goalPath);
11560
+ const current = await loadGoal(goalPath, opts.events);
11328
11561
  if (!current) {
11329
11562
  const msg2 = "No goal set. Use `/goal set <mission text>` to create one.";
11330
11563
  opts.renderer.write(msg2);
@@ -11349,7 +11582,7 @@ function buildGoalCommand(opts) {
11349
11582
  if (!refined) {
11350
11583
  refined = refineGoalHeuristic(setText);
11351
11584
  }
11352
- const existing = await loadGoal(goalPath);
11585
+ const existing = await loadGoal(goalPath, opts.events);
11353
11586
  const now = (/* @__PURE__ */ new Date()).toISOString();
11354
11587
  const next = existing ? {
11355
11588
  ...existing,
@@ -11366,7 +11599,7 @@ function buildGoalCommand(opts) {
11366
11599
  refinedGoal: refined.refinedGoal,
11367
11600
  deliverables: refined.deliverables
11368
11601
  };
11369
- await saveGoal(goalPath, next);
11602
+ await saveGoal(goalPath, next, opts.events);
11370
11603
  const lines = [];
11371
11604
  lines.push(`\u{1F3AF} ${color.green("Goal locked:")} ${color.bold(refined.refinedGoal)}`);
11372
11605
  if (refined.refinedGoal !== setText) {
@@ -11393,7 +11626,7 @@ function buildGoalCommand(opts) {
11393
11626
  };
11394
11627
  }
11395
11628
  case "refine": {
11396
- const current = await loadGoal(goalPath);
11629
+ const current = await loadGoal(goalPath, opts.events);
11397
11630
  if (!current) {
11398
11631
  const msg2 = "No goal set to refine. Use /goal set <text> first.";
11399
11632
  opts.renderer.writeWarning(msg2);
@@ -11412,7 +11645,7 @@ function buildGoalCommand(opts) {
11412
11645
  refinedGoal: refined.refinedGoal,
11413
11646
  deliverables: refined.deliverables
11414
11647
  };
11415
- await saveGoal(goalPath, updated);
11648
+ await saveGoal(goalPath, updated, opts.events);
11416
11649
  const msg = `${color.green("\u2713")} Goal re-refined with ${refined.deliverables.length} deliverables.`;
11417
11650
  opts.renderer.write(msg);
11418
11651
  return { message: `${msg}
@@ -11421,14 +11654,14 @@ ${formatGoal(updated)}` };
11421
11654
  }
11422
11655
  case "clear":
11423
11656
  case "reset": {
11424
- const current = await loadGoal(goalPath);
11657
+ const current = await loadGoal(goalPath, opts.events);
11425
11658
  if (!current) {
11426
11659
  const msg2 = "No goal to clear.";
11427
11660
  opts.renderer.write(msg2);
11428
11661
  return { message: msg2 };
11429
11662
  }
11430
11663
  const abandoned = { ...current, goalState: "abandoned" };
11431
- await saveGoal(goalPath, abandoned);
11664
+ await saveGoal(goalPath, abandoned, opts.events);
11432
11665
  const { unlink: unlink4 } = await import('fs/promises');
11433
11666
  try {
11434
11667
  await unlink4(goalPath);
@@ -11442,7 +11675,7 @@ ${formatGoal(updated)}` };
11442
11675
  }
11443
11676
  case "journal":
11444
11677
  case "log": {
11445
- const current = await loadGoal(goalPath);
11678
+ const current = await loadGoal(goalPath, opts.events);
11446
11679
  if (!current) {
11447
11680
  const msg2 = "No goal set.";
11448
11681
  opts.renderer.write(msg2);
@@ -11467,7 +11700,7 @@ ${lines.join("\n")}`;
11467
11700
  return { message: msg };
11468
11701
  }
11469
11702
  case "pause": {
11470
- const current = await loadGoal(goalPath);
11703
+ const current = await loadGoal(goalPath, opts.events);
11471
11704
  if (!current) {
11472
11705
  const msg2 = "No goal set \u2014 nothing to pause.";
11473
11706
  opts.renderer.writeWarning(msg2);
@@ -11479,13 +11712,13 @@ ${lines.join("\n")}`;
11479
11712
  return { message: msg2 };
11480
11713
  }
11481
11714
  const paused = { ...current, goalState: "paused" };
11482
- await saveGoal(goalPath, paused);
11715
+ await saveGoal(goalPath, paused, opts.events);
11483
11716
  const msg = `${color.cyan("Goal paused.")} Current iteration will finish, then the loop stops. Use /goal resume to continue.`;
11484
11717
  opts.renderer.write(msg);
11485
11718
  return { message: msg };
11486
11719
  }
11487
11720
  case "resume": {
11488
- const current = await loadGoal(goalPath);
11721
+ const current = await loadGoal(goalPath, opts.events);
11489
11722
  if (!current) {
11490
11723
  const msg2 = "No goal set \u2014 cannot resume.";
11491
11724
  opts.renderer.writeWarning(msg2);
@@ -11497,7 +11730,7 @@ ${lines.join("\n")}`;
11497
11730
  return { message: msg2 };
11498
11731
  }
11499
11732
  const resumed = { ...current, goalState: "active" };
11500
- await saveGoal(goalPath, resumed);
11733
+ await saveGoal(goalPath, resumed, opts.events);
11501
11734
  const msg = `${color.green("Goal resumed.")} Loop will continue from the next iteration.`;
11502
11735
  opts.renderer.write(msg);
11503
11736
  return { message: msg };
@@ -11584,8 +11817,8 @@ function buildInitCommand(opts) {
11584
11817
  description: "Create or update .wrongstack/AGENTS.md project context for the system prompt.",
11585
11818
  async run(_args, ctx) {
11586
11819
  const root = ctx?.projectRoot ?? opts.projectRoot ?? process.cwd();
11587
- const dir = path38.join(root, ".wrongstack");
11588
- const file = path38.join(dir, "AGENTS.md");
11820
+ const dir = path39.join(root, ".wrongstack");
11821
+ const file = path39.join(dir, "AGENTS.md");
11589
11822
  const isFirstInit = !await fileExists(file);
11590
11823
  const detected = await detectProjectFacts(root);
11591
11824
  const body = renderAgentsTemplate(detected);
@@ -11593,7 +11826,7 @@ function buildInitCommand(opts) {
11593
11826
  await fsp5.writeFile(file, body, "utf8");
11594
11827
  let nodePkg = false;
11595
11828
  try {
11596
- await fsp5.access(path38.join(root, "package.json"));
11829
+ await fsp5.access(path39.join(root, "package.json"));
11597
11830
  nodePkg = true;
11598
11831
  } catch {
11599
11832
  }
@@ -11755,6 +11988,7 @@ function buildMailboxCommand(opts) {
11755
11988
  " /mailbox broadcast <message> Message every agent on the project.",
11756
11989
  " /mailbox history [n] Last n messages on the project (default 20).",
11757
11990
  " /mailbox clear Delete all messages from the mailbox.",
11991
+ " /mailbox purge Remove stale/orphaned messages (completed >1d, incomplete >7d).",
11758
11992
  "",
11759
11993
  "Examples:",
11760
11994
  " /mailbox broadcast pausing deploys, hold off on main",
@@ -11863,8 +12097,17 @@ function buildMailboxCommand(opts) {
11863
12097
  await mb.clearAll();
11864
12098
  return { message: color.green("\u2713 All messages deleted from the mailbox.") };
11865
12099
  }
12100
+ if (sub === "purge") {
12101
+ const result = await mb.purgeStale();
12102
+ if (result.totalPurged === 0) {
12103
+ return { message: color.green("\u2713 No stale messages found. Mailbox is clean.") };
12104
+ }
12105
+ return {
12106
+ message: `\u2713 Purged ${result.totalPurged} message(s): ${result.completedPurged} completed, ${result.incompletePurged} incomplete. ${result.remaining} message(s) remain.`
12107
+ };
12108
+ }
11866
12109
  return {
11867
- message: `Unknown subcommand "${sub}". Use: /mailbox [agents|online|send|broadcast|history|clear]`
12110
+ message: `Unknown subcommand "${sub}". Use: /mailbox [agents|online|send|broadcast|history|clear purge]`
11868
12111
  };
11869
12112
  }
11870
12113
  };
@@ -12277,9 +12520,9 @@ function stateBadge(state) {
12277
12520
  return color.dim(state);
12278
12521
  }
12279
12522
  }
12280
- async function readConfig(path39) {
12523
+ async function readConfig(path40) {
12281
12524
  try {
12282
- return JSON.parse(await fsp5.readFile(path39, "utf8"));
12525
+ return JSON.parse(await fsp5.readFile(path40, "utf8"));
12283
12526
  } catch {
12284
12527
  return {};
12285
12528
  }
@@ -12287,11 +12530,11 @@ async function readConfig(path39) {
12287
12530
  function isMcpServerRecord(value) {
12288
12531
  return !!value && typeof value === "object" && !Array.isArray(value);
12289
12532
  }
12290
- async function writeConfig(path39, cfg) {
12533
+ async function writeConfig(path40, cfg) {
12291
12534
  const raw = JSON.stringify(cfg, null, 2);
12292
- const tmp = path39 + ".tmp";
12535
+ const tmp = path40 + ".tmp";
12293
12536
  await fsp5.writeFile(tmp, raw, "utf8");
12294
- await fsp5.rename(tmp, path39);
12537
+ await fsp5.rename(tmp, path40);
12295
12538
  }
12296
12539
 
12297
12540
  // src/slash-commands/mcp.ts
@@ -12481,7 +12724,7 @@ async function runCompact(opts) {
12481
12724
  responseText = response.content.filter((b) => b.type === "text").map((b) => b.text).join("").trim();
12482
12725
  } catch (err) {
12483
12726
  return {
12484
- message: `LLM call failed: ${err instanceof Error ? err.message : String(err)}`
12727
+ message: `LLM call failed: ${toErrorMessage(err)}`
12485
12728
  };
12486
12729
  }
12487
12730
  if (!responseText) {
@@ -12497,7 +12740,7 @@ ${responseText.slice(0, 500)}` };
12497
12740
  parsed = JSON.parse(jsonMatch[0]);
12498
12741
  } catch (err) {
12499
12742
  return {
12500
- message: `Failed to parse LLM response: ${err instanceof Error ? err.message : String(err)}
12743
+ message: `Failed to parse LLM response: ${toErrorMessage(err)}
12501
12744
 
12502
12745
  Raw response:
12503
12746
  ${responseText.slice(0, 500)}`
@@ -12555,7 +12798,7 @@ ${responseText.slice(0, 500)}`
12555
12798
  }
12556
12799
  } catch (err) {
12557
12800
  errors.push(
12558
- `${op.action} failed for ${op.targets.join(", ")}: ${err instanceof Error ? err.message : String(err)}`
12801
+ `${op.action} failed for ${op.targets.join(", ")}: ${toErrorMessage(err)}`
12559
12802
  );
12560
12803
  }
12561
12804
  }
@@ -13192,7 +13435,7 @@ function buildModelsCommand(opts) {
13192
13435
  };
13193
13436
  } catch (err) {
13194
13437
  return {
13195
- message: `${color.red("models error")}: ${err instanceof Error ? err.message : String(err)}`
13438
+ message: `${color.red("models error")}: ${toErrorMessage(err)}`
13196
13439
  };
13197
13440
  }
13198
13441
  }
@@ -13876,7 +14119,7 @@ async function killSession(sessionId) {
13876
14119
  } catch (err) {
13877
14120
  return {
13878
14121
  message: color.red(
13879
- `Failed to kill session: ${err instanceof Error ? err.message : String(err)}`
14122
+ `Failed to kill session: ${toErrorMessage(err)}`
13880
14123
  )
13881
14124
  };
13882
14125
  }
@@ -14247,7 +14490,7 @@ function buildSetModelCommand(opts) {
14247
14490
  };
14248
14491
  } catch (err) {
14249
14492
  return {
14250
- message: `${color.red("setmodel error")}: ${err instanceof Error ? err.message : String(err)}`
14493
+ message: `${color.red("setmodel error")}: ${toErrorMessage(err)}`
14251
14494
  };
14252
14495
  }
14253
14496
  }
@@ -14350,7 +14593,7 @@ function buildSuggestCommand(opts) {
14350
14593
  opts.onSuggestions?.(suggestions);
14351
14594
  return { message: formatSuggestions(suggestions) };
14352
14595
  } catch (err) {
14353
- const msg = `Suggestion generation failed: ${err instanceof Error ? err.message : String(err)}`;
14596
+ const msg = `Suggestion generation failed: ${toErrorMessage(err)}`;
14354
14597
  opts.renderer.writeWarning(msg);
14355
14598
  return { message: msg };
14356
14599
  }
@@ -14590,7 +14833,7 @@ async function listProjectsCommand(opts, ctx) {
14590
14833
  return { message: lines.join("\n") };
14591
14834
  }
14592
14835
  async function addProjectCommand(opts, ctx, targetPath, displayName) {
14593
- const resolved = path38.resolve(ctx?.projectRoot ?? ctx?.cwd ?? process.cwd(), targetPath);
14836
+ const resolved = path39.resolve(ctx?.projectRoot ?? ctx?.cwd ?? process.cwd(), targetPath);
14594
14837
  try {
14595
14838
  await fsp5.access(resolved);
14596
14839
  } catch {
@@ -14607,7 +14850,7 @@ async function addProjectCommand(opts, ctx, targetPath, displayName) {
14607
14850
  message: color.yellow(`Project already registered: "${existing.name}" (${existing.slug})`)
14608
14851
  };
14609
14852
  }
14610
- const name = displayName?.trim() || path38.basename(resolved);
14853
+ const name = displayName?.trim() || path39.basename(resolved);
14611
14854
  const slug = generateSlug(resolved);
14612
14855
  const now = (/* @__PURE__ */ new Date()).toISOString();
14613
14856
  await ensureProjectDataDir(slug, opts.paths?.globalConfig);
@@ -14660,7 +14903,7 @@ async function removeProjectCommand(opts, _ctx, slugOrName) {
14660
14903
  };
14661
14904
  }
14662
14905
  async function switchProjectCommand(opts, ctx, target, displayName) {
14663
- const resolved = path38.resolve(ctx?.projectRoot ?? ctx?.cwd ?? process.cwd(), target);
14906
+ const resolved = path39.resolve(ctx?.projectRoot ?? ctx?.cwd ?? process.cwd(), target);
14664
14907
  try {
14665
14908
  await fsp5.access(resolved);
14666
14909
  } catch {
@@ -14674,8 +14917,8 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
14674
14917
  try {
14675
14918
  const req2 = createRequire(import.meta.url);
14676
14919
  const pkgPath = req2.resolve("@wrongstack/cli/package.json");
14677
- const pkgDir = path38.dirname(pkgPath);
14678
- cliPath = path38.join(pkgDir, "dist", "index.js");
14920
+ const pkgDir = path39.dirname(pkgPath);
14921
+ cliPath = path39.join(pkgDir, "dist", "index.js");
14679
14922
  await fsp5.access(cliPath);
14680
14923
  } catch {
14681
14924
  cliPath = process.argv[1] ?? "";
@@ -14692,13 +14935,13 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
14692
14935
  if (existing) {
14693
14936
  existing.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
14694
14937
  } else {
14695
- const name = displayName?.trim() || path38.basename(resolved);
14938
+ const name = displayName?.trim() || path39.basename(resolved);
14696
14939
  const slug = generateSlug(resolved);
14697
14940
  manifest.projects.push({ name, root: resolved, slug, lastSeen: (/* @__PURE__ */ new Date()).toISOString() });
14698
14941
  await ensureProjectDataDir(slug, opts.paths?.globalConfig);
14699
14942
  }
14700
14943
  await saveManifest(manifest, opts.paths?.globalConfig);
14701
- const targetName = displayName?.trim() || path38.basename(resolved);
14944
+ const targetName = displayName?.trim() || path39.basename(resolved);
14702
14945
  const canSwitch = await confirmProjectSwitch(opts, targetName);
14703
14946
  if (!canSwitch) return { message: "" };
14704
14947
  const nodeExe = process.execPath;
@@ -14814,8 +15057,8 @@ async function spawnInProject(opts, _ctx, root, projectName) {
14814
15057
  try {
14815
15058
  const req2 = createRequire(import.meta.url);
14816
15059
  const pkgPath = req2.resolve("@wrongstack/cli/package.json");
14817
- const pkgDir = path38.dirname(pkgPath);
14818
- cliPath = path38.join(pkgDir, "dist", "index.js");
15060
+ const pkgDir = path39.dirname(pkgPath);
15061
+ cliPath = path39.join(pkgDir, "dist", "index.js");
14819
15062
  await fsp5.access(cliPath);
14820
15063
  } catch {
14821
15064
  cliPath = process.argv[1] ?? "";
@@ -14832,7 +15075,7 @@ async function spawnInProject(opts, _ctx, root, projectName) {
14832
15075
  if (existing) {
14833
15076
  existing.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
14834
15077
  } else {
14835
- const name = projectName || path38.basename(root);
15078
+ const name = projectName || path39.basename(root);
14836
15079
  const slug = generateSlug(root);
14837
15080
  manifest.projects.push({ name, root, slug, lastSeen: (/* @__PURE__ */ new Date()).toISOString() });
14838
15081
  await ensureProjectDataDir(slug, opts.paths?.globalConfig);
@@ -14864,8 +15107,8 @@ async function handleNewSession(_opts, _ctx) {
14864
15107
  try {
14865
15108
  const req2 = createRequire(import.meta.url);
14866
15109
  const pkgPath = req2.resolve("@wrongstack/cli/package.json");
14867
- const pkgDir = path38.dirname(pkgPath);
14868
- cliPath = path38.join(pkgDir, "dist", "index.js");
15110
+ const pkgDir = path39.dirname(pkgPath);
15111
+ cliPath = path39.join(pkgDir, "dist", "index.js");
14869
15112
  await fsp5.access(cliPath);
14870
15113
  } catch {
14871
15114
  cliPath = process.argv[1] ?? "";
@@ -14923,7 +15166,7 @@ async function handlePrevSessions(opts, _ctx) {
14923
15166
  return { message: lines.join("\n") };
14924
15167
  }
14925
15168
  async function runGit(args, cwd) {
14926
- return new Promise((resolve10) => {
15169
+ return new Promise((resolve11) => {
14927
15170
  const child = spawn("git", args, {
14928
15171
  cwd,
14929
15172
  stdio: ["ignore", "pipe", "pipe"],
@@ -14934,8 +15177,8 @@ async function runGit(args, cwd) {
14934
15177
  child.stdout?.on("data", (d) => {
14935
15178
  stdout += d;
14936
15179
  });
14937
- child.on("error", () => resolve10({ stdout, code: 1 }));
14938
- child.on("close", (code) => resolve10({ stdout, code: code ?? 0 }));
15180
+ child.on("error", () => resolve11({ stdout, code: 1 }));
15181
+ child.on("close", (code) => resolve11({ stdout, code: code ?? 0 }));
14939
15182
  });
14940
15183
  }
14941
15184
  async function getChangedFiles(cwd) {
@@ -14976,7 +15219,7 @@ function buildReviewCommand(opts) {
14976
15219
  for (const f of allChanged) {
14977
15220
  if (f.path.startsWith(".wrongstack/")) continue;
14978
15221
  try {
14979
- await fsp5.access(path38.join(cwd, f.path));
15222
+ await fsp5.access(path39.join(cwd, f.path));
14980
15223
  existing.push(f);
14981
15224
  } catch {
14982
15225
  }
@@ -14987,7 +15230,7 @@ function buildReviewCommand(opts) {
14987
15230
  const filesWithContent = [];
14988
15231
  for (const f of existing.slice(0, 30)) {
14989
15232
  try {
14990
- const content = await fsp5.readFile(path38.join(cwd, f.path), "utf8");
15233
+ const content = await fsp5.readFile(path39.join(cwd, f.path), "utf8");
14991
15234
  filesWithContent.push({ ...f, content });
14992
15235
  } catch {
14993
15236
  }
@@ -15229,14 +15472,12 @@ function buildSettingsCommand(opts) {
15229
15472
  };
15230
15473
  } catch (err) {
15231
15474
  return {
15232
- message: `${color.red("Settings error")}: ${err instanceof Error ? err.message : String(err)}`
15475
+ message: `${color.red("Settings error")}: ${toErrorMessage(err)}`
15233
15476
  };
15234
15477
  }
15235
15478
  }
15236
15479
  };
15237
15480
  }
15238
-
15239
- // src/slash-commands/spawn-agents.ts
15240
15481
  function buildSpawnCommand(opts) {
15241
15482
  return {
15242
15483
  name: "spawn",
@@ -15273,7 +15514,7 @@ function buildSpawnCommand(opts) {
15273
15514
  const summary = Object.keys(parsed).length > 0 ? await opts.onSpawn(description, parsed) : await opts.onSpawn(description);
15274
15515
  return { message: summary };
15275
15516
  } catch (err) {
15276
- return { message: `Spawn failed: ${err instanceof Error ? err.message : String(err)}` };
15517
+ return { message: `Spawn failed: ${toErrorMessage(err)}` };
15277
15518
  }
15278
15519
  }
15279
15520
  };
@@ -15360,7 +15601,7 @@ var DEFAULTS = {
15360
15601
  working_dir: true
15361
15602
  };
15362
15603
  function resolveConfigPath() {
15363
- return process.env[CONFIG_ENV] ?? path38.join(process.env.HOME ?? "", ".wrongstack", "statusline.json");
15604
+ return process.env[CONFIG_ENV] ?? path39.join(process.env.HOME ?? "", ".wrongstack", "statusline.json");
15364
15605
  }
15365
15606
  async function loadStatuslineConfig() {
15366
15607
  const p = resolveConfigPath();
@@ -15374,11 +15615,11 @@ async function loadStatuslineConfig() {
15374
15615
  async function saveStatuslineConfig(cfg) {
15375
15616
  const p = resolveConfigPath();
15376
15617
  try {
15377
- await fsp5.mkdir(path38.dirname(p), { recursive: true });
15618
+ await fsp5.mkdir(path39.dirname(p), { recursive: true });
15378
15619
  await atomicWrite(p, JSON.stringify(cfg, null, 2));
15379
15620
  } catch (err) {
15380
15621
  throw new FsError({
15381
- message: err instanceof Error ? err.message : String(err),
15622
+ message: toErrorMessage(err),
15382
15623
  code: err instanceof Error && err.message.includes("mkdir") ? ERROR_CODES.FS_MKDIR_FAILED : ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
15383
15624
  path: p,
15384
15625
  cause: err
@@ -15613,7 +15854,7 @@ ${formatPlan(updated)}`;
15613
15854
  const priority = validatePriority(parts[2] ?? "") ?? "medium";
15614
15855
  const now = (/* @__PURE__ */ new Date()).toISOString();
15615
15856
  file.tasks.push({
15616
- id: `task_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
15857
+ id: `task_${randomUUID()}`,
15617
15858
  title,
15618
15859
  type,
15619
15860
  priority,
@@ -15736,7 +15977,7 @@ ${formatTaskProgress(file.tasks)}`;
15736
15977
  ];
15737
15978
  if (found.item.description) {
15738
15979
  todos.push({
15739
- id: `todo_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
15980
+ id: `todo_${randomUUID()}`,
15740
15981
  content: found.item.description.slice(0, 200),
15741
15982
  status: "pending",
15742
15983
  promotedFromTask: found.item.id
@@ -15786,13 +16027,13 @@ ${formatTaskProgress(file.tasks)}`;
15786
16027
  }
15787
16028
  async function discoverPackageFiles(projectRoot) {
15788
16029
  const files = [];
15789
- const rootPkg = path38.join(projectRoot, "package.json");
16030
+ const rootPkg = path39.join(projectRoot, "package.json");
15790
16031
  try {
15791
16032
  await fsp5.access(rootPkg);
15792
16033
  files.push(rootPkg);
15793
16034
  } catch {
15794
16035
  }
15795
- const workspaceFile = path38.join(projectRoot, "pnpm-workspace.yaml");
16036
+ const workspaceFile = path39.join(projectRoot, "pnpm-workspace.yaml");
15796
16037
  try {
15797
16038
  await fsp5.access(workspaceFile);
15798
16039
  const content = await fsp5.readFile(workspaceFile, "utf8");
@@ -15802,12 +16043,12 @@ async function discoverPackageFiles(projectRoot) {
15802
16043
  const globs = rawGlobs.split(/[\s,]+/).filter(Boolean).map((g) => g.replace(/['"]/g, ""));
15803
16044
  for (const g of globs) {
15804
16045
  const dirPrefix = g.replace(/\/?\*$/, "").replace(/\/\*$/, "");
15805
- const dir = path38.join(projectRoot, dirPrefix);
16046
+ const dir = path39.join(projectRoot, dirPrefix);
15806
16047
  try {
15807
16048
  const entries = await fsp5.readdir(dir, { withFileTypes: true });
15808
16049
  for (const e of entries) {
15809
16050
  if (!e.isDirectory()) continue;
15810
- const subPkg = path38.join(dir, e.name, "package.json");
16051
+ const subPkg = path39.join(dir, e.name, "package.json");
15811
16052
  try {
15812
16053
  await fsp5.access(subPkg);
15813
16054
  files.push(subPkg);
@@ -15822,7 +16063,7 @@ async function discoverPackageFiles(projectRoot) {
15822
16063
  return files;
15823
16064
  }
15824
16065
  function buildTechStackTask(opts) {
15825
- const pkgList = opts.packageFiles.map((f) => ` - ${path38.relative(opts.projectRoot, f)}`).join("\n");
16066
+ const pkgList = opts.packageFiles.map((f) => ` - ${path39.relative(opts.projectRoot, f)}`).join("\n");
15826
16067
  const header = opts.isInit ? [
15827
16068
  "## Tech Stack Audit \u2014 First-Time Project Init",
15828
16069
  "",
@@ -15944,7 +16185,7 @@ function buildTechStackCommand(opts) {
15944
16185
  }
15945
16186
  } catch (err) {
15946
16187
  discoveryNote = color.red(
15947
- `Could not scan for package files: ${err instanceof Error ? err.message : String(err)}`
16188
+ `Could not scan for package files: ${toErrorMessage(err)}`
15948
16189
  );
15949
16190
  }
15950
16191
  const task = buildTechStackTask({
@@ -15972,7 +16213,7 @@ function buildTechStackCommand(opts) {
15972
16213
  const summary = await opts.onSpawnAndWait(task, { name });
15973
16214
  return { message: summary };
15974
16215
  } catch (err) {
15975
- const msg = `Tech stack scan failed: ${err instanceof Error ? err.message : String(err)}`;
16216
+ const msg = `Tech stack scan failed: ${toErrorMessage(err)}`;
15976
16217
  opts.renderer.writeWarning(msg);
15977
16218
  return { message: msg };
15978
16219
  }
@@ -16123,6 +16364,163 @@ ${color.dim("No default chat set. You can add it later: /telegram-setup <token>
16123
16364
  }
16124
16365
  };
16125
16366
  }
16367
+ init_helpers();
16368
+ var HELP2 = [
16369
+ "Usage:",
16370
+ " /telegram-settings Show current Telegram notification settings",
16371
+ " /telegram-settings session-end on|off Notify on session end",
16372
+ " /telegram-settings delegate on|off Notify when a delegated subagent finishes",
16373
+ " /telegram-settings long-tool <ms|off> Notify for tools slower than <ms> (0/off = disabled)",
16374
+ " /telegram-settings poll <seconds> Bot polling interval (1\u201360)",
16375
+ " /telegram-settings chat <chatId> Default chat for notifications",
16376
+ "",
16377
+ "Aliases: /tg-settings",
16378
+ "",
16379
+ "Settings apply immediately \u2014 no restart needed."
16380
+ ].join("\n");
16381
+ function buildTelegramSettingsCommand(opts) {
16382
+ function currentView() {
16383
+ const config = opts.configStore.get();
16384
+ const tg = config.extensions?.telegram ?? {};
16385
+ const sessionEnd = tg.notifyOnSessionEnd === true;
16386
+ const delegate = tg.notifyOnDelegate !== false;
16387
+ const longToolMs = typeof tg.longToolThresholdMs === "number" ? tg.longToolThresholdMs : 3e4;
16388
+ const longTool = longToolMs > 0 ? `${longToolMs}ms` : "off";
16389
+ const poll = typeof tg.pollIntervalSec === "number" ? `${tg.pollIntervalSec}s` : "2s";
16390
+ const chat = tg.notifyChatId !== void 0 && tg.notifyChatId !== null ? String(tg.notifyChatId) : "not set";
16391
+ const hasToken = typeof tg.botToken === "string" && tg.botToken.length > 0;
16392
+ return [
16393
+ `${color.bold("Telegram")} ${color.dim("\u2014 Notification Settings")}`,
16394
+ "",
16395
+ ` session end: ${sessionEnd ? color.cyan("on") : color.dim("off")} ${color.dim("change: /telegram-settings session-end on|off")}`,
16396
+ ` delegate done: ${delegate ? color.cyan("on") : color.dim("off")} ${color.dim("change: /telegram-settings delegate on|off")}`,
16397
+ ` long tool: ${color.cyan(longTool)} ${color.dim("change: /telegram-settings long-tool <ms|off>")}`,
16398
+ ` poll interval: ${color.cyan(poll)} ${color.dim("change: /telegram-settings poll <seconds>")}`,
16399
+ ` notify chat: ${color.cyan(chat)} ${color.dim("change: /telegram-settings chat <chatId>")}`,
16400
+ "",
16401
+ hasToken ? color.dim(" Bot token configured. Changes apply immediately.") : `${color.amber("\u26A0")} No bot token configured. Run: /telegram-setup <botToken> [chatId]`
16402
+ ].join("\n");
16403
+ }
16404
+ return {
16405
+ name: "telegram-settings",
16406
+ category: "Config",
16407
+ aliases: ["tg-settings"],
16408
+ description: "Toggle which agent events are reported to Telegram.",
16409
+ argsHint: "[session-end|delegate|long-tool|poll|chat <value>]",
16410
+ help: HELP2,
16411
+ async run(args) {
16412
+ const { cmd: sub, rest } = parseSubcommand(args);
16413
+ if (sub === "help" || sub === "--help" || sub === "-h") {
16414
+ return { message: HELP2 };
16415
+ }
16416
+ if (!opts.configStore || !opts.paths?.globalConfig) {
16417
+ return { message: `${color.red("Error")} config store not available.` };
16418
+ }
16419
+ if (!sub) {
16420
+ return { message: currentView() };
16421
+ }
16422
+ const persistDeps = {
16423
+ configStore: opts.configStore,
16424
+ globalConfigPath: opts.paths.globalConfig,
16425
+ vault: noOpVault
16426
+ };
16427
+ try {
16428
+ if (sub === "session-end") {
16429
+ const raw = (rest[0] ?? "").toLowerCase();
16430
+ if (!["on", "off"].includes(raw)) {
16431
+ return { message: `${color.amber("Usage:")} /telegram-settings session-end on|off` };
16432
+ }
16433
+ const on = raw === "on";
16434
+ await persistTelegramConfig(persistDeps, (tg) => {
16435
+ tg.notifyOnSessionEnd = on;
16436
+ });
16437
+ return {
16438
+ message: `${color.green("\u2713")} session-end \u2192 ${on ? color.cyan("on") : color.dim("off")}`
16439
+ };
16440
+ }
16441
+ if (sub === "delegate") {
16442
+ const raw = (rest[0] ?? "").toLowerCase();
16443
+ if (!["on", "off"].includes(raw)) {
16444
+ return { message: `${color.amber("Usage:")} /telegram-settings delegate on|off` };
16445
+ }
16446
+ const on = raw === "on";
16447
+ await persistTelegramConfig(persistDeps, (tg) => {
16448
+ tg.notifyOnDelegate = on;
16449
+ });
16450
+ return {
16451
+ message: `${color.green("\u2713")} delegate \u2192 ${on ? color.cyan("on") : color.dim("off")}`
16452
+ };
16453
+ }
16454
+ if (sub === "long-tool") {
16455
+ const raw = rest[0];
16456
+ if (raw === void 0) {
16457
+ return {
16458
+ message: `${color.amber("Usage:")} /telegram-settings long-tool <ms|off> ${color.dim("(0 or off disables)")}`
16459
+ };
16460
+ }
16461
+ if (raw === "off") {
16462
+ await persistTelegramConfig(persistDeps, (tg) => {
16463
+ tg.longToolThresholdMs = 0;
16464
+ });
16465
+ return {
16466
+ message: `${color.green("\u2713")} long-tool \u2192 ${color.dim("off")}`
16467
+ };
16468
+ }
16469
+ const ms = Number.parseInt(raw, 10);
16470
+ if (Number.isNaN(ms) || ms < 0) {
16471
+ return {
16472
+ message: `${color.red("Invalid number")}: "${raw}". Enter milliseconds, e.g. /telegram-settings long-tool 15000`
16473
+ };
16474
+ }
16475
+ await persistTelegramConfig(persistDeps, (tg) => {
16476
+ tg.longToolThresholdMs = ms;
16477
+ });
16478
+ return {
16479
+ message: `${color.green("\u2713")} long-tool \u2192 ${color.cyan(`${ms}ms`)}`
16480
+ };
16481
+ }
16482
+ if (sub === "poll") {
16483
+ const raw = rest[0];
16484
+ if (raw === void 0) {
16485
+ return { message: `${color.amber("Usage:")} /telegram-settings poll <seconds> ${color.dim("(1\u201360)")}` };
16486
+ }
16487
+ const sec = Number.parseInt(raw, 10);
16488
+ if (Number.isNaN(sec) || sec < 1 || sec > 60) {
16489
+ return {
16490
+ message: `${color.red("Invalid value")}: "${raw}". Enter seconds between 1 and 60.`
16491
+ };
16492
+ }
16493
+ await persistTelegramConfig(persistDeps, (tg) => {
16494
+ tg.pollIntervalSec = sec;
16495
+ });
16496
+ return {
16497
+ message: `${color.green("\u2713")} poll \u2192 ${color.cyan(`${sec}s`)}`
16498
+ };
16499
+ }
16500
+ if (sub === "chat") {
16501
+ const raw = rest[0];
16502
+ if (!raw) {
16503
+ return { message: `${color.amber("Usage:")} /telegram-settings chat <chatId>` };
16504
+ }
16505
+ const chatId = /^\d+$/.test(raw) ? Number(raw) : raw;
16506
+ await persistTelegramConfig(persistDeps, (tg) => {
16507
+ tg.notifyChatId = chatId;
16508
+ });
16509
+ return {
16510
+ message: `${color.green("\u2713")} notify chat \u2192 ${color.cyan(raw)}`
16511
+ };
16512
+ }
16513
+ return {
16514
+ message: `${color.red("Unknown setting")} "${sub}". ${unknownSubcommand(sub, ["session-end", "delegate", "long-tool", "poll", "chat"], "telegram-settings")}`
16515
+ };
16516
+ } catch (err) {
16517
+ return {
16518
+ message: `${color.red("Settings error")}: ${toErrorMessage(err)}`
16519
+ };
16520
+ }
16521
+ }
16522
+ };
16523
+ }
16126
16524
 
16127
16525
  // src/slash-commands/todos.ts
16128
16526
  init_helpers();
@@ -16250,7 +16648,7 @@ function buildWorkingDirCommand(_opts) {
16250
16648
  }
16251
16649
  const trimmed = args.trim();
16252
16650
  if (!trimmed) {
16253
- const rel2 = path38.relative(ctx.projectRoot, ctx.workingDir) || ".";
16651
+ const rel2 = path39.relative(ctx.projectRoot, ctx.workingDir) || ".";
16254
16652
  return {
16255
16653
  message: [
16256
16654
  `Working directory: ${color.bold(ctx.workingDir)}`,
@@ -16259,10 +16657,10 @@ function buildWorkingDirCommand(_opts) {
16259
16657
  ].join("\n")
16260
16658
  };
16261
16659
  }
16262
- const resolved = path38.isAbsolute(trimmed) ? path38.resolve(trimmed) : path38.resolve(ctx.projectRoot, trimmed);
16263
- const root = path38.resolve(ctx.projectRoot);
16264
- const rel = path38.relative(root, resolved);
16265
- if (rel.startsWith("..") || path38.isAbsolute(rel)) {
16660
+ const resolved = path39.isAbsolute(trimmed) ? path39.resolve(trimmed) : path39.resolve(ctx.projectRoot, trimmed);
16661
+ const root = path39.resolve(ctx.projectRoot);
16662
+ const rel = path39.relative(root, resolved);
16663
+ if (rel.startsWith("..") || path39.isAbsolute(rel)) {
16266
16664
  return {
16267
16665
  message: color.red(
16268
16666
  `Directory "${trimmed}" is outside the project root.
@@ -16284,11 +16682,11 @@ function buildWorkingDirCommand(_opts) {
16284
16682
  ctx.setWorkingDir(resolved);
16285
16683
  } catch (err) {
16286
16684
  return {
16287
- message: color.red(err instanceof Error ? err.message : String(err))
16685
+ message: color.red(toErrorMessage(err))
16288
16686
  };
16289
16687
  }
16290
- const prevRel = path38.relative(ctx.projectRoot, previous) || ".";
16291
- const newRel = path38.relative(ctx.projectRoot, resolved) || ".";
16688
+ const prevRel = path39.relative(ctx.projectRoot, previous) || ".";
16689
+ const newRel = path39.relative(ctx.projectRoot, resolved) || ".";
16292
16690
  return {
16293
16691
  message: [
16294
16692
  color.green(` \u2713 ${prevRel} \u2192 ${color.bold(newRel)}`),
@@ -16431,6 +16829,7 @@ function buildBuiltinSlashCommands(opts) {
16431
16829
  buildDirectorCommand(opts),
16432
16830
  buildFleetCommand(opts),
16433
16831
  buildEnhanceCommand(opts),
16832
+ buildEnsembleCommand(),
16434
16833
  buildMemoryCommand(opts),
16435
16834
  buildTodosCommand(opts),
16436
16835
  buildTasksCommand(),
@@ -16453,6 +16852,7 @@ function buildBuiltinSlashCommands(opts) {
16453
16852
  buildWorktreeCommand(opts),
16454
16853
  buildSettingsCommand(opts),
16455
16854
  buildTelegramSetupCommand(opts),
16855
+ buildTelegramSettingsCommand(opts),
16456
16856
  buildSetModelCommand(opts),
16457
16857
  buildFallbackCommand(opts),
16458
16858
  buildModelCapsCommand(opts),
@@ -16472,8 +16872,6 @@ function buildBuiltinSlashCommands(opts) {
16472
16872
  })
16473
16873
  ];
16474
16874
  }
16475
-
16476
- // src/pre-launch.ts
16477
16875
  var MANIFESTS = [
16478
16876
  "package.json",
16479
16877
  "pyproject.toml",
@@ -16488,13 +16886,13 @@ var MANIFESTS = [
16488
16886
  ];
16489
16887
  async function detectProjectKind(projectRoot) {
16490
16888
  try {
16491
- await fsp5.access(path38.join(projectRoot, ".wrongstack", "AGENTS.md"));
16889
+ await fsp5.access(path39.join(projectRoot, ".wrongstack", "AGENTS.md"));
16492
16890
  return "initialized";
16493
16891
  } catch {
16494
16892
  }
16495
16893
  for (const m of MANIFESTS) {
16496
16894
  try {
16497
- await fsp5.access(path38.join(projectRoot, m));
16895
+ await fsp5.access(path39.join(projectRoot, m));
16498
16896
  return "project";
16499
16897
  } catch {
16500
16898
  }
@@ -16502,8 +16900,8 @@ async function detectProjectKind(projectRoot) {
16502
16900
  return "empty";
16503
16901
  }
16504
16902
  async function scaffoldAgentsMd(projectRoot) {
16505
- const dir = path38.join(projectRoot, ".wrongstack");
16506
- const file = path38.join(dir, "AGENTS.md");
16903
+ const dir = path39.join(projectRoot, ".wrongstack");
16904
+ const file = path39.join(dir, "AGENTS.md");
16507
16905
  const facts = await detectProjectFacts(projectRoot);
16508
16906
  const body = renderAgentsTemplate(facts);
16509
16907
  await fsp5.mkdir(dir, { recursive: true });
@@ -16516,7 +16914,7 @@ async function runProjectCheck(opts) {
16516
16914
  if (kind === "initialized") {
16517
16915
  renderer.write(
16518
16916
  `
16519
- ${color.green("\u2713")} Project initialized ${color.dim(`(${path38.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
16917
+ ${color.green("\u2713")} Project initialized ${color.dim(`(${path39.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
16520
16918
  `
16521
16919
  );
16522
16920
  return true;
@@ -16541,13 +16939,13 @@ async function runProjectCheck(opts) {
16541
16939
  `);
16542
16940
  } catch (err) {
16543
16941
  renderer.writeError(
16544
- `Failed to scaffold AGENTS.md: ${err instanceof Error ? err.message : String(err)}`
16942
+ `Failed to scaffold AGENTS.md: ${toErrorMessage(err)}`
16545
16943
  );
16546
16944
  }
16547
16945
  }
16548
16946
  return true;
16549
16947
  }
16550
- const gitDir = path38.join(projectRoot, ".git");
16948
+ const gitDir = path39.join(projectRoot, ".git");
16551
16949
  let hasGit = false;
16552
16950
  try {
16553
16951
  await fsp5.access(gitDir);
@@ -16570,7 +16968,7 @@ async function runProjectCheck(opts) {
16570
16968
  if (answer2 === "y" || answer2 === "yes") {
16571
16969
  try {
16572
16970
  const { spawn: spawn6 } = await import('child_process');
16573
- await new Promise((resolve10, reject) => {
16971
+ await new Promise((resolve11, reject) => {
16574
16972
  const child = spawn6("git", ["init"], {
16575
16973
  cwd,
16576
16974
  signal: AbortSignal.timeout(1e4),
@@ -16579,14 +16977,14 @@ async function runProjectCheck(opts) {
16579
16977
  child.on("error", reject);
16580
16978
  child.on(
16581
16979
  "close",
16582
- (code) => code === 0 ? resolve10() : reject(new Error(`git init failed with ${code}`))
16980
+ (code) => code === 0 ? resolve11() : reject(new Error(`git init failed with ${code}`))
16583
16981
  );
16584
16982
  });
16585
16983
  renderer.write(` ${color.green("\u2713")} Git repository initialized
16586
16984
  `);
16587
16985
  } catch (err) {
16588
16986
  renderer.writeError(
16589
- `git init failed: ${err instanceof Error ? err.message : String(err)}
16987
+ `git init failed: ${toErrorMessage(err)}
16590
16988
  `
16591
16989
  );
16592
16990
  }
@@ -16782,11 +17180,11 @@ async function countProjectFiles(projectRoot, threshold) {
16782
17180
  for (const e of entries) {
16783
17181
  if (SKIP_DIRS.has(e.name)) continue;
16784
17182
  if (count >= threshold) return;
16785
- const full = path38.join(dir, e.name);
17183
+ const full = path39.join(dir, e.name);
16786
17184
  if (e.isDirectory()) {
16787
17185
  await walk(full);
16788
17186
  } else if (e.isFile()) {
16789
- if (INDEXABLE_EXTS.has(path38.extname(e.name))) {
17187
+ if (INDEXABLE_EXTS.has(path39.extname(e.name))) {
16790
17188
  count++;
16791
17189
  }
16792
17190
  }
@@ -17127,14 +17525,14 @@ function summarize(value, name) {
17127
17525
  if (typeof v === "object" && v !== null) {
17128
17526
  const o = v;
17129
17527
  if (name === "edit") {
17130
- const path39 = typeof o["path"] === "string" ? o["path"] : "";
17528
+ const path40 = typeof o["path"] === "string" ? o["path"] : "";
17131
17529
  const reps = typeof o["replacements"] === "number" ? o["replacements"] : 0;
17132
- return `${path39} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
17530
+ return `${path40} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
17133
17531
  }
17134
17532
  if (name === "write") {
17135
- const path39 = typeof o["path"] === "string" ? o["path"] : "";
17533
+ const path40 = typeof o["path"] === "string" ? o["path"] : "";
17136
17534
  const bytes = typeof o["bytes"] === "number" ? o["bytes"] : void 0;
17137
- return bytes !== void 0 ? `${path39} ${bytes}B` : path39;
17535
+ return bytes !== void 0 ? `${path40} ${bytes}B` : path40;
17138
17536
  }
17139
17537
  if (typeof o["count"] === "number") {
17140
17538
  return `${o["count"]} match${o["count"] === 1 ? "" : "es"}`;
@@ -17145,6 +17543,17 @@ function summarize(value, name) {
17145
17543
 
17146
17544
  // src/boot.ts
17147
17545
  init_project_utils();
17546
+ function resolveCmdFromCatalog(subagentId) {
17547
+ const desc = findAgentDescriptor(subagentId);
17548
+ if (!desc) return null;
17549
+ const out = {
17550
+ command: desc.acp.command,
17551
+ args: [...desc.acp.args ?? []],
17552
+ role: subagentId
17553
+ };
17554
+ if (desc.acp.env) out.env = desc.acp.env;
17555
+ return out;
17556
+ }
17148
17557
  var acpCmd = async (args, deps) => {
17149
17558
  const sub = args[0];
17150
17559
  if (!sub || sub === "server" || sub === "serve") {
@@ -17159,6 +17568,9 @@ Usage:
17159
17568
  wstack acp list List available ACP agents
17160
17569
  wstack acp spawn <id> <task>
17161
17570
  Spawn an ACP agent as a subagent and wait for result
17571
+ wstack acp parallel <agent-id-csv> <task>
17572
+ Fan a task out to multiple ACP agents in parallel
17573
+ and aggregate the results
17162
17574
  wstack acp help Show this help
17163
17575
 
17164
17576
  ACP Mode:
@@ -17168,9 +17580,17 @@ ACP Mode:
17168
17580
  Press Ctrl+C to stop.
17169
17581
 
17170
17582
  spawn:
17171
- Spawns a named ACP agent (cline, gemini-cli, copilot, openhands, goose)
17172
- with the given task and waits for its result.
17583
+ Spawns a named ACP agent (claude-code, gemini-cli, codex-cli, copilot,
17584
+ cline, goose, openhands, qwen-code, kiro-cli, opencode, mistral-vibe,
17585
+ cursor) with the given task and waits for its result.
17173
17586
  Example: wstack acp spawn cline "fix the login bug"
17587
+
17588
+ parallel:
17589
+ Runs the same task on a comma-separated list of ACP agents concurrently.
17590
+ Example: wstack acp parallel claude-code,gemini-cli,codex-cli "review this diff"
17591
+ Each agent's result is rendered under a clearly-marked header. Returns 0
17592
+ if at least one agent succeeds, 1 if all fail. Agents that aren't
17593
+ installed are skipped with a warning.
17174
17594
  `);
17175
17595
  return 0;
17176
17596
  }
@@ -17180,20 +17600,20 @@ spawn:
17180
17600
  if (sub === "spawn") {
17181
17601
  return spawnACPAgent(args.slice(1), deps);
17182
17602
  }
17603
+ if (sub === "parallel") {
17604
+ return parallelACPAgents(args.slice(1), deps);
17605
+ }
17183
17606
  deps.renderer.writeError(`Unknown acp subcommand: ${sub}
17184
17607
  `);
17185
17608
  deps.renderer.write("Run `wstack acp help` for usage.\n");
17186
17609
  return 1;
17187
17610
  };
17188
17611
  async function runACPServer(deps) {
17189
- const toolRegistry = deps.toolRegistry;
17190
- const tools = toolRegistry?.list() ?? [];
17191
17612
  deps.renderer.writeInfo("Starting WrongStack ACP server...\n");
17192
- deps.renderer.writeInfo(`Exposing ${tools.length} tool(s) via ACP protocol.
17193
- `);
17194
17613
  deps.renderer.writeInfo("Waiting for ACP client connection on stdin/stdout...\n");
17614
+ deps.renderer.writeInfo("(default runTurn is a no-op echo \u2014 wire makeACPServerAgentTurn for a real agent)\n");
17195
17615
  deps.renderer.writeInfo("Press Ctrl+C to stop.\n");
17196
- const server = new WrongStackACPServer({ tools });
17616
+ const server = new WrongStackACPServer({});
17197
17617
  const shutdown = () => {
17198
17618
  deps.renderer.writeWarning("\nShutting down ACP server...");
17199
17619
  server.stop();
@@ -17212,16 +17632,25 @@ async function runACPServer(deps) {
17212
17632
  }
17213
17633
  return 0;
17214
17634
  }
17215
- function listACPAgents(deps) {
17216
- deps.renderer.write("Available ACP agents:\n\n");
17217
- for (const a of ACP_AGENTS) {
17218
- const id = a.id ?? a.role;
17219
- const name = a.name ?? a.role ?? "";
17220
- const desc = a.prompt?.slice(0, 50) ?? "";
17221
- deps.renderer.write(` ${id.padEnd(16)} ${name.padEnd(20)} ${desc}\u2026
17635
+ async function listACPAgents(deps) {
17636
+ const registry = new EnsembleRegistry();
17637
+ const detected = await registry.list();
17638
+ deps.renderer.write("Detected ACP agents:\n\n");
17639
+ const installed = detected.filter((a) => a.installed);
17640
+ const missing = detected.filter((a) => !a.installed);
17641
+ for (const a of installed) {
17642
+ const ver = a.version ? ` (${a.version.split("\n")[0]})` : "";
17643
+ deps.renderer.write(` \u2713 ${a.id.padEnd(16)} ${a.displayName}${ver}
17222
17644
  `);
17223
17645
  }
17224
- deps.renderer.write("\nUse `wstack acp spawn <agent> <task>` to delegate a task.\n");
17646
+ for (const a of missing) {
17647
+ deps.renderer.write(` \u2717 ${a.id.padEnd(16)} ${a.displayName} (${a.reason ?? "not installed"})
17648
+ `);
17649
+ }
17650
+ deps.renderer.write(`
17651
+ ${installed.length} of ${detected.length} agents available.
17652
+ `);
17653
+ deps.renderer.write("Use `wstack acp spawn <agent-id> <task>` to delegate a task.\n");
17225
17654
  return 0;
17226
17655
  }
17227
17656
  async function spawnACPAgent(args, deps) {
@@ -17237,7 +17666,7 @@ async function spawnACPAgent(args, deps) {
17237
17666
  deps.renderer.write("Task description is required.\n");
17238
17667
  return 1;
17239
17668
  }
17240
- const cmd = ACP_AGENT_COMMANDS[subagentId];
17669
+ const cmd = ACP_AGENT_COMMANDS[subagentId] ?? resolveCmdFromCatalog(subagentId);
17241
17670
  if (!cmd) {
17242
17671
  deps.renderer.writeError(`Unknown ACP agent: ${subagentId}
17243
17672
  `);
@@ -17291,10 +17720,11 @@ async function spawnACPAgent(args, deps) {
17291
17720
  );
17292
17721
  return 0;
17293
17722
  } catch (err) {
17294
- deps.renderer.writeError(
17295
- `ACP agent error: ${err instanceof Error ? err.message : String(err)}
17296
- `
17297
- );
17723
+ const e = err;
17724
+ const detail = e.kind ? `[${e.kind}] ` : "";
17725
+ const message = e.message ?? (err instanceof Error ? err.message : String(err));
17726
+ deps.renderer.writeError(`ACP agent error: ${detail}${message}
17727
+ `);
17298
17728
  return 1;
17299
17729
  } finally {
17300
17730
  cleanup();
@@ -17302,6 +17732,94 @@ async function spawnACPAgent(args, deps) {
17302
17732
  process.off("SIGTERM", cleanup);
17303
17733
  }
17304
17734
  }
17735
+ async function parallelACPAgents(args, deps) {
17736
+ const [csv, ...taskParts] = args;
17737
+ if (!csv) {
17738
+ deps.renderer.writeError("Usage: wstack acp parallel <agent-id-csv> <task>\n");
17739
+ deps.renderer.write('Example: wstack acp parallel claude-code,gemini-cli "review this diff"\n');
17740
+ return 1;
17741
+ }
17742
+ const task = taskParts.join(" ");
17743
+ if (!task) {
17744
+ deps.renderer.writeError("Usage: wstack acp parallel <agent-id-csv> <task>\n");
17745
+ deps.renderer.writeError("Task description is required.\n");
17746
+ return 1;
17747
+ }
17748
+ const ac = new AbortController();
17749
+ const onSignal = () => ac.abort();
17750
+ process.on("SIGINT", onSignal);
17751
+ process.on("SIGTERM", onSignal);
17752
+ try {
17753
+ const result = await runEnsemble({
17754
+ agentIds: csv,
17755
+ task,
17756
+ resolveCmd: resolveCmdFromCatalog,
17757
+ signal: ac.signal
17758
+ });
17759
+ const skipped = result.results.filter((r) => r.status === "skipped");
17760
+ if (skipped.length > 0) {
17761
+ deps.renderer.writeWarning(
17762
+ `Skipping ${skipped.length} agent(s) not installed: ${skipped.map((s) => `${s.agentId} (${s.reason ?? "not installed"})`).join(", ")}
17763
+ `
17764
+ );
17765
+ }
17766
+ if (result.summary.succeeded + result.summary.failed + result.summary.cancelled === 0) {
17767
+ deps.renderer.writeError("No installed agents to run.\n");
17768
+ deps.renderer.write("Run `wstack acp list` to see what is available.\n");
17769
+ return 1;
17770
+ }
17771
+ const fannedOut = result.results.filter((r) => r.status !== "skipped").map((r) => r.agentId).join(", ");
17772
+ deps.renderer.writeInfo(
17773
+ `Fanning out to ${result.summary.succeeded + result.summary.failed + result.summary.cancelled} agent(s): ${fannedOut}
17774
+ `
17775
+ );
17776
+ deps.renderer.writeInfo(`Task: ${result.task}
17777
+
17778
+ `);
17779
+ for (const r of result.results) {
17780
+ if (r.status === "skipped") continue;
17781
+ deps.renderer.write(`
17782
+ === ${r.agentId} ===
17783
+ `);
17784
+ if (r.status === "success") {
17785
+ deps.renderer.write(r.result && r.result.length > 0 ? r.result : "(no result)");
17786
+ deps.renderer.write(
17787
+ `
17788
+ [${r.agentId}] success ${r.durationMs}ms iterations=${r.iterations} toolCalls=${r.toolCalls}
17789
+ `
17790
+ );
17791
+ } else if (r.status === "failed") {
17792
+ deps.renderer.writeError(
17793
+ `[${r.error?.kind ?? "unknown"}] ${r.error?.message ?? "failed"}
17794
+ `
17795
+ );
17796
+ deps.renderer.write(
17797
+ `[${r.agentId}] failed ${r.durationMs}ms
17798
+ `
17799
+ );
17800
+ } else {
17801
+ deps.renderer.writeError(
17802
+ `[${r.error?.kind ?? "aborted"}] ${r.error?.message ?? "cancelled"}
17803
+ `
17804
+ );
17805
+ deps.renderer.write(
17806
+ `[${r.agentId}] cancelled ${r.durationMs}ms
17807
+ `
17808
+ );
17809
+ }
17810
+ }
17811
+ const { succeeded, failed, cancelled, skipped: skip } = result.summary;
17812
+ deps.renderer.write(
17813
+ `
17814
+ Parallel summary: ${succeeded} succeeded, ${failed} failed, ${cancelled} cancelled, ${skip} skipped.
17815
+ `
17816
+ );
17817
+ return succeeded > 0 ? 0 : 1;
17818
+ } finally {
17819
+ process.off("SIGINT", onSignal);
17820
+ process.off("SIGTERM", onSignal);
17821
+ }
17822
+ }
17305
17823
  var auditCmd = async (args, deps) => {
17306
17824
  const wpaths = resolveWstackPaths({
17307
17825
  projectRoot: deps.projectRoot,
@@ -17351,14 +17869,14 @@ var auditCmd = async (args, deps) => {
17351
17869
  return verify.ok ? 0 : 1;
17352
17870
  };
17353
17871
  async function listAudits(log, dir, deps) {
17354
- const fs38 = await import('fs/promises');
17355
- const path39 = await import('path');
17872
+ const fs39 = await import('fs/promises');
17873
+ const path40 = await import('path');
17356
17874
  const out = [];
17357
17875
  let foundRoot = true;
17358
17876
  const scan = async (scanDir, prefix, depth) => {
17359
17877
  let entries;
17360
17878
  try {
17361
- entries = await fs38.readdir(scanDir, { withFileTypes: true });
17879
+ entries = await fs39.readdir(scanDir, { withFileTypes: true });
17362
17880
  } catch {
17363
17881
  if (depth === 0) foundRoot = false;
17364
17882
  return;
@@ -17366,7 +17884,7 @@ async function listAudits(log, dir, deps) {
17366
17884
  for (const entry of entries) {
17367
17885
  if (entry.name.startsWith(".")) continue;
17368
17886
  if (entry.isDirectory()) {
17369
- if (depth === 0) await scan(path39.join(scanDir, entry.name), entry.name, depth + 1);
17887
+ if (depth === 0) await scan(path40.join(scanDir, entry.name), entry.name, depth + 1);
17370
17888
  continue;
17371
17889
  }
17372
17890
  if (!entry.isFile() || !entry.name.endsWith(".audit.jsonl")) continue;
@@ -18089,6 +18607,35 @@ ${color.amber("?")} Pick: `)).trim().toLowerCase();
18089
18607
  }
18090
18608
  }
18091
18609
 
18610
+ // src/auth-menu/local.ts
18611
+ init_provider_config_utils();
18612
+
18613
+ // src/auth-menu/local.ts
18614
+ var LOCAL_LLM_PRESETS = [
18615
+ {
18616
+ id: "ollama",
18617
+ label: "Ollama",
18618
+ defaultBaseUrl: "http://localhost:11434/v1",
18619
+ noAuth: true,
18620
+ hint: "https://ollama.com \u2014 port 11434, no auth"
18621
+ },
18622
+ {
18623
+ id: "vllm",
18624
+ label: "vLLM",
18625
+ defaultBaseUrl: "http://localhost:8000/v1",
18626
+ noAuth: false,
18627
+ hint: "https://docs.vllm.ai \u2014 port 8000, optional Bearer"
18628
+ },
18629
+ {
18630
+ id: "lmstudio",
18631
+ label: "LM Studio",
18632
+ defaultBaseUrl: "http://localhost:1234/v1",
18633
+ noAuth: false,
18634
+ hint: "https://lmstudio.ai \u2014 port 1234, optional Bearer"
18635
+ }
18636
+ ];
18637
+ new Map(LOCAL_LLM_PRESETS.map((p) => [p.id, p]));
18638
+
18092
18639
  // src/subcommands/handlers/auth.ts
18093
18640
  init_provider_config_utils();
18094
18641
  var authCmd = async (args, deps) => {
@@ -18285,7 +18832,200 @@ try {
18285
18832
  } catch {
18286
18833
  }
18287
18834
 
18288
- // src/subcommands/handlers/diag-doctor.ts
18835
+ // src/subcommands/handlers/bench.ts
18836
+ var benchCmd = async (args, deps) => {
18837
+ const sub = args[0];
18838
+ const rest = args.slice(1);
18839
+ switch (sub) {
18840
+ case "run":
18841
+ return benchRun(rest, deps);
18842
+ case "report":
18843
+ return benchReport(rest, deps);
18844
+ case "list":
18845
+ return benchList(rest, deps);
18846
+ default:
18847
+ printUsage(deps);
18848
+ return sub === void 0 ? 0 : 1;
18849
+ }
18850
+ };
18851
+ function printUsage(deps) {
18852
+ deps.renderer.write(
18853
+ [
18854
+ color.bold("wstack bench") + " \u2014 model-independent agentic benchmarks",
18855
+ "",
18856
+ " run Run a suite across a model matrix and write a report",
18857
+ " report Re-render report.md from a finished run directory",
18858
+ " list Show available suites and configured model cells",
18859
+ "",
18860
+ color.dim("Examples:"),
18861
+ color.dim(
18862
+ " wstack bench run --suite polyglot --polyglot-dir ./polyglot --models bench.config.json --limit 5"
18863
+ ),
18864
+ color.dim(" wstack bench report ./bench-results/2026-06-14T10-00-00"),
18865
+ ""
18866
+ ].join("\n") + "\n"
18867
+ );
18868
+ }
18869
+ function flagStr(deps, name) {
18870
+ const v = deps.flags?.[name];
18871
+ return typeof v === "string" ? v : void 0;
18872
+ }
18873
+ function flagBool(deps, name) {
18874
+ const v = deps.flags?.[name];
18875
+ return v === true || v === "true";
18876
+ }
18877
+ async function resolveWstackEntry() {
18878
+ try {
18879
+ const req2 = createRequire(import.meta.url);
18880
+ const pkgPath = req2.resolve("@wrongstack/cli/package.json");
18881
+ const entry = path39.join(path39.dirname(pkgPath), "dist", "index.js");
18882
+ await fsp5.access(entry);
18883
+ return entry;
18884
+ } catch {
18885
+ return process.argv[1] ?? "";
18886
+ }
18887
+ }
18888
+ async function benchRun(_args, deps) {
18889
+ const suiteId = flagStr(deps, "suite") ?? "polyglot";
18890
+ const modelsPath = flagStr(deps, "models") ?? "bench.config.json";
18891
+ const limitRaw = flagStr(deps, "limit");
18892
+ const limit = limitRaw ? Math.max(1, Number.parseInt(limitRaw, 10)) : void 0;
18893
+ const outBase = flagStr(deps, "out") ?? "bench-results";
18894
+ let config;
18895
+ try {
18896
+ config = await loadBenchConfig(path39.resolve(deps.cwd, modelsPath));
18897
+ } catch (err) {
18898
+ deps.renderer.writeError(toErrorMessage(err));
18899
+ return 1;
18900
+ }
18901
+ const concurrencyRaw = flagStr(deps, "concurrency");
18902
+ if (concurrencyRaw) {
18903
+ const c = Number.parseInt(concurrencyRaw, 10);
18904
+ if (c > 0) config.concurrency = c;
18905
+ }
18906
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
18907
+ const outDir = path39.resolve(deps.cwd, outBase, stamp);
18908
+ const predictionsDir = path39.join(outDir, "predictions");
18909
+ let suite;
18910
+ let grade;
18911
+ let isSwebench = false;
18912
+ if (suiteId === "polyglot") {
18913
+ const polyglotDir = flagStr(deps, "polyglot-dir");
18914
+ if (!polyglotDir) {
18915
+ deps.renderer.writeError("--polyglot-dir <path> is required for the polyglot suite.");
18916
+ return 1;
18917
+ }
18918
+ const languagesRaw = flagStr(deps, "languages");
18919
+ const languages = languagesRaw ? languagesRaw.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
18920
+ suite = createPolyglotSuite({ polyglotDir: path39.resolve(deps.cwd, polyglotDir), languages });
18921
+ grade = (a) => gradePolyglot(a);
18922
+ } else if (suiteId === "swebench") {
18923
+ isSwebench = true;
18924
+ const datasetDir = flagStr(deps, "dataset-dir");
18925
+ const docker = flagBool(deps, "docker");
18926
+ suite = createSwebenchSuite({
18927
+ datasetDir: datasetDir ? path39.resolve(deps.cwd, datasetDir) : void 0,
18928
+ docker
18929
+ });
18930
+ grade = (a) => gradeSwebench({ ...a, predictionsDir });
18931
+ } else {
18932
+ deps.renderer.writeError(`unknown suite "${suiteId}" (expected: polyglot | swebench)`);
18933
+ return 1;
18934
+ }
18935
+ const toolNames = deps.toolRegistry?.list().map((t) => t.name) ?? [];
18936
+ const wstackEntry = await resolveWstackEntry();
18937
+ deps.renderer.writeInfo(`Running ${suiteId} across ${config.cells.length} model(s)\u2026`);
18938
+ let report;
18939
+ try {
18940
+ report = await runBenchmark({
18941
+ suite,
18942
+ grade,
18943
+ config,
18944
+ cliVersion: CLI_VERSION,
18945
+ toolNames,
18946
+ nodeBin: process.execPath,
18947
+ wstackEntry,
18948
+ limit,
18949
+ onProgress: (msg) => deps.renderer.write(color.dim(msg) + "\n")
18950
+ });
18951
+ } catch (err) {
18952
+ deps.renderer.writeError(toErrorMessage(err));
18953
+ return 1;
18954
+ }
18955
+ await writeJsonArtifacts(outDir, report);
18956
+ const md = renderMarkdownReport(report);
18957
+ await fsp5.writeFile(path39.join(outDir, "report.md"), md, "utf8");
18958
+ deps.renderer.write("\n" + md + "\n");
18959
+ if (isSwebench) {
18960
+ for (const cell of config.cells) {
18961
+ const preds = await collectCellPredictions(predictionsDir, cell.label);
18962
+ if (preds.length === 0) continue;
18963
+ const file = await writePredictionsJsonl(outDir, cell.label, preds);
18964
+ deps.renderer.writeInfo(`Predictions for "${cell.label}" \u2192 ${file}`);
18965
+ }
18966
+ deps.renderer.writeInfo(
18967
+ "Grade with the official SWE-bench harness: python -m swebench.harness.run_evaluation --predictions_path <file> --run_id <id>"
18968
+ );
18969
+ }
18970
+ deps.renderer.writeInfo(`Report written to ${path39.join(outDir, "report.md")}`);
18971
+ return 0;
18972
+ }
18973
+ async function benchReport(args, deps) {
18974
+ const dir = args.find((a) => !a.startsWith("-"));
18975
+ if (!dir) {
18976
+ deps.renderer.writeError("Usage: wstack bench report <run-directory>");
18977
+ return 1;
18978
+ }
18979
+ const outDir = path39.resolve(deps.cwd, dir);
18980
+ let summary;
18981
+ try {
18982
+ summary = await readSummary(outDir);
18983
+ } catch (err) {
18984
+ deps.renderer.writeError(
18985
+ `cannot read summary.json in ${outDir}: ${toErrorMessage(err)}`
18986
+ );
18987
+ return 1;
18988
+ }
18989
+ const md = renderMarkdownReport(summary);
18990
+ await fsp5.writeFile(path39.join(outDir, "report.md"), md, "utf8");
18991
+ deps.renderer.write("\n" + md + "\n");
18992
+ return 0;
18993
+ }
18994
+ async function benchList(_args, deps) {
18995
+ deps.renderer.write(color.bold("Suites\n"));
18996
+ deps.renderer.write(
18997
+ " polyglot " + color.dim("Aider polyglot (edit accuracy) \u2014 Phase 1, Docker-free\n")
18998
+ );
18999
+ deps.renderer.write(
19000
+ " swebench " + color.dim("SWE-bench Verified (end-to-end) \u2014 Phase 2, Docker-gated\n")
19001
+ );
19002
+ const modelsPath = flagStr(deps, "models");
19003
+ if (modelsPath) {
19004
+ try {
19005
+ const config = await loadBenchConfig(path39.resolve(deps.cwd, modelsPath));
19006
+ deps.renderer.write("\n" + color.bold("Model cells\n"));
19007
+ for (const cell of config.cells) {
19008
+ deps.renderer.write(
19009
+ ` ${cell.label.padEnd(16)} ${color.dim(`${cell.provider}/${cell.model}`)}
19010
+ `
19011
+ );
19012
+ }
19013
+ const fp = reportHeaderLine({
19014
+ cliVersion: CLI_VERSION,
19015
+ toolNames: deps.toolRegistry?.list().map((t) => t.name) ?? [],
19016
+ maxIterations: config.maxIterations,
19017
+ yolo: true,
19018
+ subsetId: "(computed at run time)",
19019
+ hash: "(computed at run time)"
19020
+ });
19021
+ deps.renderer.write("\n" + color.dim(`Harness: ${fp}`) + "\n");
19022
+ } catch (err) {
19023
+ deps.renderer.writeError(toErrorMessage(err));
19024
+ return 1;
19025
+ }
19026
+ }
19027
+ return 0;
19028
+ }
18289
19029
  var diagCmd = async (_args, deps) => {
18290
19030
  const cfg = deps.config;
18291
19031
  const age = await deps.modelsRegistry.ageSeconds();
@@ -18364,7 +19104,7 @@ var doctorCmd = async (_args, deps) => {
18364
19104
  checks.push({
18365
19105
  name: "models cache",
18366
19106
  status: "warn",
18367
- detail: `read failed: ${err instanceof Error ? err.message : String(err)}`
19107
+ detail: `read failed: ${toErrorMessage(err)}`
18368
19108
  });
18369
19109
  }
18370
19110
  try {
@@ -18379,7 +19119,7 @@ var doctorCmd = async (_args, deps) => {
18379
19119
  }
18380
19120
  try {
18381
19121
  await fsp5.mkdir(deps.paths.projectSessions, { recursive: true });
18382
- const probe = path38.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
19122
+ const probe = path39.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
18383
19123
  await fsp5.writeFile(probe, "");
18384
19124
  await fsp5.unlink(probe);
18385
19125
  checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
@@ -18387,7 +19127,7 @@ var doctorCmd = async (_args, deps) => {
18387
19127
  checks.push({
18388
19128
  name: "sessions writable",
18389
19129
  status: "fail",
18390
- detail: `cannot write to ${deps.paths.projectSessions}: ${err instanceof Error ? err.message : String(err)}`
19130
+ detail: `cannot write to ${deps.paths.projectSessions}: ${toErrorMessage(err)}`
18391
19131
  });
18392
19132
  }
18393
19133
  const mcpEntries = Object.entries(cfg.mcpServers ?? {});
@@ -18478,12 +19218,12 @@ var exportCmd = async (args, deps) => {
18478
19218
  try {
18479
19219
  rendered = await reader.export(sessionId, { format, includeTools, includeDiagnostics });
18480
19220
  } catch (err) {
18481
- deps.renderer.writeError(`Export failed: ${err instanceof Error ? err.message : String(err)}`);
19221
+ deps.renderer.writeError(`Export failed: ${toErrorMessage(err)}`);
18482
19222
  return 1;
18483
19223
  }
18484
19224
  if (output) {
18485
- await fsp5.mkdir(path38.dirname(path38.resolve(deps.cwd, output)), { recursive: true });
18486
- await fsp5.writeFile(path38.resolve(deps.cwd, output), rendered, "utf8");
19225
+ await fsp5.mkdir(path39.dirname(path39.resolve(deps.cwd, output)), { recursive: true });
19226
+ await fsp5.writeFile(path39.resolve(deps.cwd, output), rendered, "utf8");
18487
19227
  deps.renderer.write(`Wrote ${rendered.length} bytes to ${output}
18488
19228
  `);
18489
19229
  } else {
@@ -18559,8 +19299,8 @@ var initCmd = async (_args, deps) => {
18559
19299
  const vault = new DefaultSecretVault({ keyFile: deps.paths.secretsKey });
18560
19300
  const encrypted = encryptConfigSecrets$1(config, vault);
18561
19301
  await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2), { mode: 384 });
18562
- await fsp5.mkdir(path38.join(deps.projectRoot, ".wrongstack"), { recursive: true });
18563
- const agentsFile = path38.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
19302
+ await fsp5.mkdir(path39.join(deps.projectRoot, ".wrongstack"), { recursive: true });
19303
+ const agentsFile = path39.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
18564
19304
  const projectFacts = await detectProjectFacts(deps.projectRoot);
18565
19305
  await atomicWrite(agentsFile, renderAgentsTemplate(projectFacts));
18566
19306
  deps.renderer.writeInfo(`Wrote ${deps.paths.globalConfig}`);
@@ -18699,8 +19439,8 @@ async function serveMcpStdio(deps) {
18699
19439
  log(
18700
19440
  `wrongstack MCP server ready at ${handle2.url} \u2014 exposing ${allowed.length} tool(s) (${mode})${token ? " [token auth]" : ""}.`
18701
19441
  );
18702
- await new Promise((resolve10) => {
18703
- const stop = () => resolve10();
19442
+ await new Promise((resolve11) => {
19443
+ const stop = () => resolve11();
18704
19444
  process.once("SIGINT", stop);
18705
19445
  process.once("SIGTERM", stop);
18706
19446
  });
@@ -19451,7 +20191,7 @@ var modeldiagCmd = async (args, deps) => {
19451
20191
  ` ${label} ${provColor(modelKey.padEnd(50))} ${scoreBar(c.score, 110).slice(0, 11)} ${color.amber(fmtMs(latency).padEnd(8))} ${color.dim(`in${usage?.input ?? "?"}/out${usage?.output ?? "?"}`.padEnd(12))} ${firstLineClean}`
19452
20192
  );
19453
20193
  } catch (err) {
19454
- const errMsg = err instanceof Error ? err.message : String(err);
20194
+ const errMsg = toErrorMessage(err);
19455
20195
  writeLine(
19456
20196
  ` ${label} ${color.red(modelKey.padEnd(50))} ${scoreBar(c.score, 110).slice(0, 11)} ${color.red("FAILED")} ${color.dim(errMsg.slice(0, 40))}`
19457
20197
  );
@@ -19923,7 +20663,7 @@ var usageCmd = async (_args, deps) => {
19923
20663
  return 0;
19924
20664
  };
19925
20665
  var projectsCmd = async (_args, deps) => {
19926
- const projectsRoot = path38.join(deps.paths.globalRoot, "projects");
20666
+ const projectsRoot = path39.join(deps.paths.globalRoot, "projects");
19927
20667
  try {
19928
20668
  const entries = await fsp5.readdir(projectsRoot);
19929
20669
  if (entries.length === 0) {
@@ -19933,7 +20673,7 @@ var projectsCmd = async (_args, deps) => {
19933
20673
  for (const hash of entries) {
19934
20674
  try {
19935
20675
  const meta = JSON.parse(
19936
- await fsp5.readFile(path38.join(projectsRoot, hash, "meta.json"), "utf8")
20676
+ await fsp5.readFile(path39.join(projectsRoot, hash, "meta.json"), "utf8")
19937
20677
  );
19938
20678
  deps.renderer.write(
19939
20679
  ` ${color.dim(hash)} ${color.dim(meta.lastSeen ?? "")} ${meta.root ?? "?"}
@@ -20382,7 +21122,7 @@ function findSessionId(args) {
20382
21122
  var rewindCmd = async (args, deps) => {
20383
21123
  const flags = parseRewindFlags(args);
20384
21124
  const wpaths = resolveWstackPaths({ projectRoot: deps.projectRoot });
20385
- const sessionsDir = path38.join(wpaths.globalRoot, "sessions");
21125
+ const sessionsDir = path39.join(wpaths.globalRoot, "sessions");
20386
21126
  const rewind = new DefaultSessionRewinder(sessionsDir, deps.projectRoot);
20387
21127
  let sessionId = findSessionId(args);
20388
21128
  if (!sessionId) {
@@ -20495,7 +21235,7 @@ ${result.errors.length} error(s):
20495
21235
  }
20496
21236
  return 0;
20497
21237
  } catch (err) {
20498
- deps.renderer.writeError(err instanceof Error ? err.message : String(err));
21238
+ deps.renderer.writeError(toErrorMessage(err));
20499
21239
  return 1;
20500
21240
  }
20501
21241
  };
@@ -20527,7 +21267,7 @@ async function listFleetRuns(deps) {
20527
21267
  }
20528
21268
  const runs = [];
20529
21269
  for (const id of entries) {
20530
- const runDir = path38.join(deps.paths.projectSessions, id);
21270
+ const runDir = path39.join(deps.paths.projectSessions, id);
20531
21271
  let stat7;
20532
21272
  try {
20533
21273
  stat7 = await fsp5.stat(runDir);
@@ -20540,17 +21280,17 @@ async function listFleetRuns(deps) {
20540
21280
  let subagentCount = 0;
20541
21281
  let subagentsDir;
20542
21282
  try {
20543
- await fsp5.access(path38.join(runDir, "fleet.json"));
21283
+ await fsp5.access(path39.join(runDir, "fleet.json"));
20544
21284
  manifest = true;
20545
21285
  } catch {
20546
21286
  }
20547
21287
  try {
20548
- await fsp5.access(path38.join(runDir, "checkpoint.json"));
21288
+ await fsp5.access(path39.join(runDir, "checkpoint.json"));
20549
21289
  checkpoint = true;
20550
21290
  } catch {
20551
21291
  }
20552
21292
  try {
20553
- subagentsDir = path38.join(runDir, "subagents");
21293
+ subagentsDir = path39.join(runDir, "subagents");
20554
21294
  const files = await fsp5.readdir(subagentsDir);
20555
21295
  subagentCount = files.filter((f) => f.endsWith(".jsonl")).length;
20556
21296
  } catch {
@@ -20577,7 +21317,7 @@ async function listFleetRuns(deps) {
20577
21317
  return 0;
20578
21318
  }
20579
21319
  async function showFleetRun(runId, deps) {
20580
- const runDir = path38.join(deps.paths.projectSessions, runId);
21320
+ const runDir = path39.join(deps.paths.projectSessions, runId);
20581
21321
  let stat7;
20582
21322
  try {
20583
21323
  stat7 = await fsp5.stat(runDir);
@@ -20594,7 +21334,7 @@ async function showFleetRun(runId, deps) {
20594
21334
  deps.renderer.write(color.bold(`
20595
21335
  Fleet Run: ${runId}
20596
21336
  `) + "\n");
20597
- const manifestPath = path38.join(runDir, "fleet.json");
21337
+ const manifestPath = path39.join(runDir, "fleet.json");
20598
21338
  let manifestData = null;
20599
21339
  try {
20600
21340
  manifestData = await fsp5.readFile(manifestPath, "utf8");
@@ -20612,7 +21352,7 @@ Fleet Run: ${runId}
20612
21352
  deps.renderer.write(` ${color.dim("\u25CB")} fleet.json \u2014 not found
20613
21353
  `);
20614
21354
  }
20615
- const checkpointPath = path38.join(runDir, "checkpoint.json");
21355
+ const checkpointPath = path39.join(runDir, "checkpoint.json");
20616
21356
  let checkpointData = null;
20617
21357
  try {
20618
21358
  checkpointData = await fsp5.readFile(checkpointPath, "utf8");
@@ -20659,7 +21399,7 @@ Fleet Run: ${runId}
20659
21399
  } catch {
20660
21400
  }
20661
21401
  }
20662
- const subagentsDir = path38.join(runDir, "subagents");
21402
+ const subagentsDir = path39.join(runDir, "subagents");
20663
21403
  let subagentFiles = [];
20664
21404
  try {
20665
21405
  subagentFiles = await fsp5.readdir(subagentsDir);
@@ -20671,7 +21411,7 @@ Fleet Run: ${runId}
20671
21411
  Subagent transcripts (${subagentFiles.length}):
20672
21412
  `);
20673
21413
  for (const f of subagentFiles.sort()) {
20674
- const filePath = path38.join(subagentsDir, f);
21414
+ const filePath = path39.join(subagentsDir, f);
20675
21415
  let size;
20676
21416
  try {
20677
21417
  const s = await fsp5.stat(filePath);
@@ -20688,7 +21428,7 @@ Fleet Run: ${runId}
20688
21428
  ${color.dim("\u25CB")} No subagent transcripts
20689
21429
  `);
20690
21430
  }
20691
- const sharedDir = path38.join(runDir, "shared");
21431
+ const sharedDir = path39.join(runDir, "shared");
20692
21432
  try {
20693
21433
  const files = await fsp5.readdir(sharedDir);
20694
21434
  deps.renderer.write(`
@@ -20872,7 +21612,7 @@ var updateCmd = async (args, deps) => {
20872
21612
  deps.renderer.write(`Updating wrongstack from v${info.current} to v${info.latest}...
20873
21613
  `);
20874
21614
  try {
20875
- const result = await new Promise((resolve10, reject) => {
21615
+ const result = await new Promise((resolve11, reject) => {
20876
21616
  const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
20877
21617
  const child = spawn(npmCommand, ["install", "-g", "wrongstack@latest"], {
20878
21618
  cwd,
@@ -20885,7 +21625,7 @@ var updateCmd = async (args, deps) => {
20885
21625
  _stderr += d;
20886
21626
  });
20887
21627
  child.on("error", reject);
20888
- child.on("close", (code) => resolve10({ code: code ?? 0 }));
21628
+ child.on("close", (code) => resolve11({ code: code ?? 0 }));
20889
21629
  });
20890
21630
  if (result.code === 0) {
20891
21631
  deps.renderer.write(
@@ -20996,7 +21736,8 @@ var subcommands = {
20996
21736
  help: helpCmd,
20997
21737
  projects: projectsCmd,
20998
21738
  modeldiag: modeldiagCmd,
20999
- quick: quickCmd
21739
+ quick: quickCmd,
21740
+ bench: benchCmd
21000
21741
  };
21001
21742
 
21002
21743
  // src/boot.ts
@@ -21014,7 +21755,7 @@ function resolveBundledSkillsDir() {
21014
21755
  try {
21015
21756
  const req2 = createRequire(import.meta.url);
21016
21757
  const corePkg = req2.resolve("@wrongstack/core/package.json");
21017
- return path38.join(path38.dirname(corePkg), "skills");
21758
+ return path39.join(path39.dirname(corePkg), "skills");
21018
21759
  } catch {
21019
21760
  return void 0;
21020
21761
  }
@@ -21033,7 +21774,7 @@ async function boot(argv) {
21033
21774
  try {
21034
21775
  bootResult = await bootConfig(flags);
21035
21776
  } catch (err) {
21036
- writeErr(`Config error: ${err instanceof Error ? err.message : String(err)}
21777
+ writeErr(`Config error: ${toErrorMessage(err)}
21037
21778
  `);
21038
21779
  return 2;
21039
21780
  }
@@ -21086,7 +21827,7 @@ async function boot(argv) {
21086
21827
  await modelsRegistry.refresh();
21087
21828
  logger.info("models.dev catalog refreshed");
21088
21829
  } catch (err) {
21089
- const msg = err instanceof Error ? err.message : String(err);
21830
+ const msg = toErrorMessage(err);
21090
21831
  logger.warn(`models.dev refresh failed (${msg}); using cached catalog`);
21091
21832
  }
21092
21833
  }
@@ -21235,7 +21976,7 @@ async function boot(argv) {
21235
21976
  const created = await registerProjectAtBoot({ projectRoot, cwd, wpaths });
21236
21977
  if (created && isInteractiveTTY) {
21237
21978
  renderer.write(
21238
- color.dim(` \u2713 Registered "${path38.basename(projectRoot)}" in projects.json.
21979
+ color.dim(` \u2713 Registered "${path39.basename(projectRoot)}" in projects.json.
21239
21980
  `)
21240
21981
  );
21241
21982
  }
@@ -21315,7 +22056,7 @@ async function boot(argv) {
21315
22056
  } catch {
21316
22057
  }
21317
22058
  printLaunchHints(renderer, flags, {
21318
- cursorFile: path38.join(wpaths.cacheDir, "hint-cursor")
22059
+ cursorFile: path39.join(wpaths.cacheDir, "hint-cursor")
21319
22060
  });
21320
22061
  } else {
21321
22062
  const effectiveChoices = config.launch ? {
@@ -21353,7 +22094,7 @@ async function boot(argv) {
21353
22094
  }
21354
22095
  async function checkGitInCwd(opts) {
21355
22096
  const { cwd, renderer, reader } = opts;
21356
- const cwdGit = path38.join(cwd, ".git");
22097
+ const cwdGit = path39.join(cwd, ".git");
21357
22098
  let hasCwdGit = false;
21358
22099
  try {
21359
22100
  await fsp5.access(cwdGit);
@@ -21370,7 +22111,7 @@ async function checkGitInCwd(opts) {
21370
22111
  if (answer === "y" || answer === "yes") {
21371
22112
  try {
21372
22113
  const { spawn: spawn6 } = await import('child_process');
21373
- await new Promise((resolve10, reject) => {
22114
+ await new Promise((resolve11, reject) => {
21374
22115
  const child = spawn6("git", ["init"], {
21375
22116
  cwd,
21376
22117
  signal: AbortSignal.timeout(1e4),
@@ -21379,7 +22120,7 @@ async function checkGitInCwd(opts) {
21379
22120
  child.on("error", reject);
21380
22121
  child.on(
21381
22122
  "close",
21382
- (code) => code === 0 ? resolve10() : reject(new Error(`git init failed with ${code}`))
22123
+ (code) => code === 0 ? resolve11() : reject(new Error(`git init failed with ${code}`))
21383
22124
  );
21384
22125
  });
21385
22126
  renderer.write(` ${color.green("\u2713")} Git repository initialized
@@ -21387,16 +22128,16 @@ async function checkGitInCwd(opts) {
21387
22128
  hasCwdGit = true;
21388
22129
  } catch (err) {
21389
22130
  renderer.writeError(
21390
- `git init failed: ${err instanceof Error ? err.message : String(err)}
22131
+ `git init failed: ${toErrorMessage(err)}
21391
22132
  `
21392
22133
  );
21393
22134
  }
21394
22135
  }
21395
22136
  }
21396
- const parentDir = path38.dirname(cwd);
22137
+ const parentDir = path39.dirname(cwd);
21397
22138
  if (parentDir !== cwd) {
21398
22139
  try {
21399
- await fsp5.access(path38.join(parentDir, ".git"));
22140
+ await fsp5.access(path39.join(parentDir, ".git"));
21400
22141
  renderer.write(
21401
22142
  ` ${color.dim("\u2139")} A ${color.bold(".git")} repo exists in the parent directory: ${color.dim(parentDir)}
21402
22143
  `
@@ -21408,7 +22149,7 @@ async function checkGitInCwd(opts) {
21408
22149
  async function registerProjectAtBoot(opts) {
21409
22150
  const { projectRoot, cwd, wpaths } = opts;
21410
22151
  const manifest = await loadManifest(wpaths.globalConfig);
21411
- const existed = manifest.projects.some((p) => path38.resolve(p.root) === path38.resolve(projectRoot));
22152
+ const existed = manifest.projects.some((p) => path39.resolve(p.root) === path39.resolve(projectRoot));
21412
22153
  await touchProjectInManifest({
21413
22154
  projectRoot,
21414
22155
  globalConfigPath: wpaths.globalConfig,
@@ -21609,7 +22350,7 @@ function resolveBundledSkillsDir2() {
21609
22350
  try {
21610
22351
  const req2 = createRequire(import.meta.url);
21611
22352
  const corePkg = req2.resolve("@wrongstack/core/package.json");
21612
- return path38.join(path38.dirname(corePkg), "skills");
22353
+ return path39.join(path39.dirname(corePkg), "skills");
21613
22354
  } catch {
21614
22355
  return void 0;
21615
22356
  }
@@ -22097,7 +22838,7 @@ async function runRepl(opts) {
22097
22838
  clientMailbox.registerClient({
22098
22839
  clientId,
22099
22840
  sessionId: replProjectRoot,
22100
- name: `REPL [${path38.basename(replProjectRoot)}]`,
22841
+ name: `REPL [${path39.basename(replProjectRoot)}]`,
22101
22842
  source: "repl",
22102
22843
  pid: process.pid
22103
22844
  }).then(() => {
@@ -22162,10 +22903,10 @@ async function runRepl(opts) {
22162
22903
  }
22163
22904
  } catch (err) {
22164
22905
  opts.renderer.writeError(
22165
- `[eternal] ${err instanceof Error ? err.message : String(err)}`
22906
+ `[eternal] ${toErrorMessage(err)}`
22166
22907
  );
22167
22908
  }
22168
- await new Promise((resolve10) => setTimeout(resolve10, 250));
22909
+ await new Promise((resolve11) => setTimeout(resolve11, 250));
22169
22910
  continue;
22170
22911
  }
22171
22912
  } else if (opts.getAutonomy?.() === "eternal-parallel") {
@@ -22238,10 +22979,10 @@ async function runRepl(opts) {
22238
22979
  }
22239
22980
  } catch (err) {
22240
22981
  opts.renderer.writeError(
22241
- `[parallel] ${err instanceof Error ? err.message : String(err)}`
22982
+ `[parallel] ${toErrorMessage(err)}`
22242
22983
  );
22243
22984
  }
22244
- await new Promise((resolve10) => setTimeout(resolve10, 250));
22985
+ await new Promise((resolve11) => setTimeout(resolve11, 250));
22245
22986
  continue;
22246
22987
  }
22247
22988
  }
@@ -22311,7 +23052,7 @@ ${lines.join("\n")}
22311
23052
  if (res?.message) opts.renderer.write(`${res.message}
22312
23053
  `);
22313
23054
  } catch (err) {
22314
- opts.renderer.writeError(err instanceof Error ? err.message : String(err));
23055
+ opts.renderer.writeError(toErrorMessage(err));
22315
23056
  }
22316
23057
  continue;
22317
23058
  }
@@ -22400,7 +23141,7 @@ ${color.dim(taskList2)}
22400
23141
  }
22401
23142
  }
22402
23143
  } catch (err) {
22403
- opts.renderer.writeError(err instanceof Error ? err.message : String(err));
23144
+ opts.renderer.writeError(toErrorMessage(err));
22404
23145
  }
22405
23146
  continue;
22406
23147
  }
@@ -22589,7 +23330,7 @@ ${color.dim(
22589
23330
  }
22590
23331
  } catch (err) {
22591
23332
  opts.renderer.writeError(
22592
- `[autonomy] ${err instanceof Error ? err.message : String(err)}`
23333
+ `[autonomy] ${toErrorMessage(err)}`
22593
23334
  );
22594
23335
  } finally {
22595
23336
  activeCtrl = void 0;
@@ -22660,7 +23401,7 @@ ${color.dim(lines)}
22660
23401
  }
22661
23402
  }
22662
23403
  } catch (err) {
22663
- opts.renderer.writeError(err instanceof Error ? err.message : String(err));
23404
+ opts.renderer.writeError(toErrorMessage(err));
22664
23405
  } finally {
22665
23406
  activeCtrl = void 0;
22666
23407
  }
@@ -22687,7 +23428,7 @@ async function pasteClipboardImage(builder, opts) {
22687
23428
  `));
22688
23429
  } catch (err) {
22689
23430
  opts.renderer.writeError(
22690
- `Clipboard image error: ${err instanceof Error ? err.message : String(err)}`
23431
+ `Clipboard image error: ${toErrorMessage(err)}`
22691
23432
  );
22692
23433
  }
22693
23434
  }
@@ -22725,7 +23466,7 @@ async function renderGoalBanner(opts) {
22725
23466
  await opts.slashRegistry.dispatch("/autonomy eternal", opts.agent.ctx);
22726
23467
  } catch (err) {
22727
23468
  opts.renderer.writeError(
22728
- `Auto-resume failed: ${err instanceof Error ? err.message : String(err)}`
23469
+ `Auto-resume failed: ${toErrorMessage(err)}`
22729
23470
  );
22730
23471
  }
22731
23472
  } else {
@@ -22808,12 +23549,12 @@ ${color.cyan("\u23F3 Auto")} ${color.dim("(Ctrl+C to cancel)")}
22808
23549
  let interval;
22809
23550
  let lastTickedSecond = sec + 1;
22810
23551
  let onAbort;
22811
- return new Promise((resolve10) => {
22812
- onAbort = () => resolve10(false);
23552
+ return new Promise((resolve11) => {
23553
+ onAbort = () => resolve11(false);
22813
23554
  signal.addEventListener("abort", onAbort, { once: true });
22814
23555
  interval = setInterval(() => {
22815
23556
  if (signal.aborted) {
22816
- resolve10(false);
23557
+ resolve11(false);
22817
23558
  return;
22818
23559
  }
22819
23560
  const elapsed = Date.now() - start;
@@ -22821,7 +23562,7 @@ ${color.cyan("\u23F3 Auto")} ${color.dim("(Ctrl+C to cancel)")}
22821
23562
  if (remaining <= 0) {
22822
23563
  opts.renderer.write(color.dim(` \u21B3 ${truncated}
22823
23564
  `));
22824
- resolve10(true);
23565
+ resolve11(true);
22825
23566
  return;
22826
23567
  }
22827
23568
  if (opts.onCountdownTick && remaining !== lastTickedSecond) {
@@ -22832,7 +23573,7 @@ ${color.cyan("\u23F3 Auto")} ${color.dim("(Ctrl+C to cancel)")}
22832
23573
  opts.renderer.write(
22833
23574
  color.yellow(" \u21B3 Countdown cancelled \u2014 switching to manual mode\n")
22834
23575
  );
22835
- resolve10(false);
23576
+ resolve11(false);
22836
23577
  return;
22837
23578
  }
22838
23579
  } catch {
@@ -22937,6 +23678,7 @@ async function execute(deps) {
22937
23678
  effectiveMaxContext,
22938
23679
  queueStore,
22939
23680
  context,
23681
+ mailbox,
22940
23682
  stats,
22941
23683
  detachTodosCheckpoint,
22942
23684
  savedProviderCfg,
@@ -22978,6 +23720,23 @@ async function execute(deps) {
22978
23720
  restoredToolCalls,
22979
23721
  needsSetup
22980
23722
  } = deps;
23723
+ const rootTraceId = context.traceId;
23724
+ const storageLog = (event, payload) => {
23725
+ const traceId = payload.traceId ?? rootTraceId;
23726
+ console.warn(JSON.stringify({
23727
+ level: "info",
23728
+ event,
23729
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
23730
+ traceId,
23731
+ ...payload
23732
+ }));
23733
+ };
23734
+ const onStorageRead = (...args) => storageLog("storage.read", args[0]);
23735
+ const onStorageWrite = (...args) => storageLog("storage.write", args[0]);
23736
+ const onStorageError = (...args) => storageLog("storage.error", args[0]);
23737
+ events.on("storage.read", onStorageRead);
23738
+ events.on("storage.write", onStorageWrite);
23739
+ events.on("storage.error", onStorageError);
22981
23740
  let pendingChimeraWork;
22982
23741
  events.onPattern("chimera.review_needed", (_event, payload) => {
22983
23742
  const p = payload;
@@ -23009,8 +23768,8 @@ async function execute(deps) {
23009
23768
  timeoutMs: 3e5
23010
23769
  };
23011
23770
  const subagentId = await dir.spawn(cfg);
23012
- const { randomUUID: randomUUID6 } = await import('crypto');
23013
- const taskId = randomUUID6();
23771
+ const { randomUUID: randomUUID7 } = await import('crypto');
23772
+ const taskId = randomUUID7();
23014
23773
  await dir.assign({
23015
23774
  id: taskId,
23016
23775
  description: taskDesc,
@@ -23223,6 +23982,76 @@ async function execute(deps) {
23223
23982
  };
23224
23983
  const PROJECT_SWITCH_EXIT_CODE = 42;
23225
23984
  let pendingProjectSwitch = null;
23985
+ const coordinatorEvents = /* @__PURE__ */ new Set();
23986
+ let autonomousCoordinator = null;
23987
+ const onDirectorReady = (dir) => {
23988
+ if (autonomousCoordinator) return;
23989
+ const transcript = context.session.transcriptPath;
23990
+ const sessionDir = transcript ? path39.dirname(transcript) : wpaths.projectDir;
23991
+ const llmProvider = {
23992
+ decide: async (prompt) => {
23993
+ const sysPrompt = [
23994
+ {
23995
+ type: "text",
23996
+ text: 'You are the autonomous brain of a multi-agent coordination system. Pick the best option for the decision described and reply with JSON: {"optionId":"<id>","rationale":"<short why>"}.'
23997
+ }
23998
+ ];
23999
+ const userPrompt = {
24000
+ type: "text",
24001
+ text: `Decision: ${prompt.question}
24002
+
24003
+ Context: ${prompt.context}
24004
+
24005
+ Options:
24006
+ ${prompt.options.map((o, i) => ` ${i + 1}. [${o.id}] ${o.label}${o.consequence ? ` \u2014 ${o.consequence}` : ""}`).join("\n")}
24007
+
24008
+ Risk: ${prompt.risk}
24009
+
24010
+ Reply with ONLY the JSON object.`
24011
+ };
24012
+ const resp = await context.provider.complete(
24013
+ {
24014
+ model: context.model,
24015
+ system: sysPrompt,
24016
+ messages: [
24017
+ {
24018
+ role: "user",
24019
+ content: [userPrompt]
24020
+ }
24021
+ ],
24022
+ maxTokens: 1024,
24023
+ temperature: 0
24024
+ },
24025
+ { signal: context.signal }
24026
+ );
24027
+ const text = resp.content.filter((b) => b.type === "text").map((b) => b.text).join("\n").trim();
24028
+ const cleaned = text.replace(/^```(?:json)?\s*/i, "").replace(/```$/, "").trim();
24029
+ try {
24030
+ const parsed = JSON.parse(cleaned);
24031
+ const optId = parsed.optionId ?? prompt.options[0]?.id ?? "";
24032
+ return { optionId: optId, rationale: parsed.rationale ?? "" };
24033
+ } catch {
24034
+ return { optionId: prompt.options[0]?.id ?? "", rationale: text };
24035
+ }
24036
+ }
24037
+ };
24038
+ autonomousCoordinator = new AutonomousCoordinator({
24039
+ sessionDir,
24040
+ fleet: dir.fleet,
24041
+ mailbox,
24042
+ selfAgentId: `leader@${context.session.id ?? "unknown"}`,
24043
+ selfAgentName: "Leader",
24044
+ llmProvider
24045
+ });
24046
+ };
24047
+ if (director) onDirectorReady(director);
24048
+ const offDirectorSpawned = events.onPattern("subagent.spawned", () => {
24049
+ const dir = director;
24050
+ if (dir) {
24051
+ offDirectorSpawned();
24052
+ onDirectorReady(dir);
24053
+ }
24054
+ });
23226
24055
  try {
23227
24056
  code = await runTui({
23228
24057
  agent,
@@ -23307,6 +24136,7 @@ async function execute(deps) {
23307
24136
  featureSkills: cfg.features?.skills !== false,
23308
24137
  featureModelsRegistry: cfg.features?.modelsRegistry !== false,
23309
24138
  featureTokenSaving: cfg.features?.tokenSavingMode ?? false,
24139
+ allowOutsideProjectRoot: cfg.features?.allowOutsideProjectRoot ?? true,
23310
24140
  contextAutoCompact: cfg.context?.autoCompact !== false,
23311
24141
  contextStrategy: cfg.context?.strategy ?? "hybrid",
23312
24142
  logLevel: cfg.log?.level ?? "info",
@@ -23345,9 +24175,11 @@ async function execute(deps) {
23345
24175
  if (s.enhanceEnabled !== void 0) a["enhance"] = s.enhanceEnabled;
23346
24176
  if (s.enhanceLanguage !== void 0) a["enhanceLanguage"] = s.enhanceLanguage;
23347
24177
  if (s.autonomyNextPrompt !== void 0) a["autonomyNextPrompt"] = s.autonomyNextPrompt;
24178
+ if (s.autoProceedMaxIterations !== void 0)
24179
+ a["autoProceedMaxIterations"] = s.autoProceedMaxIterations;
23348
24180
  }
23349
24181
  );
23350
- if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 || s.logLevel !== void 0 || s.auditLevel !== void 0 || s.indexOnStart !== void 0 || s.maxIterations !== void 0 || s.nextPrediction !== void 0 || s.debugStream !== void 0 || s.configScope !== void 0 || s.enhanceDelayMs !== void 0) {
24182
+ if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.featureTokenSaving !== void 0 || s.allowOutsideProjectRoot !== void 0 || s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 || s.logLevel !== void 0 || s.auditLevel !== void 0 || s.indexOnStart !== void 0 || s.maxIterations !== void 0 || s.nextPrediction !== void 0 || s.debugStream !== void 0 || s.configScope !== void 0 || s.enhanceDelayMs !== void 0) {
23351
24183
  const configScope = s.configScope ?? configStore.get().configScope ?? "global";
23352
24184
  const targetPath = configScope === "project" && wpaths.inProjectConfig ? wpaths.inProjectConfig : wpaths.globalConfig;
23353
24185
  let raw;
@@ -23364,7 +24196,7 @@ async function execute(deps) {
23364
24196
  if (s.nextPrediction !== void 0) {
23365
24197
  decrypted.nextPrediction = s.nextPrediction;
23366
24198
  }
23367
- if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.featureTokenSaving !== void 0) {
24199
+ if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.featureTokenSaving !== void 0 || s.allowOutsideProjectRoot !== void 0) {
23368
24200
  const feats = decrypted.features ?? {};
23369
24201
  if (s.featureMcp !== void 0) feats.mcp = s.featureMcp;
23370
24202
  if (s.featurePlugins !== void 0) feats.plugins = s.featurePlugins;
@@ -23374,6 +24206,8 @@ async function execute(deps) {
23374
24206
  feats.modelsRegistry = s.featureModelsRegistry;
23375
24207
  if (s.featureTokenSaving !== void 0)
23376
24208
  feats.tokenSavingMode = s.featureTokenSaving;
24209
+ if (s.allowOutsideProjectRoot !== void 0)
24210
+ feats.allowOutsideProjectRoot = s.allowOutsideProjectRoot;
23377
24211
  decrypted.features = feats;
23378
24212
  }
23379
24213
  if (s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0) {
@@ -23423,7 +24257,7 @@ async function execute(deps) {
23423
24257
  const toWrite = targetPath === wpaths.globalConfig ? decrypted : filterSafeForProject(decrypted);
23424
24258
  const encrypted = encryptConfigSecrets(toWrite, noOpVault);
23425
24259
  if (targetPath !== wpaths.globalConfig) {
23426
- await fsp5.mkdir(path38.dirname(targetPath), { recursive: true });
24260
+ await fsp5.mkdir(path39.dirname(targetPath), { recursive: true });
23427
24261
  }
23428
24262
  await atomicWrite(targetPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
23429
24263
  configStore.update({
@@ -23459,6 +24293,7 @@ async function execute(deps) {
23459
24293
  if (s.streamFleet !== void 0) {
23460
24294
  fleetStreamController?.setEnabled(s.streamFleet);
23461
24295
  }
24296
+ deps.applyLiveSettings?.(s);
23462
24297
  return null;
23463
24298
  } catch (err) {
23464
24299
  const message = err instanceof Error ? err.message : String(err);
@@ -23483,6 +24318,17 @@ async function execute(deps) {
23483
24318
  confirmExit: config.autonomy?.["confirmExit"] ?? true,
23484
24319
  director,
23485
24320
  fleetRoster,
24321
+ // ── AutonomousCoordinator: project-level multi-session coordination ─────────
24322
+ // The coordinator tracks goals, tasks, knowledge, and consensus across all
24323
+ // active sessions in the same project. It runs independently of the leader
24324
+ // agent and is accessible to any session in the project via the GlobalMailbox.
24325
+ getAutonomousCoordinator: () => autonomousCoordinator,
24326
+ subscribeCoordinatorEvents: (fn) => {
24327
+ coordinatorEvents.add(fn);
24328
+ return () => {
24329
+ coordinatorEvents.delete(fn);
24330
+ };
24331
+ },
23486
24332
  // /clear: signal the TUI to wipe entries and reset fleet/leader stats
23487
24333
  // AND bump the context chip version — so the display reflects a
23488
24334
  // completely fresh session after the backend has been cleared.
@@ -23500,7 +24346,7 @@ async function execute(deps) {
23500
24346
  agentsMonitorController,
23501
24347
  getLiveSessions: async () => {
23502
24348
  const { SessionRegistry } = await import('@wrongstack/core');
23503
- const globalRoot = path38.dirname(wpaths.globalConfig);
24349
+ const globalRoot = path39.dirname(wpaths.globalConfig);
23504
24350
  const registry = new SessionRegistry(globalRoot);
23505
24351
  const sessions = await registry.list();
23506
24352
  return sessions.filter((s) => s.status !== "stale").map((s) => ({
@@ -23629,7 +24475,7 @@ async function execute(deps) {
23629
24475
  if (!sessionStore) return null;
23630
24476
  try {
23631
24477
  const { SessionRegistry } = await import('@wrongstack/core');
23632
- const registry = new SessionRegistry(path38.dirname(wpaths.globalConfig));
24478
+ const registry = new SessionRegistry(path39.dirname(wpaths.globalConfig));
23633
24479
  const live = (await registry.list()).find(
23634
24480
  (s) => s.sessionId === sessionId && s.status !== "stale" && s.pid !== process.pid
23635
24481
  );
@@ -23767,7 +24613,7 @@ async function execute(deps) {
23767
24613
  if (slug === "new-session") {
23768
24614
  pendingProjectSwitch = {
23769
24615
  root: projectRoot,
23770
- name: path38.basename(projectRoot) || projectRoot
24616
+ name: path39.basename(projectRoot) || projectRoot
23771
24617
  };
23772
24618
  }
23773
24619
  return;
@@ -23828,13 +24674,13 @@ ${parts.join("\n")}
23828
24674
  const { root, name, resumeSessionId } = pendingProjectSwitch;
23829
24675
  process.stdout.write("\x1B[2J\x1B[H");
23830
24676
  const { spawn: spawn6 } = await import('child_process');
23831
- const { createRequire: createRequire7 } = await import('module');
24677
+ const { createRequire: createRequire8 } = await import('module');
23832
24678
  let cliPath;
23833
24679
  try {
23834
- const req2 = createRequire7(import.meta.url);
24680
+ const req2 = createRequire8(import.meta.url);
23835
24681
  const pkgPath = req2.resolve("@wrongstack/cli/package.json");
23836
- const pkgDir = path38.dirname(pkgPath);
23837
- cliPath = path38.join(pkgDir, "dist", "index.js");
24682
+ const pkgDir = path39.dirname(pkgPath);
24683
+ cliPath = path39.join(pkgDir, "dist", "index.js");
23838
24684
  await fsp5.access(cliPath);
23839
24685
  } catch {
23840
24686
  cliPath = process.argv[1] ?? "";
@@ -23866,6 +24712,7 @@ ${parts.join("\n")}
23866
24712
  }
23867
24713
  } finally {
23868
24714
  renderer.setSilent(false);
24715
+ offDirectorSpawned();
23869
24716
  }
23870
24717
  } else if (flags.webui) {
23871
24718
  agent.disableInteractiveConfirmation();
@@ -23908,12 +24755,12 @@ ${parts.join("\n")}
23908
24755
  )
23909
24756
  );
23910
24757
  renderer.writeInfo(color.dim(" Press Ctrl+C in this terminal to stop the WebUI server.\n"));
23911
- const webuiExit = new Promise((resolve10) => {
24758
+ const webuiExit = new Promise((resolve11) => {
23912
24759
  const onSigint = () => {
23913
24760
  renderer.setSilent(false);
23914
24761
  renderer.write("\n");
23915
24762
  renderer.writeInfo(color.yellow(" Shutting down WebUI server\u2026"));
23916
- resolve10(0);
24763
+ resolve11(0);
23917
24764
  };
23918
24765
  process.on("SIGINT", onSigint);
23919
24766
  process.on("SIGTERM", onSigint);
@@ -23921,13 +24768,13 @@ ${parts.join("\n")}
23921
24768
  renderer.setSilent(false);
23922
24769
  process.off("SIGINT", onSigint);
23923
24770
  process.off("SIGTERM", onSigint);
23924
- resolve10(0);
24771
+ resolve11(0);
23925
24772
  }).catch((err) => {
23926
24773
  renderer.setSilent(false);
23927
24774
  process.off("SIGINT", onSigint);
23928
24775
  process.off("SIGTERM", onSigint);
23929
24776
  console.debug(`[execution] webui error: ${err}`);
23930
- resolve10(1);
24777
+ resolve11(1);
23931
24778
  });
23932
24779
  });
23933
24780
  code = await webuiExit;
@@ -23942,7 +24789,7 @@ ${parts.join("\n")}
23942
24789
  supportsVision,
23943
24790
  attachments,
23944
24791
  effectiveMaxContext,
23945
- projectName: path38.basename(projectRoot) || void 0,
24792
+ projectName: path39.basename(projectRoot) || void 0,
23946
24793
  getAutonomy,
23947
24794
  onAutonomy,
23948
24795
  getNextPredict,
@@ -24155,7 +25002,8 @@ var MultiAgentHost = class _MultiAgentHost {
24155
25002
  if (this.opts.sessionsRoot && !this.sessionFactory) {
24156
25003
  this.sessionFactory = makeDirectorSessionFactory({
24157
25004
  sessionsRoot: this.opts.sessionsRoot,
24158
- directorRunId: this.opts.directorRunId
25005
+ directorRunId: this.opts.directorRunId,
25006
+ traceId: this.opts.traceId
24159
25007
  });
24160
25008
  }
24161
25009
  const coordinatorConfig = {
@@ -24163,7 +25011,7 @@ var MultiAgentHost = class _MultiAgentHost {
24163
25011
  doneCondition: { type: "all_tasks_done" },
24164
25012
  maxConcurrent: this.opts.maxConcurrent ?? 4
24165
25013
  };
24166
- const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ? path38.join(this.opts.sessionsRoot, this.opts.directorRunId, "shared") : void 0);
25014
+ const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ? path39.join(this.opts.sessionsRoot, this.opts.directorRunId, "shared") : void 0);
24167
25015
  this.director = new Director({
24168
25016
  config: coordinatorConfig,
24169
25017
  manifestPath: this.opts.manifestPath,
@@ -24720,16 +25568,16 @@ var MultiAgentHost = class _MultiAgentHost {
24720
25568
  if (this.director) return this.director;
24721
25569
  this.opts.directorMode = true;
24722
25570
  if (this.opts.fleetRoot && !this.opts.manifestPath) {
24723
- this.opts.manifestPath = path38.join(this.opts.fleetRoot, "fleet.json");
25571
+ this.opts.manifestPath = path39.join(this.opts.fleetRoot, "fleet.json");
24724
25572
  }
24725
25573
  if (this.opts.fleetRoot && !this.opts.sharedScratchpadPath) {
24726
- this.opts.sharedScratchpadPath = path38.join(this.opts.fleetRoot, "shared");
25574
+ this.opts.sharedScratchpadPath = path39.join(this.opts.fleetRoot, "shared");
24727
25575
  }
24728
25576
  if (this.opts.fleetRoot && !this.opts.sessionsRoot) {
24729
- this.opts.sessionsRoot = path38.join(this.opts.fleetRoot, "subagents");
25577
+ this.opts.sessionsRoot = path39.join(this.opts.fleetRoot, "subagents");
24730
25578
  }
24731
25579
  if (this.opts.fleetRoot && !this.opts.stateCheckpointPath) {
24732
- this.opts.stateCheckpointPath = path38.join(this.opts.fleetRoot, "director-state.json");
25580
+ this.opts.stateCheckpointPath = path39.join(this.opts.fleetRoot, "director-state.json");
24733
25581
  }
24734
25582
  await this.ensureDirector();
24735
25583
  return this.director ?? null;
@@ -24858,11 +25706,11 @@ var SessionStats = class {
24858
25706
  if (tool.name === "bash") this.bashCommands++;
24859
25707
  else if (tool.name === "fetch") this.fetches++;
24860
25708
  if (!tool.ok) return;
24861
- const path39 = typeof input?.path === "string" ? input.path : void 0;
24862
- if (tool.name === "read" && path39) this.readPaths.add(path39);
24863
- else if (tool.name === "edit" && path39) this.editedPaths.add(path39);
24864
- else if (tool.name === "write" && path39) {
24865
- this.writtenPaths.add(path39);
25709
+ const path40 = typeof input?.path === "string" ? input.path : void 0;
25710
+ if (tool.name === "read" && path40) this.readPaths.add(path40);
25711
+ else if (tool.name === "edit" && path40) this.editedPaths.add(path40);
25712
+ else if (tool.name === "write" && path40) {
25713
+ this.writtenPaths.add(path40);
24866
25714
  const content = typeof input?.content === "string" ? input.content : "";
24867
25715
  this.bytesWritten += Buffer.byteLength(content, "utf8");
24868
25716
  }
@@ -25174,7 +26022,7 @@ async function setupCodebaseIndexing(deps) {
25174
26022
  if (tool?.mutating && FILE_EDIT_TOOLS.has(tool.name)) {
25175
26023
  const fp = payload.toolUse.input?.file_path;
25176
26024
  if (typeof fp === "string" && fp.length > 0) {
25177
- const abs = path38.resolve(payload.ctx.cwd, fp);
26025
+ const abs = path39.resolve(payload.ctx.cwd, fp);
25178
26026
  if (isIndexableFile(abs)) {
25179
26027
  enqueueReindex({ projectRoot, files: [abs], debounceMs, onError });
25180
26028
  }
@@ -25193,7 +26041,7 @@ async function setupCodebaseIndexing(deps) {
25193
26041
  if (!filename) return;
25194
26042
  const rel = filename.toString();
25195
26043
  if (isIgnored(rel)) return;
25196
- const abs = path38.resolve(projectRoot, rel);
26044
+ const abs = path39.resolve(projectRoot, rel);
25197
26045
  if (!isIndexableFile(abs)) return;
25198
26046
  enqueueReindex({ projectRoot, files: [abs], debounceMs, onError });
25199
26047
  });
@@ -25247,7 +26095,7 @@ function setupMetrics(params) {
25247
26095
  const dumpMetrics = () => {
25248
26096
  if (!metricsSink) return;
25249
26097
  try {
25250
- const out = path38.join(wpaths.projectSessions, "metrics.json");
26098
+ const out = path39.join(wpaths.projectSessions, "metrics.json");
25251
26099
  const snap = metricsSink.snapshot();
25252
26100
  writeFileSync(out, JSON.stringify(snap, null, 2));
25253
26101
  } catch {
@@ -25271,7 +26119,7 @@ function setupMetrics(params) {
25271
26119
  });
25272
26120
  } catch (err) {
25273
26121
  logger.warn(
25274
- `metrics endpoint failed to start: ${err instanceof Error ? err.message : String(err)}`
26122
+ `metrics endpoint failed to start: ${toErrorMessage(err)}`
25275
26123
  );
25276
26124
  }
25277
26125
  }
@@ -25327,9 +26175,11 @@ async function setupCompaction(params) {
25327
26175
  context.meta["contextWindowMode"] = initialPolicy.id;
25328
26176
  context.meta["contextWindowPolicy"] = initialPolicy;
25329
26177
  let autoCompactor;
25330
- if (config.context.autoCompact !== false && effectiveMaxContext > 0) {
26178
+ let resolvedBridge;
26179
+ if (effectiveMaxContext > 0) {
25331
26180
  const auditLevel = resolveAuditLevel(fullConfig ?? config);
25332
26181
  const sessionBridge = providedBridge ?? createSessionEventBridge(sessionWriter, auditLevel);
26182
+ resolvedBridge = sessionBridge;
25333
26183
  autoCompactor = new AutoCompactionMiddleware(
25334
26184
  compactor,
25335
26185
  effectiveMaxContext,
@@ -25353,9 +26203,10 @@ async function setupCompaction(params) {
25353
26203
  sessionBridge
25354
26204
  }
25355
26205
  );
26206
+ autoCompactor.setEnabled(config.context.autoCompact !== false);
25356
26207
  pipelines.contextWindow.use({ name: "AutoCompaction", handler: autoCompactor.handler() });
25357
26208
  }
25358
- return { effectiveMaxContext, autoCompactor };
26209
+ return { effectiveMaxContext, autoCompactor, sessionBridge: resolvedBridge };
25359
26210
  }
25360
26211
  function createAgent(params) {
25361
26212
  const secretScrubber = params.container.resolve(TOKENS.SecretScrubber);
@@ -25425,7 +26276,9 @@ async function setupSession(params) {
25425
26276
  tokenCounter,
25426
26277
  renderer,
25427
26278
  flags,
25428
- onRecovery
26279
+ onRecovery,
26280
+ // Optional EventBus for storage observability
26281
+ events: eventsBus
25429
26282
  } = params;
25430
26283
  sessionStore.prune(DEFAULT_SESSION_PRUNE_DAYS).then((count) => {
25431
26284
  if (count > 0) renderer.writeInfo(`Pruned ${count} old session${count === 1 ? "" : "s"}.`);
@@ -25459,7 +26312,7 @@ async function setupSession(params) {
25459
26312
  `Resumed session ${resumed.data.metadata.id} \u2014 ${restoredMessages.length} messages, ${restoredToolCalls.length} tool executions, ${resumed.data.usage.input + resumed.data.usage.output} tokens used previously.`
25460
26313
  );
25461
26314
  } catch (err) {
25462
- renderer.writeError(`Resume failed: ${err instanceof Error ? err.message : String(err)}`);
26315
+ renderer.writeError(`Resume failed: ${toErrorMessage(err)}`);
25463
26316
  throw Object.assign(new Error("RESUME_FAILED"), { exitCode: 2 });
25464
26317
  }
25465
26318
  } else {
@@ -25482,10 +26335,10 @@ async function setupSession(params) {
25482
26335
  );
25483
26336
  });
25484
26337
  const attachments = new DefaultAttachmentStore({
25485
- spoolDir: path38.join(wpaths.projectSessions, session?.id, "attachments")
26338
+ spoolDir: path39.join(wpaths.projectSessions, session?.id, "attachments")
25486
26339
  });
25487
- const queueStore = new QueueStore({ dir: path38.join(wpaths.projectSessions, session?.id) });
25488
26340
  const ctxSignal = new AbortController().signal;
26341
+ const traceId = randomBytes(16).toString("hex");
25489
26342
  const context = new Context({
25490
26343
  systemPrompt,
25491
26344
  provider,
@@ -25496,17 +26349,28 @@ async function setupSession(params) {
25496
26349
  projectRoot,
25497
26350
  model: config.model,
25498
26351
  agentId: "leader",
25499
- agentName: "Leader Agent"
26352
+ agentName: "Leader Agent",
26353
+ traceId,
26354
+ allowOutsideProjectRoot: config.features?.allowOutsideProjectRoot ?? true
25500
26355
  });
25501
26356
  context.meta["packageTrackerOpts"] = {
25502
26357
  storageDir: wpaths.projectDir,
25503
26358
  projectRoot
25504
26359
  };
25505
26360
  if (restoredMessages.length > 0) context.state.replaceMessages(restoredMessages);
25506
- const todosCheckpointPath = path38.join(wpaths.projectSessions, `${session?.id}.todos.json`);
26361
+ const queueStore = new QueueStore({
26362
+ dir: path39.join(wpaths.projectSessions, session?.id),
26363
+ ...eventsBus ? { events: eventsBus } : {},
26364
+ ...traceId ? { traceId } : {}
26365
+ });
26366
+ const todosCheckpointPath = path39.join(wpaths.projectSessions, `${session?.id}.todos.json`);
25507
26367
  if (resumeId) {
25508
26368
  try {
25509
- const restoredTodos = await loadTodosCheckpoint(todosCheckpointPath);
26369
+ const restoredTodos = await loadTodosCheckpoint(
26370
+ todosCheckpointPath,
26371
+ eventsBus,
26372
+ traceId
26373
+ );
25510
26374
  if (restoredTodos && restoredTodos.length > 0) {
25511
26375
  context.state.replaceTodos(restoredTodos);
25512
26376
  renderer.writeInfo(
@@ -25519,17 +26383,19 @@ async function setupSession(params) {
25519
26383
  const detachTodosCheckpoint = attachTodosCheckpoint(
25520
26384
  context.state,
25521
26385
  todosCheckpointPath,
25522
- session?.id
26386
+ session?.id,
26387
+ eventsBus,
26388
+ traceId
25523
26389
  );
25524
- const planPath = path38.join(wpaths.projectSessions, `${session?.id}.plan.json`);
26390
+ const planPath = path39.join(wpaths.projectSessions, `${session?.id}.plan.json`);
25525
26391
  context.state.setMeta("plan.path", planPath);
25526
- const taskPath = path38.join(wpaths.projectSessions, `${session?.id}.tasks.json`);
26392
+ const taskPath = path39.join(wpaths.projectSessions, `${session?.id}.tasks.json`);
25527
26393
  context.state.setMeta("task.path", taskPath);
25528
26394
  let dirState;
25529
26395
  if (resumeId) {
25530
26396
  try {
25531
- const fleetRoot = path38.join(wpaths.projectSessions, session?.id);
25532
- dirState = await loadDirectorState(path38.join(fleetRoot, "director-state.json"));
26397
+ const fleetRoot = path39.join(wpaths.projectSessions, session?.id);
26398
+ dirState = await loadDirectorState(path39.join(fleetRoot, "director-state.json"));
25533
26399
  if (dirState) {
25534
26400
  const tCounts = {};
25535
26401
  for (const t of dirState.tasks) tCounts[t.status] = (tCounts[t.status] ?? 0) + 1;
@@ -25555,6 +26421,7 @@ async function setupSession(params) {
25555
26421
  return {
25556
26422
  session: expectDefined(session),
25557
26423
  sessionRef,
26424
+ traceId,
25558
26425
  context,
25559
26426
  restoredMessages,
25560
26427
  attachments,
@@ -25775,7 +26642,7 @@ async function main(argv) {
25775
26642
  projectGoal: wpaths.projectGoal,
25776
26643
  projectSessions: wpaths.projectSessions
25777
26644
  },
25778
- pathJoiner: { join: (a, b) => path38.join(a, b) },
26645
+ pathJoiner: { join: (a, b) => path39.join(a, b) },
25779
26646
  systemPromptBuilderToken: TOKENS.SystemPromptBuilder
25780
26647
  });
25781
26648
  const toolRegistry = new ToolRegistry();
@@ -25804,7 +26671,10 @@ async function main(argv) {
25804
26671
  const evOn = (event, handler) => {
25805
26672
  events.on(event, handler);
25806
26673
  teardownHandlers.push(
25807
- () => events.off(event, handler)
26674
+ () => (
26675
+ // biome-ignore lint/suspicious/noExplicitAny: dynamic event dispatcher signature
26676
+ events.off(event, handler)
26677
+ )
25808
26678
  );
25809
26679
  };
25810
26680
  evOn("provider.response", (e) => {
@@ -25895,6 +26765,7 @@ async function main(argv) {
25895
26765
  tokenCounter,
25896
26766
  renderer,
25897
26767
  flags,
26768
+ events,
25898
26769
  onRecovery: (abandoned, autoRecover) => promptRecovery(reader, renderer, abandoned, autoRecover)
25899
26770
  });
25900
26771
  const session = sessResult.session;
@@ -25906,12 +26777,13 @@ async function main(argv) {
25906
26777
  const planPath = sessResult.planPath;
25907
26778
  const detachTodosCheckpoint = sessResult.detachTodosCheckpoint;
25908
26779
  const priorFleetState = sessResult.priorFleetState;
26780
+ memoryStore.withTraceId(sessResult.traceId);
25909
26781
  let tracker;
25910
26782
  try {
25911
26783
  const { getSessionRegistry, AgentStatusTracker } = await import('@wrongstack/core');
25912
26784
  const registry = getSessionRegistry(wpaths.globalRoot);
25913
- const projectSlug2 = path38.basename(wpaths.projectDir);
25914
- const projectName = path38.basename(projectRoot);
26785
+ const projectSlug2 = path39.basename(wpaths.projectDir);
26786
+ const projectName = path39.basename(projectRoot);
25915
26787
  let gitBranch;
25916
26788
  try {
25917
26789
  const { execSync } = await import('child_process');
@@ -26010,7 +26882,7 @@ async function main(argv) {
26010
26882
  if (e.ok && (e.name === "write" || e.name === "edit" || e.name === "replace" || e.name === "patch")) {
26011
26883
  const filePath = e.input?.path;
26012
26884
  if (filePath) {
26013
- const projectDir = path38.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
26885
+ const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
26014
26886
  void recordFileAction(
26015
26887
  { storageDir: projectDir, projectRoot },
26016
26888
  {
@@ -26176,7 +27048,7 @@ async function main(argv) {
26176
27048
  let depWatcherDispose;
26177
27049
  if (dwCfg?.["enabled"] === true) {
26178
27050
  try {
26179
- const projectDir = path38.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
27051
+ const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
26180
27052
  const dwMailbox = new GlobalMailbox(projectDir, events);
26181
27053
  depWatcherDispose = attachDepWatcherBridge({
26182
27054
  events,
@@ -26275,12 +27147,12 @@ async function main(argv) {
26275
27147
  }
26276
27148
  }
26277
27149
  };
26278
- const fleetRoot = directorMode ? path38.join(wpaths.projectSessions, session.id) : void 0;
26279
- const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path38.join(expectDefined(fleetRoot), "fleet.json") : void 0;
26280
- const sharedScratchpadPath = directorMode ? path38.join(expectDefined(fleetRoot), "shared") : void 0;
26281
- const subagentSessionsRoot = directorMode ? path38.join(expectDefined(fleetRoot), "subagents") : void 0;
26282
- const stateCheckpointPath = directorMode ? path38.join(expectDefined(fleetRoot), "director-state.json") : void 0;
26283
- const fleetRootForPromotion = path38.join(wpaths.projectSessions, session.id);
27150
+ const fleetRoot = directorMode ? path39.join(wpaths.projectSessions, session.id) : void 0;
27151
+ const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path39.join(expectDefined(fleetRoot), "fleet.json") : void 0;
27152
+ const sharedScratchpadPath = directorMode ? path39.join(expectDefined(fleetRoot), "shared") : void 0;
27153
+ const subagentSessionsRoot = directorMode ? path39.join(expectDefined(fleetRoot), "subagents") : void 0;
27154
+ const stateCheckpointPath = directorMode ? path39.join(expectDefined(fleetRoot), "director-state.json") : void 0;
27155
+ const fleetRootForPromotion = path39.join(wpaths.projectSessions, session.id);
26284
27156
  const brainSettings = {
26285
27157
  maxAutoRisk: "medium"
26286
27158
  };
@@ -26385,7 +27257,8 @@ async function main(argv) {
26385
27257
  sessionWriter: session,
26386
27258
  maxConcurrent,
26387
27259
  getLeaderMaxContext: () => effectiveMaxContext,
26388
- brain
27260
+ brain,
27261
+ traceId: sessResult.traceId
26389
27262
  }
26390
27263
  );
26391
27264
  toolRegistry.register(
@@ -26422,7 +27295,7 @@ async function main(argv) {
26422
27295
  let techStackConsumerDispose;
26423
27296
  if (dwCfg?.["enabled"] === true) {
26424
27297
  try {
26425
- const projectDir = path38.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
27298
+ const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
26426
27299
  const tsMailbox = new GlobalMailbox(projectDir, events);
26427
27300
  const fileAuthorOpts = {
26428
27301
  storageDir: projectDir,
@@ -26455,7 +27328,7 @@ async function main(argv) {
26455
27328
  let pkgOutdatedDispose;
26456
27329
  if (dwCfg?.["enabled"] === true) {
26457
27330
  try {
26458
- const projectDir = path38.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
27331
+ const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
26459
27332
  const pkgMailbox = new GlobalMailbox(projectDir, events);
26460
27333
  const pkgTrackerOpts = {
26461
27334
  storageDir: projectDir,
@@ -26829,7 +27702,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
26829
27702
  return director.spawn(cfg);
26830
27703
  },
26831
27704
  onFleetLog: async (subagentId, mode) => {
26832
- const subagentsRoot = path38.join(fleetRootForPromotion, "subagents");
27705
+ const subagentsRoot = path39.join(fleetRootForPromotion, "subagents");
26833
27706
  let runDirs;
26834
27707
  try {
26835
27708
  runDirs = await fsp5.readdir(subagentsRoot);
@@ -26838,7 +27711,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
26838
27711
  }
26839
27712
  const found = [];
26840
27713
  for (const runId of runDirs) {
26841
- const runDir = path38.join(subagentsRoot, runId);
27714
+ const runDir = path39.join(subagentsRoot, runId);
26842
27715
  let files;
26843
27716
  try {
26844
27717
  files = await fsp5.readdir(runDir);
@@ -26847,7 +27720,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
26847
27720
  }
26848
27721
  for (const f of files) {
26849
27722
  if (!f.endsWith(".jsonl")) continue;
26850
- const full = path38.join(runDir, f);
27723
+ const full = path39.join(runDir, f);
26851
27724
  try {
26852
27725
  const stat7 = await fsp5.stat(full);
26853
27726
  found.push({
@@ -26948,7 +27821,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
26948
27821
  }
26949
27822
  const dir = await multiAgentHost.ensureDirector();
26950
27823
  if (!dir) return "Director is not available.";
26951
- const dirStatePath = path38.join(fleetRootForPromotion, "director-state.json");
27824
+ const dirStatePath = path39.join(fleetRootForPromotion, "director-state.json");
26952
27825
  const prior = await loadDirectorState(dirStatePath);
26953
27826
  if (!prior) {
26954
27827
  return "No prior director-state.json found \u2014 nothing to retry.";
@@ -27017,9 +27890,9 @@ ${color.dim("\u2500".repeat(40))}` : "";
27017
27890
  for (const tool of director2.tools(FLEET_ROSTER)) {
27018
27891
  toolRegistry.register(tool);
27019
27892
  }
27020
- const mp = path38.join(fleetRootForPromotion, "fleet.json");
27021
- const sp = path38.join(fleetRootForPromotion, "shared");
27022
- const ss = path38.join(fleetRootForPromotion, "subagents");
27893
+ const mp = path39.join(fleetRootForPromotion, "fleet.json");
27894
+ const sp = path39.join(fleetRootForPromotion, "shared");
27895
+ const ss = path39.join(fleetRootForPromotion, "subagents");
27023
27896
  const lines = [
27024
27897
  `${color.green("\u2713")} Promoted to director mode.`,
27025
27898
  ` Roster: ${Object.keys(FLEET_ROSTER).join(", ")}`,
@@ -27123,7 +27996,8 @@ Restart WrongStack to load or unload plugin code in this session.`;
27123
27996
  // Real per-role factory: each dispatched slot runs as a fresh,
27124
27997
  // isolated agent with the role's filtered tools + persona prompt
27125
27998
  // (instead of sharing the leader agent's Context).
27126
- subagentFactory: multiAgentHost.makeSubagentFactory(config)
27999
+ subagentFactory: multiAgentHost.makeSubagentFactory(config),
28000
+ events
27127
28001
  };
27128
28002
  parallelEngine = new ParallelEternalEngine(parallelOptions);
27129
28003
  }
@@ -27137,7 +28011,8 @@ Restart WrongStack to load or unload plugin code in this session.`;
27137
28011
  maxContextTokens: effectiveMaxContext > 0 ? effectiveMaxContext : void 0,
27138
28012
  onIteration: broadcastEternalIteration,
27139
28013
  onStage: broadcastAutonomyStage,
27140
- brain
28014
+ brain,
28015
+ events
27141
28016
  });
27142
28017
  }
27143
28018
  void eternalEngine.prime();
@@ -27160,7 +28035,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
27160
28035
  onBeforeExit: async () => {
27161
28036
  const cwd2 = projectRoot;
27162
28037
  const statusResult = await new Promise(
27163
- (resolve10, reject) => {
28038
+ (resolve11, reject) => {
27164
28039
  const child = spawn("git", ["status", "--porcelain"], {
27165
28040
  cwd: cwd2,
27166
28041
  stdio: ["ignore", "pipe", "pipe"],
@@ -27172,7 +28047,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
27172
28047
  stdout += d;
27173
28048
  });
27174
28049
  child.on("error", reject);
27175
- child.on("close", (code) => resolve10({ stdout, code: code ?? 0 }));
28050
+ child.on("close", (code) => resolve11({ stdout, code: code ?? 0 }));
27176
28051
  }
27177
28052
  );
27178
28053
  if (statusResult.stdout.trim().length > 0) {
@@ -27318,6 +28193,10 @@ Restart WrongStack to load or unload plugin code in this session.`;
27318
28193
  tokenCounter,
27319
28194
  config,
27320
28195
  configStore,
28196
+ // Project-scoped mailbox — the AutonomousCoordinator in execution.ts
28197
+ // subscribes to it so goals/tasks/knowledge are shared with other
28198
+ // terminals working on the same project.
28199
+ mailbox: brainMailbox,
27321
28200
  renderer,
27322
28201
  reader,
27323
28202
  session,
@@ -27357,6 +28236,34 @@ Restart WrongStack to load or unload plugin code in this session.`;
27357
28236
  return autonomyMode;
27358
28237
  },
27359
28238
  getNextPredict: () => nextPredictEnabled,
28239
+ applyLiveSettings: (s) => {
28240
+ try {
28241
+ if (s.yolo !== void 0) {
28242
+ container.resolve(TOKENS.PermissionPolicy).setYolo?.(s.yolo);
28243
+ config = patchConfig(config, { yolo: s.yolo });
28244
+ }
28245
+ if (s.nextPrediction !== void 0) {
28246
+ nextPredictEnabled = s.nextPrediction;
28247
+ config = patchConfig(config, { nextPrediction: s.nextPrediction });
28248
+ }
28249
+ if (s.enhanceEnabled !== void 0) {
28250
+ enhanceController?.setEnabled(s.enhanceEnabled);
28251
+ }
28252
+ if (s.maxIterations !== void 0) {
28253
+ agent.maxIterations = s.maxIterations;
28254
+ }
28255
+ if (s.logLevel !== void 0) {
28256
+ container.resolve(TOKENS.Logger).level = s.logLevel;
28257
+ }
28258
+ if (s.auditLevel !== void 0) {
28259
+ sessionBridge.setAuditLevel(s.auditLevel);
28260
+ }
28261
+ if (s.contextAutoCompact !== void 0) {
28262
+ autoCompactor?.setEnabled(s.contextAutoCompact);
28263
+ }
28264
+ } catch {
28265
+ }
28266
+ },
27360
28267
  onSuggestionsParsed: (suggestions) => {
27361
28268
  currentSuggestions = suggestions ?? [];
27362
28269
  setSuggestions(suggestions ?? []);
@@ -27439,7 +28346,9 @@ Reply YES to auto-proceed, NO to wait for human input.`
27439
28346
  getBrainLog: () => brainLog,
27440
28347
  // Clean up SessionStats event listeners and all EventBus handlers when the REPL exits.
27441
28348
  onDestroy: () => {
27442
- teardownHandlers.forEach((fn) => fn());
28349
+ teardownHandlers.forEach((fn) => {
28350
+ fn();
28351
+ });
27443
28352
  stats.destroy(events);
27444
28353
  }
27445
28354
  });