@wrongstack/cli 0.299.0 → 0.301.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.
Files changed (82) hide show
  1. package/dist/{acp-S4LZQB66.js → acp-DATHHPNT.js} +36 -17
  2. package/dist/acp-server-agent.d.ts +1 -1
  3. package/dist/{audit-2ZSFGNIF.js → audit-QRCLEHHW.js} +5 -1
  4. package/dist/{auth-G5YKKSRH.js → auth-JASC4K3F.js} +151 -38
  5. package/dist/auth-menu/loopback-server.d.ts +41 -55
  6. package/dist/auth-menu/openai-codex-oauth.d.ts +36 -123
  7. package/dist/auto-discover-providers-DRW4OZHG.js +8 -0
  8. package/dist/boot/dispatch-webui.d.ts +3 -0
  9. package/dist/boot/launch-menu.d.ts +2 -4
  10. package/dist/boot/tool-registry.d.ts +3 -0
  11. package/dist/chimera-reviewer-policy.d.ts +10 -3
  12. package/dist/chimera-work-registry.d.ts +19 -0
  13. package/dist/{chronicle-QQEQFSSE.js → chronicle-63MFCZN6.js} +26 -5
  14. package/dist/{chunk-YWUPHRGB.js → chunk-263G3FMB.js} +33 -10
  15. package/dist/{chunk-2SLSUDYS.js → chunk-2KDOFTTM.js} +312 -211
  16. package/dist/{chunk-TGDHN4LM.js → chunk-4GVH7V3S.js} +4 -24
  17. package/dist/chunk-CSAPOCBP.js +24 -0
  18. package/dist/{chunk-66T43Q4Y.js → chunk-ETOEGL3V.js} +6 -9
  19. package/dist/{chunk-PEKUKUCH.js → chunk-GUHQQG63.js} +32 -14
  20. package/dist/chunk-KE7E7DPX.js +38 -0
  21. package/dist/{chunk-ZNZJ34RF.js → chunk-MHF2IJDE.js} +21 -4
  22. package/dist/{chunk-4HH3YGG4.js → chunk-MRLYDHQ7.js} +8 -5
  23. package/dist/{chunk-L5CE52XW.js → chunk-O7XNGCRT.js} +317 -309
  24. package/dist/chunk-PYTFS4MC.js +238 -0
  25. package/dist/{chunk-DPJZQCVP.js → chunk-YMXXOOFN.js} +1 -1
  26. package/dist/{chunk-4U3DMA2Y.js → chunk-ZECLGUTF.js} +28 -21
  27. package/dist/{cli-main-3MQDFSYK.js → cli-main-XWC57MGE.js} +849 -242
  28. package/dist/{execution-ANOYMWPO.js → execution-LHMSMDHB.js} +174 -55
  29. package/dist/execution-chimera-cascade.d.ts +3 -4
  30. package/dist/execution-chimera-review.d.ts +2 -3
  31. package/dist/execution-cleanup.d.ts +3 -2
  32. package/dist/execution.d.ts +1 -1
  33. package/dist/{export-TPORYVRZ.js → export-DOBGPY23.js} +12 -1
  34. package/dist/fleet/budget-source.d.ts +18 -0
  35. package/dist/fleet/host-context.d.ts +1 -1
  36. package/dist/fleet/host-helpers.d.ts +14 -0
  37. package/dist/fleet/host-status.d.ts +31 -0
  38. package/dist/fleet/host-types.d.ts +8 -0
  39. package/dist/fleet/host.d.ts +5 -0
  40. package/dist/fleet/status-broadcast.d.ts +1 -1
  41. package/dist/{hq-BDO56CX6.js → hq-LN5CJHCM.js} +22 -3
  42. package/dist/hq-server/auth-state.d.ts +29 -1
  43. package/dist/hq-server/auth.d.ts +16 -0
  44. package/dist/hq-server/client-address.d.ts +56 -0
  45. package/dist/hq-server/login-attempt-store.d.ts +30 -10
  46. package/dist/hq-server/routes/auth-handlers.d.ts +66 -26
  47. package/dist/hq-server/routes.d.ts +23 -1
  48. package/dist/hq-server/types.d.ts +10 -0
  49. package/dist/hq-server/ws.d.ts +17 -6
  50. package/dist/{hq-server-RJLJJZU7.js → hq-server-S7HZG3PA.js} +3 -2
  51. package/dist/hq-server.d.ts +19 -0
  52. package/dist/index.js +69 -104
  53. package/dist/live-settings-input.d.ts +5 -0
  54. package/dist/{mcp-CYLPUEHC.js → mcp-OHPJH73U.js} +6 -2
  55. package/dist/{modeldiag-HENYJ4N7.js → modeldiag-3DOK4BWW.js} +3 -3
  56. package/dist/{plugin-usage-VTL7ZTVC.js → plugin-usage-YEVLOR33.js} +7 -3
  57. package/dist/{plugins-ETY6W3DC.js → plugins-K3BAUP4D.js} +3 -3
  58. package/dist/profile-config-path.d.ts +0 -2
  59. package/dist/provider-helpers.d.ts +0 -5
  60. package/dist/{providers-models-GDELBHZK.js → providers-models-WEZ4EGLL.js} +38 -12
  61. package/dist/{replay-KCTXNMZQ.js → replay-FCBNHW3K.js} +5 -1
  62. package/dist/{rewind-EJMVFVLS.js → rewind-AOD3JIYX.js} +5 -1
  63. package/dist/{sessions-config-NLCMK3BY.js → sessions-config-XXN7267N.js} +8 -3
  64. package/dist/slash-commands/command-context.d.ts +19 -0
  65. package/dist/subcommands/flags.d.ts +23 -0
  66. package/dist/terminal-format.d.ts +15 -0
  67. package/dist/webui-server/setup-events.d.ts +17 -0
  68. package/dist/webui-server/terminal-log-view.d.ts +29 -12
  69. package/dist/{webui-server-PCLSXX6L.js → webui-server-DD35QFNO.js} +113 -50
  70. package/dist/webui-server-options.d.ts +16 -0
  71. package/dist/wiring/brain-and-orchestration.d.ts +4 -1
  72. package/dist/wiring/director-setup.d.ts +4 -0
  73. package/dist/wiring/fallback-gate.d.ts +31 -0
  74. package/dist/wiring/fleet-command-handlers.d.ts +1 -1
  75. package/dist/wiring/mailbox-bridge-bootstrap.d.ts +3 -1
  76. package/dist/wiring/management-tools.d.ts +9 -1
  77. package/dist/wiring/pipeline.d.ts +5 -5
  78. package/dist/wiring/plugins.d.ts +3 -10
  79. package/dist/wiring/provider-utility-tools.d.ts +27 -1
  80. package/dist/wiring/tools.d.ts +4 -5
  81. package/package.json +23 -23
  82. package/dist/chunk-GO3TJICK.js +0 -489
@@ -31,12 +31,19 @@ import {
31
31
  import {
32
32
  fmtTok
33
33
  } from "./chunk-TYO2OVAD.js";
34
+ import {
35
+ terminalLink,
36
+ terminalText
37
+ } from "./chunk-KE7E7DPX.js";
34
38
  import {
35
39
  CLI_VERSION
36
40
  } from "./chunk-XJXDOF63.js";
37
41
  import {
38
42
  resolveActiveApiKey
39
43
  } from "./chunk-PJHET45A.js";
44
+ import {
45
+ activeProfileConfigPath
46
+ } from "./chunk-YMXXOOFN.js";
40
47
  import "./chunk-7OCVIDC7.js";
41
48
 
42
49
  // src/execution.ts
@@ -182,12 +189,13 @@ async function runWebUIDispatch(ctx) {
182
189
  rebindTodosCheckpoint,
183
190
  agentTranscripts,
184
191
  sddSubagentFactory,
185
- onKanbanDispatch
192
+ onKanbanDispatch,
193
+ getFleetBudget
186
194
  } = ctx;
187
195
  const isSimpleUi = flags["simpleui"] === true;
188
196
  agent.disableInteractiveConfirmation();
189
197
  renderer.setSilent(true);
190
- const { runWebUI } = await import("./webui-server-PCLSXX6L.js");
198
+ const { runWebUI } = await import("./webui-server-DD35QFNO.js");
191
199
  const flagValue = (names) => {
192
200
  for (const name of names) {
193
201
  if (!Object.hasOwn(flags, name)) continue;
@@ -274,6 +282,7 @@ async function runWebUIDispatch(ctx) {
274
282
  mcpRegistry,
275
283
  subscribeEternalIteration,
276
284
  sessionStore,
285
+ ...getFleetBudget ? { getFleetBudget } : {},
277
286
  sessionsDir: projectSessionsDir,
278
287
  claimSession: activateSessionIdentity ? async (sessionId) => {
279
288
  const previousSessionId = agent.ctx.session?.id ?? session.id;
@@ -309,10 +318,12 @@ async function runWebUIDispatch(ctx) {
309
318
  // the default when it is taken (a second instance, leftover socket).
310
319
  onListening: ({ url }) => {
311
320
  const surface = isSimpleUi ? "SimpleUI" : "WebUI";
312
- renderer.writeInfo(color2.green(` \u2726 ${surface} running \u2192 ${color2.bold(url)}`));
313
321
  renderer.writeInfo(
314
- color2.dim(` Press Ctrl+C in this terminal to stop the ${surface} server.
315
- `)
322
+ ` \u2726 ${terminalText(surface, "success", { bold: true })} ${terminalText("running \u2192", "muted")} ${terminalLink(url)}`
323
+ );
324
+ renderer.writeInfo(
325
+ ` ${terminalText(`Press Ctrl+C in this terminal to stop the ${surface} server.`, "muted")}
326
+ `
316
327
  );
317
328
  },
318
329
  // Make autonomy.switch from the browser flip the CLI's real
@@ -336,7 +347,11 @@ async function runWebUIDispatch(ctx) {
336
347
  resolve3(0);
337
348
  }).catch((err) => {
338
349
  renderer.setSilent(false);
339
- console.debug(`[execution] webui error: ${err}`);
350
+ renderer.writeInfo(
351
+ color2.red(
352
+ ` \u2717 ${isSimpleUi ? "SimpleUI" : "WebUI"} failed to start: ${err instanceof Error ? err.message : String(err)}`
353
+ )
354
+ );
340
355
  resolve3(1);
341
356
  });
342
357
  });
@@ -831,13 +846,16 @@ async function handleProjectSwitchSpawn(opts) {
831
846
  const nodeExe = process.execPath;
832
847
  const spawnArgs = [cliPath, "--no-interactive"];
833
848
  if (resumeSessionId) spawnArgs.push("--resume", resumeSessionId);
834
- spawn(nodeExe, spawnArgs, {
849
+ const child = spawn(nodeExe, spawnArgs, {
835
850
  cwd: root,
836
851
  stdio: "ignore",
837
- detached: true
838
- }).on("error", (err) => {
852
+ detached: true,
853
+ windowsHide: true
854
+ });
855
+ child.on("error", (err) => {
839
856
  console.error(color4.red(`Failed to spawn wstack: ${err.message}`));
840
857
  });
858
+ child.unref();
841
859
  console.log(
842
860
  [
843
861
  "",
@@ -932,6 +950,7 @@ async function switchProjectInPlace(ctx, targetRoot, displayName) {
932
950
  context.state.replaceMessages([]);
933
951
  context.state.replaceTodos([]);
934
952
  context.clearFileTracking();
953
+ context.clearMemoryEvidence?.();
935
954
  context.tokenCounter.reset();
936
955
  context.meta["packageTrackerOpts"] = { storageDir: nextWpaths.projectDir, projectRoot: resolved };
937
956
  context.state.setMeta(
@@ -973,7 +992,8 @@ async function switchProjectInPlace(ctx, targetRoot, displayName) {
973
992
  context.systemPrompt = await switchBuilder.build({
974
993
  cwd: resolved,
975
994
  projectRoot: resolved,
976
- tools: agent.tools.list(),
995
+ tools: agent.tools.listForProvider(),
996
+ catalogTools: agent.tools.list(),
977
997
  provider: context.provider.id,
978
998
  model: context.model
979
999
  });
@@ -1287,7 +1307,7 @@ import * as fs3 from "node:fs/promises";
1287
1307
  import * as path7 from "node:path";
1288
1308
  import { decryptConfigSecrets, encryptConfigSecrets, noOpVault } from "@wrongstack/core/security";
1289
1309
  import { normalizeTokenSavingTier, resolveFleetChatVerbosity } from "@wrongstack/core/types";
1290
- import { atomicWrite } from "@wrongstack/core/utils";
1310
+ import { atomicWrite, deepMerge } from "@wrongstack/core/utils";
1291
1311
  import { getProcessRegistry } from "@wrongstack/tools";
1292
1312
  var PANEL_IDS_CLI = [
1293
1313
  "projectPicker",
@@ -1366,6 +1386,7 @@ function createSettingsAdapter(ctx) {
1366
1386
  auditLevel: cfg.session?.auditLevel ?? "standard",
1367
1387
  indexOnStart: cfg.indexing?.onSessionStart !== false,
1368
1388
  maxIterations: cfg.tools?.maxIterations ?? 500,
1389
+ nextStepsTool: cfg.tools?.nextsteps?.enabled === true,
1369
1390
  restrictFsToRoot: resolvedRestrict,
1370
1391
  autoProceedMaxIterations: cfg.autonomy?.autoProceedMaxIterations ?? 50,
1371
1392
  debugStream: cfg.debugStream ?? false,
@@ -1405,12 +1426,15 @@ function createSettingsAdapter(ctx) {
1405
1426
  }
1406
1427
  async function saveSettings(s) {
1407
1428
  try {
1408
- if (s.mode !== void 0 || s.delayMs !== void 0 || s.titleAnimation !== void 0 || s.yolo !== void 0 || s.fleetChatVerbosity !== void 0 || s.chime !== void 0 || s.confirmExit !== void 0 || s.mouseMode !== void 0 || s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.featureTokenSaving !== void 0 || s.allowOutsideProjectRoot !== void 0 || s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 || s.contextMode !== void 0 || s.maxConcurrent !== void 0 || s.logLevel !== void 0 || s.auditLevel !== void 0 || s.indexOnStart !== void 0 || s.maxIterations !== void 0 || s.restrictFsToRoot !== void 0 || s.nextPrediction !== void 0 || s.debugStream !== void 0 || s.shellBangWarningDontShowAgain !== void 0 || s.configScope !== void 0 || s.enhanceDelayMs !== void 0 || s.enhanceEnabled !== void 0 || s.enhanceLanguage !== void 0 || s.midRunSendPicker !== void 0 || s.statuslineMode !== void 0 || s.thinkingWord !== void 0 || s.animationStyle !== void 0 || s.autonomyNextPrompt !== void 0 || s.autoProceedMaxIterations !== void 0 || s.reasoningMode !== void 0 || s.reasoningEffort !== void 0 || s.reasoningPreserve !== void 0 || s.cacheTtl !== void 0 || s.breakerEnabled !== void 0 || s.breakerAutoKillResetMs !== void 0 || s.showModelReasoning !== void 0 || s.showAgentSwarmPanel !== void 0 || s.panelPositions !== void 0 || s.showSageMemoryInject !== void 0 || s.sageMemoryInjectThreshold !== void 0 || s.readSymbols !== void 0) {
1429
+ if (s.mode !== void 0 || s.delayMs !== void 0 || s.titleAnimation !== void 0 || s.yolo !== void 0 || s.fleetChatVerbosity !== void 0 || s.chime !== void 0 || s.confirmExit !== void 0 || s.mouseMode !== void 0 || s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.featureTokenSaving !== void 0 || s.allowOutsideProjectRoot !== void 0 || s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 || s.contextMode !== void 0 || s.maxConcurrent !== void 0 || s.logLevel !== void 0 || s.auditLevel !== void 0 || s.indexOnStart !== void 0 || s.maxIterations !== void 0 || s.nextStepsTool !== void 0 || s.restrictFsToRoot !== void 0 || s.nextPrediction !== void 0 || s.debugStream !== void 0 || s.shellBangWarningDontShowAgain !== void 0 || s.configScope !== void 0 || s.enhanceDelayMs !== void 0 || s.enhanceEnabled !== void 0 || s.enhanceLanguage !== void 0 || s.midRunSendPicker !== void 0 || s.statuslineMode !== void 0 || s.thinkingWord !== void 0 || s.animationStyle !== void 0 || s.autonomyNextPrompt !== void 0 || s.autoProceedMaxIterations !== void 0 || s.reasoningMode !== void 0 || s.reasoningEffort !== void 0 || s.reasoningPreserve !== void 0 || s.cacheTtl !== void 0 || s.breakerEnabled !== void 0 || s.breakerAutoKillResetMs !== void 0 || s.showModelReasoning !== void 0 || s.showAgentSwarmPanel !== void 0 || s.panelPositions !== void 0 || s.showSageMemoryInject !== void 0 || s.sageMemoryInjectThreshold !== void 0 || s.readSymbols !== void 0) {
1409
1430
  const cfg = configStore.get();
1410
- const activeProfileName = cfg.activeProfile ?? "default";
1411
1431
  const persistDeps = {
1412
1432
  configStore,
1413
- profileConfigPath: wpaths.profileConfig(activeProfileName),
1433
+ // Third copy of "which profile is active" — an inline cast plus a
1434
+ // `?? 'default'`, identical to `activeProfileConfigPath` two files
1435
+ // over. The comment above already promised this resolution was
1436
+ // delegated; now it is.
1437
+ profileConfigPath: activeProfileConfigPath(wpaths, cfg),
1414
1438
  inProjectConfigPath: wpaths.inProjectConfig,
1415
1439
  vault: noOpVault,
1416
1440
  resolveProfilePath: (name) => wpaths.profileConfig(name)
@@ -1500,9 +1524,10 @@ function createSettingsAdapter(ctx) {
1500
1524
  idx.onSessionStart = s.indexOnStart;
1501
1525
  decrypted.indexing = idx;
1502
1526
  }
1503
- if (s.maxIterations !== void 0 || fsAccess !== void 0) {
1527
+ if (s.maxIterations !== void 0 || s.nextStepsTool !== void 0 || fsAccess !== void 0) {
1504
1528
  const tools = decrypted.tools ?? {};
1505
1529
  if (s.maxIterations !== void 0) tools.maxIterations = s.maxIterations;
1530
+ if (s.nextStepsTool !== void 0) tools.nextsteps = { enabled: s.nextStepsTool };
1506
1531
  if (fsAccess !== void 0) tools.restrictToProjectRoot = fsAccess.restrictToProjectRoot;
1507
1532
  decrypted.tools = tools;
1508
1533
  }
@@ -1557,7 +1582,7 @@ function createSettingsAdapter(ctx) {
1557
1582
  const destRaw = await fs3.readFile(actualTarget, "utf8");
1558
1583
  const destParsed = JSON.parse(destRaw);
1559
1584
  const destDecrypted = decryptConfigSecrets(destParsed, noOpVault);
1560
- mergedToWrite = { ...destDecrypted, ...decrypted };
1585
+ mergedToWrite = deepMerge(destDecrypted, decrypted);
1561
1586
  } catch (err) {
1562
1587
  if (err.code !== "ENOENT") {
1563
1588
  throw new Error(
@@ -1614,7 +1639,7 @@ function createSettingsAdapter(ctx) {
1614
1639
  ...decrypted.indexing ?? {}
1615
1640
  }
1616
1641
  } : {},
1617
- ...s.maxIterations !== void 0 || fsAccess !== void 0 ? {
1642
+ ...s.maxIterations !== void 0 || s.nextStepsTool !== void 0 || fsAccess !== void 0 ? {
1618
1643
  tools: {
1619
1644
  ...currentConfig.tools,
1620
1645
  ...decrypted.tools ?? {}
@@ -1728,6 +1753,7 @@ function createBrainPanelHost(deps) {
1728
1753
  councilDistinctness: snap.council.distinctness,
1729
1754
  councilPerCallTimeoutMs: snap.council.perCallTimeoutMs,
1730
1755
  councilMaxConcurrency: snap.council.maxConcurrency,
1756
+ councilVoterMaxTokens: snap.council.voterMaxTokens,
1731
1757
  councilJudgeMaxTokens: snap.council.judgeMaxTokens,
1732
1758
  // The EFFECTIVE judge, not `council.judge`. The configured one is
1733
1759
  // usually absent, so the panel used to read "auto" and say nothing
@@ -1809,6 +1835,7 @@ function createBrainPanelHost(deps) {
1809
1835
  setCouncilDistinctness: (mode) => apply({ council: { distinctness: mode } }),
1810
1836
  setCouncilPerCallTimeout: (ms) => apply({ council: { perCallTimeoutMs: ms ?? null } }),
1811
1837
  setCouncilMaxConcurrency: (count) => apply({ council: { maxConcurrency: count ?? null } }),
1838
+ setCouncilVoterMaxTokens: (tokens) => apply({ council: { voterMaxTokens: tokens ?? null } }),
1812
1839
  setCouncilJudgeMaxTokens: (tokens) => apply({ council: { judgeMaxTokens: tokens ?? null } }),
1813
1840
  setLedgerEnabled: (on) => apply({ ledger: { enabled: on } }),
1814
1841
  setAutoDeny: (count) => apply({ ledger: { autoDenyAfterFailures: count ?? null } }),
@@ -1845,7 +1872,7 @@ var reviewerRoundRobinCursor = 0;
1845
1872
  function __resetReviewerRoundRobinCursor(value = 0) {
1846
1873
  reviewerRoundRobinCursor = value;
1847
1874
  }
1848
- function assignReviewerModelsRoundRobin(provider, model, fallbackModels, statusTracker) {
1875
+ function assignReviewerModels(provider, model, fallbackModels, selection = "round-robin", statusTracker, random = Math.random) {
1849
1876
  const pool = buildReviewerModelPool(provider, model, fallbackModels, statusTracker);
1850
1877
  if (pool.length <= 1) {
1851
1878
  return {
@@ -1854,20 +1881,24 @@ function assignReviewerModelsRoundRobin(provider, model, fallbackModels, statusT
1854
1881
  fallbackModels: [...fallbackModels]
1855
1882
  };
1856
1883
  }
1884
+ const cursor = selection === "random" ? Math.min(pool.length - 1, Math.floor(Math.max(0, random()) * pool.length)) : reviewerRoundRobinCursor;
1857
1885
  const assignment = selectRoundRobinReviewerAssignment(
1858
1886
  pool,
1859
- reviewerRoundRobinCursor,
1887
+ cursor,
1860
1888
  provider,
1861
1889
  model,
1862
1890
  statusTracker
1863
1891
  );
1864
- reviewerRoundRobinCursor = assignment.nextCursor;
1892
+ if (selection === "round-robin") reviewerRoundRobinCursor = assignment.nextCursor;
1865
1893
  return {
1866
1894
  provider: assignment.provider,
1867
1895
  model: assignment.model,
1868
1896
  fallbackModels: assignment.fallbackModels
1869
1897
  };
1870
1898
  }
1899
+ function assignReviewerModelsRoundRobin(provider, model, fallbackModels, statusTracker) {
1900
+ return assignReviewerModels(provider, model, fallbackModels, "round-robin", statusTracker);
1901
+ }
1871
1902
  var MAX_REVIEWER_ATTEMPTS = 4;
1872
1903
  var REVIEWER_ATTEMPT_TIMEOUT_MS = 9e5;
1873
1904
  var REVIEWER_LADDER_BUDGET_MS = 12e5;
@@ -1970,7 +2001,7 @@ async function finalizeExecutionCleanup(input) {
1970
2001
  session,
1971
2002
  tokenCounter,
1972
2003
  events,
1973
- getPendingChimeraWork,
2004
+ chimeraWork,
1974
2005
  director,
1975
2006
  reader
1976
2007
  } = input;
@@ -1982,6 +2013,19 @@ async function finalizeExecutionCleanup(input) {
1982
2013
  } catch (_err) {
1983
2014
  }
1984
2015
  await Promise.resolve(detachTodosCheckpoint?.()).catch(() => void 0);
2016
+ try {
2017
+ const { getProcessRegistry: getProcessRegistry2 } = await import("@wrongstack/tools");
2018
+ getProcessRegistry2().killAll({ force: true, includeProtected: true });
2019
+ } catch (err) {
2020
+ console.warn(
2021
+ JSON.stringify({
2022
+ level: "warn",
2023
+ event: "shutdown.process_kill_all_failed",
2024
+ message: `Process registry killAll failed: ${err instanceof Error ? err.message : String(err)}`,
2025
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2026
+ })
2027
+ );
2028
+ }
1985
2029
  await mcpRegistry.stopAll().catch((err) => {
1986
2030
  console.warn(
1987
2031
  JSON.stringify({
@@ -2009,25 +2053,34 @@ async function finalizeExecutionCleanup(input) {
2009
2053
  })
2010
2054
  );
2011
2055
  });
2012
- events.emit("session.ended", {
2013
- id: activeSession.id,
2014
- sessionId: activeSession.id,
2015
- usage: tokenCounter.total()
2016
- });
2017
- if (getPendingChimeraWork) {
2018
- const chimeraWork = getPendingChimeraWork();
2019
- if (chimeraWork) {
2020
- await chimeraWork.catch((err) => {
2021
- console.warn(
2022
- JSON.stringify({
2023
- level: "warn",
2024
- event: "shutdown.chimera_work_failed",
2025
- message: `Pending chimera work failed: ${err instanceof Error ? err.message : String(err)}`,
2026
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
2027
- })
2028
- );
2029
- });
2056
+ const sessionEndProducers = /* @__PURE__ */ new Set();
2057
+ events.emit(
2058
+ "session.ended",
2059
+ {
2060
+ id: activeSession.id,
2061
+ sessionId: activeSession.id,
2062
+ usage: tokenCounter.total(),
2063
+ waitUntil: (work) => {
2064
+ const tracked = Promise.resolve(work).finally(() => sessionEndProducers.delete(tracked));
2065
+ void tracked.catch(() => void 0);
2066
+ sessionEndProducers.add(tracked);
2067
+ }
2068
+ }
2069
+ );
2070
+ try {
2071
+ while (sessionEndProducers.size > 0) {
2072
+ await Promise.allSettled([...sessionEndProducers]);
2030
2073
  }
2074
+ await chimeraWork?.drainAndClose();
2075
+ } catch (err) {
2076
+ console.warn(
2077
+ JSON.stringify({
2078
+ level: "warn",
2079
+ event: "shutdown.chimera_work_failed",
2080
+ message: `Pending chimera work failed: ${err instanceof Error ? err.message : String(err)}`,
2081
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2082
+ })
2083
+ );
2031
2084
  }
2032
2085
  if (director) {
2033
2086
  try {
@@ -2428,6 +2481,43 @@ var FleetStatusLine = class {
2428
2481
  }
2429
2482
  };
2430
2483
 
2484
+ // src/chimera-work-registry.ts
2485
+ function createChimeraWorkRegistry() {
2486
+ const pending = /* @__PURE__ */ new Set();
2487
+ let accepting = true;
2488
+ let closePromise;
2489
+ const track = (work) => {
2490
+ if (!accepting) return false;
2491
+ const tracked = Promise.resolve(work).finally(() => {
2492
+ pending.delete(tracked);
2493
+ });
2494
+ void tracked.catch(() => void 0);
2495
+ pending.add(tracked);
2496
+ return true;
2497
+ };
2498
+ const snapshot = () => {
2499
+ if (pending.size === 0) return void 0;
2500
+ return Promise.allSettled([...pending]).then(() => void 0);
2501
+ };
2502
+ const drainAndClose = () => {
2503
+ closePromise ??= (async () => {
2504
+ while (pending.size > 0) {
2505
+ await Promise.allSettled([...pending]);
2506
+ }
2507
+ accepting = false;
2508
+ })();
2509
+ return closePromise;
2510
+ };
2511
+ return {
2512
+ track,
2513
+ pending: snapshot,
2514
+ drainAndClose,
2515
+ get size() {
2516
+ return pending.size;
2517
+ }
2518
+ };
2519
+ }
2520
+
2431
2521
  // src/execution-chimera-cascade.ts
2432
2522
  import * as fsp from "node:fs/promises";
2433
2523
  import * as path8 from "node:path";
@@ -2689,7 +2779,7 @@ function installChimeraCascadeHandler({
2689
2779
  director,
2690
2780
  session,
2691
2781
  getPendingWork,
2692
- setPendingWork,
2782
+ trackWork,
2693
2783
  buildLadder
2694
2784
  }) {
2695
2785
  events.onPattern("chimera.cascade_needed", (_event, payload) => {
@@ -2719,6 +2809,9 @@ function installChimeraCascadeHandler({
2719
2809
  maxIterations: 40,
2720
2810
  maxToolCalls: 200,
2721
2811
  timeoutMs,
2812
+ // Cascade agents are ephemeral infrastructure: like reviewers,
2813
+ // they must not consume the leader's lifetime maxSpawns budget.
2814
+ spawnBudgetExempt: true,
2722
2815
  // Rung 0 stays unpinned so the role model matrix still decides;
2723
2816
  // later rungs pin a model precisely because it just failed.
2724
2817
  ...attempt.tier === "inherit" ? {} : {
@@ -2775,7 +2868,7 @@ function installChimeraCascadeHandler({
2775
2868
  }
2776
2869
  await maybeReReviewCascade({ events, session, bundle: p.bundle });
2777
2870
  })();
2778
- setPendingWork(pendingWork);
2871
+ trackWork(pendingWork);
2779
2872
  });
2780
2873
  }
2781
2874
  async function maybeReReviewCascade({
@@ -2814,7 +2907,7 @@ async function maybeReReviewCascade({
2814
2907
  stopReason: "end_turn",
2815
2908
  usage: { input: 0, output: 0 }
2816
2909
  });
2817
- const emitted = emitReviewIfChanged(
2910
+ const emitted = await emitReviewIfChanged(
2818
2911
  { events, emitCustom: events.emitCustom.bind(events) },
2819
2912
  reReviewBundle
2820
2913
  );
@@ -2886,13 +2979,14 @@ function installChimeraReviewHandler({
2886
2979
  projectDir,
2887
2980
  statusTracker,
2888
2981
  persistReview = persistChimeraReview,
2889
- setPendingWork
2982
+ trackWork
2890
2983
  }) {
2891
2984
  events.onPattern("chimera.review_needed", (_event, payload) => {
2892
2985
  const p = payload;
2893
2986
  const reviewSessionId = agent.ctx.activeRunSessionId ?? agent.ctx.session?.id ?? session.id;
2894
2987
  const dir = director;
2895
2988
  if (!dir) {
2989
+ void recordCompletedReview(events, { bundle: p }).catch(() => void 0);
2896
2990
  return;
2897
2991
  }
2898
2992
  if (p.files.length === 0) return;
@@ -2935,11 +3029,11 @@ function installChimeraReviewHandler({
2935
3029
  `\u{1F982} Chimera report persistence failed: ${message}. The review remains in the session transcript and mailbox.`
2936
3030
  );
2937
3031
  } finally {
2938
- recordCompletedReview(events, completion);
3032
+ await recordCompletedReview(events, completion);
2939
3033
  events.emitCustom("chimera.review_complete", completion);
2940
3034
  }
2941
3035
  };
2942
- setPendingWork(
3036
+ trackWork(
2943
3037
  (async () => {
2944
3038
  let subagentId;
2945
3039
  try {
@@ -2951,10 +3045,11 @@ function installChimeraReviewHandler({
2951
3045
  const baseProvider = rawProvider || tProvider || config.provider;
2952
3046
  const baseModel = rawModel || tModel || config.model;
2953
3047
  const baseFallbacks = p.reviewFallbackModels ? [...p.reviewFallbackModels] : resolveReviewerFallbackModels(void 0);
2954
- const assigned = assignReviewerModelsRoundRobin(
3048
+ const assigned = assignReviewerModels(
2955
3049
  baseProvider,
2956
3050
  baseModel,
2957
3051
  baseFallbacks,
3052
+ p.reviewModelSelection ?? "round-robin",
2958
3053
  statusTracker
2959
3054
  );
2960
3055
  const ladder = buildReviewerAttemptLadder({
@@ -2977,7 +3072,10 @@ function installChimeraReviewHandler({
2977
3072
  timeoutMs,
2978
3073
  provider: attempt.provider,
2979
3074
  model: attempt.model,
2980
- fallbackModels: attempt.fallbackModels
3075
+ fallbackModels: attempt.fallbackModels,
3076
+ // Reviewers are ephemeral background infrastructure: they must
3077
+ // not consume the leader's lifetime maxSpawns budget.
3078
+ spawnBudgetExempt: true
2981
3079
  }),
2982
3080
  // The reviewer is read-only, so a retry re-runs the task verbatim.
2983
3081
  buildTask: () => taskDesc,
@@ -4530,7 +4628,7 @@ async function execute(deps) {
4530
4628
  const profileName = config.activeProfile ?? "default";
4531
4629
  const bootUpdateInfo = initialUpdateInfo;
4532
4630
  const offStorageObservability = installStorageObservability(events, context.traceId);
4533
- let pendingChimeraWork;
4631
+ const chimeraWork = createChimeraWorkRegistry();
4534
4632
  installChimeraReviewHandler({
4535
4633
  events,
4536
4634
  director,
@@ -4544,8 +4642,8 @@ async function execute(deps) {
4544
4642
  // this, a 429-stricken model is re-spawned on every concurrent reviewer
4545
4643
  // turn and burns the whole chain instead of staying quarantined.
4546
4644
  statusTracker,
4547
- setPendingWork: (work) => {
4548
- pendingChimeraWork = work;
4645
+ trackWork: (work) => {
4646
+ chimeraWork.track(work);
4549
4647
  }
4550
4648
  });
4551
4649
  installChimeraCascadeHandler({
@@ -4556,9 +4654,9 @@ async function execute(deps) {
4556
4654
  profileChain: effectiveFallbackChain2(config),
4557
4655
  session: { provider: config.provider, model: config.model }
4558
4656
  }),
4559
- getPendingWork: () => pendingChimeraWork,
4560
- setPendingWork: (work) => {
4561
- pendingChimeraWork = work;
4657
+ getPendingWork: () => chimeraWork.pending(),
4658
+ trackWork: (work) => {
4659
+ chimeraWork.track(work);
4562
4660
  }
4563
4661
  });
4564
4662
  let code = 0;
@@ -4885,6 +4983,26 @@ async function execute(deps) {
4885
4983
  sddSubagentFactory,
4886
4984
  statusTracker,
4887
4985
  updateInfo: bootUpdateInfo,
4986
+ getFleetBudget: () => {
4987
+ const d = getDirector?.() ?? null;
4988
+ if (!d) return null;
4989
+ const snap = d.fleetManager?.budgetSnapshot?.();
4990
+ const maxSpawns = snap?.maxSpawns ?? d.maxSpawns;
4991
+ const usedSpawns = snap?.usedSpawns ?? d.spawnCount;
4992
+ const remainingSpawns = snap?.remainingSpawns ?? Math.max(
4993
+ 0,
4994
+ (Number.isFinite(maxSpawns) ? maxSpawns : Number.POSITIVE_INFINITY) - usedSpawns
4995
+ );
4996
+ const activeAgents = d.status().subagents.filter((s) => s.status === "running" || s.status === "idle").length;
4997
+ return {
4998
+ maxSpawns,
4999
+ usedSpawns,
5000
+ remainingSpawns,
5001
+ activeAgents,
5002
+ ...snap?.checkpointMaxSpawns !== void 0 ? { checkpointMaxSpawns: snap.checkpointMaxSpawns } : {},
5003
+ ...snap?.ceilingMismatch ? { ceilingMismatch: true } : {}
5004
+ };
5005
+ },
4888
5006
  ...createKanbanDispatchHandler({ config, events, skillLoader, sddSubagentFactory })
4889
5007
  });
4890
5008
  } else {
@@ -4950,7 +5068,7 @@ async function execute(deps) {
4950
5068
  session,
4951
5069
  tokenCounter,
4952
5070
  events,
4953
- getPendingChimeraWork: () => pendingChimeraWork,
5071
+ chimeraWork,
4954
5072
  director,
4955
5073
  reader
4956
5074
  });
@@ -4961,8 +5079,9 @@ export {
4961
5079
  CHIMERA_REVIEW_READ_ONLY_TOOLS,
4962
5080
  __resetReviewerRoundRobinCursor,
4963
5081
  applyChimeraReviewerReadOnlyPolicy,
5082
+ assignReviewerModels,
4964
5083
  assignReviewerModelsRoundRobin,
4965
5084
  execute,
4966
5085
  resolveReviewerFallbackModels
4967
5086
  };
4968
- //# sourceMappingURL=execution-ANOYMWPO.js.map
5087
+ //# sourceMappingURL=execution-LHMSMDHB.js.map
@@ -3,19 +3,18 @@ import type { ExecuteDeps } from './execute-deps.js';
3
3
  type Director = NonNullable<ExecuteDeps['fleet']['director']>;
4
4
  type Events = ExecuteDeps['core']['events'];
5
5
  type Session = ExecuteDeps['session']['session'];
6
- type PendingChimeraWork = Promise<void> | undefined;
7
6
  export type InstallChimeraCascadeHandlerOptions = {
8
7
  events: Events;
9
8
  director: Director | null | undefined;
10
9
  session: Session;
11
- getPendingWork: () => PendingChimeraWork;
12
- setPendingWork: (work: PendingChimeraWork) => void;
10
+ getPendingWork: () => Promise<void> | undefined;
11
+ trackWork: (work: Promise<void>) => void;
13
12
  /**
14
13
  * Model ladder for cascade spawns. Supplied by the caller because only it
15
14
  * holds the live config. Omit to keep the single unpinned spawn.
16
15
  */
17
16
  buildLadder?: (() => ReviewerAttempt[]) | undefined;
18
17
  };
19
- export declare function installChimeraCascadeHandler({ events, director, session, getPendingWork, setPendingWork, buildLadder, }: InstallChimeraCascadeHandlerOptions): void;
18
+ export declare function installChimeraCascadeHandler({ events, director, session, getPendingWork, trackWork, buildLadder, }: InstallChimeraCascadeHandlerOptions): void;
20
19
  export {};
21
20
  //# sourceMappingURL=execution-chimera-cascade.d.ts.map
@@ -7,7 +7,6 @@ type Session = ExecuteDeps['session']['session'];
7
7
  type Mailbox = ExecuteDeps['session']['mailbox'];
8
8
  type Agent = ExecuteDeps['core']['agent'];
9
9
  type Config = ExecuteDeps['core']['config'];
10
- type PendingChimeraWork = Promise<void> | undefined;
11
10
  /**
12
11
  * Canonicalize a file path for citation gating. The reviewer can cite
13
12
  * absolute Windows paths (`D:\Codebox\...`) or mix casing on a
@@ -38,8 +37,8 @@ export type InstallChimeraReviewHandlerOptions = {
38
37
  */
39
38
  statusTracker?: ProviderModelStatusTracker | undefined;
40
39
  persistReview?: ((payload: ChimeraReviewCompletePayload, projectDir: string) => Promise<void>) | undefined;
41
- setPendingWork: (work: PendingChimeraWork) => void;
40
+ trackWork: (work: Promise<void>) => void;
42
41
  };
43
- export declare function installChimeraReviewHandler({ events, director, session, mailbox, agent, config, projectDir, statusTracker, persistReview, setPendingWork, }: InstallChimeraReviewHandlerOptions): void;
42
+ export declare function installChimeraReviewHandler({ events, director, session, mailbox, agent, config, projectDir, statusTracker, persistReview, trackWork, }: InstallChimeraReviewHandlerOptions): void;
44
43
  export {};
45
44
  //# sourceMappingURL=execution-chimera-review.d.ts.map
@@ -1,4 +1,5 @@
1
1
  import type { Director } from '@wrongstack/core/coordination';
2
+ import type { ChimeraWorkRegistry } from './chimera-work-registry.js';
2
3
  import type { ExecuteDeps } from './execute-deps.js';
3
4
  import type { FleetStatusLine } from './fleet-statusline.js';
4
5
  export interface ExecutionCleanupInput {
@@ -13,8 +14,8 @@ export interface ExecutionCleanupInput {
13
14
  session: ExecuteDeps['session']['session'];
14
15
  tokenCounter: ExecuteDeps['core']['tokenCounter'];
15
16
  events: ExecuteDeps['core']['events'];
16
- /** Getter for chimera's in-flight work promise avoids stale captures. */
17
- getPendingChimeraWork?: () => Promise<void> | undefined;
17
+ /** Session-scoped Chimera work tracker; drained before agent/session teardown. */
18
+ chimeraWork?: ChimeraWorkRegistry | undefined;
18
19
  /** Optional director reference for fleet cleanup at session end. */
19
20
  director?: Director | null | undefined;
20
21
  reader: ExecuteDeps['ui']['reader'];
@@ -1,6 +1,6 @@
1
1
  import type { ExecuteDeps } from './execute-deps.js';
2
2
  export type { LiveSettingsInput } from './live-settings-input.js';
3
- export { __resetReviewerRoundRobinCursor, applyChimeraReviewerReadOnlyPolicy, assignReviewerModelsRoundRobin, CHIMERA_REVIEW_READ_ONLY_TOOLS, resolveReviewerFallbackModels, } from './chimera-reviewer-policy.js';
3
+ export { __resetReviewerRoundRobinCursor, applyChimeraReviewerReadOnlyPolicy, assignReviewerModels, assignReviewerModelsRoundRobin, CHIMERA_REVIEW_READ_ONLY_TOOLS, resolveReviewerFallbackModels, } from './chimera-reviewer-policy.js';
4
4
  export type { BrainData, BrainLogEntry, ExecuteDeps, McpPickerItem, PluginPickerItem, RestoredToolCall, ToolPickerItem, } from './execute-deps.js';
5
5
  export declare function execute(deps: ExecuteDeps): Promise<number>;
6
6
  //# sourceMappingURL=execution.d.ts.map
@@ -1,3 +1,6 @@
1
+ import {
2
+ restoreFlags
3
+ } from "./chunk-CSAPOCBP.js";
1
4
  import "./chunk-7OCVIDC7.js";
2
5
 
3
6
  // src/subcommands/handlers/export.ts
@@ -7,6 +10,14 @@ import { DefaultSessionReader } from "@wrongstack/core/storage";
7
10
  import { expectDefined } from "@wrongstack/core/utils";
8
11
  import { toErrorMessage } from "@wrongstack/core/utils";
9
12
  var exportCmd = async (args, deps) => {
13
+ args = restoreFlags(args, deps, [
14
+ "format",
15
+ "f",
16
+ "out",
17
+ "o",
18
+ "no-tools",
19
+ "no-diagnostics"
20
+ ]);
10
21
  if (!deps.sessionStore) {
11
22
  deps.renderer.writeError("No session store configured.");
12
23
  return 1;
@@ -61,4 +72,4 @@ var exportCmd = async (args, deps) => {
61
72
  export {
62
73
  exportCmd
63
74
  };
64
- //# sourceMappingURL=export-TPORYVRZ.js.map
75
+ //# sourceMappingURL=export-DOBGPY23.js.map
@@ -0,0 +1,18 @@
1
+ import type { Config } from '@wrongstack/core/types';
2
+ export type FleetBudgetSource = 'cli-flag' | 'env' | 'profile' | 'default';
3
+ export interface ResolvedFleetBudget {
4
+ maxConcurrent: number;
5
+ maxConcurrentSource: FleetBudgetSource;
6
+ maxSpawns: number;
7
+ maxSpawnsSource: FleetBudgetSource;
8
+ }
9
+ export declare function resolveFleetBudgetSources(input: {
10
+ flags?: Record<string, string | boolean> | undefined;
11
+ env?: NodeJS.ProcessEnv | undefined;
12
+ config?: Config | undefined;
13
+ /** Built-in maxConcurrent when nothing else wins. */
14
+ defaultMaxConcurrent?: number | undefined;
15
+ /** Built-in maxSpawns when nothing else wins. */
16
+ defaultMaxSpawns?: number | undefined;
17
+ }): ResolvedFleetBudget;
18
+ //# sourceMappingURL=budget-source.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import type { SubagentConfig } from '@wrongstack/core/types';
2
2
  import type { MultiAgentDeps } from './host-types.js';
3
- export declare function resolveHostSubagentSkillContent(deps: MultiAgentDeps, roster: Record<string, SubagentConfig>, subCfg: SubagentConfig): Promise<string>;
3
+ export declare function resolveHostSubagentSkillContent(deps: MultiAgentDeps, roster: Record<string, SubagentConfig>, subCfg: SubagentConfig, availableToolNames?: readonly string[]): Promise<string>;
4
4
  export declare function retrieveHostSubagentMemory(deps: MultiAgentDeps, getLeaderMode: (() => string | undefined) | undefined, subCfg: SubagentConfig, taskContext?: Record<string, unknown>): Promise<string[]>;
5
5
  //# sourceMappingURL=host-context.d.ts.map
@@ -10,6 +10,20 @@ export declare function buildShadowAgentTaskDescription(reason: string): string;
10
10
  export declare function touchLruKey(order: string[], key: string): void;
11
11
  export declare function setBoundedLruEntry<T>(map: Map<string, T>, order: string[], key: string, value: T, maxEntries: number): void;
12
12
  export declare const DEFAULT_SUBAGENT_HIDDEN_TOOLS: Set<string>;
13
+ /**
14
+ * Tools a subagent can never receive — not even through an explicit
15
+ * `tools: [...]` allowlist.
16
+ *
17
+ * This is stricter than {@link DEFAULT_SUBAGENT_HIDDEN_TOOLS}, which only hides
18
+ * orchestration controls from the unrestricted default slice and still hands
19
+ * them over when a spawn asks for them by name. `nextsteps` records the
20
+ * leader's after-task suggestions: the runtime folds a recorded block into a
21
+ * LEADER turn only (`next-steps-slot.ts`), and `WIDE_SUBAGENT_CAPABILITIES`
22
+ * does not carry `session.nextsteps`. Granting it would produce either a
23
+ * permission rejection or a silent no-op, so asking for it by name is a
24
+ * mistake to correct rather than an intent to honor.
25
+ */
26
+ export declare const NEVER_SUBAGENT_TOOLS: Set<string>;
13
27
  export declare function selectSubagentTools(allTools: readonly Tool[], directorToolsByName: ReadonlyMap<string, Tool>, allow?: readonly string[] | undefined): Tool[];
14
28
  export declare function resolveSubagentCapabilities(subCfg: SubagentConfig, toolsForAllow: (allow: readonly string[]) => readonly Tool[]): readonly string[] | undefined;
15
29
  export declare function resolveFleetWorktreePolicy(config: Config): FleetWorktreePolicy;