@wrongstack/cli 0.257.0 → 0.260.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,11 +1,11 @@
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, 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';
@@ -25,6 +25,7 @@ import { parseNextSteps } from '@wrongstack/tui';
25
25
  import { ACP_AGENT_COMMANDS, makeACPSubagentRunner, makeACPSubagentRunnerWithStop } from '@wrongstack/acp';
26
26
  import { WrongStackACPServer } from '@wrongstack/acp/agent';
27
27
  import { ACP_AGENTS, SubagentBudget } from '@wrongstack/core/coordination';
28
+ import { loadBenchConfig, reportHeaderLine, readSummary, renderMarkdownReport, createPolyglotSuite, createSwebenchSuite, runBenchmark, writeJsonArtifacts, collectCellPredictions, writePredictionsJsonl, gradePolyglot, gradeSwebench } from '@wrongstack/bench';
28
29
  import { allServers } from '@wrongstack/core/infrastructure';
29
30
  import { ToolExecutor } from '@wrongstack/core/execution';
30
31
  import { createToolVisionAdapters } from '@wrongstack/runtime/vision';
@@ -70,10 +71,10 @@ async function detectPackageManager2(root, declared) {
70
71
  const name = declared.split("@")[0];
71
72
  if (name) return name;
72
73
  }
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";
74
+ if (await pathExists(path39.join(root, "pnpm-lock.yaml"))) return "pnpm";
75
+ if (await pathExists(path39.join(root, "bun.lockb"))) return "bun";
76
+ if (await pathExists(path39.join(root, "bun.lock"))) return "bun";
77
+ if (await pathExists(path39.join(root, "yarn.lock"))) return "yarn";
77
78
  return "npm";
78
79
  }
79
80
  function hasUsableScript(scripts, name) {
@@ -94,7 +95,7 @@ function parseMakeTargets(makefile) {
94
95
  async function detectProjectFacts(root) {
95
96
  const facts = { hints: [] };
96
97
  try {
97
- const pkg = JSON.parse(await fsp5.readFile(path38.join(root, "package.json"), "utf8"));
98
+ const pkg = JSON.parse(await fsp5.readFile(path39.join(root, "package.json"), "utf8"));
98
99
  const scripts = pkg.scripts ?? {};
99
100
  const pm = await detectPackageManager2(root, pkg.packageManager);
100
101
  if (hasUsableScript(scripts, "build")) facts.build = `${pm} run build`;
@@ -108,14 +109,14 @@ async function detectProjectFacts(root) {
108
109
  } catch {
109
110
  }
110
111
  try {
111
- if (!await pathExists(path38.join(root, "pyproject.toml"))) throw new Error("not python");
112
+ if (!await pathExists(path39.join(root, "pyproject.toml"))) throw new Error("not python");
112
113
  facts.test ??= "pytest";
113
114
  facts.lint ??= "ruff check .";
114
115
  facts.hints.push("pyproject.toml");
115
116
  } catch {
116
117
  }
117
118
  try {
118
- if (!await pathExists(path38.join(root, "go.mod"))) throw new Error("not go");
119
+ if (!await pathExists(path39.join(root, "go.mod"))) throw new Error("not go");
119
120
  facts.build ??= "go build ./...";
120
121
  facts.test ??= "go test ./...";
121
122
  facts.run ??= "go run .";
@@ -123,7 +124,7 @@ async function detectProjectFacts(root) {
123
124
  } catch {
124
125
  }
125
126
  try {
126
- if (!await pathExists(path38.join(root, "Cargo.toml"))) throw new Error("not rust");
127
+ if (!await pathExists(path39.join(root, "Cargo.toml"))) throw new Error("not rust");
127
128
  facts.build ??= "cargo build";
128
129
  facts.test ??= "cargo test";
129
130
  facts.lint ??= "cargo clippy";
@@ -132,7 +133,7 @@ async function detectProjectFacts(root) {
132
133
  } catch {
133
134
  }
134
135
  try {
135
- const makefile = await fsp5.readFile(path38.join(root, "Makefile"), "utf8");
136
+ const makefile = await fsp5.readFile(path39.join(root, "Makefile"), "utf8");
136
137
  const targets = parseMakeTargets(makefile);
137
138
  facts.build ??= targets.has("build") ? "make build" : "make";
138
139
  if (targets.has("test")) facts.test ??= "make test";
@@ -371,26 +372,26 @@ function fmtDuration(ms) {
371
372
  const remMin = m - h * 60;
372
373
  return `${h}h${remMin}m`;
373
374
  }
374
- function fmtTaskResultLine(r, color71) {
375
+ function fmtTaskResultLine(r, color72) {
375
376
  const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
376
377
  const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
377
378
  const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
378
379
  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)}` : "";
380
+ const errKindChip = errKind ? color72.dim(` [${errKind}]`) : "";
381
+ const errSnip = errMsg || errKind ? `${errKindChip}${color72.dim(errTail)}` : "";
381
382
  switch (r.status) {
382
383
  case "success":
383
- return { mark: color71.green("\u2713"), stats, tail: "" };
384
+ return { mark: color72.green("\u2713"), stats, tail: "" };
384
385
  case "timeout":
385
386
  return {
386
- mark: color71.yellow("\u23F1"),
387
- stats: `${color71.yellow("timeout")} ${stats}`,
387
+ mark: color72.yellow("\u23F1"),
388
+ stats: `${color72.yellow("timeout")} ${stats}`,
388
389
  tail: errSnip
389
390
  };
390
391
  case "stopped":
391
- return { mark: color71.dim("\u2298"), stats: `${color71.dim("stopped")} ${stats}`, tail: errSnip };
392
+ return { mark: color72.dim("\u2298"), stats: `${color72.dim("stopped")} ${stats}`, tail: errSnip };
392
393
  case "failed":
393
- return { mark: color71.red("\u2717"), stats: `${color71.red("failed")} ${stats}`, tail: errSnip };
394
+ return { mark: color72.red("\u2717"), stats: `${color72.red("failed")} ${stats}`, tail: errSnip };
394
395
  }
395
396
  }
396
397
  var init_utils = __esm({
@@ -675,7 +676,7 @@ async function findSpec(store, idOrTitle) {
675
676
  async function gatherProjectContext2(projectRoot) {
676
677
  const parts = [];
677
678
  try {
678
- const pkgPath = path38.join(projectRoot, "package.json");
679
+ const pkgPath = path39.join(projectRoot, "package.json");
679
680
  const pkgRaw = await fsp5.readFile(pkgPath, "utf8");
680
681
  const pkg = JSON.parse(pkgRaw);
681
682
  parts.push(`Project: ${String(pkg.name ?? "unknown")}`);
@@ -693,13 +694,13 @@ async function gatherProjectContext2(projectRoot) {
693
694
  } catch {
694
695
  }
695
696
  try {
696
- const tsconfigPath = path38.join(projectRoot, "tsconfig.json");
697
+ const tsconfigPath = path39.join(projectRoot, "tsconfig.json");
697
698
  await fsp5.access(tsconfigPath);
698
699
  parts.push("Language: TypeScript");
699
700
  } catch {
700
701
  }
701
702
  try {
702
- const srcDir = path38.join(projectRoot, "src");
703
+ const srcDir = path39.join(projectRoot, "src");
703
704
  const entries = await fsp5.readdir(srcDir, { withFileTypes: true });
704
705
  const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
705
706
  if (dirs.length > 0) parts.push(`Source structure: src/${dirs.join(", src/")}`);
@@ -2124,12 +2125,12 @@ __export(project_utils_exports, {
2124
2125
  touchProjectInManifest: () => touchProjectInManifest
2125
2126
  });
2126
2127
  function projectsJsonPath(globalConfigPath) {
2127
- const base = globalConfigPath ? path38.dirname(globalConfigPath) : wstackGlobalRoot();
2128
- return path38.join(base, "projects.json");
2128
+ const base = globalConfigPath ? path39.dirname(globalConfigPath) : wstackGlobalRoot();
2129
+ return path39.join(base, "projects.json");
2129
2130
  }
2130
2131
  function projectsDataDir(globalConfigPath) {
2131
- const base = globalConfigPath ? path38.dirname(globalConfigPath) : wstackGlobalRoot();
2132
- return path38.join(base, "projects");
2132
+ const base = globalConfigPath ? path39.dirname(globalConfigPath) : wstackGlobalRoot();
2133
+ return path39.join(base, "projects");
2133
2134
  }
2134
2135
  async function loadManifest(globalConfigPath) {
2135
2136
  const file = projectsJsonPath(globalConfigPath);
@@ -2143,12 +2144,12 @@ async function loadManifest(globalConfigPath) {
2143
2144
  }
2144
2145
  async function saveManifest(manifest, globalConfigPath) {
2145
2146
  const file = projectsJsonPath(globalConfigPath);
2146
- await fsp5.mkdir(path38.dirname(file), { recursive: true });
2147
+ await fsp5.mkdir(path39.dirname(file), { recursive: true });
2147
2148
  await fsp5.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
2148
2149
  }
2149
2150
  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);
2151
+ const base = path39.basename(root).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
2152
+ const hash = createHash("sha256").update(path39.resolve(root)).digest("hex").slice(0, 6);
2152
2153
  return `${base}-${hash}`;
2153
2154
  }
2154
2155
  function findProject(manifest, query) {
@@ -2163,29 +2164,29 @@ function findProject(manifest, query) {
2163
2164
  return found;
2164
2165
  }
2165
2166
  async function ensureProjectDataDir(slug, globalConfigPath) {
2166
- const dir = path38.join(projectsDataDir(globalConfigPath), slug);
2167
+ const dir = path39.join(projectsDataDir(globalConfigPath), slug);
2167
2168
  await fsp5.mkdir(dir, { recursive: true });
2168
2169
  return dir;
2169
2170
  }
2170
2171
  async function touchProjectInManifest(opts) {
2171
- const root = path38.resolve(opts.projectRoot);
2172
+ const root = path39.resolve(opts.projectRoot);
2172
2173
  const file = projectsJsonPath(opts.globalConfigPath);
2173
2174
  let entry;
2174
2175
  await withFileLock(file, async () => {
2175
2176
  const manifest = await loadManifest(opts.globalConfigPath);
2176
2177
  const now = (/* @__PURE__ */ new Date()).toISOString();
2177
- entry = manifest.projects.find((p) => path38.resolve(p.root) === root);
2178
+ entry = manifest.projects.find((p) => path39.resolve(p.root) === root);
2178
2179
  if (entry) {
2179
2180
  entry.lastSeen = now;
2180
- if (opts.workingDir) entry.lastWorkingDir = path38.resolve(opts.workingDir);
2181
+ if (opts.workingDir) entry.lastWorkingDir = path39.resolve(opts.workingDir);
2181
2182
  } else {
2182
2183
  entry = {
2183
- name: opts.name ?? path38.basename(root),
2184
+ name: opts.name ?? path39.basename(root),
2184
2185
  root,
2185
2186
  slug: generateSlug(root),
2186
2187
  createdAt: now,
2187
2188
  lastSeen: now,
2188
- lastWorkingDir: opts.workingDir ? path38.resolve(opts.workingDir) : void 0
2189
+ lastWorkingDir: opts.workingDir ? path39.resolve(opts.workingDir) : void 0
2189
2190
  };
2190
2191
  manifest.projects.push(entry);
2191
2192
  }
@@ -2379,7 +2380,7 @@ async function runProjectPicker(opts) {
2379
2380
  const reservedBottom = 3;
2380
2381
  const headerHeight = reservedTop + reservedBottom;
2381
2382
  const baseVisibleHeight = Math.max(5, terminalHeight() - headerHeight);
2382
- return new Promise((resolve10) => {
2383
+ return new Promise((resolve11) => {
2383
2384
  const wasRaw = stdin.isRaw;
2384
2385
  const wasPaused = stdin.isPaused();
2385
2386
  let filter = "";
@@ -2503,7 +2504,7 @@ async function runProjectPicker(opts) {
2503
2504
  cleanup();
2504
2505
  out.write(CURSOR_SHOW);
2505
2506
  out.write("\n");
2506
- resolve10(void 0);
2507
+ resolve11(void 0);
2507
2508
  return;
2508
2509
  }
2509
2510
  if (ch === ESC) {
@@ -2516,7 +2517,7 @@ async function runProjectPicker(opts) {
2516
2517
  cleanup();
2517
2518
  out.write(CURSOR_SHOW);
2518
2519
  out.write("\n");
2519
- resolve10(void 0);
2520
+ resolve11(void 0);
2520
2521
  return;
2521
2522
  }
2522
2523
  if (ch === BS || ch === "\b") {
@@ -2533,22 +2534,22 @@ async function runProjectPicker(opts) {
2533
2534
  out.write(CURSOR_SHOW);
2534
2535
  out.write("\n");
2535
2536
  if (!item || item.key === "__divider__") {
2536
- resolve10(void 0);
2537
+ resolve11(void 0);
2537
2538
  return;
2538
2539
  }
2539
2540
  if (item.key === "quit") {
2540
- resolve10(void 0);
2541
+ resolve11(void 0);
2541
2542
  return;
2542
2543
  }
2543
2544
  if (item.key === "new-session") {
2544
- resolve10({ kind: "action", key: "new-session", action: "new-session" });
2545
+ resolve11({ kind: "action", key: "new-session", action: "new-session" });
2545
2546
  return;
2546
2547
  }
2547
2548
  if (item.key === "prev-sessions") {
2548
- resolve10({ kind: "action", key: "prev-sessions", action: "prev-sessions" });
2549
+ resolve11({ kind: "action", key: "prev-sessions", action: "prev-sessions" });
2549
2550
  return;
2550
2551
  }
2551
- resolve10({ kind: "project", key: item.key });
2552
+ resolve11({ kind: "project", key: item.key });
2552
2553
  return;
2553
2554
  }
2554
2555
  if (filter.length === 0) {
@@ -2556,7 +2557,7 @@ async function runProjectPicker(opts) {
2556
2557
  cleanup();
2557
2558
  out.write(CURSOR_SHOW);
2558
2559
  out.write("\n");
2559
- resolve10(void 0);
2560
+ resolve11(void 0);
2560
2561
  return;
2561
2562
  }
2562
2563
  if (ch === "j") {
@@ -2585,7 +2586,7 @@ async function runProjectPicker(opts) {
2585
2586
  try {
2586
2587
  stdin.setRawMode(true);
2587
2588
  } catch {
2588
- resolve10(void 0);
2589
+ resolve11(void 0);
2589
2590
  return;
2590
2591
  }
2591
2592
  stdin.resume();
@@ -2596,7 +2597,7 @@ async function runProjectPicker(opts) {
2596
2597
  stdin.once("close", () => {
2597
2598
  cleanup();
2598
2599
  out.write(CURSOR_SHOW);
2599
- resolve10(void 0);
2600
+ resolve11(void 0);
2600
2601
  });
2601
2602
  });
2602
2603
  }
@@ -2631,7 +2632,7 @@ __export(update_check_exports, {
2631
2632
  getUpdateNotification: () => getUpdateNotification
2632
2633
  });
2633
2634
  function cachePath(homeFn = defaultHomeDir2) {
2634
- return path38.join(homeFn(), ".wrongstack", "update-cache.json");
2635
+ return path39.join(homeFn(), ".wrongstack", "update-cache.json");
2635
2636
  }
2636
2637
  function currentVersion() {
2637
2638
  const req2 = createRequire(import.meta.url);
@@ -2668,7 +2669,7 @@ async function readCache(homeFn = defaultHomeDir2) {
2668
2669
  }
2669
2670
  async function writeCache(entry, homeFn = defaultHomeDir2) {
2670
2671
  try {
2671
- const dir = path38.dirname(cachePath(homeFn));
2672
+ const dir = path39.dirname(cachePath(homeFn));
2672
2673
  await fsp5.mkdir(dir, { recursive: true });
2673
2674
  await fsp5.writeFile(cachePath(homeFn), JSON.stringify(entry, null, 2), "utf8");
2674
2675
  } catch {
@@ -2754,7 +2755,7 @@ function registerWebuiInstance(p, deps = {}) {
2754
2755
  wsPort: p.wsPort,
2755
2756
  host: p.host,
2756
2757
  projectRoot: p.projectRoot,
2757
- projectName: path38.basename(p.projectRoot) || p.projectRoot,
2758
+ projectName: path39.basename(p.projectRoot) || p.projectRoot,
2758
2759
  startedAt: p.startedAt,
2759
2760
  url: `http://${p.host}:${p.httpPort}`
2760
2761
  },
@@ -2818,7 +2819,7 @@ var init_lifecycle = __esm({
2818
2819
  }
2819
2820
  });
2820
2821
  function getVault(globalConfigPath) {
2821
- const keyFile = path38.join(path38.dirname(globalConfigPath ?? ""), ".key");
2822
+ const keyFile = path39.join(path39.dirname(globalConfigPath ?? ""), ".key");
2822
2823
  return new DefaultSecretVault({ keyFile });
2823
2824
  }
2824
2825
  async function loadSavedProviders(globalConfigPath) {
@@ -2852,7 +2853,7 @@ function resolveDistDir() {
2852
2853
  try {
2853
2854
  const requireFromHere = createRequire(import.meta.url);
2854
2855
  const serverEntry = requireFromHere.resolve("@wrongstack/webui/server");
2855
- return path38.resolve(path38.dirname(serverEntry), "..");
2856
+ return path39.resolve(path39.dirname(serverEntry), "..");
2856
2857
  } catch {
2857
2858
  return null;
2858
2859
  }
@@ -3124,10 +3125,10 @@ function handlePing(ctx, ws) {
3124
3125
  ctx.send(ws, { type: "pong", payload: {} });
3125
3126
  }
3126
3127
  function handleToolConfirmResult(ctx, id, decision) {
3127
- const resolve10 = ctx.pendingConfirms.get(id);
3128
- if (resolve10) {
3128
+ const resolve11 = ctx.pendingConfirms.get(id);
3129
+ if (resolve11) {
3129
3130
  ctx.pendingConfirms.delete(id);
3130
- resolve10(decision);
3131
+ resolve11(decision);
3131
3132
  }
3132
3133
  }
3133
3134
  var init_connection = __esm({
@@ -3543,8 +3544,8 @@ function sendResult6(ctx, ws, success, message) {
3543
3544
  ctx.send(ws, { type: "key.operation_result", payload: { success, message } });
3544
3545
  }
3545
3546
  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");
3547
+ const projectsBase = ctx.opts.globalConfigPath ? path39.resolve(path39.dirname(ctx.opts.globalConfigPath)) : wstackGlobalRoot();
3548
+ const manifestPath = path39.join(projectsBase, "projects.json");
3548
3549
  try {
3549
3550
  const raw = await fsp5.readFile(manifestPath, "utf8");
3550
3551
  const manifest = JSON.parse(raw);
@@ -3557,22 +3558,22 @@ async function handleProjectsSelect(ctx, ws, payload) {
3557
3558
  const { opts } = ctx;
3558
3559
  const { root, name: projectName } = payload;
3559
3560
  try {
3560
- const resolved = path38.resolve(root);
3561
+ const resolved = path39.resolve(root);
3561
3562
  const stat7 = await fsp5.stat(resolved).catch(() => null);
3562
3563
  if (!stat7?.isDirectory()) {
3563
3564
  ctx.send(ws, {
3564
3565
  type: "projects.selected",
3565
3566
  payload: {
3566
3567
  root,
3567
- name: projectName ?? path38.basename(root),
3568
+ name: projectName ?? path39.basename(root),
3568
3569
  message: `Cannot switch: not a directory: ${resolved}`
3569
3570
  }
3570
3571
  });
3571
3572
  return;
3572
3573
  }
3573
3574
  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);
3575
+ const entry = manifest.projects.find((p) => path39.resolve(p.root) === resolved);
3576
+ const displayName = projectName?.trim() || entry?.name || path39.basename(resolved);
3576
3577
  if (entry) {
3577
3578
  entry.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
3578
3579
  } else {
@@ -3618,8 +3619,8 @@ async function handleProjectsSelect(ctx, ws, payload) {
3618
3619
  });
3619
3620
  } catch {
3620
3621
  }
3621
- const globalRoot = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : wstackGlobalRoot();
3622
- const newSessionsDir = path38.join(resolveProjectDir(resolved, globalRoot), "sessions");
3622
+ const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : wstackGlobalRoot();
3623
+ const newSessionsDir = path39.join(resolveProjectDir(resolved, globalRoot), "sessions");
3623
3624
  await fsp5.mkdir(newSessionsDir, { recursive: true });
3624
3625
  const newStore = new DefaultSessionStore({ dir: newSessionsDir });
3625
3626
  opts.sessionStore = newStore;
@@ -3649,11 +3650,11 @@ async function handleProjectsSelect(ctx, ws, payload) {
3649
3650
  async function handleProjectsAdd(ctx, ws, payload) {
3650
3651
  const { root: addRoot, name: addName } = payload;
3651
3652
  try {
3652
- const resolved = path38.resolve(addRoot);
3653
+ const resolved = path39.resolve(addRoot);
3653
3654
  const stat7 = await fsp5.stat(resolved).catch(() => null);
3654
3655
  if (!stat7?.isDirectory()) throw new Error(`Not a directory: ${resolved}`);
3655
3656
  const manifest = await loadManifest(ctx.opts.globalConfigPath);
3656
- const existing = manifest.projects.find((p) => path38.resolve(p.root) === resolved);
3657
+ const existing = manifest.projects.find((p) => path39.resolve(p.root) === resolved);
3657
3658
  if (existing) {
3658
3659
  ctx.send(ws, {
3659
3660
  type: "projects.added",
@@ -3666,7 +3667,7 @@ async function handleProjectsAdd(ctx, ws, payload) {
3666
3667
  });
3667
3668
  return;
3668
3669
  }
3669
- const name = addName?.trim() || path38.basename(resolved);
3670
+ const name = addName?.trim() || path39.basename(resolved);
3670
3671
  const slug = projectSlug(resolved);
3671
3672
  await ensureProjectDataDir(slug, ctx.opts.globalConfigPath);
3672
3673
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -3680,7 +3681,7 @@ async function handleProjectsAdd(ctx, ws, payload) {
3680
3681
  ctx.send(ws, {
3681
3682
  type: "projects.added",
3682
3683
  payload: {
3683
- name: path38.basename(addRoot),
3684
+ name: path39.basename(addRoot),
3684
3685
  root: addRoot,
3685
3686
  slug: "",
3686
3687
  message: err instanceof Error ? err.message : String(err)
@@ -3691,8 +3692,8 @@ async function handleProjectsAdd(ctx, ws, payload) {
3691
3692
  async function handleWorkingDirSet(ctx, ws, newPath) {
3692
3693
  try {
3693
3694
  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) {
3695
+ const resolved = path39.resolve(wdRoot, newPath);
3696
+ if (!resolved.startsWith(wdRoot + path39.sep) && resolved !== wdRoot) {
3696
3697
  sendResult6(ctx, ws, false, `Path must stay inside the project root: ${wdRoot}`);
3697
3698
  return;
3698
3699
  }
@@ -3933,13 +3934,13 @@ function sendResult8(ctx, ws, success, message) {
3933
3934
  }
3934
3935
  function storeFor(opts) {
3935
3936
  return opts.sessionStore ?? new DefaultSessionStore({
3936
- dir: path38.join(opts.projectRoot ?? opts.agent.ctx.projectRoot, ".wrongstack", "sessions")
3937
+ dir: path39.join(opts.projectRoot ?? opts.agent.ctx.projectRoot, ".wrongstack", "sessions")
3937
3938
  });
3938
3939
  }
3939
3940
  async function handleGoalGet(ctx, _ws) {
3940
3941
  const projectRoot = ctx.opts.projectRoot ?? ctx.opts.agent.ctx.projectRoot;
3941
3942
  try {
3942
- const goalPath = path38.join(projectRoot, ".wrongstack", "goal.json");
3943
+ const goalPath = path39.join(projectRoot, ".wrongstack", "goal.json");
3943
3944
  const raw = await fsp5.readFile(goalPath, "utf8");
3944
3945
  ctx.broadcast({ type: "goal.updated", payload: JSON.parse(raw) });
3945
3946
  } catch {
@@ -4015,7 +4016,7 @@ async function handleSessionNew(ctx, _ws) {
4015
4016
  function rewinderFor(opts) {
4016
4017
  const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot;
4017
4018
  return new DefaultSessionRewinder(
4018
- opts.sessionsDir ?? path38.join(projectRoot, ".wrongstack", "sessions"),
4019
+ opts.sessionsDir ?? path39.join(projectRoot, ".wrongstack", "sessions"),
4019
4020
  projectRoot
4020
4021
  );
4021
4022
  }
@@ -4349,14 +4350,14 @@ async function runWebUI(opts) {
4349
4350
  let customModeStoreP = null;
4350
4351
  const getCustomModeStore = () => {
4351
4352
  customModeStoreP ??= (async () => {
4352
- const dir = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : wstackGlobalRoot();
4353
+ const dir = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : wstackGlobalRoot();
4353
4354
  const store = createCustomModeStore(dir);
4354
4355
  await store.load();
4355
4356
  return store;
4356
4357
  })();
4357
4358
  return customModeStoreP;
4358
4359
  };
4359
- const autoPhaseStoreDir = opts.projectRoot ? path38.join(opts.projectRoot, ".wrongstack", "autophase") : path38.join(os.tmpdir(), ".wrongstack", "autophase");
4360
+ const autoPhaseStoreDir = opts.projectRoot ? path39.join(opts.projectRoot, ".wrongstack", "autophase") : path39.join(os.tmpdir(), ".wrongstack", "autophase");
4360
4361
  const autoPhaseHandler = new AutoPhaseWebSocketHandler(
4361
4362
  opts.agent,
4362
4363
  opts.agent.ctx,
@@ -4447,7 +4448,7 @@ async function runWebUI(opts) {
4447
4448
  return;
4448
4449
  }
4449
4450
  const vault = new DefaultSecretVault({
4450
- keyFile: path38.join(path38.dirname(configPath2), ".key")
4451
+ keyFile: path39.join(path39.dirname(configPath2), ".key")
4451
4452
  });
4452
4453
  const decrypted = decryptConfigSecrets$1(parsed, vault);
4453
4454
  const autonomyCfg = decrypted.autonomy ?? {};
@@ -4570,7 +4571,7 @@ async function runWebUI(opts) {
4570
4571
  model: opts.agent.ctx.model,
4571
4572
  provider: opts.agent.ctx.provider.id,
4572
4573
  mode: opts.modeId ?? "default",
4573
- projectName: opts.projectRoot ? path38.basename(opts.projectRoot) : void 0,
4574
+ projectName: opts.projectRoot ? path39.basename(opts.projectRoot) : void 0,
4574
4575
  // Frontend reads `projectRoot` from session.start (ws-handlers setEnv) —
4575
4576
  // omitting it left the store's projectRoot empty after a project switch.
4576
4577
  projectRoot: opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "",
@@ -4596,7 +4597,7 @@ async function runWebUI(opts) {
4596
4597
  const projectDir = resolveProjectDir(opts.projectRoot, wstackGlobalRoot());
4597
4598
  const mailbox = new GlobalMailbox(projectDir, opts.events);
4598
4599
  webuiClientId = `webui@${crypto3.randomUUID().slice(0, 8)}`;
4599
- const projectName = opts.projectRoot ? path38.basename(opts.projectRoot) : "unknown";
4600
+ const projectName = opts.projectRoot ? path39.basename(opts.projectRoot) : "unknown";
4600
4601
  await mailbox.registerClient({
4601
4602
  clientId: webuiClientId,
4602
4603
  sessionId: opts.projectRoot,
@@ -4627,7 +4628,7 @@ async function runWebUI(opts) {
4627
4628
  host,
4628
4629
  httpPort,
4629
4630
  wsPort,
4630
- globalRoot: path38.dirname(opts.globalConfigPath ?? "")
4631
+ globalRoot: path39.dirname(opts.globalConfigPath ?? "")
4631
4632
  });
4632
4633
  if (httpServer) {
4633
4634
  announceWebuiReady({
@@ -4642,7 +4643,7 @@ async function runWebUI(opts) {
4642
4643
  `[WebUI] Frontend not served (run \`pnpm --filter @wrongstack/webui build\`). WS bridge still active on ws://${host}:${wsPort}.`
4643
4644
  );
4644
4645
  }
4645
- const registryBaseDir = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : void 0;
4646
+ const registryBaseDir = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : void 0;
4646
4647
  if (opts.projectRoot) {
4647
4648
  registerWebuiInstance({
4648
4649
  pid: process.pid,
@@ -4999,12 +5000,12 @@ async function runWebUI(opts) {
4999
5000
  broadcast,
5000
5001
  log: (m) => console.log(m)
5001
5002
  };
5002
- return new Promise((resolve10) => {
5003
+ return new Promise((resolve11) => {
5003
5004
  wss.on("listening", () => {
5004
5005
  console.log(`[WebUI] WebSocket server running on ws://${host}:${port}`);
5005
5006
  setupEvents();
5006
5007
  opts.onListening?.({ httpPort, wsPort, host });
5007
- const globalRoot = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : void 0;
5008
+ const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : void 0;
5008
5009
  if (globalRoot) {
5009
5010
  const statusInterval = setInterval(async () => {
5010
5011
  try {
@@ -5123,8 +5124,8 @@ async function runWebUI(opts) {
5123
5124
  clients.delete(ws);
5124
5125
  abortControllers.delete(ws);
5125
5126
  if (clients.size === 0 && pendingConfirms.size > 0) {
5126
- for (const [id, resolve11] of pendingConfirms) {
5127
- resolve11("no");
5127
+ for (const [id, resolve12] of pendingConfirms) {
5128
+ resolve12("no");
5128
5129
  pendingConfirms.delete(id);
5129
5130
  }
5130
5131
  }
@@ -5163,7 +5164,7 @@ async function runWebUI(opts) {
5163
5164
  wss,
5164
5165
  pid: process.pid,
5165
5166
  registryBaseDir,
5166
- onStopped: resolve10
5167
+ onStopped: resolve11
5167
5168
  });
5168
5169
  registerWebuiSignalHandlers(signalShutdown);
5169
5170
  });
@@ -5553,7 +5554,7 @@ async function runWebUI(opts) {
5553
5554
  // ── Mailbox operations — project-level inter-agent messaging ────
5554
5555
  case "mailbox.messages": {
5555
5556
  const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
5556
- const globalRoot = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : "";
5557
+ const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
5557
5558
  if (!projectRoot || !globalRoot) {
5558
5559
  send(ws, {
5559
5560
  type: "mailbox.messages",
@@ -5602,7 +5603,7 @@ async function runWebUI(opts) {
5602
5603
  }
5603
5604
  case "mailbox.agents": {
5604
5605
  const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
5605
- const globalRoot = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : "";
5606
+ const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
5606
5607
  if (!projectRoot || !globalRoot) {
5607
5608
  send(ws, {
5608
5609
  type: "mailbox.agents",
@@ -5645,7 +5646,7 @@ async function runWebUI(opts) {
5645
5646
  }
5646
5647
  case "mailbox.clear": {
5647
5648
  const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
5648
- const globalRoot = opts.globalConfigPath ? path38.dirname(opts.globalConfigPath) : "";
5649
+ const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
5649
5650
  if (!projectRoot || !globalRoot) {
5650
5651
  send(ws, { type: "mailbox.cleared", payload: { error: "No project root available" } });
5651
5652
  break;
@@ -5715,7 +5716,7 @@ var init_webui_server = __esm({
5715
5716
  var WORKTREE_PHASE_CONCURRENCY = 4;
5716
5717
  var MAX_CMD_OUTPUT = 2e5;
5717
5718
  function gitText(args, cwd) {
5718
- return new Promise((resolve10, reject) => {
5719
+ return new Promise((resolve11, reject) => {
5719
5720
  let child;
5720
5721
  try {
5721
5722
  child = spawn("git", args, {
@@ -5729,15 +5730,14 @@ function gitText(args, cwd) {
5729
5730
  reject(err);
5730
5731
  return;
5731
5732
  }
5732
- let out = "";
5733
- child.stdout?.on("data", (c) => {
5734
- if (out.length < MAX_CMD_OUTPUT) out += c.toString();
5735
- });
5736
- child.stderr?.on("data", (c) => {
5737
- if (out.length < MAX_CMD_OUTPUT) out += c.toString();
5738
- });
5739
- child.on("error", () => resolve10({ code: 1, out }));
5740
- child.on("close", (code) => resolve10({ code: code ?? 1, out: out.trim() }));
5733
+ const chunks = [];
5734
+ const emit = (c) => {
5735
+ if (chunks.join("").length < MAX_CMD_OUTPUT) chunks.push(c.toString());
5736
+ };
5737
+ child.stdout?.on("data", emit);
5738
+ child.stderr?.on("data", emit);
5739
+ child.on("error", () => resolve11({ code: 1, out: chunks.join("") }));
5740
+ child.on("close", (code) => resolve11({ code: code ?? 1, out: chunks.join("").trim() }));
5741
5741
  });
5742
5742
  }
5743
5743
  async function isGitRepo(cwd) {
@@ -5773,8 +5773,8 @@ function runCmd(cmd, args, cwd, shell = false) {
5773
5773
  });
5774
5774
  }
5775
5775
  }
5776
- return new Promise((resolve10, reject) => {
5777
- let out = "";
5776
+ return new Promise((resolve11, reject) => {
5777
+ const chunks = [];
5778
5778
  let child;
5779
5779
  try {
5780
5780
  child = spawn(cmd, args, {
@@ -5792,13 +5792,16 @@ function runCmd(cmd, args, cwd, shell = false) {
5792
5792
  return;
5793
5793
  }
5794
5794
  const append = (c) => {
5795
- out += c.toString();
5796
- if (out.length > MAX_CMD_OUTPUT) out = out.slice(-MAX_CMD_OUTPUT);
5795
+ chunks.push(c.toString());
5797
5796
  };
5798
5797
  child.stdout?.on("data", append);
5799
5798
  child.stderr?.on("data", append);
5800
- child.on("error", (e) => resolve10({ code: 1, out: `${out}${String(e)}` }));
5801
- child.on("close", (code) => resolve10({ code: code ?? 1, out: out.trim() }));
5799
+ child.on("error", (e) => resolve11({ code: 1, out: `${chunks.join("")}${String(e)}` }));
5800
+ child.on("close", (code) => {
5801
+ let out = chunks.join("");
5802
+ if (out.length > MAX_CMD_OUTPUT) out = out.slice(-MAX_CMD_OUTPUT);
5803
+ resolve11({ code: code ?? 1, out: out.trim() });
5804
+ });
5802
5805
  });
5803
5806
  }
5804
5807
  function detectPackageManager(root) {
@@ -6273,7 +6276,7 @@ var ReadlineInputReader = class {
6273
6276
  history = [];
6274
6277
  pending = false;
6275
6278
  constructor(opts = {}) {
6276
- this.historyFile = opts.historyFile ?? path38.join(wstackGlobalRoot(), "history");
6279
+ this.historyFile = opts.historyFile ?? path39.join(wstackGlobalRoot(), "history");
6277
6280
  }
6278
6281
  async loadHistory() {
6279
6282
  try {
@@ -6285,7 +6288,7 @@ var ReadlineInputReader = class {
6285
6288
  }
6286
6289
  async saveHistory() {
6287
6290
  try {
6288
- await fsp5.mkdir(path38.dirname(this.historyFile), { recursive: true });
6291
+ await fsp5.mkdir(path39.dirname(this.historyFile), { recursive: true });
6289
6292
  await fsp5.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
6290
6293
  } catch {
6291
6294
  }
@@ -6304,31 +6307,31 @@ var ReadlineInputReader = class {
6304
6307
  async readLine(prompt) {
6305
6308
  if (this.history.length === 0) await this.loadHistory();
6306
6309
  while (this.pending) {
6307
- await new Promise((resolve10) => setTimeout(resolve10, 50));
6310
+ await new Promise((resolve11) => setTimeout(resolve11, 50));
6308
6311
  }
6309
6312
  this.pending = true;
6310
6313
  try {
6311
6314
  if (this.rl) {
6312
6315
  const old = this.rl;
6313
6316
  this.rl = void 0;
6314
- await new Promise((resolve10) => {
6317
+ await new Promise((resolve11) => {
6315
6318
  if (old.closed) {
6316
- resolve10();
6319
+ resolve11();
6317
6320
  } else {
6318
- old.once("close", resolve10);
6321
+ old.once("close", resolve11);
6319
6322
  old.close();
6320
6323
  }
6321
6324
  });
6322
6325
  }
6323
6326
  const fresh = this.ensure();
6324
6327
  this.installPromptGuard(fresh);
6325
- return new Promise((resolve10) => {
6328
+ return new Promise((resolve11) => {
6326
6329
  let settled = false;
6327
6330
  const settle = (line) => {
6328
6331
  if (settled) return;
6329
6332
  settled = true;
6330
6333
  setOutputLineGuard(null);
6331
- resolve10(line);
6334
+ resolve11(line);
6332
6335
  };
6333
6336
  fresh.question(prompt ?? "> ", (line) => {
6334
6337
  if (line.trim()) {
@@ -6381,7 +6384,7 @@ var ReadlineInputReader = class {
6381
6384
  async readKey(prompt, options) {
6382
6385
  setOutputLineGuard(null);
6383
6386
  writeOut(prompt);
6384
- return new Promise((resolve10) => {
6387
+ return new Promise((resolve11) => {
6385
6388
  const stdin = process.stdin;
6386
6389
  const wasRaw = stdin.isRaw;
6387
6390
  const wasPaused = stdin.isPaused();
@@ -6392,7 +6395,7 @@ var ReadlineInputReader = class {
6392
6395
  if (key === "") {
6393
6396
  cleanup();
6394
6397
  writeOut("\n");
6395
- resolve10("");
6398
+ resolve11("");
6396
6399
  return;
6397
6400
  }
6398
6401
  const opt = options.find(
@@ -6402,12 +6405,12 @@ var ReadlineInputReader = class {
6402
6405
  cleanup();
6403
6406
  writeOut(`${opt.key}
6404
6407
  `);
6405
- resolve10(opt.value);
6408
+ resolve11(opt.value);
6406
6409
  }
6407
6410
  };
6408
6411
  const onClose = () => {
6409
6412
  cleanup();
6410
- resolve10("");
6413
+ resolve11("");
6411
6414
  };
6412
6415
  const cleanup = () => {
6413
6416
  stdin.off("data", onData);
@@ -6436,7 +6439,7 @@ var ReadlineInputReader = class {
6436
6439
  this.rl?.close();
6437
6440
  this.rl = void 0;
6438
6441
  writeOut(prompt);
6439
- return new Promise((resolve10) => {
6442
+ return new Promise((resolve11) => {
6440
6443
  let buf = "";
6441
6444
  const wasRaw = stdin.isRaw;
6442
6445
  setRawMode(stdin, true);
@@ -6454,7 +6457,7 @@ var ReadlineInputReader = class {
6454
6457
  cleanup();
6455
6458
  writeOut(` ${dim(`[${buf.length} chars]`)}
6456
6459
  `);
6457
- resolve10(buf);
6460
+ resolve11(buf);
6458
6461
  return;
6459
6462
  }
6460
6463
  if (ch === "") {
@@ -6617,7 +6620,7 @@ function pickGroupIndex(opts) {
6617
6620
  if (Number.isFinite(parsed)) current = wrap(parsed);
6618
6621
  } catch {
6619
6622
  }
6620
- fs2.mkdirSync(path38.dirname(opts.cursorFile), { recursive: true });
6623
+ fs2.mkdirSync(path39.dirname(opts.cursorFile), { recursive: true });
6621
6624
  fs2.writeFileSync(opts.cursorFile, String(wrap(current + 1)));
6622
6625
  return current;
6623
6626
  } catch {
@@ -6670,11 +6673,11 @@ function assertSafeToDelete(filename, parentDir) {
6670
6673
  throw new FsError({
6671
6674
  message: `Refusing to delete protected file: ${filename}`,
6672
6675
  code: ERROR_CODES.FS_DELETE_FAILED,
6673
- path: path38.join(parentDir, filename),
6676
+ path: path39.join(parentDir, filename),
6674
6677
  context: { reason: "protected_basename" }
6675
6678
  });
6676
6679
  }
6677
- if (filename !== path38.basename(filename)) {
6680
+ if (filename !== path39.basename(filename)) {
6678
6681
  throw new FsError({
6679
6682
  message: `Refusing to delete path with traversal: ${filename}`,
6680
6683
  code: ERROR_CODES.FS_DELETE_FAILED,
@@ -6686,11 +6689,11 @@ function assertSafeToDelete(filename, parentDir) {
6686
6689
  throw new FsError({
6687
6690
  message: `Refusing to delete unknown file: ${filename}`,
6688
6691
  code: ERROR_CODES.FS_DELETE_FAILED,
6689
- path: path38.join(parentDir, filename),
6692
+ path: path39.join(parentDir, filename),
6690
6693
  context: { reason: "unknown_file_pattern" }
6691
6694
  });
6692
6695
  }
6693
- const resolvedParent = path38.resolve(parentDir);
6696
+ const resolvedParent = path39.resolve(parentDir);
6694
6697
  if (!resolvedParent.endsWith(".wrongstack")) {
6695
6698
  throw new FsError({
6696
6699
  message: `Unexpected parent directory for bak prune: ${resolvedParent}`,
@@ -6701,8 +6704,8 @@ function assertSafeToDelete(filename, parentDir) {
6701
6704
  }
6702
6705
  }
6703
6706
  async function safeDelete(filePath) {
6704
- const dir = path38.dirname(filePath);
6705
- const filename = path38.basename(filePath);
6707
+ const dir = path39.dirname(filePath);
6708
+ const filename = path39.basename(filePath);
6706
6709
  try {
6707
6710
  assertSafeToDelete(filename, dir);
6708
6711
  await fsp5.unlink(filePath);
@@ -6747,16 +6750,16 @@ function diffSummary(oldCfg, newCfg) {
6747
6750
  }
6748
6751
  var defaultHomeDir = () => os__default.homedir();
6749
6752
  function historyDir(homeFn = defaultHomeDir) {
6750
- return path38.join(homeFn(), ".wrongstack", "config.history", "entries");
6753
+ return path39.join(homeFn(), ".wrongstack", "config.history", "entries");
6751
6754
  }
6752
6755
  function historyIndexPath(homeFn = defaultHomeDir) {
6753
- return path38.join(homeFn(), ".wrongstack", "config.history", "index.json");
6756
+ return path39.join(homeFn(), ".wrongstack", "config.history", "index.json");
6754
6757
  }
6755
6758
  function configPath(homeFn = defaultHomeDir) {
6756
- return path38.join(homeFn(), ".wrongstack", "config.json");
6759
+ return path39.join(homeFn(), ".wrongstack", "config.json");
6757
6760
  }
6758
6761
  function backupLastPath(homeFn = defaultHomeDir) {
6759
- return path38.join(homeFn(), ".wrongstack", "config.json.last");
6762
+ return path39.join(homeFn(), ".wrongstack", "config.json.last");
6760
6763
  }
6761
6764
  function entryId(ts) {
6762
6765
  return ts.replace(/[:.]/g, "-").slice(0, 19);
@@ -6814,7 +6817,7 @@ async function backupCurrent(homeFn = defaultHomeDir) {
6814
6817
  }
6815
6818
  if (content !== void 0) {
6816
6819
  try {
6817
- const bakPath = path38.join(homeFn(), ".wrongstack", `config.json.${ts}.bak`);
6820
+ const bakPath = path39.join(homeFn(), ".wrongstack", `config.json.${ts}.bak`);
6818
6821
  await atomicWrite(bakPath, content);
6819
6822
  } catch (err) {
6820
6823
  writeErr(
@@ -6823,11 +6826,11 @@ async function backupCurrent(homeFn = defaultHomeDir) {
6823
6826
  }
6824
6827
  }
6825
6828
  try {
6826
- const dir = path38.join(homeFn(), ".wrongstack");
6829
+ const dir = path39.join(homeFn(), ".wrongstack");
6827
6830
  const files = await fsp5.readdir(dir);
6828
6831
  const baks = files.filter((f) => f.startsWith("config.json.") && f.endsWith(".bak")).sort().reverse();
6829
6832
  for (const f of baks.slice(10)) {
6830
- await safeDelete(path38.join(dir, f));
6833
+ await safeDelete(path39.join(dir, f));
6831
6834
  }
6832
6835
  } catch (err) {
6833
6836
  writeErr(
@@ -6848,7 +6851,7 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
6848
6851
  };
6849
6852
  try {
6850
6853
  await fsp5.writeFile(
6851
- path38.join(historyDir(homeFn), `${id}.json`),
6854
+ path39.join(historyDir(homeFn), `${id}.json`),
6852
6855
  JSON.stringify(entry, null, 2),
6853
6856
  "utf8"
6854
6857
  );
@@ -6856,7 +6859,7 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
6856
6859
  throw new FsError({
6857
6860
  message: err instanceof Error ? err.message : String(err),
6858
6861
  code: ERROR_CODES.FS_WRITE_FAILED,
6859
- path: path38.join(historyDir(homeFn), `${id}.json`),
6862
+ path: path39.join(historyDir(homeFn), `${id}.json`),
6860
6863
  cause: err
6861
6864
  });
6862
6865
  }
@@ -6871,7 +6874,7 @@ async function listHistory(homeFn = defaultHomeDir) {
6871
6874
  }
6872
6875
  async function getHistoryEntry(id, homeFn = defaultHomeDir) {
6873
6876
  try {
6874
- const raw = await fsp5.readFile(path38.join(historyDir(homeFn), `${id}.json`), "utf8");
6877
+ const raw = await fsp5.readFile(path39.join(historyDir(homeFn), `${id}.json`), "utf8");
6875
6878
  return JSON.parse(raw);
6876
6879
  } catch {
6877
6880
  return null;
@@ -6988,10 +6991,10 @@ var theme = { primary: color.amber };
6988
6991
  async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? os__default.homedir()) {
6989
6992
  try {
6990
6993
  const { atomicWrite: atomicWrite17 } = await import('@wrongstack/core');
6991
- const fs38 = await import('fs/promises');
6994
+ const fs39 = await import('fs/promises');
6992
6995
  let existing = {};
6993
6996
  try {
6994
- const raw = await fs38.readFile(configPath2, "utf8");
6997
+ const raw = await fs39.readFile(configPath2, "utf8");
6995
6998
  existing = JSON.parse(raw);
6996
6999
  } catch {
6997
7000
  }
@@ -7484,7 +7487,7 @@ function buildAutonomyCommand(opts) {
7484
7487
  const current = opts.onAutonomy();
7485
7488
  const lines = [`Autonomy mode: ${MODE_LABELS[current] ?? current}`];
7486
7489
  try {
7487
- const goal = await loadGoal(goalFilePath(opts.projectRoot));
7490
+ const goal = await loadGoal(goalFilePath(opts.projectRoot), opts.events);
7488
7491
  if (goal) {
7489
7492
  const u = summarizeUsage(goal);
7490
7493
  lines.push(
@@ -7530,7 +7533,7 @@ function buildAutonomyCommand(opts) {
7530
7533
  opts.onAutonomy("off");
7531
7534
  let summaryLine = "";
7532
7535
  try {
7533
- const goal = await loadGoal(goalFilePath(opts.projectRoot));
7536
+ const goal = await loadGoal(goalFilePath(opts.projectRoot), opts.events);
7534
7537
  if (goal) {
7535
7538
  const u = summarizeUsage(goal);
7536
7539
  if (u.iterationsWithUsage > 0) {
@@ -7570,7 +7573,7 @@ function buildAutonomyCommand(opts) {
7570
7573
  if (newMode === "eternal" || newMode === "eternal-parallel") {
7571
7574
  const wantKeep = modifiers.includes("--keep") || modifiers.includes("keep");
7572
7575
  const wantNew = modifiers.includes("--new") || modifiers.includes("new");
7573
- const goal = await loadGoal(goalFilePath(opts.projectRoot));
7576
+ const goal = await loadGoal(goalFilePath(opts.projectRoot), opts.events);
7574
7577
  if (!goal) {
7575
7578
  const msg3 = `${color.red("Eternal/parallel mode requires a goal.")} Run \`/goal set <mission>\` first.`;
7576
7579
  opts.renderer.writeWarning(msg3);
@@ -7673,7 +7676,7 @@ function formatPhaseList(graph) {
7673
7676
  }
7674
7677
  async function gatherProjectContext(projectRoot) {
7675
7678
  try {
7676
- const raw = await fsp5.readFile(path38.join(projectRoot, "package.json"), "utf8");
7679
+ const raw = await fsp5.readFile(path39.join(projectRoot, "package.json"), "utf8");
7677
7680
  const pkg = JSON.parse(raw);
7678
7681
  const parts = [
7679
7682
  `Project: ${String(pkg.name ?? "unknown")}`,
@@ -8144,7 +8147,9 @@ async function annotationsCommand(opts, sessionId) {
8144
8147
  )
8145
8148
  };
8146
8149
  }
8147
- const annotations = new AnnotationsStore({ dir: storeDir });
8150
+ const annotationsOpts = { dir: storeDir, events: opts.events };
8151
+ if (opts.context?.traceId !== void 0) annotationsOpts.traceId = opts.context.traceId;
8152
+ const annotations = new AnnotationsStore(annotationsOpts);
8148
8153
  const open = await annotations.listOpen(sessionId);
8149
8154
  if (open.length === 0) {
8150
8155
  return {
@@ -8651,8 +8656,19 @@ function listRoles() {
8651
8656
  }
8652
8657
  var DEFAULT_TIMEOUT_MS = 6e4;
8653
8658
  var MAX_OUTPUT_LINES = 500;
8659
+ var WINDOWS_CMD_METACHARACTERS = /[;&|<>^$,(){}[\]!#%'"\\/`]/;
8660
+ function validateCommand(cmd) {
8661
+ if (process.platform !== "win32") return;
8662
+ if (WINDOWS_CMD_METACHARACTERS.test(cmd)) {
8663
+ throw new Error(
8664
+ `Command contains disallowed metacharacters for Windows: ${cmd.match(WINDOWS_CMD_METACHARACTERS)?.[0] ?? "?"}
8665
+ The following characters are not allowed: ; & | < > ^ $ , ( ) { } [ ] ! # % ' " \\ / \` * ?`
8666
+ );
8667
+ }
8668
+ }
8654
8669
  function runCommand(cmd, cwd, timeout) {
8655
- return new Promise((resolve10) => {
8670
+ return new Promise((resolve11) => {
8671
+ validateCommand(cmd);
8656
8672
  const opts = {
8657
8673
  cwd,
8658
8674
  timeout,
@@ -8664,7 +8680,7 @@ function runCommand(cmd, cwd, timeout) {
8664
8680
  shell: process.platform === "win32" ? true : false
8665
8681
  };
8666
8682
  execFile(cmd, [], opts, (error, stdout, stderr) => {
8667
- resolve10({
8683
+ resolve11({
8668
8684
  stdout,
8669
8685
  stderr,
8670
8686
  exitCode: typeof error?.code === "number" ? error.code : 0,
@@ -8733,6 +8749,7 @@ Examples:
8733
8749
  /dev git diff --stat`
8734
8750
  };
8735
8751
  }
8752
+ validateCommand(cmd);
8736
8753
  const cwd = opts.cwd;
8737
8754
  const startedAt = Date.now();
8738
8755
  opts.renderer.write(color.dim(`$ ${cmd}`));
@@ -9085,17 +9102,17 @@ function diagnoseConfig(cfg, plugins = []) {
9085
9102
  function scanPlaintextSecrets(node, prefix, findings) {
9086
9103
  if (!isPlainObject(node)) return;
9087
9104
  for (const [key, value] of Object.entries(node)) {
9088
- const path39 = prefix ? `${prefix}.${key}` : key;
9105
+ const path40 = prefix ? `${prefix}.${key}` : key;
9089
9106
  if (typeof value === "string") {
9090
9107
  if (value.length > 0 && isSecretField$1(key) && !value.startsWith(ENC_PREFIX)) {
9091
9108
  findings.push({
9092
- path: path39,
9109
+ path: path40,
9093
9110
  problem: "looks like a plaintext secret (not vault-encrypted) \u2014 it will be encrypted on next boot",
9094
9111
  severity: "warning"
9095
9112
  });
9096
9113
  }
9097
9114
  } else if (isPlainObject(value)) {
9098
- scanPlaintextSecrets(value, path39, findings);
9115
+ scanPlaintextSecrets(value, path40, findings);
9099
9116
  }
9100
9117
  }
9101
9118
  }
@@ -9116,7 +9133,7 @@ function resolvePersistPath(deps) {
9116
9133
  return deps.globalConfigPath;
9117
9134
  }
9118
9135
  async function ensureProjectDir(filePath) {
9119
- const dir = path38.dirname(filePath);
9136
+ const dir = path39.dirname(filePath);
9120
9137
  try {
9121
9138
  await fsp5.mkdir(dir, { recursive: true });
9122
9139
  } catch {
@@ -9320,8 +9337,8 @@ function buildDoctorCommand(opts) {
9320
9337
  return ` ${icon} ${color.cyan(f.path)} \u2014 ${f.problem}${fix}`;
9321
9338
  }
9322
9339
  async function findParsableBackup(file) {
9323
- const dir = path38.dirname(file);
9324
- const base = path38.basename(file);
9340
+ const dir = path39.dirname(file);
9341
+ const base = path39.basename(file);
9325
9342
  const candidates = [`${base}.last`];
9326
9343
  try {
9327
9344
  const siblings = await fsp5.readdir(dir);
@@ -9332,7 +9349,7 @@ function buildDoctorCommand(opts) {
9332
9349
  }
9333
9350
  for (const name of candidates) {
9334
9351
  try {
9335
- const raw = await fsp5.readFile(path38.join(dir, name), "utf8");
9352
+ const raw = await fsp5.readFile(path39.join(dir, name), "utf8");
9336
9353
  JSON.parse(raw);
9337
9354
  return { name, raw };
9338
9355
  } catch {
@@ -9449,7 +9466,7 @@ function buildDoctorCommand(opts) {
9449
9466
  await atomicWrite(target.file, JSON.stringify(report.fixed, null, 2));
9450
9467
  if (!target.isProject) {
9451
9468
  try {
9452
- const homeFn = () => path38.dirname(path38.dirname(target.file));
9469
+ const homeFn = () => path39.dirname(path39.dirname(target.file));
9453
9470
  await appendHistory(parsed, report.fixed, "config doctor auto-fix", homeFn);
9454
9471
  } catch {
9455
9472
  }
@@ -10430,11 +10447,11 @@ function classifyError(input) {
10430
10447
  }
10431
10448
  function extractCode(s) {
10432
10449
  const ts = /\bTS\d+\b|\bCS\d+\b/.exec(s);
10433
- if (ts) return ts[0];
10450
+ if (ts !== null) return ts[0];
10434
10451
  const rust = /\bE\d{4,}\b/.exec(s);
10435
- if (rust) return rust[0];
10452
+ if (rust !== null) return rust[0];
10436
10453
  const c = /\bc\d+\b/i.exec(s);
10437
- if (c) return c[0];
10454
+ if (c !== null) return c[0];
10438
10455
  return void 0;
10439
10456
  }
10440
10457
  function needsSubagent(c) {
@@ -11310,7 +11327,7 @@ function buildGoalCommand(opts) {
11310
11327
  case "":
11311
11328
  case "show":
11312
11329
  case "status": {
11313
- const current = await loadGoal(goalPath);
11330
+ const current = await loadGoal(goalPath, opts.events);
11314
11331
  if (!current) {
11315
11332
  const msg2 = "No goal set. Use `/goal set <mission text>` to create one.";
11316
11333
  opts.renderer.write(msg2);
@@ -11335,7 +11352,7 @@ function buildGoalCommand(opts) {
11335
11352
  if (!refined) {
11336
11353
  refined = refineGoalHeuristic(setText);
11337
11354
  }
11338
- const existing = await loadGoal(goalPath);
11355
+ const existing = await loadGoal(goalPath, opts.events);
11339
11356
  const now = (/* @__PURE__ */ new Date()).toISOString();
11340
11357
  const next = existing ? {
11341
11358
  ...existing,
@@ -11352,7 +11369,7 @@ function buildGoalCommand(opts) {
11352
11369
  refinedGoal: refined.refinedGoal,
11353
11370
  deliverables: refined.deliverables
11354
11371
  };
11355
- await saveGoal(goalPath, next);
11372
+ await saveGoal(goalPath, next, opts.events);
11356
11373
  const lines = [];
11357
11374
  lines.push(`\u{1F3AF} ${color.green("Goal locked:")} ${color.bold(refined.refinedGoal)}`);
11358
11375
  if (refined.refinedGoal !== setText) {
@@ -11379,7 +11396,7 @@ function buildGoalCommand(opts) {
11379
11396
  };
11380
11397
  }
11381
11398
  case "refine": {
11382
- const current = await loadGoal(goalPath);
11399
+ const current = await loadGoal(goalPath, opts.events);
11383
11400
  if (!current) {
11384
11401
  const msg2 = "No goal set to refine. Use /goal set <text> first.";
11385
11402
  opts.renderer.writeWarning(msg2);
@@ -11398,7 +11415,7 @@ function buildGoalCommand(opts) {
11398
11415
  refinedGoal: refined.refinedGoal,
11399
11416
  deliverables: refined.deliverables
11400
11417
  };
11401
- await saveGoal(goalPath, updated);
11418
+ await saveGoal(goalPath, updated, opts.events);
11402
11419
  const msg = `${color.green("\u2713")} Goal re-refined with ${refined.deliverables.length} deliverables.`;
11403
11420
  opts.renderer.write(msg);
11404
11421
  return { message: `${msg}
@@ -11407,14 +11424,14 @@ ${formatGoal(updated)}` };
11407
11424
  }
11408
11425
  case "clear":
11409
11426
  case "reset": {
11410
- const current = await loadGoal(goalPath);
11427
+ const current = await loadGoal(goalPath, opts.events);
11411
11428
  if (!current) {
11412
11429
  const msg2 = "No goal to clear.";
11413
11430
  opts.renderer.write(msg2);
11414
11431
  return { message: msg2 };
11415
11432
  }
11416
11433
  const abandoned = { ...current, goalState: "abandoned" };
11417
- await saveGoal(goalPath, abandoned);
11434
+ await saveGoal(goalPath, abandoned, opts.events);
11418
11435
  const { unlink: unlink4 } = await import('fs/promises');
11419
11436
  try {
11420
11437
  await unlink4(goalPath);
@@ -11428,7 +11445,7 @@ ${formatGoal(updated)}` };
11428
11445
  }
11429
11446
  case "journal":
11430
11447
  case "log": {
11431
- const current = await loadGoal(goalPath);
11448
+ const current = await loadGoal(goalPath, opts.events);
11432
11449
  if (!current) {
11433
11450
  const msg2 = "No goal set.";
11434
11451
  opts.renderer.write(msg2);
@@ -11453,7 +11470,7 @@ ${lines.join("\n")}`;
11453
11470
  return { message: msg };
11454
11471
  }
11455
11472
  case "pause": {
11456
- const current = await loadGoal(goalPath);
11473
+ const current = await loadGoal(goalPath, opts.events);
11457
11474
  if (!current) {
11458
11475
  const msg2 = "No goal set \u2014 nothing to pause.";
11459
11476
  opts.renderer.writeWarning(msg2);
@@ -11465,13 +11482,13 @@ ${lines.join("\n")}`;
11465
11482
  return { message: msg2 };
11466
11483
  }
11467
11484
  const paused = { ...current, goalState: "paused" };
11468
- await saveGoal(goalPath, paused);
11485
+ await saveGoal(goalPath, paused, opts.events);
11469
11486
  const msg = `${color.cyan("Goal paused.")} Current iteration will finish, then the loop stops. Use /goal resume to continue.`;
11470
11487
  opts.renderer.write(msg);
11471
11488
  return { message: msg };
11472
11489
  }
11473
11490
  case "resume": {
11474
- const current = await loadGoal(goalPath);
11491
+ const current = await loadGoal(goalPath, opts.events);
11475
11492
  if (!current) {
11476
11493
  const msg2 = "No goal set \u2014 cannot resume.";
11477
11494
  opts.renderer.writeWarning(msg2);
@@ -11483,7 +11500,7 @@ ${lines.join("\n")}`;
11483
11500
  return { message: msg2 };
11484
11501
  }
11485
11502
  const resumed = { ...current, goalState: "active" };
11486
- await saveGoal(goalPath, resumed);
11503
+ await saveGoal(goalPath, resumed, opts.events);
11487
11504
  const msg = `${color.green("Goal resumed.")} Loop will continue from the next iteration.`;
11488
11505
  opts.renderer.write(msg);
11489
11506
  return { message: msg };
@@ -11570,8 +11587,8 @@ function buildInitCommand(opts) {
11570
11587
  description: "Create or update .wrongstack/AGENTS.md project context for the system prompt.",
11571
11588
  async run(_args, ctx) {
11572
11589
  const root = ctx?.projectRoot ?? opts.projectRoot ?? process.cwd();
11573
- const dir = path38.join(root, ".wrongstack");
11574
- const file = path38.join(dir, "AGENTS.md");
11590
+ const dir = path39.join(root, ".wrongstack");
11591
+ const file = path39.join(dir, "AGENTS.md");
11575
11592
  const isFirstInit = !await fileExists(file);
11576
11593
  const detected = await detectProjectFacts(root);
11577
11594
  const body = renderAgentsTemplate(detected);
@@ -11579,7 +11596,7 @@ function buildInitCommand(opts) {
11579
11596
  await fsp5.writeFile(file, body, "utf8");
11580
11597
  let nodePkg = false;
11581
11598
  try {
11582
- await fsp5.access(path38.join(root, "package.json"));
11599
+ await fsp5.access(path39.join(root, "package.json"));
11583
11600
  nodePkg = true;
11584
11601
  } catch {
11585
11602
  }
@@ -12160,8 +12177,20 @@ async function runAdd(name, enable, configured, configPath2, mcpRegistry, all) {
12160
12177
  }
12161
12178
  async function runRemove(name, configured, configPath2, mcpRegistry) {
12162
12179
  if (!configured[name]) return `Server "${name}" is not in config.`;
12163
- await mcpRegistry.stop(name).catch(() => {
12164
- });
12180
+ try {
12181
+ await mcpRegistry.stop(name);
12182
+ } catch (err) {
12183
+ console.error(
12184
+ JSON.stringify({
12185
+ level: "warn",
12186
+ event: "mcp.stop_failed_on_remove",
12187
+ server: name,
12188
+ message: err instanceof Error ? err.message : String(err),
12189
+ note: "config entry removed but server may still be running",
12190
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
12191
+ })
12192
+ );
12193
+ }
12165
12194
  const full = await readConfig(configPath2);
12166
12195
  const mcpServers = {
12167
12196
  ...full.mcpServers ?? {}
@@ -12200,8 +12229,20 @@ async function runEnable(name, configured, configPath2, mcpRegistry) {
12200
12229
  async function runDisable(name, configured, configPath2, mcpRegistry) {
12201
12230
  const cfg = configured[name];
12202
12231
  if (!cfg) return `Server "${name}" is not in config.`;
12203
- await mcpRegistry.stop(name).catch(() => {
12204
- });
12232
+ try {
12233
+ await mcpRegistry.stop(name);
12234
+ } catch (err) {
12235
+ console.error(
12236
+ JSON.stringify({
12237
+ level: "warn",
12238
+ event: "mcp.stop_failed_on_disable",
12239
+ server: name,
12240
+ message: err instanceof Error ? err.message : String(err),
12241
+ note: "config marked disabled but server may still be running",
12242
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
12243
+ })
12244
+ );
12245
+ }
12205
12246
  const full = await readConfig(configPath2);
12206
12247
  const mcpServers = {
12207
12248
  ...full.mcpServers ?? {}
@@ -12239,9 +12280,9 @@ function stateBadge(state) {
12239
12280
  return color.dim(state);
12240
12281
  }
12241
12282
  }
12242
- async function readConfig(path39) {
12283
+ async function readConfig(path40) {
12243
12284
  try {
12244
- return JSON.parse(await fsp5.readFile(path39, "utf8"));
12285
+ return JSON.parse(await fsp5.readFile(path40, "utf8"));
12245
12286
  } catch {
12246
12287
  return {};
12247
12288
  }
@@ -12249,11 +12290,11 @@ async function readConfig(path39) {
12249
12290
  function isMcpServerRecord(value) {
12250
12291
  return !!value && typeof value === "object" && !Array.isArray(value);
12251
12292
  }
12252
- async function writeConfig(path39, cfg) {
12293
+ async function writeConfig(path40, cfg) {
12253
12294
  const raw = JSON.stringify(cfg, null, 2);
12254
- const tmp = path39 + ".tmp";
12295
+ const tmp = path40 + ".tmp";
12255
12296
  await fsp5.writeFile(tmp, raw, "utf8");
12256
- await fsp5.rename(tmp, path39);
12297
+ await fsp5.rename(tmp, path40);
12257
12298
  }
12258
12299
 
12259
12300
  // src/slash-commands/mcp.ts
@@ -14552,7 +14593,7 @@ async function listProjectsCommand(opts, ctx) {
14552
14593
  return { message: lines.join("\n") };
14553
14594
  }
14554
14595
  async function addProjectCommand(opts, ctx, targetPath, displayName) {
14555
- const resolved = path38.resolve(ctx?.projectRoot ?? ctx?.cwd ?? process.cwd(), targetPath);
14596
+ const resolved = path39.resolve(ctx?.projectRoot ?? ctx?.cwd ?? process.cwd(), targetPath);
14556
14597
  try {
14557
14598
  await fsp5.access(resolved);
14558
14599
  } catch {
@@ -14569,7 +14610,7 @@ async function addProjectCommand(opts, ctx, targetPath, displayName) {
14569
14610
  message: color.yellow(`Project already registered: "${existing.name}" (${existing.slug})`)
14570
14611
  };
14571
14612
  }
14572
- const name = displayName?.trim() || path38.basename(resolved);
14613
+ const name = displayName?.trim() || path39.basename(resolved);
14573
14614
  const slug = generateSlug(resolved);
14574
14615
  const now = (/* @__PURE__ */ new Date()).toISOString();
14575
14616
  await ensureProjectDataDir(slug, opts.paths?.globalConfig);
@@ -14622,7 +14663,7 @@ async function removeProjectCommand(opts, _ctx, slugOrName) {
14622
14663
  };
14623
14664
  }
14624
14665
  async function switchProjectCommand(opts, ctx, target, displayName) {
14625
- const resolved = path38.resolve(ctx?.projectRoot ?? ctx?.cwd ?? process.cwd(), target);
14666
+ const resolved = path39.resolve(ctx?.projectRoot ?? ctx?.cwd ?? process.cwd(), target);
14626
14667
  try {
14627
14668
  await fsp5.access(resolved);
14628
14669
  } catch {
@@ -14636,8 +14677,8 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
14636
14677
  try {
14637
14678
  const req2 = createRequire(import.meta.url);
14638
14679
  const pkgPath = req2.resolve("@wrongstack/cli/package.json");
14639
- const pkgDir = path38.dirname(pkgPath);
14640
- cliPath = path38.join(pkgDir, "dist", "index.js");
14680
+ const pkgDir = path39.dirname(pkgPath);
14681
+ cliPath = path39.join(pkgDir, "dist", "index.js");
14641
14682
  await fsp5.access(cliPath);
14642
14683
  } catch {
14643
14684
  cliPath = process.argv[1] ?? "";
@@ -14654,13 +14695,13 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
14654
14695
  if (existing) {
14655
14696
  existing.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
14656
14697
  } else {
14657
- const name = displayName?.trim() || path38.basename(resolved);
14698
+ const name = displayName?.trim() || path39.basename(resolved);
14658
14699
  const slug = generateSlug(resolved);
14659
14700
  manifest.projects.push({ name, root: resolved, slug, lastSeen: (/* @__PURE__ */ new Date()).toISOString() });
14660
14701
  await ensureProjectDataDir(slug, opts.paths?.globalConfig);
14661
14702
  }
14662
14703
  await saveManifest(manifest, opts.paths?.globalConfig);
14663
- const targetName = displayName?.trim() || path38.basename(resolved);
14704
+ const targetName = displayName?.trim() || path39.basename(resolved);
14664
14705
  const canSwitch = await confirmProjectSwitch(opts, targetName);
14665
14706
  if (!canSwitch) return { message: "" };
14666
14707
  const nodeExe = process.execPath;
@@ -14776,8 +14817,8 @@ async function spawnInProject(opts, _ctx, root, projectName) {
14776
14817
  try {
14777
14818
  const req2 = createRequire(import.meta.url);
14778
14819
  const pkgPath = req2.resolve("@wrongstack/cli/package.json");
14779
- const pkgDir = path38.dirname(pkgPath);
14780
- cliPath = path38.join(pkgDir, "dist", "index.js");
14820
+ const pkgDir = path39.dirname(pkgPath);
14821
+ cliPath = path39.join(pkgDir, "dist", "index.js");
14781
14822
  await fsp5.access(cliPath);
14782
14823
  } catch {
14783
14824
  cliPath = process.argv[1] ?? "";
@@ -14794,7 +14835,7 @@ async function spawnInProject(opts, _ctx, root, projectName) {
14794
14835
  if (existing) {
14795
14836
  existing.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
14796
14837
  } else {
14797
- const name = projectName || path38.basename(root);
14838
+ const name = projectName || path39.basename(root);
14798
14839
  const slug = generateSlug(root);
14799
14840
  manifest.projects.push({ name, root, slug, lastSeen: (/* @__PURE__ */ new Date()).toISOString() });
14800
14841
  await ensureProjectDataDir(slug, opts.paths?.globalConfig);
@@ -14826,8 +14867,8 @@ async function handleNewSession(_opts, _ctx) {
14826
14867
  try {
14827
14868
  const req2 = createRequire(import.meta.url);
14828
14869
  const pkgPath = req2.resolve("@wrongstack/cli/package.json");
14829
- const pkgDir = path38.dirname(pkgPath);
14830
- cliPath = path38.join(pkgDir, "dist", "index.js");
14870
+ const pkgDir = path39.dirname(pkgPath);
14871
+ cliPath = path39.join(pkgDir, "dist", "index.js");
14831
14872
  await fsp5.access(cliPath);
14832
14873
  } catch {
14833
14874
  cliPath = process.argv[1] ?? "";
@@ -14885,7 +14926,7 @@ async function handlePrevSessions(opts, _ctx) {
14885
14926
  return { message: lines.join("\n") };
14886
14927
  }
14887
14928
  async function runGit(args, cwd) {
14888
- return new Promise((resolve10) => {
14929
+ return new Promise((resolve11) => {
14889
14930
  const child = spawn("git", args, {
14890
14931
  cwd,
14891
14932
  stdio: ["ignore", "pipe", "pipe"],
@@ -14896,8 +14937,8 @@ async function runGit(args, cwd) {
14896
14937
  child.stdout?.on("data", (d) => {
14897
14938
  stdout += d;
14898
14939
  });
14899
- child.on("error", () => resolve10({ stdout, code: 1 }));
14900
- child.on("close", (code) => resolve10({ stdout, code: code ?? 0 }));
14940
+ child.on("error", () => resolve11({ stdout, code: 1 }));
14941
+ child.on("close", (code) => resolve11({ stdout, code: code ?? 0 }));
14901
14942
  });
14902
14943
  }
14903
14944
  async function getChangedFiles(cwd) {
@@ -14938,7 +14979,7 @@ function buildReviewCommand(opts) {
14938
14979
  for (const f of allChanged) {
14939
14980
  if (f.path.startsWith(".wrongstack/")) continue;
14940
14981
  try {
14941
- await fsp5.access(path38.join(cwd, f.path));
14982
+ await fsp5.access(path39.join(cwd, f.path));
14942
14983
  existing.push(f);
14943
14984
  } catch {
14944
14985
  }
@@ -14949,7 +14990,7 @@ function buildReviewCommand(opts) {
14949
14990
  const filesWithContent = [];
14950
14991
  for (const f of existing.slice(0, 30)) {
14951
14992
  try {
14952
- const content = await fsp5.readFile(path38.join(cwd, f.path), "utf8");
14993
+ const content = await fsp5.readFile(path39.join(cwd, f.path), "utf8");
14953
14994
  filesWithContent.push({ ...f, content });
14954
14995
  } catch {
14955
14996
  }
@@ -15322,7 +15363,7 @@ var DEFAULTS = {
15322
15363
  working_dir: true
15323
15364
  };
15324
15365
  function resolveConfigPath() {
15325
- return process.env[CONFIG_ENV] ?? path38.join(process.env.HOME ?? "", ".wrongstack", "statusline.json");
15366
+ return process.env[CONFIG_ENV] ?? path39.join(process.env.HOME ?? "", ".wrongstack", "statusline.json");
15326
15367
  }
15327
15368
  async function loadStatuslineConfig() {
15328
15369
  const p = resolveConfigPath();
@@ -15336,7 +15377,7 @@ async function loadStatuslineConfig() {
15336
15377
  async function saveStatuslineConfig(cfg) {
15337
15378
  const p = resolveConfigPath();
15338
15379
  try {
15339
- await fsp5.mkdir(path38.dirname(p), { recursive: true });
15380
+ await fsp5.mkdir(path39.dirname(p), { recursive: true });
15340
15381
  await atomicWrite(p, JSON.stringify(cfg, null, 2));
15341
15382
  } catch (err) {
15342
15383
  throw new FsError({
@@ -15748,13 +15789,13 @@ ${formatTaskProgress(file.tasks)}`;
15748
15789
  }
15749
15790
  async function discoverPackageFiles(projectRoot) {
15750
15791
  const files = [];
15751
- const rootPkg = path38.join(projectRoot, "package.json");
15792
+ const rootPkg = path39.join(projectRoot, "package.json");
15752
15793
  try {
15753
15794
  await fsp5.access(rootPkg);
15754
15795
  files.push(rootPkg);
15755
15796
  } catch {
15756
15797
  }
15757
- const workspaceFile = path38.join(projectRoot, "pnpm-workspace.yaml");
15798
+ const workspaceFile = path39.join(projectRoot, "pnpm-workspace.yaml");
15758
15799
  try {
15759
15800
  await fsp5.access(workspaceFile);
15760
15801
  const content = await fsp5.readFile(workspaceFile, "utf8");
@@ -15764,12 +15805,12 @@ async function discoverPackageFiles(projectRoot) {
15764
15805
  const globs = rawGlobs.split(/[\s,]+/).filter(Boolean).map((g) => g.replace(/['"]/g, ""));
15765
15806
  for (const g of globs) {
15766
15807
  const dirPrefix = g.replace(/\/?\*$/, "").replace(/\/\*$/, "");
15767
- const dir = path38.join(projectRoot, dirPrefix);
15808
+ const dir = path39.join(projectRoot, dirPrefix);
15768
15809
  try {
15769
15810
  const entries = await fsp5.readdir(dir, { withFileTypes: true });
15770
15811
  for (const e of entries) {
15771
15812
  if (!e.isDirectory()) continue;
15772
- const subPkg = path38.join(dir, e.name, "package.json");
15813
+ const subPkg = path39.join(dir, e.name, "package.json");
15773
15814
  try {
15774
15815
  await fsp5.access(subPkg);
15775
15816
  files.push(subPkg);
@@ -15784,7 +15825,7 @@ async function discoverPackageFiles(projectRoot) {
15784
15825
  return files;
15785
15826
  }
15786
15827
  function buildTechStackTask(opts) {
15787
- const pkgList = opts.packageFiles.map((f) => ` - ${path38.relative(opts.projectRoot, f)}`).join("\n");
15828
+ const pkgList = opts.packageFiles.map((f) => ` - ${path39.relative(opts.projectRoot, f)}`).join("\n");
15788
15829
  const header = opts.isInit ? [
15789
15830
  "## Tech Stack Audit \u2014 First-Time Project Init",
15790
15831
  "",
@@ -16212,7 +16253,7 @@ function buildWorkingDirCommand(_opts) {
16212
16253
  }
16213
16254
  const trimmed = args.trim();
16214
16255
  if (!trimmed) {
16215
- const rel2 = path38.relative(ctx.projectRoot, ctx.workingDir) || ".";
16256
+ const rel2 = path39.relative(ctx.projectRoot, ctx.workingDir) || ".";
16216
16257
  return {
16217
16258
  message: [
16218
16259
  `Working directory: ${color.bold(ctx.workingDir)}`,
@@ -16221,10 +16262,10 @@ function buildWorkingDirCommand(_opts) {
16221
16262
  ].join("\n")
16222
16263
  };
16223
16264
  }
16224
- const resolved = path38.isAbsolute(trimmed) ? path38.resolve(trimmed) : path38.resolve(ctx.projectRoot, trimmed);
16225
- const root = path38.resolve(ctx.projectRoot);
16226
- const rel = path38.relative(root, resolved);
16227
- if (rel.startsWith("..") || path38.isAbsolute(rel)) {
16265
+ const resolved = path39.isAbsolute(trimmed) ? path39.resolve(trimmed) : path39.resolve(ctx.projectRoot, trimmed);
16266
+ const root = path39.resolve(ctx.projectRoot);
16267
+ const rel = path39.relative(root, resolved);
16268
+ if (rel.startsWith("..") || path39.isAbsolute(rel)) {
16228
16269
  return {
16229
16270
  message: color.red(
16230
16271
  `Directory "${trimmed}" is outside the project root.
@@ -16249,8 +16290,8 @@ function buildWorkingDirCommand(_opts) {
16249
16290
  message: color.red(err instanceof Error ? err.message : String(err))
16250
16291
  };
16251
16292
  }
16252
- const prevRel = path38.relative(ctx.projectRoot, previous) || ".";
16253
- const newRel = path38.relative(ctx.projectRoot, resolved) || ".";
16293
+ const prevRel = path39.relative(ctx.projectRoot, previous) || ".";
16294
+ const newRel = path39.relative(ctx.projectRoot, resolved) || ".";
16254
16295
  return {
16255
16296
  message: [
16256
16297
  color.green(` \u2713 ${prevRel} \u2192 ${color.bold(newRel)}`),
@@ -16450,13 +16491,13 @@ var MANIFESTS = [
16450
16491
  ];
16451
16492
  async function detectProjectKind(projectRoot) {
16452
16493
  try {
16453
- await fsp5.access(path38.join(projectRoot, ".wrongstack", "AGENTS.md"));
16494
+ await fsp5.access(path39.join(projectRoot, ".wrongstack", "AGENTS.md"));
16454
16495
  return "initialized";
16455
16496
  } catch {
16456
16497
  }
16457
16498
  for (const m of MANIFESTS) {
16458
16499
  try {
16459
- await fsp5.access(path38.join(projectRoot, m));
16500
+ await fsp5.access(path39.join(projectRoot, m));
16460
16501
  return "project";
16461
16502
  } catch {
16462
16503
  }
@@ -16464,8 +16505,8 @@ async function detectProjectKind(projectRoot) {
16464
16505
  return "empty";
16465
16506
  }
16466
16507
  async function scaffoldAgentsMd(projectRoot) {
16467
- const dir = path38.join(projectRoot, ".wrongstack");
16468
- const file = path38.join(dir, "AGENTS.md");
16508
+ const dir = path39.join(projectRoot, ".wrongstack");
16509
+ const file = path39.join(dir, "AGENTS.md");
16469
16510
  const facts = await detectProjectFacts(projectRoot);
16470
16511
  const body = renderAgentsTemplate(facts);
16471
16512
  await fsp5.mkdir(dir, { recursive: true });
@@ -16478,7 +16519,7 @@ async function runProjectCheck(opts) {
16478
16519
  if (kind === "initialized") {
16479
16520
  renderer.write(
16480
16521
  `
16481
- ${color.green("\u2713")} Project initialized ${color.dim(`(${path38.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
16522
+ ${color.green("\u2713")} Project initialized ${color.dim(`(${path39.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
16482
16523
  `
16483
16524
  );
16484
16525
  return true;
@@ -16509,7 +16550,7 @@ async function runProjectCheck(opts) {
16509
16550
  }
16510
16551
  return true;
16511
16552
  }
16512
- const gitDir = path38.join(projectRoot, ".git");
16553
+ const gitDir = path39.join(projectRoot, ".git");
16513
16554
  let hasGit = false;
16514
16555
  try {
16515
16556
  await fsp5.access(gitDir);
@@ -16532,7 +16573,7 @@ async function runProjectCheck(opts) {
16532
16573
  if (answer2 === "y" || answer2 === "yes") {
16533
16574
  try {
16534
16575
  const { spawn: spawn6 } = await import('child_process');
16535
- await new Promise((resolve10, reject) => {
16576
+ await new Promise((resolve11, reject) => {
16536
16577
  const child = spawn6("git", ["init"], {
16537
16578
  cwd,
16538
16579
  signal: AbortSignal.timeout(1e4),
@@ -16541,7 +16582,7 @@ async function runProjectCheck(opts) {
16541
16582
  child.on("error", reject);
16542
16583
  child.on(
16543
16584
  "close",
16544
- (code) => code === 0 ? resolve10() : reject(new Error(`git init failed with ${code}`))
16585
+ (code) => code === 0 ? resolve11() : reject(new Error(`git init failed with ${code}`))
16545
16586
  );
16546
16587
  });
16547
16588
  renderer.write(` ${color.green("\u2713")} Git repository initialized
@@ -16744,11 +16785,11 @@ async function countProjectFiles(projectRoot, threshold) {
16744
16785
  for (const e of entries) {
16745
16786
  if (SKIP_DIRS.has(e.name)) continue;
16746
16787
  if (count >= threshold) return;
16747
- const full = path38.join(dir, e.name);
16788
+ const full = path39.join(dir, e.name);
16748
16789
  if (e.isDirectory()) {
16749
16790
  await walk(full);
16750
16791
  } else if (e.isFile()) {
16751
- if (INDEXABLE_EXTS.has(path38.extname(e.name))) {
16792
+ if (INDEXABLE_EXTS.has(path39.extname(e.name))) {
16752
16793
  count++;
16753
16794
  }
16754
16795
  }
@@ -17089,14 +17130,14 @@ function summarize(value, name) {
17089
17130
  if (typeof v === "object" && v !== null) {
17090
17131
  const o = v;
17091
17132
  if (name === "edit") {
17092
- const path39 = typeof o["path"] === "string" ? o["path"] : "";
17133
+ const path40 = typeof o["path"] === "string" ? o["path"] : "";
17093
17134
  const reps = typeof o["replacements"] === "number" ? o["replacements"] : 0;
17094
- return `${path39} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
17135
+ return `${path40} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
17095
17136
  }
17096
17137
  if (name === "write") {
17097
- const path39 = typeof o["path"] === "string" ? o["path"] : "";
17138
+ const path40 = typeof o["path"] === "string" ? o["path"] : "";
17098
17139
  const bytes = typeof o["bytes"] === "number" ? o["bytes"] : void 0;
17099
- return bytes !== void 0 ? `${path39} ${bytes}B` : path39;
17140
+ return bytes !== void 0 ? `${path40} ${bytes}B` : path40;
17100
17141
  }
17101
17142
  if (typeof o["count"] === "number") {
17102
17143
  return `${o["count"]} match${o["count"] === 1 ? "" : "es"}`;
@@ -17313,14 +17354,14 @@ var auditCmd = async (args, deps) => {
17313
17354
  return verify.ok ? 0 : 1;
17314
17355
  };
17315
17356
  async function listAudits(log, dir, deps) {
17316
- const fs38 = await import('fs/promises');
17317
- const path39 = await import('path');
17357
+ const fs39 = await import('fs/promises');
17358
+ const path40 = await import('path');
17318
17359
  const out = [];
17319
17360
  let foundRoot = true;
17320
17361
  const scan = async (scanDir, prefix, depth) => {
17321
17362
  let entries;
17322
17363
  try {
17323
- entries = await fs38.readdir(scanDir, { withFileTypes: true });
17364
+ entries = await fs39.readdir(scanDir, { withFileTypes: true });
17324
17365
  } catch {
17325
17366
  if (depth === 0) foundRoot = false;
17326
17367
  return;
@@ -17328,7 +17369,7 @@ async function listAudits(log, dir, deps) {
17328
17369
  for (const entry of entries) {
17329
17370
  if (entry.name.startsWith(".")) continue;
17330
17371
  if (entry.isDirectory()) {
17331
- if (depth === 0) await scan(path39.join(scanDir, entry.name), entry.name, depth + 1);
17372
+ if (depth === 0) await scan(path40.join(scanDir, entry.name), entry.name, depth + 1);
17332
17373
  continue;
17333
17374
  }
17334
17375
  if (!entry.isFile() || !entry.name.endsWith(".audit.jsonl")) continue;
@@ -18247,7 +18288,200 @@ try {
18247
18288
  } catch {
18248
18289
  }
18249
18290
 
18250
- // src/subcommands/handlers/diag-doctor.ts
18291
+ // src/subcommands/handlers/bench.ts
18292
+ var benchCmd = async (args, deps) => {
18293
+ const sub = args[0];
18294
+ const rest = args.slice(1);
18295
+ switch (sub) {
18296
+ case "run":
18297
+ return benchRun(rest, deps);
18298
+ case "report":
18299
+ return benchReport(rest, deps);
18300
+ case "list":
18301
+ return benchList(rest, deps);
18302
+ default:
18303
+ printUsage(deps);
18304
+ return sub === void 0 ? 0 : 1;
18305
+ }
18306
+ };
18307
+ function printUsage(deps) {
18308
+ deps.renderer.write(
18309
+ [
18310
+ color.bold("wstack bench") + " \u2014 model-independent agentic benchmarks",
18311
+ "",
18312
+ " run Run a suite across a model matrix and write a report",
18313
+ " report Re-render report.md from a finished run directory",
18314
+ " list Show available suites and configured model cells",
18315
+ "",
18316
+ color.dim("Examples:"),
18317
+ color.dim(
18318
+ " wstack bench run --suite polyglot --polyglot-dir ./polyglot --models bench.config.json --limit 5"
18319
+ ),
18320
+ color.dim(" wstack bench report ./bench-results/2026-06-14T10-00-00"),
18321
+ ""
18322
+ ].join("\n") + "\n"
18323
+ );
18324
+ }
18325
+ function flagStr(deps, name) {
18326
+ const v = deps.flags?.[name];
18327
+ return typeof v === "string" ? v : void 0;
18328
+ }
18329
+ function flagBool(deps, name) {
18330
+ const v = deps.flags?.[name];
18331
+ return v === true || v === "true";
18332
+ }
18333
+ async function resolveWstackEntry() {
18334
+ try {
18335
+ const req2 = createRequire(import.meta.url);
18336
+ const pkgPath = req2.resolve("@wrongstack/cli/package.json");
18337
+ const entry = path39.join(path39.dirname(pkgPath), "dist", "index.js");
18338
+ await fsp5.access(entry);
18339
+ return entry;
18340
+ } catch {
18341
+ return process.argv[1] ?? "";
18342
+ }
18343
+ }
18344
+ async function benchRun(_args, deps) {
18345
+ const suiteId = flagStr(deps, "suite") ?? "polyglot";
18346
+ const modelsPath = flagStr(deps, "models") ?? "bench.config.json";
18347
+ const limitRaw = flagStr(deps, "limit");
18348
+ const limit = limitRaw ? Math.max(1, Number.parseInt(limitRaw, 10)) : void 0;
18349
+ const outBase = flagStr(deps, "out") ?? "bench-results";
18350
+ let config;
18351
+ try {
18352
+ config = await loadBenchConfig(path39.resolve(deps.cwd, modelsPath));
18353
+ } catch (err) {
18354
+ deps.renderer.writeError(err instanceof Error ? err.message : String(err));
18355
+ return 1;
18356
+ }
18357
+ const concurrencyRaw = flagStr(deps, "concurrency");
18358
+ if (concurrencyRaw) {
18359
+ const c = Number.parseInt(concurrencyRaw, 10);
18360
+ if (c > 0) config.concurrency = c;
18361
+ }
18362
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
18363
+ const outDir = path39.resolve(deps.cwd, outBase, stamp);
18364
+ const predictionsDir = path39.join(outDir, "predictions");
18365
+ let suite;
18366
+ let grade;
18367
+ let isSwebench = false;
18368
+ if (suiteId === "polyglot") {
18369
+ const polyglotDir = flagStr(deps, "polyglot-dir");
18370
+ if (!polyglotDir) {
18371
+ deps.renderer.writeError("--polyglot-dir <path> is required for the polyglot suite.");
18372
+ return 1;
18373
+ }
18374
+ const languagesRaw = flagStr(deps, "languages");
18375
+ const languages = languagesRaw ? languagesRaw.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
18376
+ suite = createPolyglotSuite({ polyglotDir: path39.resolve(deps.cwd, polyglotDir), languages });
18377
+ grade = (a) => gradePolyglot(a);
18378
+ } else if (suiteId === "swebench") {
18379
+ isSwebench = true;
18380
+ const datasetDir = flagStr(deps, "dataset-dir");
18381
+ const docker = flagBool(deps, "docker");
18382
+ suite = createSwebenchSuite({
18383
+ datasetDir: datasetDir ? path39.resolve(deps.cwd, datasetDir) : void 0,
18384
+ docker
18385
+ });
18386
+ grade = (a) => gradeSwebench({ ...a, predictionsDir });
18387
+ } else {
18388
+ deps.renderer.writeError(`unknown suite "${suiteId}" (expected: polyglot | swebench)`);
18389
+ return 1;
18390
+ }
18391
+ const toolNames = deps.toolRegistry?.list().map((t) => t.name) ?? [];
18392
+ const wstackEntry = await resolveWstackEntry();
18393
+ deps.renderer.writeInfo(`Running ${suiteId} across ${config.cells.length} model(s)\u2026`);
18394
+ let report;
18395
+ try {
18396
+ report = await runBenchmark({
18397
+ suite,
18398
+ grade,
18399
+ config,
18400
+ cliVersion: CLI_VERSION,
18401
+ toolNames,
18402
+ nodeBin: process.execPath,
18403
+ wstackEntry,
18404
+ limit,
18405
+ onProgress: (msg) => deps.renderer.write(color.dim(msg) + "\n")
18406
+ });
18407
+ } catch (err) {
18408
+ deps.renderer.writeError(err instanceof Error ? err.message : String(err));
18409
+ return 1;
18410
+ }
18411
+ await writeJsonArtifacts(outDir, report);
18412
+ const md = renderMarkdownReport(report);
18413
+ await fsp5.writeFile(path39.join(outDir, "report.md"), md, "utf8");
18414
+ deps.renderer.write("\n" + md + "\n");
18415
+ if (isSwebench) {
18416
+ for (const cell of config.cells) {
18417
+ const preds = await collectCellPredictions(predictionsDir, cell.label);
18418
+ if (preds.length === 0) continue;
18419
+ const file = await writePredictionsJsonl(outDir, cell.label, preds);
18420
+ deps.renderer.writeInfo(`Predictions for "${cell.label}" \u2192 ${file}`);
18421
+ }
18422
+ deps.renderer.writeInfo(
18423
+ "Grade with the official SWE-bench harness: python -m swebench.harness.run_evaluation --predictions_path <file> --run_id <id>"
18424
+ );
18425
+ }
18426
+ deps.renderer.writeInfo(`Report written to ${path39.join(outDir, "report.md")}`);
18427
+ return 0;
18428
+ }
18429
+ async function benchReport(args, deps) {
18430
+ const dir = args.find((a) => !a.startsWith("-"));
18431
+ if (!dir) {
18432
+ deps.renderer.writeError("Usage: wstack bench report <run-directory>");
18433
+ return 1;
18434
+ }
18435
+ const outDir = path39.resolve(deps.cwd, dir);
18436
+ let summary;
18437
+ try {
18438
+ summary = await readSummary(outDir);
18439
+ } catch (err) {
18440
+ deps.renderer.writeError(
18441
+ `cannot read summary.json in ${outDir}: ${err instanceof Error ? err.message : String(err)}`
18442
+ );
18443
+ return 1;
18444
+ }
18445
+ const md = renderMarkdownReport(summary);
18446
+ await fsp5.writeFile(path39.join(outDir, "report.md"), md, "utf8");
18447
+ deps.renderer.write("\n" + md + "\n");
18448
+ return 0;
18449
+ }
18450
+ async function benchList(_args, deps) {
18451
+ deps.renderer.write(color.bold("Suites\n"));
18452
+ deps.renderer.write(
18453
+ " polyglot " + color.dim("Aider polyglot (edit accuracy) \u2014 Phase 1, Docker-free\n")
18454
+ );
18455
+ deps.renderer.write(
18456
+ " swebench " + color.dim("SWE-bench Verified (end-to-end) \u2014 Phase 2, Docker-gated\n")
18457
+ );
18458
+ const modelsPath = flagStr(deps, "models");
18459
+ if (modelsPath) {
18460
+ try {
18461
+ const config = await loadBenchConfig(path39.resolve(deps.cwd, modelsPath));
18462
+ deps.renderer.write("\n" + color.bold("Model cells\n"));
18463
+ for (const cell of config.cells) {
18464
+ deps.renderer.write(
18465
+ ` ${cell.label.padEnd(16)} ${color.dim(`${cell.provider}/${cell.model}`)}
18466
+ `
18467
+ );
18468
+ }
18469
+ const fp = reportHeaderLine({
18470
+ cliVersion: CLI_VERSION,
18471
+ toolNames: deps.toolRegistry?.list().map((t) => t.name) ?? [],
18472
+ maxIterations: config.maxIterations,
18473
+ yolo: true,
18474
+ subsetId: "(computed at run time)",
18475
+ hash: "(computed at run time)"
18476
+ });
18477
+ deps.renderer.write("\n" + color.dim(`Harness: ${fp}`) + "\n");
18478
+ } catch (err) {
18479
+ deps.renderer.writeError(err instanceof Error ? err.message : String(err));
18480
+ return 1;
18481
+ }
18482
+ }
18483
+ return 0;
18484
+ }
18251
18485
  var diagCmd = async (_args, deps) => {
18252
18486
  const cfg = deps.config;
18253
18487
  const age = await deps.modelsRegistry.ageSeconds();
@@ -18341,7 +18575,7 @@ var doctorCmd = async (_args, deps) => {
18341
18575
  }
18342
18576
  try {
18343
18577
  await fsp5.mkdir(deps.paths.projectSessions, { recursive: true });
18344
- const probe = path38.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
18578
+ const probe = path39.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
18345
18579
  await fsp5.writeFile(probe, "");
18346
18580
  await fsp5.unlink(probe);
18347
18581
  checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
@@ -18444,8 +18678,8 @@ var exportCmd = async (args, deps) => {
18444
18678
  return 1;
18445
18679
  }
18446
18680
  if (output) {
18447
- await fsp5.mkdir(path38.dirname(path38.resolve(deps.cwd, output)), { recursive: true });
18448
- await fsp5.writeFile(path38.resolve(deps.cwd, output), rendered, "utf8");
18681
+ await fsp5.mkdir(path39.dirname(path39.resolve(deps.cwd, output)), { recursive: true });
18682
+ await fsp5.writeFile(path39.resolve(deps.cwd, output), rendered, "utf8");
18449
18683
  deps.renderer.write(`Wrote ${rendered.length} bytes to ${output}
18450
18684
  `);
18451
18685
  } else {
@@ -18521,8 +18755,8 @@ var initCmd = async (_args, deps) => {
18521
18755
  const vault = new DefaultSecretVault({ keyFile: deps.paths.secretsKey });
18522
18756
  const encrypted = encryptConfigSecrets$1(config, vault);
18523
18757
  await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2), { mode: 384 });
18524
- await fsp5.mkdir(path38.join(deps.projectRoot, ".wrongstack"), { recursive: true });
18525
- const agentsFile = path38.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
18758
+ await fsp5.mkdir(path39.join(deps.projectRoot, ".wrongstack"), { recursive: true });
18759
+ const agentsFile = path39.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
18526
18760
  const projectFacts = await detectProjectFacts(deps.projectRoot);
18527
18761
  await atomicWrite(agentsFile, renderAgentsTemplate(projectFacts));
18528
18762
  deps.renderer.writeInfo(`Wrote ${deps.paths.globalConfig}`);
@@ -18661,8 +18895,8 @@ async function serveMcpStdio(deps) {
18661
18895
  log(
18662
18896
  `wrongstack MCP server ready at ${handle2.url} \u2014 exposing ${allowed.length} tool(s) (${mode})${token ? " [token auth]" : ""}.`
18663
18897
  );
18664
- await new Promise((resolve10) => {
18665
- const stop = () => resolve10();
18898
+ await new Promise((resolve11) => {
18899
+ const stop = () => resolve11();
18666
18900
  process.once("SIGINT", stop);
18667
18901
  process.once("SIGTERM", stop);
18668
18902
  });
@@ -19885,7 +20119,7 @@ var usageCmd = async (_args, deps) => {
19885
20119
  return 0;
19886
20120
  };
19887
20121
  var projectsCmd = async (_args, deps) => {
19888
- const projectsRoot = path38.join(deps.paths.globalRoot, "projects");
20122
+ const projectsRoot = path39.join(deps.paths.globalRoot, "projects");
19889
20123
  try {
19890
20124
  const entries = await fsp5.readdir(projectsRoot);
19891
20125
  if (entries.length === 0) {
@@ -19895,7 +20129,7 @@ var projectsCmd = async (_args, deps) => {
19895
20129
  for (const hash of entries) {
19896
20130
  try {
19897
20131
  const meta = JSON.parse(
19898
- await fsp5.readFile(path38.join(projectsRoot, hash, "meta.json"), "utf8")
20132
+ await fsp5.readFile(path39.join(projectsRoot, hash, "meta.json"), "utf8")
19899
20133
  );
19900
20134
  deps.renderer.write(
19901
20135
  ` ${color.dim(hash)} ${color.dim(meta.lastSeen ?? "")} ${meta.root ?? "?"}
@@ -20344,7 +20578,7 @@ function findSessionId(args) {
20344
20578
  var rewindCmd = async (args, deps) => {
20345
20579
  const flags = parseRewindFlags(args);
20346
20580
  const wpaths = resolveWstackPaths({ projectRoot: deps.projectRoot });
20347
- const sessionsDir = path38.join(wpaths.globalRoot, "sessions");
20581
+ const sessionsDir = path39.join(wpaths.globalRoot, "sessions");
20348
20582
  const rewind = new DefaultSessionRewinder(sessionsDir, deps.projectRoot);
20349
20583
  let sessionId = findSessionId(args);
20350
20584
  if (!sessionId) {
@@ -20489,7 +20723,7 @@ async function listFleetRuns(deps) {
20489
20723
  }
20490
20724
  const runs = [];
20491
20725
  for (const id of entries) {
20492
- const runDir = path38.join(deps.paths.projectSessions, id);
20726
+ const runDir = path39.join(deps.paths.projectSessions, id);
20493
20727
  let stat7;
20494
20728
  try {
20495
20729
  stat7 = await fsp5.stat(runDir);
@@ -20502,17 +20736,17 @@ async function listFleetRuns(deps) {
20502
20736
  let subagentCount = 0;
20503
20737
  let subagentsDir;
20504
20738
  try {
20505
- await fsp5.access(path38.join(runDir, "fleet.json"));
20739
+ await fsp5.access(path39.join(runDir, "fleet.json"));
20506
20740
  manifest = true;
20507
20741
  } catch {
20508
20742
  }
20509
20743
  try {
20510
- await fsp5.access(path38.join(runDir, "checkpoint.json"));
20744
+ await fsp5.access(path39.join(runDir, "checkpoint.json"));
20511
20745
  checkpoint = true;
20512
20746
  } catch {
20513
20747
  }
20514
20748
  try {
20515
- subagentsDir = path38.join(runDir, "subagents");
20749
+ subagentsDir = path39.join(runDir, "subagents");
20516
20750
  const files = await fsp5.readdir(subagentsDir);
20517
20751
  subagentCount = files.filter((f) => f.endsWith(".jsonl")).length;
20518
20752
  } catch {
@@ -20539,7 +20773,7 @@ async function listFleetRuns(deps) {
20539
20773
  return 0;
20540
20774
  }
20541
20775
  async function showFleetRun(runId, deps) {
20542
- const runDir = path38.join(deps.paths.projectSessions, runId);
20776
+ const runDir = path39.join(deps.paths.projectSessions, runId);
20543
20777
  let stat7;
20544
20778
  try {
20545
20779
  stat7 = await fsp5.stat(runDir);
@@ -20556,7 +20790,7 @@ async function showFleetRun(runId, deps) {
20556
20790
  deps.renderer.write(color.bold(`
20557
20791
  Fleet Run: ${runId}
20558
20792
  `) + "\n");
20559
- const manifestPath = path38.join(runDir, "fleet.json");
20793
+ const manifestPath = path39.join(runDir, "fleet.json");
20560
20794
  let manifestData = null;
20561
20795
  try {
20562
20796
  manifestData = await fsp5.readFile(manifestPath, "utf8");
@@ -20574,7 +20808,7 @@ Fleet Run: ${runId}
20574
20808
  deps.renderer.write(` ${color.dim("\u25CB")} fleet.json \u2014 not found
20575
20809
  `);
20576
20810
  }
20577
- const checkpointPath = path38.join(runDir, "checkpoint.json");
20811
+ const checkpointPath = path39.join(runDir, "checkpoint.json");
20578
20812
  let checkpointData = null;
20579
20813
  try {
20580
20814
  checkpointData = await fsp5.readFile(checkpointPath, "utf8");
@@ -20621,7 +20855,7 @@ Fleet Run: ${runId}
20621
20855
  } catch {
20622
20856
  }
20623
20857
  }
20624
- const subagentsDir = path38.join(runDir, "subagents");
20858
+ const subagentsDir = path39.join(runDir, "subagents");
20625
20859
  let subagentFiles = [];
20626
20860
  try {
20627
20861
  subagentFiles = await fsp5.readdir(subagentsDir);
@@ -20633,7 +20867,7 @@ Fleet Run: ${runId}
20633
20867
  Subagent transcripts (${subagentFiles.length}):
20634
20868
  `);
20635
20869
  for (const f of subagentFiles.sort()) {
20636
- const filePath = path38.join(subagentsDir, f);
20870
+ const filePath = path39.join(subagentsDir, f);
20637
20871
  let size;
20638
20872
  try {
20639
20873
  const s = await fsp5.stat(filePath);
@@ -20650,7 +20884,7 @@ Fleet Run: ${runId}
20650
20884
  ${color.dim("\u25CB")} No subagent transcripts
20651
20885
  `);
20652
20886
  }
20653
- const sharedDir = path38.join(runDir, "shared");
20887
+ const sharedDir = path39.join(runDir, "shared");
20654
20888
  try {
20655
20889
  const files = await fsp5.readdir(sharedDir);
20656
20890
  deps.renderer.write(`
@@ -20834,7 +21068,7 @@ var updateCmd = async (args, deps) => {
20834
21068
  deps.renderer.write(`Updating wrongstack from v${info.current} to v${info.latest}...
20835
21069
  `);
20836
21070
  try {
20837
- const result = await new Promise((resolve10, reject) => {
21071
+ const result = await new Promise((resolve11, reject) => {
20838
21072
  const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
20839
21073
  const child = spawn(npmCommand, ["install", "-g", "wrongstack@latest"], {
20840
21074
  cwd,
@@ -20847,7 +21081,7 @@ var updateCmd = async (args, deps) => {
20847
21081
  _stderr += d;
20848
21082
  });
20849
21083
  child.on("error", reject);
20850
- child.on("close", (code) => resolve10({ code: code ?? 0 }));
21084
+ child.on("close", (code) => resolve11({ code: code ?? 0 }));
20851
21085
  });
20852
21086
  if (result.code === 0) {
20853
21087
  deps.renderer.write(
@@ -20958,7 +21192,8 @@ var subcommands = {
20958
21192
  help: helpCmd,
20959
21193
  projects: projectsCmd,
20960
21194
  modeldiag: modeldiagCmd,
20961
- quick: quickCmd
21195
+ quick: quickCmd,
21196
+ bench: benchCmd
20962
21197
  };
20963
21198
 
20964
21199
  // src/boot.ts
@@ -20976,7 +21211,7 @@ function resolveBundledSkillsDir() {
20976
21211
  try {
20977
21212
  const req2 = createRequire(import.meta.url);
20978
21213
  const corePkg = req2.resolve("@wrongstack/core/package.json");
20979
- return path38.join(path38.dirname(corePkg), "skills");
21214
+ return path39.join(path39.dirname(corePkg), "skills");
20980
21215
  } catch {
20981
21216
  return void 0;
20982
21217
  }
@@ -21197,7 +21432,7 @@ async function boot(argv) {
21197
21432
  const created = await registerProjectAtBoot({ projectRoot, cwd, wpaths });
21198
21433
  if (created && isInteractiveTTY) {
21199
21434
  renderer.write(
21200
- color.dim(` \u2713 Registered "${path38.basename(projectRoot)}" in projects.json.
21435
+ color.dim(` \u2713 Registered "${path39.basename(projectRoot)}" in projects.json.
21201
21436
  `)
21202
21437
  );
21203
21438
  }
@@ -21277,7 +21512,7 @@ async function boot(argv) {
21277
21512
  } catch {
21278
21513
  }
21279
21514
  printLaunchHints(renderer, flags, {
21280
- cursorFile: path38.join(wpaths.cacheDir, "hint-cursor")
21515
+ cursorFile: path39.join(wpaths.cacheDir, "hint-cursor")
21281
21516
  });
21282
21517
  } else {
21283
21518
  const effectiveChoices = config.launch ? {
@@ -21315,7 +21550,7 @@ async function boot(argv) {
21315
21550
  }
21316
21551
  async function checkGitInCwd(opts) {
21317
21552
  const { cwd, renderer, reader } = opts;
21318
- const cwdGit = path38.join(cwd, ".git");
21553
+ const cwdGit = path39.join(cwd, ".git");
21319
21554
  let hasCwdGit = false;
21320
21555
  try {
21321
21556
  await fsp5.access(cwdGit);
@@ -21332,7 +21567,7 @@ async function checkGitInCwd(opts) {
21332
21567
  if (answer === "y" || answer === "yes") {
21333
21568
  try {
21334
21569
  const { spawn: spawn6 } = await import('child_process');
21335
- await new Promise((resolve10, reject) => {
21570
+ await new Promise((resolve11, reject) => {
21336
21571
  const child = spawn6("git", ["init"], {
21337
21572
  cwd,
21338
21573
  signal: AbortSignal.timeout(1e4),
@@ -21341,7 +21576,7 @@ async function checkGitInCwd(opts) {
21341
21576
  child.on("error", reject);
21342
21577
  child.on(
21343
21578
  "close",
21344
- (code) => code === 0 ? resolve10() : reject(new Error(`git init failed with ${code}`))
21579
+ (code) => code === 0 ? resolve11() : reject(new Error(`git init failed with ${code}`))
21345
21580
  );
21346
21581
  });
21347
21582
  renderer.write(` ${color.green("\u2713")} Git repository initialized
@@ -21355,10 +21590,10 @@ async function checkGitInCwd(opts) {
21355
21590
  }
21356
21591
  }
21357
21592
  }
21358
- const parentDir = path38.dirname(cwd);
21593
+ const parentDir = path39.dirname(cwd);
21359
21594
  if (parentDir !== cwd) {
21360
21595
  try {
21361
- await fsp5.access(path38.join(parentDir, ".git"));
21596
+ await fsp5.access(path39.join(parentDir, ".git"));
21362
21597
  renderer.write(
21363
21598
  ` ${color.dim("\u2139")} A ${color.bold(".git")} repo exists in the parent directory: ${color.dim(parentDir)}
21364
21599
  `
@@ -21370,7 +21605,7 @@ async function checkGitInCwd(opts) {
21370
21605
  async function registerProjectAtBoot(opts) {
21371
21606
  const { projectRoot, cwd, wpaths } = opts;
21372
21607
  const manifest = await loadManifest(wpaths.globalConfig);
21373
- const existed = manifest.projects.some((p) => path38.resolve(p.root) === path38.resolve(projectRoot));
21608
+ const existed = manifest.projects.some((p) => path39.resolve(p.root) === path39.resolve(projectRoot));
21374
21609
  await touchProjectInManifest({
21375
21610
  projectRoot,
21376
21611
  globalConfigPath: wpaths.globalConfig,
@@ -21571,7 +21806,7 @@ function resolveBundledSkillsDir2() {
21571
21806
  try {
21572
21807
  const req2 = createRequire(import.meta.url);
21573
21808
  const corePkg = req2.resolve("@wrongstack/core/package.json");
21574
- return path38.join(path38.dirname(corePkg), "skills");
21809
+ return path39.join(path39.dirname(corePkg), "skills");
21575
21810
  } catch {
21576
21811
  return void 0;
21577
21812
  }
@@ -21735,22 +21970,22 @@ function renderFleetLine(states, now, columns, version) {
21735
21970
  const visible = line.replace(/\x1b\[[0-9;]*m/g, "");
21736
21971
  if (visible.length > max) {
21737
21972
  let count = 0;
21738
- let out = "";
21973
+ const out = [];
21739
21974
  let i = 0;
21740
21975
  while (i < line.length && count < max - 1) {
21741
21976
  if (line[i] === "\x1B") {
21742
21977
  const end = line.indexOf("m", i);
21743
21978
  if (end !== -1) {
21744
- out += line.slice(i, end + 1);
21979
+ out.push(line.slice(i, end + 1));
21745
21980
  i = end + 1;
21746
21981
  continue;
21747
21982
  }
21748
21983
  }
21749
- out += line[i];
21984
+ if (line[i] !== void 0) out.push(line[i]);
21750
21985
  count++;
21751
21986
  i++;
21752
21987
  }
21753
- line = out + "\u2026";
21988
+ line = out.join("") + "\u2026";
21754
21989
  }
21755
21990
  return line;
21756
21991
  }
@@ -22059,7 +22294,7 @@ async function runRepl(opts) {
22059
22294
  clientMailbox.registerClient({
22060
22295
  clientId,
22061
22296
  sessionId: replProjectRoot,
22062
- name: `REPL [${path38.basename(replProjectRoot)}]`,
22297
+ name: `REPL [${path39.basename(replProjectRoot)}]`,
22063
22298
  source: "repl",
22064
22299
  pid: process.pid
22065
22300
  }).then(() => {
@@ -22127,7 +22362,7 @@ async function runRepl(opts) {
22127
22362
  `[eternal] ${err instanceof Error ? err.message : String(err)}`
22128
22363
  );
22129
22364
  }
22130
- await new Promise((resolve10) => setTimeout(resolve10, 250));
22365
+ await new Promise((resolve11) => setTimeout(resolve11, 250));
22131
22366
  continue;
22132
22367
  }
22133
22368
  } else if (opts.getAutonomy?.() === "eternal-parallel") {
@@ -22203,7 +22438,7 @@ async function runRepl(opts) {
22203
22438
  `[parallel] ${err instanceof Error ? err.message : String(err)}`
22204
22439
  );
22205
22440
  }
22206
- await new Promise((resolve10) => setTimeout(resolve10, 250));
22441
+ await new Promise((resolve11) => setTimeout(resolve11, 250));
22207
22442
  continue;
22208
22443
  }
22209
22444
  }
@@ -22770,12 +23005,12 @@ ${color.cyan("\u23F3 Auto")} ${color.dim("(Ctrl+C to cancel)")}
22770
23005
  let interval;
22771
23006
  let lastTickedSecond = sec + 1;
22772
23007
  let onAbort;
22773
- return new Promise((resolve10) => {
22774
- onAbort = () => resolve10(false);
23008
+ return new Promise((resolve11) => {
23009
+ onAbort = () => resolve11(false);
22775
23010
  signal.addEventListener("abort", onAbort, { once: true });
22776
23011
  interval = setInterval(() => {
22777
23012
  if (signal.aborted) {
22778
- resolve10(false);
23013
+ resolve11(false);
22779
23014
  return;
22780
23015
  }
22781
23016
  const elapsed = Date.now() - start;
@@ -22783,7 +23018,7 @@ ${color.cyan("\u23F3 Auto")} ${color.dim("(Ctrl+C to cancel)")}
22783
23018
  if (remaining <= 0) {
22784
23019
  opts.renderer.write(color.dim(` \u21B3 ${truncated}
22785
23020
  `));
22786
- resolve10(true);
23021
+ resolve11(true);
22787
23022
  return;
22788
23023
  }
22789
23024
  if (opts.onCountdownTick && remaining !== lastTickedSecond) {
@@ -22794,7 +23029,7 @@ ${color.cyan("\u23F3 Auto")} ${color.dim("(Ctrl+C to cancel)")}
22794
23029
  opts.renderer.write(
22795
23030
  color.yellow(" \u21B3 Countdown cancelled \u2014 switching to manual mode\n")
22796
23031
  );
22797
- resolve10(false);
23032
+ resolve11(false);
22798
23033
  return;
22799
23034
  }
22800
23035
  } catch {
@@ -22940,6 +23175,23 @@ async function execute(deps) {
22940
23175
  restoredToolCalls,
22941
23176
  needsSetup
22942
23177
  } = deps;
23178
+ const rootTraceId = context.traceId;
23179
+ const storageLog = (event, payload) => {
23180
+ const traceId = payload.traceId ?? rootTraceId;
23181
+ console.warn(JSON.stringify({
23182
+ level: "info",
23183
+ event,
23184
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
23185
+ traceId,
23186
+ ...payload
23187
+ }));
23188
+ };
23189
+ const onStorageRead = (...args) => storageLog("storage.read", args[0]);
23190
+ const onStorageWrite = (...args) => storageLog("storage.write", args[0]);
23191
+ const onStorageError = (...args) => storageLog("storage.error", args[0]);
23192
+ events.on("storage.read", onStorageRead);
23193
+ events.on("storage.write", onStorageWrite);
23194
+ events.on("storage.error", onStorageError);
22943
23195
  let pendingChimeraWork;
22944
23196
  events.onPattern("chimera.review_needed", (_event, payload) => {
22945
23197
  const p = payload;
@@ -23385,7 +23637,7 @@ async function execute(deps) {
23385
23637
  const toWrite = targetPath === wpaths.globalConfig ? decrypted : filterSafeForProject(decrypted);
23386
23638
  const encrypted = encryptConfigSecrets(toWrite, noOpVault);
23387
23639
  if (targetPath !== wpaths.globalConfig) {
23388
- await fsp5.mkdir(path38.dirname(targetPath), { recursive: true });
23640
+ await fsp5.mkdir(path39.dirname(targetPath), { recursive: true });
23389
23641
  }
23390
23642
  await atomicWrite(targetPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
23391
23643
  configStore.update({
@@ -23462,7 +23714,7 @@ async function execute(deps) {
23462
23714
  agentsMonitorController,
23463
23715
  getLiveSessions: async () => {
23464
23716
  const { SessionRegistry } = await import('@wrongstack/core');
23465
- const globalRoot = path38.dirname(wpaths.globalConfig);
23717
+ const globalRoot = path39.dirname(wpaths.globalConfig);
23466
23718
  const registry = new SessionRegistry(globalRoot);
23467
23719
  const sessions = await registry.list();
23468
23720
  return sessions.filter((s) => s.status !== "stale").map((s) => ({
@@ -23537,31 +23789,35 @@ async function execute(deps) {
23537
23789
  const metaMode = context.meta?.["mode"];
23538
23790
  return typeof metaMode === "string" ? metaMode : modeId ?? "default";
23539
23791
  },
23540
- registerDebugStreamCallback: (cb) => {
23541
- void import('@wrongstack/providers').then(({ setDebugStreamCallback }) => setDebugStreamCallback(cb)).catch(
23542
- (err) => console.error(
23792
+ registerDebugStreamCallback: async (cb) => {
23793
+ try {
23794
+ const { setDebugStreamCallback } = await import('@wrongstack/providers');
23795
+ setDebugStreamCallback(cb);
23796
+ } catch (err) {
23797
+ console.error(
23543
23798
  JSON.stringify({
23544
23799
  level: "error",
23545
23800
  event: "execution.debug_stream_register_failed",
23546
23801
  message: err instanceof Error ? err.message : String(err),
23547
23802
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
23548
23803
  })
23549
- )
23550
- );
23804
+ );
23805
+ }
23551
23806
  },
23552
- restoreDebugStreamCallback: () => {
23553
- void import('@wrongstack/providers').then(
23554
- ({ setDebugStreamCallback, defaultDebugStreamCallback }) => setDebugStreamCallback(defaultDebugStreamCallback)
23555
- ).catch(
23556
- (err) => console.error(
23807
+ restoreDebugStreamCallback: async () => {
23808
+ try {
23809
+ const { setDebugStreamCallback, defaultDebugStreamCallback } = await import('@wrongstack/providers');
23810
+ setDebugStreamCallback(defaultDebugStreamCallback);
23811
+ } catch (err) {
23812
+ console.error(
23557
23813
  JSON.stringify({
23558
23814
  level: "error",
23559
23815
  event: "execution.debug_stream_restore_failed",
23560
23816
  message: err instanceof Error ? err.message : String(err),
23561
23817
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
23562
23818
  })
23563
- )
23564
- );
23819
+ );
23820
+ }
23565
23821
  },
23566
23822
  restoredMessages,
23567
23823
  restoredToolCalls,
@@ -23587,7 +23843,7 @@ async function execute(deps) {
23587
23843
  if (!sessionStore) return null;
23588
23844
  try {
23589
23845
  const { SessionRegistry } = await import('@wrongstack/core');
23590
- const registry = new SessionRegistry(path38.dirname(wpaths.globalConfig));
23846
+ const registry = new SessionRegistry(path39.dirname(wpaths.globalConfig));
23591
23847
  const live = (await registry.list()).find(
23592
23848
  (s) => s.sessionId === sessionId && s.status !== "stale" && s.pid !== process.pid
23593
23849
  );
@@ -23616,12 +23872,14 @@ async function execute(deps) {
23616
23872
  if (oldWriter && oldWriter !== resumed.writer) {
23617
23873
  const endedUsage = tokenCounter.total();
23618
23874
  void (async () => {
23875
+ let appendOk = false;
23619
23876
  try {
23620
23877
  await oldWriter.append({
23621
23878
  type: "session_end",
23622
23879
  ts: (/* @__PURE__ */ new Date()).toISOString(),
23623
23880
  usage: endedUsage
23624
23881
  });
23882
+ appendOk = true;
23625
23883
  } catch (err) {
23626
23884
  console.error(
23627
23885
  JSON.stringify({
@@ -23632,32 +23890,50 @@ async function execute(deps) {
23632
23890
  })
23633
23891
  );
23634
23892
  }
23635
- try {
23636
- await oldWriter.close();
23637
- } catch (err) {
23638
- console.error(
23639
- JSON.stringify({
23640
- level: "error",
23641
- event: "execution.session_close_failed",
23642
- message: err instanceof Error ? err.message : String(err),
23643
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
23644
- })
23645
- );
23893
+ if (appendOk) {
23894
+ try {
23895
+ await oldWriter.close();
23896
+ } catch (err) {
23897
+ console.error(
23898
+ JSON.stringify({
23899
+ level: "error",
23900
+ event: "execution.session_close_failed",
23901
+ message: err instanceof Error ? err.message : String(err),
23902
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
23903
+ })
23904
+ );
23905
+ }
23646
23906
  }
23647
23907
  })();
23648
23908
  }
23649
23909
  agent.ctx.session = resumed.writer;
23650
23910
  tokenCounter.reset();
23651
- void recoveryLock.clear().then(() => recoveryLock.write(resumed.writer.id)).catch(
23652
- (err) => console.error(
23653
- JSON.stringify({
23654
- level: "error",
23655
- event: "execution.recovery_lock_update_failed",
23656
- message: err instanceof Error ? err.message : String(err),
23657
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
23658
- })
23659
- )
23660
- );
23911
+ void (async () => {
23912
+ try {
23913
+ await recoveryLock.clear();
23914
+ } catch (err) {
23915
+ console.error(
23916
+ JSON.stringify({
23917
+ level: "warn",
23918
+ event: "execution.recovery_lock_clear_failed",
23919
+ message: err instanceof Error ? err.message : String(err),
23920
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
23921
+ })
23922
+ );
23923
+ }
23924
+ try {
23925
+ await recoveryLock.write(resumed.writer.id);
23926
+ } catch (err) {
23927
+ console.error(
23928
+ JSON.stringify({
23929
+ level: "error",
23930
+ event: "execution.recovery_lock_update_failed",
23931
+ message: err instanceof Error ? err.message : String(err),
23932
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
23933
+ })
23934
+ );
23935
+ }
23936
+ })();
23661
23937
  const { replaySessionEvents } = await import('@wrongstack/tui');
23662
23938
  const entries = replaySessionEvents(
23663
23939
  resumed.data.events,
@@ -23705,7 +23981,7 @@ async function execute(deps) {
23705
23981
  if (slug === "new-session") {
23706
23982
  pendingProjectSwitch = {
23707
23983
  root: projectRoot,
23708
- name: path38.basename(projectRoot) || projectRoot
23984
+ name: path39.basename(projectRoot) || projectRoot
23709
23985
  };
23710
23986
  }
23711
23987
  return;
@@ -23766,13 +24042,13 @@ ${parts.join("\n")}
23766
24042
  const { root, name, resumeSessionId } = pendingProjectSwitch;
23767
24043
  process.stdout.write("\x1B[2J\x1B[H");
23768
24044
  const { spawn: spawn6 } = await import('child_process');
23769
- const { createRequire: createRequire7 } = await import('module');
24045
+ const { createRequire: createRequire8 } = await import('module');
23770
24046
  let cliPath;
23771
24047
  try {
23772
- const req2 = createRequire7(import.meta.url);
24048
+ const req2 = createRequire8(import.meta.url);
23773
24049
  const pkgPath = req2.resolve("@wrongstack/cli/package.json");
23774
- const pkgDir = path38.dirname(pkgPath);
23775
- cliPath = path38.join(pkgDir, "dist", "index.js");
24050
+ const pkgDir = path39.dirname(pkgPath);
24051
+ cliPath = path39.join(pkgDir, "dist", "index.js");
23776
24052
  await fsp5.access(cliPath);
23777
24053
  } catch {
23778
24054
  cliPath = process.argv[1] ?? "";
@@ -23846,12 +24122,12 @@ ${parts.join("\n")}
23846
24122
  )
23847
24123
  );
23848
24124
  renderer.writeInfo(color.dim(" Press Ctrl+C in this terminal to stop the WebUI server.\n"));
23849
- const webuiExit = new Promise((resolve10) => {
24125
+ const webuiExit = new Promise((resolve11) => {
23850
24126
  const onSigint = () => {
23851
24127
  renderer.setSilent(false);
23852
24128
  renderer.write("\n");
23853
24129
  renderer.writeInfo(color.yellow(" Shutting down WebUI server\u2026"));
23854
- resolve10(0);
24130
+ resolve11(0);
23855
24131
  };
23856
24132
  process.on("SIGINT", onSigint);
23857
24133
  process.on("SIGTERM", onSigint);
@@ -23859,13 +24135,13 @@ ${parts.join("\n")}
23859
24135
  renderer.setSilent(false);
23860
24136
  process.off("SIGINT", onSigint);
23861
24137
  process.off("SIGTERM", onSigint);
23862
- resolve10(0);
24138
+ resolve11(0);
23863
24139
  }).catch((err) => {
23864
24140
  renderer.setSilent(false);
23865
24141
  process.off("SIGINT", onSigint);
23866
24142
  process.off("SIGTERM", onSigint);
23867
24143
  console.debug(`[execution] webui error: ${err}`);
23868
- resolve10(1);
24144
+ resolve11(1);
23869
24145
  });
23870
24146
  });
23871
24147
  code = await webuiExit;
@@ -23880,7 +24156,7 @@ ${parts.join("\n")}
23880
24156
  supportsVision,
23881
24157
  attachments,
23882
24158
  effectiveMaxContext,
23883
- projectName: path38.basename(projectRoot) || void 0,
24159
+ projectName: path39.basename(projectRoot) || void 0,
23884
24160
  getAutonomy,
23885
24161
  onAutonomy,
23886
24162
  getNextPredict,
@@ -24003,7 +24279,7 @@ function buildRoutingRunner(config, host) {
24003
24279
  }
24004
24280
 
24005
24281
  // src/fleet/host.ts
24006
- var MultiAgentHost = class {
24282
+ var MultiAgentHost = class _MultiAgentHost {
24007
24283
  constructor(deps, opts = {}) {
24008
24284
  this.deps = deps;
24009
24285
  this.opts = opts;
@@ -24032,6 +24308,19 @@ var MultiAgentHost = class {
24032
24308
  * actionable error instead of a generic "Director could not be activated".
24033
24309
  */
24034
24310
  promotionBlockReason = null;
24311
+ /** Guards `buildDirector` from overwriting a runner set by `spawnACP`. */
24312
+ directorRunnerSet = false;
24313
+ /** Event-bus off-handles registered in `buildDirector` — cleaned up in `dispose()`. */
24314
+ directorOffHandles = [];
24315
+ /** Coordinator task.assigned listener — cleaned up in `dispose()`. */
24316
+ coordinatorOffHandle = null;
24317
+ /** ACP runner cache — keyed by role/subagentId, reused across tasks to avoid
24318
+ * creating a new transport process on every ACP task dispatch. Stores the
24319
+ * pending promise so concurrent calls for the same subagentId share one spawn.
24320
+ * Bounded to 20 entries with LRU eviction to prevent unbounded memory growth. */
24321
+ acpRunnerCache = /* @__PURE__ */ new Map();
24322
+ acpRunnerAccessOrder = [];
24323
+ static ACP_CACHE_MAX = 20;
24035
24324
  /**
24036
24325
  * Force the lazy build path to run *now* and return the live Director,
24037
24326
  * or null when director mode is off. Used by the CLI to register the
@@ -24080,7 +24369,8 @@ var MultiAgentHost = class {
24080
24369
  if (this.opts.sessionsRoot && !this.sessionFactory) {
24081
24370
  this.sessionFactory = makeDirectorSessionFactory({
24082
24371
  sessionsRoot: this.opts.sessionsRoot,
24083
- directorRunId: this.opts.directorRunId
24372
+ directorRunId: this.opts.directorRunId,
24373
+ traceId: this.opts.traceId
24084
24374
  });
24085
24375
  }
24086
24376
  const coordinatorConfig = {
@@ -24088,7 +24378,7 @@ var MultiAgentHost = class {
24088
24378
  doneCondition: { type: "all_tasks_done" },
24089
24379
  maxConcurrent: this.opts.maxConcurrent ?? 4
24090
24380
  };
24091
- const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ? path38.join(this.opts.sessionsRoot, this.opts.directorRunId, "shared") : void 0);
24381
+ const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ? path39.join(this.opts.sessionsRoot, this.opts.directorRunId, "shared") : void 0);
24092
24382
  this.director = new Director({
24093
24383
  config: coordinatorConfig,
24094
24384
  manifestPath: this.opts.manifestPath,
@@ -24113,60 +24403,71 @@ var MultiAgentHost = class {
24113
24403
  this.fleetManager?.removePendingTask(task.id);
24114
24404
  this.emitLifecycleCompleted(task.id, result);
24115
24405
  });
24116
- this.director.fleet.filter("budget.threshold_reached", (e) => {
24117
- const payload = e.payload;
24118
- this.deps.events.emit("subagent.budget_warning", {
24119
- subagentId: e.subagentId,
24120
- kind: payload.kind,
24121
- used: payload.used,
24122
- limit: payload.limit
24123
- });
24124
- });
24125
- this.director.fleet.filter("budget.extended", (e) => {
24126
- const payload = e.payload;
24127
- this.deps.events.emit("subagent.budget_extended", {
24128
- subagentId: e.subagentId,
24129
- kind: payload.kind,
24130
- newLimit: payload.newLimit,
24131
- totalExtensions: payload.totalExtensions
24132
- });
24133
- });
24134
- this.director.fleet.filter("ctx.pct", (e) => {
24135
- const payload = e.payload;
24136
- this.deps.events.emit("subagent.ctx_pct", {
24137
- subagentId: e.subagentId,
24138
- load: payload.load,
24139
- tokens: payload.tokens,
24140
- maxContext: payload.maxContext
24141
- });
24142
- });
24143
- this.director.fleet.filter("subagent.spawned", (e) => {
24144
- const payload = e.payload;
24145
- this.deps.events.emit("subagent.spawned", {
24146
- subagentId: payload.subagentId,
24147
- taskId: payload.taskId,
24148
- name: payload.name,
24149
- provider: payload.provider,
24150
- model: payload.model
24151
- });
24152
- });
24153
- this.getCoordinator().on(
24154
- "task.assigned",
24155
- ({
24156
- task,
24157
- subagentId
24158
- }) => {
24159
- this.deps.events.emit("subagent.task_started", {
24160
- subagentId,
24161
- taskId: task.id,
24162
- description: task.description
24406
+ this.directorOffHandles.push(
24407
+ this.director.fleet.filter("budget.threshold_reached", (e) => {
24408
+ const payload = e.payload;
24409
+ this.deps.events.emit("subagent.budget_warning", {
24410
+ subagentId: e.subagentId,
24411
+ kind: payload.kind,
24412
+ used: payload.used,
24413
+ limit: payload.limit
24163
24414
  });
24164
- }
24415
+ })
24416
+ );
24417
+ this.directorOffHandles.push(
24418
+ this.director.fleet.filter("budget.extended", (e) => {
24419
+ const payload = e.payload;
24420
+ this.deps.events.emit("subagent.budget_extended", {
24421
+ subagentId: e.subagentId,
24422
+ kind: payload.kind,
24423
+ newLimit: payload.newLimit,
24424
+ totalExtensions: payload.totalExtensions
24425
+ });
24426
+ })
24427
+ );
24428
+ this.directorOffHandles.push(
24429
+ this.director.fleet.filter("ctx.pct", (e) => {
24430
+ const payload = e.payload;
24431
+ this.deps.events.emit("subagent.ctx_pct", {
24432
+ subagentId: e.subagentId,
24433
+ load: payload.load,
24434
+ tokens: payload.tokens,
24435
+ maxContext: payload.maxContext
24436
+ });
24437
+ })
24438
+ );
24439
+ this.directorOffHandles.push(
24440
+ this.director.fleet.filter("subagent.spawned", (e) => {
24441
+ const payload = e.payload;
24442
+ this.deps.events.emit("subagent.spawned", {
24443
+ subagentId: payload.subagentId,
24444
+ taskId: payload.taskId,
24445
+ name: payload.name,
24446
+ provider: payload.provider,
24447
+ model: payload.model
24448
+ });
24449
+ })
24165
24450
  );
24451
+ const coordinatorTaskAssignedHandler = ({
24452
+ task,
24453
+ subagentId
24454
+ }) => {
24455
+ this.deps.events.emit("subagent.task_started", {
24456
+ subagentId,
24457
+ taskId: task.id,
24458
+ description: task.description
24459
+ });
24460
+ };
24461
+ const coordinator = this.getCoordinator();
24462
+ coordinator.on("task.assigned", coordinatorTaskAssignedHandler);
24463
+ this.coordinatorOffHandle = () => coordinator.off("task.assigned", coordinatorTaskAssignedHandler);
24166
24464
  this.fleetEmitTool = makeFleetEmitTool(this.director);
24167
24465
  this.fleetStatusTool = makeFleetStatusTool(this.director);
24168
24466
  const runner = await this.buildSubagentRunner(config);
24169
- this.getCoordinator().setRunner(runner);
24467
+ if (!this.directorRunnerSet) {
24468
+ this.getCoordinator().setRunner(runner);
24469
+ this.directorRunnerSet = true;
24470
+ }
24170
24471
  }
24171
24472
  /**
24172
24473
  * Returns the FleetEmitTool for director-mode subagents, if the director
@@ -24368,9 +24669,23 @@ var MultiAgentHost = class {
24368
24669
  return buildRoutingRunner(config, this);
24369
24670
  }
24370
24671
  async buildACPRunner(subagentId) {
24672
+ const cached = this.acpRunnerCache.get(subagentId);
24673
+ if (cached) {
24674
+ const idx = this.acpRunnerAccessOrder.indexOf(subagentId);
24675
+ if (idx !== -1) this.acpRunnerAccessOrder.splice(idx, 1);
24676
+ this.acpRunnerAccessOrder.push(subagentId);
24677
+ return cached;
24678
+ }
24371
24679
  const cmd = ACP_AGENT_COMMANDS[subagentId];
24372
24680
  if (!cmd) throw new Error(`Unknown ACP agent: ${subagentId}`);
24373
- return makeACPSubagentRunner(cmd);
24681
+ while (this.acpRunnerAccessOrder.length >= _MultiAgentHost.ACP_CACHE_MAX) {
24682
+ const oldest = this.acpRunnerAccessOrder.shift();
24683
+ if (oldest) this.acpRunnerCache.delete(oldest);
24684
+ }
24685
+ const p = makeACPSubagentRunner(cmd);
24686
+ this.acpRunnerCache.set(subagentId, p);
24687
+ this.acpRunnerAccessOrder.push(subagentId);
24688
+ return p;
24374
24689
  }
24375
24690
  /**
24376
24691
  * Build a Provider for a subagent. When `overrideId` is supplied (from
@@ -24413,6 +24728,7 @@ var MultiAgentHost = class {
24413
24728
  const coordinator = this.getCoordinator();
24414
24729
  const acpRunner = await this.buildACPRunner(subagentId);
24415
24730
  coordinator.setRunner(acpRunner);
24731
+ this.directorRunnerSet = true;
24416
24732
  await coordinator.spawn({
24417
24733
  id: subagentId,
24418
24734
  name: subagentId,
@@ -24482,8 +24798,9 @@ var MultiAgentHost = class {
24482
24798
  */
24483
24799
  async spawnAndWait(description, opts) {
24484
24800
  const { taskId } = await this.spawn(description, opts);
24485
- if (!this.director) throw new Error("Director is not initialized");
24486
- const results = await this.director.awaitTasks([taskId]);
24801
+ const director = this.director;
24802
+ if (!director) throw new Error("Director is not initialized");
24803
+ const results = await director.awaitTasks([taskId]);
24487
24804
  const result = results[0];
24488
24805
  if (!result) throw new Error(`Task ${taskId} completed but no result returned`);
24489
24806
  return result;
@@ -24618,16 +24935,16 @@ var MultiAgentHost = class {
24618
24935
  if (this.director) return this.director;
24619
24936
  this.opts.directorMode = true;
24620
24937
  if (this.opts.fleetRoot && !this.opts.manifestPath) {
24621
- this.opts.manifestPath = path38.join(this.opts.fleetRoot, "fleet.json");
24938
+ this.opts.manifestPath = path39.join(this.opts.fleetRoot, "fleet.json");
24622
24939
  }
24623
24940
  if (this.opts.fleetRoot && !this.opts.sharedScratchpadPath) {
24624
- this.opts.sharedScratchpadPath = path38.join(this.opts.fleetRoot, "shared");
24941
+ this.opts.sharedScratchpadPath = path39.join(this.opts.fleetRoot, "shared");
24625
24942
  }
24626
24943
  if (this.opts.fleetRoot && !this.opts.sessionsRoot) {
24627
- this.opts.sessionsRoot = path38.join(this.opts.fleetRoot, "subagents");
24944
+ this.opts.sessionsRoot = path39.join(this.opts.fleetRoot, "subagents");
24628
24945
  }
24629
24946
  if (this.opts.fleetRoot && !this.opts.stateCheckpointPath) {
24630
- this.opts.stateCheckpointPath = path38.join(this.opts.fleetRoot, "director-state.json");
24947
+ this.opts.stateCheckpointPath = path39.join(this.opts.fleetRoot, "director-state.json");
24631
24948
  }
24632
24949
  await this.ensureDirector();
24633
24950
  return this.director ?? null;
@@ -24697,6 +25014,24 @@ var MultiAgentHost = class {
24697
25014
  this.getCoordinator().setMaxConcurrent(v);
24698
25015
  }
24699
25016
  }
25017
+ /**
25018
+ * Clean up all listeners and resources held by the host.
25019
+ * Unregisters all EventBus/FleetBus listeners registered in `buildDirector`
25020
+ * and stops the Director and its coordinator.
25021
+ *
25022
+ * Safe to call multiple times — subsequent calls are no-ops.
25023
+ */
25024
+ async dispose() {
25025
+ for (const off of this.directorOffHandles) {
25026
+ off();
25027
+ }
25028
+ this.directorOffHandles.length = 0;
25029
+ this.coordinatorOffHandle?.();
25030
+ this.coordinatorOffHandle = null;
25031
+ if (this.director) {
25032
+ await this.director.shutdown();
25033
+ }
25034
+ }
24700
25035
  };
24701
25036
 
24702
25037
  // src/session-stats.ts
@@ -24738,11 +25073,11 @@ var SessionStats = class {
24738
25073
  if (tool.name === "bash") this.bashCommands++;
24739
25074
  else if (tool.name === "fetch") this.fetches++;
24740
25075
  if (!tool.ok) return;
24741
- const path39 = typeof input?.path === "string" ? input.path : void 0;
24742
- if (tool.name === "read" && path39) this.readPaths.add(path39);
24743
- else if (tool.name === "edit" && path39) this.editedPaths.add(path39);
24744
- else if (tool.name === "write" && path39) {
24745
- this.writtenPaths.add(path39);
25076
+ const path40 = typeof input?.path === "string" ? input.path : void 0;
25077
+ if (tool.name === "read" && path40) this.readPaths.add(path40);
25078
+ else if (tool.name === "edit" && path40) this.editedPaths.add(path40);
25079
+ else if (tool.name === "write" && path40) {
25080
+ this.writtenPaths.add(path40);
24746
25081
  const content = typeof input?.content === "string" ? input.content : "";
24747
25082
  this.bytesWritten += Buffer.byteLength(content, "utf8");
24748
25083
  }
@@ -25054,7 +25389,7 @@ async function setupCodebaseIndexing(deps) {
25054
25389
  if (tool?.mutating && FILE_EDIT_TOOLS.has(tool.name)) {
25055
25390
  const fp = payload.toolUse.input?.file_path;
25056
25391
  if (typeof fp === "string" && fp.length > 0) {
25057
- const abs = path38.resolve(payload.ctx.cwd, fp);
25392
+ const abs = path39.resolve(payload.ctx.cwd, fp);
25058
25393
  if (isIndexableFile(abs)) {
25059
25394
  enqueueReindex({ projectRoot, files: [abs], debounceMs, onError });
25060
25395
  }
@@ -25073,7 +25408,7 @@ async function setupCodebaseIndexing(deps) {
25073
25408
  if (!filename) return;
25074
25409
  const rel = filename.toString();
25075
25410
  if (isIgnored(rel)) return;
25076
- const abs = path38.resolve(projectRoot, rel);
25411
+ const abs = path39.resolve(projectRoot, rel);
25077
25412
  if (!isIndexableFile(abs)) return;
25078
25413
  enqueueReindex({ projectRoot, files: [abs], debounceMs, onError });
25079
25414
  });
@@ -25127,7 +25462,7 @@ function setupMetrics(params) {
25127
25462
  const dumpMetrics = () => {
25128
25463
  if (!metricsSink) return;
25129
25464
  try {
25130
- const out = path38.join(wpaths.projectSessions, "metrics.json");
25465
+ const out = path39.join(wpaths.projectSessions, "metrics.json");
25131
25466
  const snap = metricsSink.snapshot();
25132
25467
  writeFileSync(out, JSON.stringify(snap, null, 2));
25133
25468
  } catch {
@@ -25305,7 +25640,9 @@ async function setupSession(params) {
25305
25640
  tokenCounter,
25306
25641
  renderer,
25307
25642
  flags,
25308
- onRecovery
25643
+ onRecovery,
25644
+ // Optional EventBus for storage observability
25645
+ events: eventsBus
25309
25646
  } = params;
25310
25647
  sessionStore.prune(DEFAULT_SESSION_PRUNE_DAYS).then((count) => {
25311
25648
  if (count > 0) renderer.writeInfo(`Pruned ${count} old session${count === 1 ? "" : "s"}.`);
@@ -25362,10 +25699,10 @@ async function setupSession(params) {
25362
25699
  );
25363
25700
  });
25364
25701
  const attachments = new DefaultAttachmentStore({
25365
- spoolDir: path38.join(wpaths.projectSessions, session?.id, "attachments")
25702
+ spoolDir: path39.join(wpaths.projectSessions, session?.id, "attachments")
25366
25703
  });
25367
- const queueStore = new QueueStore({ dir: path38.join(wpaths.projectSessions, session?.id) });
25368
25704
  const ctxSignal = new AbortController().signal;
25705
+ const traceId = randomBytes(16).toString("hex");
25369
25706
  const context = new Context({
25370
25707
  systemPrompt,
25371
25708
  provider,
@@ -25376,17 +25713,27 @@ async function setupSession(params) {
25376
25713
  projectRoot,
25377
25714
  model: config.model,
25378
25715
  agentId: "leader",
25379
- agentName: "Leader Agent"
25716
+ agentName: "Leader Agent",
25717
+ traceId
25380
25718
  });
25381
25719
  context.meta["packageTrackerOpts"] = {
25382
25720
  storageDir: wpaths.projectDir,
25383
25721
  projectRoot
25384
25722
  };
25385
25723
  if (restoredMessages.length > 0) context.state.replaceMessages(restoredMessages);
25386
- const todosCheckpointPath = path38.join(wpaths.projectSessions, `${session?.id}.todos.json`);
25724
+ const queueStore = new QueueStore({
25725
+ dir: path39.join(wpaths.projectSessions, session?.id),
25726
+ ...eventsBus ? { events: eventsBus } : {},
25727
+ ...traceId ? { traceId } : {}
25728
+ });
25729
+ const todosCheckpointPath = path39.join(wpaths.projectSessions, `${session?.id}.todos.json`);
25387
25730
  if (resumeId) {
25388
25731
  try {
25389
- const restoredTodos = await loadTodosCheckpoint(todosCheckpointPath);
25732
+ const restoredTodos = await loadTodosCheckpoint(
25733
+ todosCheckpointPath,
25734
+ eventsBus,
25735
+ traceId
25736
+ );
25390
25737
  if (restoredTodos && restoredTodos.length > 0) {
25391
25738
  context.state.replaceTodos(restoredTodos);
25392
25739
  renderer.writeInfo(
@@ -25399,17 +25746,19 @@ async function setupSession(params) {
25399
25746
  const detachTodosCheckpoint = attachTodosCheckpoint(
25400
25747
  context.state,
25401
25748
  todosCheckpointPath,
25402
- session?.id
25749
+ session?.id,
25750
+ eventsBus,
25751
+ traceId
25403
25752
  );
25404
- const planPath = path38.join(wpaths.projectSessions, `${session?.id}.plan.json`);
25753
+ const planPath = path39.join(wpaths.projectSessions, `${session?.id}.plan.json`);
25405
25754
  context.state.setMeta("plan.path", planPath);
25406
- const taskPath = path38.join(wpaths.projectSessions, `${session?.id}.tasks.json`);
25755
+ const taskPath = path39.join(wpaths.projectSessions, `${session?.id}.tasks.json`);
25407
25756
  context.state.setMeta("task.path", taskPath);
25408
25757
  let dirState;
25409
25758
  if (resumeId) {
25410
25759
  try {
25411
- const fleetRoot = path38.join(wpaths.projectSessions, session?.id);
25412
- dirState = await loadDirectorState(path38.join(fleetRoot, "director-state.json"));
25760
+ const fleetRoot = path39.join(wpaths.projectSessions, session?.id);
25761
+ dirState = await loadDirectorState(path39.join(fleetRoot, "director-state.json"));
25413
25762
  if (dirState) {
25414
25763
  const tCounts = {};
25415
25764
  for (const t of dirState.tasks) tCounts[t.status] = (tCounts[t.status] ?? 0) + 1;
@@ -25435,6 +25784,7 @@ async function setupSession(params) {
25435
25784
  return {
25436
25785
  session: expectDefined(session),
25437
25786
  sessionRef,
25787
+ traceId,
25438
25788
  context,
25439
25789
  restoredMessages,
25440
25790
  attachments,
@@ -25655,7 +26005,7 @@ async function main(argv) {
25655
26005
  projectGoal: wpaths.projectGoal,
25656
26006
  projectSessions: wpaths.projectSessions
25657
26007
  },
25658
- pathJoiner: { join: (a, b) => path38.join(a, b) },
26008
+ pathJoiner: { join: (a, b) => path39.join(a, b) },
25659
26009
  systemPromptBuilderToken: TOKENS.SystemPromptBuilder
25660
26010
  });
25661
26011
  const toolRegistry = new ToolRegistry();
@@ -25775,6 +26125,7 @@ async function main(argv) {
25775
26125
  tokenCounter,
25776
26126
  renderer,
25777
26127
  flags,
26128
+ events,
25778
26129
  onRecovery: (abandoned, autoRecover) => promptRecovery(reader, renderer, abandoned, autoRecover)
25779
26130
  });
25780
26131
  const session = sessResult.session;
@@ -25786,12 +26137,13 @@ async function main(argv) {
25786
26137
  const planPath = sessResult.planPath;
25787
26138
  const detachTodosCheckpoint = sessResult.detachTodosCheckpoint;
25788
26139
  const priorFleetState = sessResult.priorFleetState;
26140
+ memoryStore.withTraceId(sessResult.traceId);
25789
26141
  let tracker;
25790
26142
  try {
25791
26143
  const { getSessionRegistry, AgentStatusTracker } = await import('@wrongstack/core');
25792
26144
  const registry = getSessionRegistry(wpaths.globalRoot);
25793
- const projectSlug2 = path38.basename(wpaths.projectDir);
25794
- const projectName = path38.basename(projectRoot);
26145
+ const projectSlug2 = path39.basename(wpaths.projectDir);
26146
+ const projectName = path39.basename(projectRoot);
25795
26147
  let gitBranch;
25796
26148
  try {
25797
26149
  const { execSync } = await import('child_process');
@@ -25890,7 +26242,7 @@ async function main(argv) {
25890
26242
  if (e.ok && (e.name === "write" || e.name === "edit" || e.name === "replace" || e.name === "patch")) {
25891
26243
  const filePath = e.input?.path;
25892
26244
  if (filePath) {
25893
- const projectDir = path38.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
26245
+ const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
25894
26246
  void recordFileAction(
25895
26247
  { storageDir: projectDir, projectRoot },
25896
26248
  {
@@ -26056,7 +26408,7 @@ async function main(argv) {
26056
26408
  let depWatcherDispose;
26057
26409
  if (dwCfg?.["enabled"] === true) {
26058
26410
  try {
26059
- const projectDir = path38.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
26411
+ const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
26060
26412
  const dwMailbox = new GlobalMailbox(projectDir, events);
26061
26413
  depWatcherDispose = attachDepWatcherBridge({
26062
26414
  events,
@@ -26155,12 +26507,12 @@ async function main(argv) {
26155
26507
  }
26156
26508
  }
26157
26509
  };
26158
- const fleetRoot = directorMode ? path38.join(wpaths.projectSessions, session.id) : void 0;
26159
- const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path38.join(expectDefined(fleetRoot), "fleet.json") : void 0;
26160
- const sharedScratchpadPath = directorMode ? path38.join(expectDefined(fleetRoot), "shared") : void 0;
26161
- const subagentSessionsRoot = directorMode ? path38.join(expectDefined(fleetRoot), "subagents") : void 0;
26162
- const stateCheckpointPath = directorMode ? path38.join(expectDefined(fleetRoot), "director-state.json") : void 0;
26163
- const fleetRootForPromotion = path38.join(wpaths.projectSessions, session.id);
26510
+ const fleetRoot = directorMode ? path39.join(wpaths.projectSessions, session.id) : void 0;
26511
+ const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path39.join(expectDefined(fleetRoot), "fleet.json") : void 0;
26512
+ const sharedScratchpadPath = directorMode ? path39.join(expectDefined(fleetRoot), "shared") : void 0;
26513
+ const subagentSessionsRoot = directorMode ? path39.join(expectDefined(fleetRoot), "subagents") : void 0;
26514
+ const stateCheckpointPath = directorMode ? path39.join(expectDefined(fleetRoot), "director-state.json") : void 0;
26515
+ const fleetRootForPromotion = path39.join(wpaths.projectSessions, session.id);
26164
26516
  const brainSettings = {
26165
26517
  maxAutoRisk: "medium"
26166
26518
  };
@@ -26265,7 +26617,8 @@ async function main(argv) {
26265
26617
  sessionWriter: session,
26266
26618
  maxConcurrent,
26267
26619
  getLeaderMaxContext: () => effectiveMaxContext,
26268
- brain
26620
+ brain,
26621
+ traceId: sessResult.traceId
26269
26622
  }
26270
26623
  );
26271
26624
  toolRegistry.register(
@@ -26302,7 +26655,7 @@ async function main(argv) {
26302
26655
  let techStackConsumerDispose;
26303
26656
  if (dwCfg?.["enabled"] === true) {
26304
26657
  try {
26305
- const projectDir = path38.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
26658
+ const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
26306
26659
  const tsMailbox = new GlobalMailbox(projectDir, events);
26307
26660
  const fileAuthorOpts = {
26308
26661
  storageDir: projectDir,
@@ -26335,7 +26688,7 @@ async function main(argv) {
26335
26688
  let pkgOutdatedDispose;
26336
26689
  if (dwCfg?.["enabled"] === true) {
26337
26690
  try {
26338
- const projectDir = path38.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
26691
+ const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
26339
26692
  const pkgMailbox = new GlobalMailbox(projectDir, events);
26340
26693
  const pkgTrackerOpts = {
26341
26694
  storageDir: projectDir,
@@ -26709,7 +27062,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
26709
27062
  return director.spawn(cfg);
26710
27063
  },
26711
27064
  onFleetLog: async (subagentId, mode) => {
26712
- const subagentsRoot = path38.join(fleetRootForPromotion, "subagents");
27065
+ const subagentsRoot = path39.join(fleetRootForPromotion, "subagents");
26713
27066
  let runDirs;
26714
27067
  try {
26715
27068
  runDirs = await fsp5.readdir(subagentsRoot);
@@ -26718,7 +27071,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
26718
27071
  }
26719
27072
  const found = [];
26720
27073
  for (const runId of runDirs) {
26721
- const runDir = path38.join(subagentsRoot, runId);
27074
+ const runDir = path39.join(subagentsRoot, runId);
26722
27075
  let files;
26723
27076
  try {
26724
27077
  files = await fsp5.readdir(runDir);
@@ -26727,7 +27080,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
26727
27080
  }
26728
27081
  for (const f of files) {
26729
27082
  if (!f.endsWith(".jsonl")) continue;
26730
- const full = path38.join(runDir, f);
27083
+ const full = path39.join(runDir, f);
26731
27084
  try {
26732
27085
  const stat7 = await fsp5.stat(full);
26733
27086
  found.push({
@@ -26828,7 +27181,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
26828
27181
  }
26829
27182
  const dir = await multiAgentHost.ensureDirector();
26830
27183
  if (!dir) return "Director is not available.";
26831
- const dirStatePath = path38.join(fleetRootForPromotion, "director-state.json");
27184
+ const dirStatePath = path39.join(fleetRootForPromotion, "director-state.json");
26832
27185
  const prior = await loadDirectorState(dirStatePath);
26833
27186
  if (!prior) {
26834
27187
  return "No prior director-state.json found \u2014 nothing to retry.";
@@ -26897,9 +27250,9 @@ ${color.dim("\u2500".repeat(40))}` : "";
26897
27250
  for (const tool of director2.tools(FLEET_ROSTER)) {
26898
27251
  toolRegistry.register(tool);
26899
27252
  }
26900
- const mp = path38.join(fleetRootForPromotion, "fleet.json");
26901
- const sp = path38.join(fleetRootForPromotion, "shared");
26902
- const ss = path38.join(fleetRootForPromotion, "subagents");
27253
+ const mp = path39.join(fleetRootForPromotion, "fleet.json");
27254
+ const sp = path39.join(fleetRootForPromotion, "shared");
27255
+ const ss = path39.join(fleetRootForPromotion, "subagents");
26903
27256
  const lines = [
26904
27257
  `${color.green("\u2713")} Promoted to director mode.`,
26905
27258
  ` Roster: ${Object.keys(FLEET_ROSTER).join(", ")}`,
@@ -27003,7 +27356,8 @@ Restart WrongStack to load or unload plugin code in this session.`;
27003
27356
  // Real per-role factory: each dispatched slot runs as a fresh,
27004
27357
  // isolated agent with the role's filtered tools + persona prompt
27005
27358
  // (instead of sharing the leader agent's Context).
27006
- subagentFactory: multiAgentHost.makeSubagentFactory(config)
27359
+ subagentFactory: multiAgentHost.makeSubagentFactory(config),
27360
+ events
27007
27361
  };
27008
27362
  parallelEngine = new ParallelEternalEngine(parallelOptions);
27009
27363
  }
@@ -27017,7 +27371,8 @@ Restart WrongStack to load or unload plugin code in this session.`;
27017
27371
  maxContextTokens: effectiveMaxContext > 0 ? effectiveMaxContext : void 0,
27018
27372
  onIteration: broadcastEternalIteration,
27019
27373
  onStage: broadcastAutonomyStage,
27020
- brain
27374
+ brain,
27375
+ events
27021
27376
  });
27022
27377
  }
27023
27378
  void eternalEngine.prime();
@@ -27033,11 +27388,14 @@ Restart WrongStack to load or unload plugin code in this session.`;
27033
27388
  brainMonitor.stop();
27034
27389
  brainQueue.dispose();
27035
27390
  void mcpRegistry.stopAll();
27391
+ multiAgentHost.dispose().catch(
27392
+ (err) => logger.warn(`multiAgentHost.dispose() failed: ${err instanceof Error ? err.message : String(err)}`)
27393
+ );
27036
27394
  },
27037
27395
  onBeforeExit: async () => {
27038
27396
  const cwd2 = projectRoot;
27039
27397
  const statusResult = await new Promise(
27040
- (resolve10, reject) => {
27398
+ (resolve11, reject) => {
27041
27399
  const child = spawn("git", ["status", "--porcelain"], {
27042
27400
  cwd: cwd2,
27043
27401
  stdio: ["ignore", "pipe", "pipe"],
@@ -27049,7 +27407,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
27049
27407
  stdout += d;
27050
27408
  });
27051
27409
  child.on("error", reject);
27052
- child.on("close", (code) => resolve10({ stdout, code: code ?? 0 }));
27410
+ child.on("close", (code) => resolve11({ stdout, code: code ?? 0 }));
27053
27411
  }
27054
27412
  );
27055
27413
  if (statusResult.stdout.trim().length > 0) {
@@ -27314,8 +27672,11 @@ Reply YES to auto-proceed, NO to wait for human input.`
27314
27672
  brain,
27315
27673
  brainSettings,
27316
27674
  getBrainLog: () => brainLog,
27317
- // Clean up SessionStats event listeners when the REPL exits.
27318
- onDestroy: () => stats.destroy(events)
27675
+ // Clean up SessionStats event listeners and all EventBus handlers when the REPL exits.
27676
+ onDestroy: () => {
27677
+ teardownHandlers.forEach((fn) => fn());
27678
+ stats.destroy(events);
27679
+ }
27319
27680
  });
27320
27681
  }
27321
27682
  var isMain = import.meta.url === `file://${process.argv[1]?.replace(/\\/g, "/")}` || process.argv[1]?.endsWith("/cli/dist/index.js") || process.argv[1]?.endsWith("\\cli\\dist\\index.js");