@wrongstack/cli 0.300.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 (56) hide show
  1. package/dist/{acp-IDNS2YVE.js → acp-DATHHPNT.js} +34 -15
  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-6T6ZOT2R.js → auth-JASC4K3F.js} +150 -37
  5. package/dist/boot/launch-menu.d.ts +2 -4
  6. package/dist/boot/tool-registry.d.ts +3 -0
  7. package/dist/chimera-reviewer-policy.d.ts +10 -3
  8. package/dist/chimera-work-registry.d.ts +19 -0
  9. package/dist/{chronicle-QQEQFSSE.js → chronicle-63MFCZN6.js} +26 -5
  10. package/dist/{chunk-YWUPHRGB.js → chunk-263G3FMB.js} +33 -10
  11. package/dist/{chunk-GYDQABMA.js → chunk-2KDOFTTM.js} +44 -32
  12. package/dist/chunk-CSAPOCBP.js +24 -0
  13. package/dist/{chunk-N7ULWBO5.js → chunk-ETOEGL3V.js} +3 -7
  14. package/dist/{chunk-3MWUZMOL.js → chunk-GUHQQG63.js} +14 -3
  15. package/dist/chunk-KE7E7DPX.js +38 -0
  16. package/dist/{chunk-PEMN4T5O.js → chunk-MHF2IJDE.js} +21 -4
  17. package/dist/{chunk-V76R7DC5.js → chunk-O7XNGCRT.js} +311 -309
  18. package/dist/{chunk-DPJZQCVP.js → chunk-YMXXOOFN.js} +1 -1
  19. package/dist/{chunk-4WJKVQJY.js → chunk-ZECLGUTF.js} +15 -3
  20. package/dist/{cli-main-7B3G2YSG.js → cli-main-XWC57MGE.js} +333 -98
  21. package/dist/{execution-SHGM7BVC.js → execution-LHMSMDHB.js} +126 -52
  22. package/dist/execution-chimera-cascade.d.ts +3 -4
  23. package/dist/execution-chimera-review.d.ts +2 -3
  24. package/dist/execution-cleanup.d.ts +3 -2
  25. package/dist/execution.d.ts +1 -1
  26. package/dist/{export-TPORYVRZ.js → export-DOBGPY23.js} +12 -1
  27. package/dist/fleet/host-context.d.ts +1 -1
  28. package/dist/fleet/host-helpers.d.ts +14 -0
  29. package/dist/fleet/status-broadcast.d.ts +1 -1
  30. package/dist/{hq-DVDD4XPV.js → hq-LN5CJHCM.js} +2 -2
  31. package/dist/hq-server/routes.d.ts +10 -1
  32. package/dist/hq-server/ws.d.ts +17 -6
  33. package/dist/{hq-server-DQHLNHDL.js → hq-server-S7HZG3PA.js} +3 -2
  34. package/dist/index.js +63 -98
  35. package/dist/live-settings-input.d.ts +5 -0
  36. package/dist/{mcp-CYLPUEHC.js → mcp-OHPJH73U.js} +6 -2
  37. package/dist/{modeldiag-YOQZHBQV.js → modeldiag-3DOK4BWW.js} +2 -2
  38. package/dist/{plugin-usage-EOG4ET4S.js → plugin-usage-YEVLOR33.js} +7 -3
  39. package/dist/{plugins-56CMWMPR.js → plugins-K3BAUP4D.js} +3 -3
  40. package/dist/profile-config-path.d.ts +0 -2
  41. package/dist/provider-helpers.d.ts +0 -5
  42. package/dist/{providers-models-K6RT7ABI.js → providers-models-WEZ4EGLL.js} +6 -5
  43. package/dist/{replay-KCTXNMZQ.js → replay-FCBNHW3K.js} +5 -1
  44. package/dist/{rewind-EJMVFVLS.js → rewind-AOD3JIYX.js} +5 -1
  45. package/dist/{sessions-config-NLCMK3BY.js → sessions-config-XXN7267N.js} +8 -3
  46. package/dist/subcommands/flags.d.ts +23 -0
  47. package/dist/terminal-format.d.ts +15 -0
  48. package/dist/{webui-server-G3EKFXLL.js → webui-server-DD35QFNO.js} +5 -13
  49. package/dist/wiring/brain-and-orchestration.d.ts +1 -1
  50. package/dist/wiring/mailbox-bridge-bootstrap.d.ts +3 -1
  51. package/dist/wiring/management-tools.d.ts +9 -1
  52. package/dist/wiring/pipeline.d.ts +5 -5
  53. package/dist/wiring/plugins.d.ts +2 -3
  54. package/dist/wiring/provider-utility-tools.d.ts +1 -0
  55. package/dist/wiring/tools.d.ts +4 -5
  56. package/package.json +23 -23
@@ -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
@@ -188,7 +195,7 @@ async function runWebUIDispatch(ctx) {
188
195
  const isSimpleUi = flags["simpleui"] === true;
189
196
  agent.disableInteractiveConfirmation();
190
197
  renderer.setSilent(true);
191
- const { runWebUI } = await import("./webui-server-G3EKFXLL.js");
198
+ const { runWebUI } = await import("./webui-server-DD35QFNO.js");
192
199
  const flagValue = (names) => {
193
200
  for (const name of names) {
194
201
  if (!Object.hasOwn(flags, name)) continue;
@@ -311,10 +318,12 @@ async function runWebUIDispatch(ctx) {
311
318
  // the default when it is taken (a second instance, leftover socket).
312
319
  onListening: ({ url }) => {
313
320
  const surface = isSimpleUi ? "SimpleUI" : "WebUI";
314
- renderer.writeInfo(color2.green(` \u2726 ${surface} running \u2192 ${color2.bold(url)}`));
315
321
  renderer.writeInfo(
316
- color2.dim(` Press Ctrl+C in this terminal to stop the ${surface} server.
317
- `)
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
+ `
318
327
  );
319
328
  },
320
329
  // Make autonomy.switch from the browser flip the CLI's real
@@ -837,13 +846,16 @@ async function handleProjectSwitchSpawn(opts) {
837
846
  const nodeExe = process.execPath;
838
847
  const spawnArgs = [cliPath, "--no-interactive"];
839
848
  if (resumeSessionId) spawnArgs.push("--resume", resumeSessionId);
840
- spawn(nodeExe, spawnArgs, {
849
+ const child = spawn(nodeExe, spawnArgs, {
841
850
  cwd: root,
842
851
  stdio: "ignore",
843
- detached: true
844
- }).on("error", (err) => {
852
+ detached: true,
853
+ windowsHide: true
854
+ });
855
+ child.on("error", (err) => {
845
856
  console.error(color4.red(`Failed to spawn wstack: ${err.message}`));
846
857
  });
858
+ child.unref();
847
859
  console.log(
848
860
  [
849
861
  "",
@@ -938,6 +950,7 @@ async function switchProjectInPlace(ctx, targetRoot, displayName) {
938
950
  context.state.replaceMessages([]);
939
951
  context.state.replaceTodos([]);
940
952
  context.clearFileTracking();
953
+ context.clearMemoryEvidence?.();
941
954
  context.tokenCounter.reset();
942
955
  context.meta["packageTrackerOpts"] = { storageDir: nextWpaths.projectDir, projectRoot: resolved };
943
956
  context.state.setMeta(
@@ -979,7 +992,8 @@ async function switchProjectInPlace(ctx, targetRoot, displayName) {
979
992
  context.systemPrompt = await switchBuilder.build({
980
993
  cwd: resolved,
981
994
  projectRoot: resolved,
982
- tools: agent.tools.list(),
995
+ tools: agent.tools.listForProvider(),
996
+ catalogTools: agent.tools.list(),
983
997
  provider: context.provider.id,
984
998
  model: context.model
985
999
  });
@@ -1293,7 +1307,7 @@ import * as fs3 from "node:fs/promises";
1293
1307
  import * as path7 from "node:path";
1294
1308
  import { decryptConfigSecrets, encryptConfigSecrets, noOpVault } from "@wrongstack/core/security";
1295
1309
  import { normalizeTokenSavingTier, resolveFleetChatVerbosity } from "@wrongstack/core/types";
1296
- import { atomicWrite } from "@wrongstack/core/utils";
1310
+ import { atomicWrite, deepMerge } from "@wrongstack/core/utils";
1297
1311
  import { getProcessRegistry } from "@wrongstack/tools";
1298
1312
  var PANEL_IDS_CLI = [
1299
1313
  "projectPicker",
@@ -1372,6 +1386,7 @@ function createSettingsAdapter(ctx) {
1372
1386
  auditLevel: cfg.session?.auditLevel ?? "standard",
1373
1387
  indexOnStart: cfg.indexing?.onSessionStart !== false,
1374
1388
  maxIterations: cfg.tools?.maxIterations ?? 500,
1389
+ nextStepsTool: cfg.tools?.nextsteps?.enabled === true,
1375
1390
  restrictFsToRoot: resolvedRestrict,
1376
1391
  autoProceedMaxIterations: cfg.autonomy?.autoProceedMaxIterations ?? 50,
1377
1392
  debugStream: cfg.debugStream ?? false,
@@ -1411,12 +1426,15 @@ function createSettingsAdapter(ctx) {
1411
1426
  }
1412
1427
  async function saveSettings(s) {
1413
1428
  try {
1414
- 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) {
1415
1430
  const cfg = configStore.get();
1416
- const activeProfileName = cfg.activeProfile ?? "default";
1417
1431
  const persistDeps = {
1418
1432
  configStore,
1419
- 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),
1420
1438
  inProjectConfigPath: wpaths.inProjectConfig,
1421
1439
  vault: noOpVault,
1422
1440
  resolveProfilePath: (name) => wpaths.profileConfig(name)
@@ -1506,9 +1524,10 @@ function createSettingsAdapter(ctx) {
1506
1524
  idx.onSessionStart = s.indexOnStart;
1507
1525
  decrypted.indexing = idx;
1508
1526
  }
1509
- if (s.maxIterations !== void 0 || fsAccess !== void 0) {
1527
+ if (s.maxIterations !== void 0 || s.nextStepsTool !== void 0 || fsAccess !== void 0) {
1510
1528
  const tools = decrypted.tools ?? {};
1511
1529
  if (s.maxIterations !== void 0) tools.maxIterations = s.maxIterations;
1530
+ if (s.nextStepsTool !== void 0) tools.nextsteps = { enabled: s.nextStepsTool };
1512
1531
  if (fsAccess !== void 0) tools.restrictToProjectRoot = fsAccess.restrictToProjectRoot;
1513
1532
  decrypted.tools = tools;
1514
1533
  }
@@ -1563,7 +1582,7 @@ function createSettingsAdapter(ctx) {
1563
1582
  const destRaw = await fs3.readFile(actualTarget, "utf8");
1564
1583
  const destParsed = JSON.parse(destRaw);
1565
1584
  const destDecrypted = decryptConfigSecrets(destParsed, noOpVault);
1566
- mergedToWrite = { ...destDecrypted, ...decrypted };
1585
+ mergedToWrite = deepMerge(destDecrypted, decrypted);
1567
1586
  } catch (err) {
1568
1587
  if (err.code !== "ENOENT") {
1569
1588
  throw new Error(
@@ -1620,7 +1639,7 @@ function createSettingsAdapter(ctx) {
1620
1639
  ...decrypted.indexing ?? {}
1621
1640
  }
1622
1641
  } : {},
1623
- ...s.maxIterations !== void 0 || fsAccess !== void 0 ? {
1642
+ ...s.maxIterations !== void 0 || s.nextStepsTool !== void 0 || fsAccess !== void 0 ? {
1624
1643
  tools: {
1625
1644
  ...currentConfig.tools,
1626
1645
  ...decrypted.tools ?? {}
@@ -1734,6 +1753,7 @@ function createBrainPanelHost(deps) {
1734
1753
  councilDistinctness: snap.council.distinctness,
1735
1754
  councilPerCallTimeoutMs: snap.council.perCallTimeoutMs,
1736
1755
  councilMaxConcurrency: snap.council.maxConcurrency,
1756
+ councilVoterMaxTokens: snap.council.voterMaxTokens,
1737
1757
  councilJudgeMaxTokens: snap.council.judgeMaxTokens,
1738
1758
  // The EFFECTIVE judge, not `council.judge`. The configured one is
1739
1759
  // usually absent, so the panel used to read "auto" and say nothing
@@ -1815,6 +1835,7 @@ function createBrainPanelHost(deps) {
1815
1835
  setCouncilDistinctness: (mode) => apply({ council: { distinctness: mode } }),
1816
1836
  setCouncilPerCallTimeout: (ms) => apply({ council: { perCallTimeoutMs: ms ?? null } }),
1817
1837
  setCouncilMaxConcurrency: (count) => apply({ council: { maxConcurrency: count ?? null } }),
1838
+ setCouncilVoterMaxTokens: (tokens) => apply({ council: { voterMaxTokens: tokens ?? null } }),
1818
1839
  setCouncilJudgeMaxTokens: (tokens) => apply({ council: { judgeMaxTokens: tokens ?? null } }),
1819
1840
  setLedgerEnabled: (on) => apply({ ledger: { enabled: on } }),
1820
1841
  setAutoDeny: (count) => apply({ ledger: { autoDenyAfterFailures: count ?? null } }),
@@ -1851,7 +1872,7 @@ var reviewerRoundRobinCursor = 0;
1851
1872
  function __resetReviewerRoundRobinCursor(value = 0) {
1852
1873
  reviewerRoundRobinCursor = value;
1853
1874
  }
1854
- function assignReviewerModelsRoundRobin(provider, model, fallbackModels, statusTracker) {
1875
+ function assignReviewerModels(provider, model, fallbackModels, selection = "round-robin", statusTracker, random = Math.random) {
1855
1876
  const pool = buildReviewerModelPool(provider, model, fallbackModels, statusTracker);
1856
1877
  if (pool.length <= 1) {
1857
1878
  return {
@@ -1860,20 +1881,24 @@ function assignReviewerModelsRoundRobin(provider, model, fallbackModels, statusT
1860
1881
  fallbackModels: [...fallbackModels]
1861
1882
  };
1862
1883
  }
1884
+ const cursor = selection === "random" ? Math.min(pool.length - 1, Math.floor(Math.max(0, random()) * pool.length)) : reviewerRoundRobinCursor;
1863
1885
  const assignment = selectRoundRobinReviewerAssignment(
1864
1886
  pool,
1865
- reviewerRoundRobinCursor,
1887
+ cursor,
1866
1888
  provider,
1867
1889
  model,
1868
1890
  statusTracker
1869
1891
  );
1870
- reviewerRoundRobinCursor = assignment.nextCursor;
1892
+ if (selection === "round-robin") reviewerRoundRobinCursor = assignment.nextCursor;
1871
1893
  return {
1872
1894
  provider: assignment.provider,
1873
1895
  model: assignment.model,
1874
1896
  fallbackModels: assignment.fallbackModels
1875
1897
  };
1876
1898
  }
1899
+ function assignReviewerModelsRoundRobin(provider, model, fallbackModels, statusTracker) {
1900
+ return assignReviewerModels(provider, model, fallbackModels, "round-robin", statusTracker);
1901
+ }
1877
1902
  var MAX_REVIEWER_ATTEMPTS = 4;
1878
1903
  var REVIEWER_ATTEMPT_TIMEOUT_MS = 9e5;
1879
1904
  var REVIEWER_LADDER_BUDGET_MS = 12e5;
@@ -1976,7 +2001,7 @@ async function finalizeExecutionCleanup(input) {
1976
2001
  session,
1977
2002
  tokenCounter,
1978
2003
  events,
1979
- getPendingChimeraWork,
2004
+ chimeraWork,
1980
2005
  director,
1981
2006
  reader
1982
2007
  } = input;
@@ -2028,25 +2053,34 @@ async function finalizeExecutionCleanup(input) {
2028
2053
  })
2029
2054
  );
2030
2055
  });
2031
- events.emit("session.ended", {
2032
- id: activeSession.id,
2033
- sessionId: activeSession.id,
2034
- usage: tokenCounter.total()
2035
- });
2036
- if (getPendingChimeraWork) {
2037
- const chimeraWork = getPendingChimeraWork();
2038
- if (chimeraWork) {
2039
- await chimeraWork.catch((err) => {
2040
- console.warn(
2041
- JSON.stringify({
2042
- level: "warn",
2043
- event: "shutdown.chimera_work_failed",
2044
- message: `Pending chimera work failed: ${err instanceof Error ? err.message : String(err)}`,
2045
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
2046
- })
2047
- );
2048
- });
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
+ }
2049
2068
  }
2069
+ );
2070
+ try {
2071
+ while (sessionEndProducers.size > 0) {
2072
+ await Promise.allSettled([...sessionEndProducers]);
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
+ );
2050
2084
  }
2051
2085
  if (director) {
2052
2086
  try {
@@ -2447,6 +2481,43 @@ var FleetStatusLine = class {
2447
2481
  }
2448
2482
  };
2449
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
+
2450
2521
  // src/execution-chimera-cascade.ts
2451
2522
  import * as fsp from "node:fs/promises";
2452
2523
  import * as path8 from "node:path";
@@ -2708,7 +2779,7 @@ function installChimeraCascadeHandler({
2708
2779
  director,
2709
2780
  session,
2710
2781
  getPendingWork,
2711
- setPendingWork,
2782
+ trackWork,
2712
2783
  buildLadder
2713
2784
  }) {
2714
2785
  events.onPattern("chimera.cascade_needed", (_event, payload) => {
@@ -2797,7 +2868,7 @@ function installChimeraCascadeHandler({
2797
2868
  }
2798
2869
  await maybeReReviewCascade({ events, session, bundle: p.bundle });
2799
2870
  })();
2800
- setPendingWork(pendingWork);
2871
+ trackWork(pendingWork);
2801
2872
  });
2802
2873
  }
2803
2874
  async function maybeReReviewCascade({
@@ -2836,7 +2907,7 @@ async function maybeReReviewCascade({
2836
2907
  stopReason: "end_turn",
2837
2908
  usage: { input: 0, output: 0 }
2838
2909
  });
2839
- const emitted = emitReviewIfChanged(
2910
+ const emitted = await emitReviewIfChanged(
2840
2911
  { events, emitCustom: events.emitCustom.bind(events) },
2841
2912
  reReviewBundle
2842
2913
  );
@@ -2908,13 +2979,14 @@ function installChimeraReviewHandler({
2908
2979
  projectDir,
2909
2980
  statusTracker,
2910
2981
  persistReview = persistChimeraReview,
2911
- setPendingWork
2982
+ trackWork
2912
2983
  }) {
2913
2984
  events.onPattern("chimera.review_needed", (_event, payload) => {
2914
2985
  const p = payload;
2915
2986
  const reviewSessionId = agent.ctx.activeRunSessionId ?? agent.ctx.session?.id ?? session.id;
2916
2987
  const dir = director;
2917
2988
  if (!dir) {
2989
+ void recordCompletedReview(events, { bundle: p }).catch(() => void 0);
2918
2990
  return;
2919
2991
  }
2920
2992
  if (p.files.length === 0) return;
@@ -2957,11 +3029,11 @@ function installChimeraReviewHandler({
2957
3029
  `\u{1F982} Chimera report persistence failed: ${message}. The review remains in the session transcript and mailbox.`
2958
3030
  );
2959
3031
  } finally {
2960
- recordCompletedReview(events, completion);
3032
+ await recordCompletedReview(events, completion);
2961
3033
  events.emitCustom("chimera.review_complete", completion);
2962
3034
  }
2963
3035
  };
2964
- setPendingWork(
3036
+ trackWork(
2965
3037
  (async () => {
2966
3038
  let subagentId;
2967
3039
  try {
@@ -2973,10 +3045,11 @@ function installChimeraReviewHandler({
2973
3045
  const baseProvider = rawProvider || tProvider || config.provider;
2974
3046
  const baseModel = rawModel || tModel || config.model;
2975
3047
  const baseFallbacks = p.reviewFallbackModels ? [...p.reviewFallbackModels] : resolveReviewerFallbackModels(void 0);
2976
- const assigned = assignReviewerModelsRoundRobin(
3048
+ const assigned = assignReviewerModels(
2977
3049
  baseProvider,
2978
3050
  baseModel,
2979
3051
  baseFallbacks,
3052
+ p.reviewModelSelection ?? "round-robin",
2980
3053
  statusTracker
2981
3054
  );
2982
3055
  const ladder = buildReviewerAttemptLadder({
@@ -4555,7 +4628,7 @@ async function execute(deps) {
4555
4628
  const profileName = config.activeProfile ?? "default";
4556
4629
  const bootUpdateInfo = initialUpdateInfo;
4557
4630
  const offStorageObservability = installStorageObservability(events, context.traceId);
4558
- let pendingChimeraWork;
4631
+ const chimeraWork = createChimeraWorkRegistry();
4559
4632
  installChimeraReviewHandler({
4560
4633
  events,
4561
4634
  director,
@@ -4569,8 +4642,8 @@ async function execute(deps) {
4569
4642
  // this, a 429-stricken model is re-spawned on every concurrent reviewer
4570
4643
  // turn and burns the whole chain instead of staying quarantined.
4571
4644
  statusTracker,
4572
- setPendingWork: (work) => {
4573
- pendingChimeraWork = work;
4645
+ trackWork: (work) => {
4646
+ chimeraWork.track(work);
4574
4647
  }
4575
4648
  });
4576
4649
  installChimeraCascadeHandler({
@@ -4581,9 +4654,9 @@ async function execute(deps) {
4581
4654
  profileChain: effectiveFallbackChain2(config),
4582
4655
  session: { provider: config.provider, model: config.model }
4583
4656
  }),
4584
- getPendingWork: () => pendingChimeraWork,
4585
- setPendingWork: (work) => {
4586
- pendingChimeraWork = work;
4657
+ getPendingWork: () => chimeraWork.pending(),
4658
+ trackWork: (work) => {
4659
+ chimeraWork.track(work);
4587
4660
  }
4588
4661
  });
4589
4662
  let code = 0;
@@ -4995,7 +5068,7 @@ async function execute(deps) {
4995
5068
  session,
4996
5069
  tokenCounter,
4997
5070
  events,
4998
- getPendingChimeraWork: () => pendingChimeraWork,
5071
+ chimeraWork,
4999
5072
  director,
5000
5073
  reader
5001
5074
  });
@@ -5006,8 +5079,9 @@ export {
5006
5079
  CHIMERA_REVIEW_READ_ONLY_TOOLS,
5007
5080
  __resetReviewerRoundRobinCursor,
5008
5081
  applyChimeraReviewerReadOnlyPolicy,
5082
+ assignReviewerModels,
5009
5083
  assignReviewerModelsRoundRobin,
5010
5084
  execute,
5011
5085
  resolveReviewerFallbackModels
5012
5086
  };
5013
- //# sourceMappingURL=execution-SHGM7BVC.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
@@ -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;
@@ -22,8 +22,8 @@
22
22
  *
23
23
  * @module fleet/status-broadcast
24
24
  */
25
- import type { EventBus } from '@wrongstack/core/kernel';
26
25
  import type { Mailbox } from '@wrongstack/core/coordination';
26
+ import type { EventBus } from '@wrongstack/core/kernel';
27
27
  import type { FleetConfig } from '@wrongstack/core/types';
28
28
  export interface FleetStatusBroadcasterOptions {
29
29
  /** Host EventBus carrying the re-emitted `subagent.*` lifecycle events. */
@@ -36,7 +36,7 @@ var hqCmd = async (args, deps) => {
36
36
  return 1;
37
37
  };
38
38
  async function startServer(deps) {
39
- const { startHqServer } = await import("./hq-server-DQHLNHDL.js");
39
+ const { startHqServer } = await import("./hq-server-S7HZG3PA.js");
40
40
  const dataDir = resolveDataDir(deps);
41
41
  const flags = deps.flags ?? {};
42
42
  const host = typeof flags["host"] === "string" ? flags["host"] : HQ_CLI_DEFAULT_HOST;
@@ -563,4 +563,4 @@ export {
563
563
  hqCmd,
564
564
  resolveAuditActor
565
565
  };
566
- //# sourceMappingURL=hq-DVDD4XPV.js.map
566
+ //# sourceMappingURL=hq-LN5CJHCM.js.map
@@ -51,7 +51,16 @@ export interface HqRouterMailboxGateway {
51
51
  export interface HqRouterDeps {
52
52
  trustBoundary: TrustBoundary;
53
53
  host: string;
54
- listeningPort: number;
54
+ /**
55
+ * Getter, not a value. The router is constructed BEFORE `listen()`
56
+ * resolves, so a copied number froze the REQUESTED port. When the default
57
+ * is busy and `strictPort` is off, the scan binds `port+1` and the banner
58
+ * prints it — but the origin guard kept comparing against the old number and
59
+ * answered `403 forbidden: untrusted request origin` to the dashboard and
60
+ * every `/api/*` call. (The WS upgrade handler read the live local, so the
61
+ * failure looked like "the socket connects but the page won't load".)
62
+ */
63
+ listeningPort: () => number;
55
64
  trustedPublicOrigins: Set<string>;
56
65
  /** Trust `Origin: file://`. Off by default — see StartHqServerOptions (WS-081). */
57
66
  allowFileOrigin?: boolean | undefined;
@@ -32,11 +32,22 @@ export declare function detectLeaderLoss(lostClient: ConnectedClient, clients: M
32
32
  /**
33
33
  * Server-side fanout of a `hq.kanban_snapshot` envelope after a merge.
34
34
  *
35
- * C2 — broadcasts the post-merge `delta` payload to:
36
- * - every client whose `projectId` matches `projectId`, and
37
- * - every browser in `browsers` (browsers are not project-scoped at the
38
- * WS level; the browser-side projection filters by project when it
39
- * applies the snapshot).
35
+ * C2 — broadcasts the post-merge `delta` payload to every client whose
36
+ * `projectId` matches `projectId`.
37
+ *
38
+ * BROWSERS ARE DELIBERATELY NOT NOTIFIED. They used to be — unscoped, so
39
+ * every browser received every project's deltas — on the strength of a
40
+ * comment claiming "the browser-side projection filters by project when it
41
+ * applies the snapshot". No such projection exists: `hq.kanban_snapshot` is
42
+ * absent from `HqBrowserMessage` and from the SPA's dispatch chain, so
43
+ * `HqWsClient` parsed each frame and dropped it. The board view learns about
44
+ * changes from the `kanban.snapshot` HQ *event* and refetches over HTTP
45
+ * (`views/kanban.tsx`), which is why the drift was invisible.
46
+ *
47
+ * `browsers` stays in the signature: the fan-out is the natural place to wire
48
+ * a real browser consumer, and the parity test in
49
+ * `tests/hq-browser-protocol-parity.test.ts` now fails the moment a
50
+ * browser-bound frame has no handler.
40
51
  *
41
52
  * The `delta` payload is the same touched-only payload the merge handler
42
53
  * builds (full-set broadcasts drive V8 heap exhaustion on long-lived
@@ -44,7 +55,7 @@ export declare function detectLeaderLoss(lostClient: ConnectedClient, clients: M
44
55
  *
45
56
  * Exported for direct unit testing in `tests/hq-kanban-fanout.test.ts`.
46
57
  */
47
- export declare function fanoutKanbanDelta(message: string, clients: Map<WebSocket, ConnectedClient>, browsers: Set<WebSocket>, projectId: string): {
58
+ export declare function fanoutKanbanDelta(message: string, clients: Map<WebSocket, ConnectedClient>, _browsers: Set<WebSocket>, projectId: string): {
48
59
  clientsNotified: number;
49
60
  browsersNotified: number;
50
61
  };
@@ -15,7 +15,8 @@ import {
15
15
  readLocalSubagentTranscript,
16
16
  sanitizeApiError,
17
17
  startHqServer
18
- } from "./chunk-GYDQABMA.js";
18
+ } from "./chunk-2KDOFTTM.js";
19
+ import "./chunk-KE7E7DPX.js";
19
20
  import "./chunk-Q5GTM25S.js";
20
21
  import "./chunk-7OCVIDC7.js";
21
22
  export {
@@ -36,4 +37,4 @@ export {
36
37
  sanitizeApiError,
37
38
  startHqServer
38
39
  };
39
- //# sourceMappingURL=hq-server-DQHLNHDL.js.map
40
+ //# sourceMappingURL=hq-server-S7HZG3PA.js.map