@wrongstack/cli 0.298.2 → 0.299.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.
@@ -1576,7 +1576,7 @@ async function handleApiPassword(req, res, mutableAuth, sessions, dataDir, secur
1576
1576
  res.end(JSON.stringify({ error: { code: "BAD_REQUEST", message: "Invalid JSON body." } }));
1577
1577
  return;
1578
1578
  }
1579
- const hasAdminCapability = auth !== void 0 && "capabilities" in auth && auth.capabilities !== void 0 && auth.capabilities.includes("auth.admin");
1579
+ const hasAdminCapability = auth !== void 0 && "capabilities" in auth && auth.capabilities?.includes("auth.admin");
1580
1580
  if (!localOpenBootstrap && mutableAuth.passwordHash !== void 0 && !hasAdminCapability) {
1581
1581
  const currentPassword = typeof body.currentPassword === "string" ? body.currentPassword : "";
1582
1582
  if (!currentPassword || !await verifyHqPassword(currentPassword, mutableAuth.passwordHash)) {
@@ -1846,7 +1846,7 @@ async function handleApiLoginVerify(req, res, _url, mutableAuth, sessions, login
1846
1846
  return;
1847
1847
  }
1848
1848
  const session = sessions.get(sessionId);
1849
- if (!session || !session.pending2fa) {
1849
+ if (!session?.pending2fa) {
1850
1850
  res.writeHead(401, { "Content-Type": "application/json" });
1851
1851
  res.end(JSON.stringify({ error: { code: "NO_PENDING_SESSION", message: "Session is not pending 2FA." } }));
1852
1852
  return;
@@ -4505,4 +4505,4 @@ export {
4505
4505
  startHqServer,
4506
4506
  HqInsecureExposureError2 as HqInsecureExposureError
4507
4507
  };
4508
- //# sourceMappingURL=chunk-C5GH4YBL.js.map
4508
+ //# sourceMappingURL=chunk-2SLSUDYS.js.map
@@ -2555,7 +2555,8 @@ function wireSessionEvents(deps) {
2555
2555
  attempt: e.attempt,
2556
2556
  delayMs: e.delayMs,
2557
2557
  status: e.status,
2558
- description: e.description
2558
+ description: e.description,
2559
+ ...e.errorBody ? { errorBody: e.errorBody } : {}
2559
2560
  });
2560
2561
  }
2561
2562
  );
@@ -2568,7 +2569,8 @@ function wireSessionEvents(deps) {
2568
2569
  providerId: e.providerId,
2569
2570
  status: e.status,
2570
2571
  description: e.description,
2571
- retryable: e.retryable ?? false
2572
+ retryable: e.retryable ?? false,
2573
+ ...e.errorBody ? { errorBody: e.errorBody } : {}
2572
2574
  });
2573
2575
  }
2574
2576
  );
@@ -3023,7 +3025,7 @@ import {
3023
3025
  } from "@wrongstack/acp";
3024
3026
  import { ToolValidationError } from "@wrongstack/core/types";
3025
3027
  function buildAcpSubagentRunner(subagentId) {
3026
- let cmd = Object.prototype.hasOwnProperty.call(ACP_AGENT_COMMANDS, subagentId) ? ACP_AGENT_COMMANDS[subagentId] : void 0;
3028
+ let cmd = Object.hasOwn(ACP_AGENT_COMMANDS, subagentId) ? ACP_AGENT_COMMANDS[subagentId] : void 0;
3027
3029
  if (!cmd) {
3028
3030
  const desc = findAgentDescriptor(subagentId);
3029
3031
  if (desc) {
@@ -7516,11 +7518,9 @@ async function setupLifecycleAndPlugins(deps) {
7516
7518
  for (const cfg of Object.values(config.mcpServers ?? {})) {
7517
7519
  const preset = presets[cfg.name];
7518
7520
  const merged = preset ? { ...preset, ...cfg } : cfg;
7519
- try {
7520
- await mcpRegistry.start(merged);
7521
- } catch (err) {
7521
+ void mcpRegistry.start(merged).catch((err) => {
7522
7522
  logger.warn(`MCP server "${cfg.name}" failed to start`, err);
7523
- }
7523
+ });
7524
7524
  }
7525
7525
  }
7526
7526
  registerMcpObservability(healthRegistry, metricsSink, mcpRegistry);
@@ -7980,7 +7980,12 @@ function warnUnlessMissing(logger, operation, error) {
7980
7980
  // src/wiring/provider-utility-tools.ts
7981
7981
  import { createContextManagerTool as createContextManagerTool2 } from "@wrongstack/core/infrastructure";
7982
7982
  import { createCouncilTool, createOneShotLLMTool } from "@wrongstack/core/tools";
7983
- import { OneShotOrchestrator } from "@wrongstack/core/execution";
7983
+ import {
7984
+ createCouncilPersonaRegistry,
7985
+ createCouncilProfileRegistry,
7986
+ OneShotOrchestrator
7987
+ } from "@wrongstack/core/execution";
7988
+ import { ModelRouter } from "@wrongstack/core/models";
7984
7989
  async function adoptResumedProvider(input) {
7985
7990
  if (!input.resumedProvider && !input.resumedModel) return;
7986
7991
  const config = input.getConfig();
@@ -7995,6 +8000,42 @@ async function adoptResumedProvider(input) {
7995
8000
  );
7996
8001
  }
7997
8002
  }
8003
+ function createLiveModelRouter(getConfig) {
8004
+ return {
8005
+ pickForTask(role, description) {
8006
+ const config = getConfig();
8007
+ const router = new ModelRouter({
8008
+ ...config.modelMatrix ? { matrix: config.modelMatrix } : {},
8009
+ config: {
8010
+ provider: config.provider,
8011
+ model: config.model,
8012
+ ...config.providers ? { providers: config.providers } : {}
8013
+ }
8014
+ });
8015
+ return router.pickForTask(role, description);
8016
+ }
8017
+ };
8018
+ }
8019
+ function buildCouncilRegistries(cfg) {
8020
+ const base = {
8021
+ ...cfg?.defaultProfile ? { defaultProfile: cfg.defaultProfile } : {},
8022
+ ...cfg?.maxConcurrency !== void 0 ? { maxConcurrency: cfg.maxConcurrency } : {}
8023
+ };
8024
+ if (!cfg?.personas?.length && !cfg?.profiles?.length) return base;
8025
+ try {
8026
+ const personas = createCouncilPersonaRegistry(cfg.personas ?? []);
8027
+ const profiles = createCouncilProfileRegistry(
8028
+ cfg.profiles ?? [],
8029
+ personas
8030
+ );
8031
+ return { ...base, personas, profiles };
8032
+ } catch (error) {
8033
+ console.warn(
8034
+ `Council tool: ignoring tools.council personas/profiles \u2014 ${error instanceof Error ? error.message : String(error)}. Falling back to the built-in lenses and panels.`
8035
+ );
8036
+ return base;
8037
+ }
8038
+ }
7998
8039
  function registerProviderUtilityTools(input) {
7999
8040
  const config = input.getConfig();
8000
8041
  const llmTool = createOneShotLLMTool({
@@ -8009,12 +8050,22 @@ function registerProviderUtilityTools(input) {
8009
8050
  buildProvider: input.buildProvider,
8010
8051
  getConfig: input.getConfig,
8011
8052
  fallbackProfileManager: input.fallbackProfileManager,
8012
- statusTracker: input.statusTracker
8053
+ statusTracker: input.statusTracker,
8054
+ // Council profiles route each seat by ROLE ('planner', 'critic',
8055
+ // 'analyst', 'security-reviewer'…) rather than pinning models. Without a
8056
+ // router those hints were inert: every seat fell through to the session
8057
+ // provider/model, so a three-seat panel asked one model three times and
8058
+ // reported a distinctness warning on every single call.
8059
+ modelRouter: createLiveModelRouter(input.getConfig)
8013
8060
  });
8014
8061
  registerOrOverride(
8015
8062
  input.toolRegistry,
8016
8063
  "council",
8017
- createCouncilTool({ caller: councilOrchestrator, fallbackProfileManager: input.fallbackProfileManager })
8064
+ createCouncilTool({
8065
+ caller: councilOrchestrator,
8066
+ fallbackProfileManager: input.fallbackProfileManager,
8067
+ ...buildCouncilRegistries(config.tools?.council)
8068
+ })
8018
8069
  );
8019
8070
  try {
8020
8071
  const summarizer = new OneShotOrchestrator({
@@ -10533,11 +10584,18 @@ ${color13.dim("YOLO enabled; tool calls run without approval unless an explicit
10533
10584
  // src/slash-commands/brain.ts
10534
10585
  import { randomUUID as randomUUID5 } from "node:crypto";
10535
10586
  import { readFile as readFile7 } from "node:fs/promises";
10587
+ import { BUILTIN_COUNCIL_PERSONA_IDS, BUILTIN_COUNCIL_PERSONAS } from "@wrongstack/core/execution";
10536
10588
  import { color as color14 } from "@wrongstack/core/utils";
10537
10589
  import { parseModelRef } from "@wrongstack/core/agent";
10538
10590
  var RISK_LEVELS = /* @__PURE__ */ new Set(["off", "low", "medium", "high", "all"]);
10539
10591
  var COUNCIL_RISKS = /* @__PURE__ */ new Set(["medium", "high", "critical"]);
10540
- var PERSONA_SHORTHANDS = /* @__PURE__ */ new Set(["executor", "skeptic", "auditor"]);
10592
+ var COUNCIL_DISTINCTNESS = /* @__PURE__ */ new Set(["none", "model", "provider"]);
10593
+ var COUNCIL_NUMERIC_OPS = {
10594
+ timeout: "perCallTimeoutMs",
10595
+ concurrency: "maxConcurrency",
10596
+ judgetokens: "judgeMaxTokens"
10597
+ };
10598
+ var PERSONA_SHORTHANDS = new Set(BUILTIN_COUNCIL_PERSONA_IDS);
10541
10599
  var HEURISTIC_FIELDS = {
10542
10600
  lowrisk: "lowRiskAutoAnswer",
10543
10601
  blocked: "blockedResolved",
@@ -10613,8 +10671,11 @@ function buildBrainCommand(opts) {
10613
10671
  " /brain human-timeout <ms|off> Interactive escalation wait before terminal policy",
10614
10672
  " /brain council on|off Enable/disable the multi-LLM council",
10615
10673
  " /brain council minrisk <medium|high|critical>",
10616
- " /brain council voters <seat> [<seat> ...] seat = <ref>[:executor|:skeptic|:auditor][:veto][:w=N]",
10674
+ " /brain council voters <seat> [<seat> ...] seat = <ref>[:<persona>][:veto][:w=N]",
10675
+ " /brain council personas List the built-in decision lenses",
10617
10676
  " /brain council judge <ref|auto> | quorum <0..1> | approval <0..1>",
10677
+ " /brain council distinctness <none|model|provider> warn on a non-diverse panel",
10678
+ " /brain council timeout|concurrency|judgetokens <n|default>",
10618
10679
  " /brain ledger [n] Show the last n rows (default 15) of the persistent decision ledger",
10619
10680
  " /brain ledger on|off | autodeny <n>",
10620
10681
  " /brain stats Per-tier decision counts: how often the Brain actually calls a model",
@@ -10835,12 +10896,24 @@ function buildBrainCommand(opts) {
10835
10896
  if (op === "voters") {
10836
10897
  const seats = rest.slice(1).map(parseSeat);
10837
10898
  if (seats.length === 0 || seats.some((s) => s === null)) {
10838
- const msg3 = "Usage: /brain council voters <ref[:executor|:skeptic|:auditor][:veto][:w=N]> [...]";
10899
+ const msg3 = `Usage: /brain council voters <ref[:<persona>][:veto][:w=N]> [...] \u2014 personas: ${BUILTIN_COUNCIL_PERSONA_IDS.join(", ")} (see /brain council personas)`;
10839
10900
  opts.renderer.writeWarning(msg3);
10840
10901
  return { message: msg3 };
10841
10902
  }
10842
10903
  return applyPatch({ council: { voters: seats } }, councilSummary);
10843
10904
  }
10905
+ if (op === "personas") {
10906
+ const lines = [
10907
+ "Council decision lenses (built-in):",
10908
+ ...BUILTIN_COUNCIL_PERSONAS.map(
10909
+ (p) => ` ${color14.cyan(p.id.padEnd(15))} ${p.description}${p.defaultVeto ? color14.dim(" [veto by default]") : ""}`
10910
+ ),
10911
+ color14.dim("Any other string is used verbatim as a custom lens instruction.")
10912
+ ];
10913
+ const msg3 = lines.join("\n");
10914
+ opts.renderer.write(msg3);
10915
+ return { message: msg3 };
10916
+ }
10844
10917
  if (op === "judge") {
10845
10918
  const ref = rest[1];
10846
10919
  if (!ref) {
@@ -10857,7 +10930,33 @@ function buildBrainCommand(opts) {
10857
10930
  (s) => `Brain council ${op} set to ${color14.cyan(String(op === "quorum" ? s.council.quorum ?? 0.5 : s.council.approval ?? 0.5))}`
10858
10931
  );
10859
10932
  }
10860
- const msg2 = `Unknown council subcommand: ${op}. Use on, off, minrisk, voters, judge, quorum, or approval.`;
10933
+ if (op === "distinctness") {
10934
+ const mode = (rest[1] ?? "").toLowerCase();
10935
+ if (!COUNCIL_DISTINCTNESS.has(mode)) {
10936
+ const msg3 = "Usage: /brain council distinctness <none|model|provider>";
10937
+ opts.renderer.writeWarning(msg3);
10938
+ return { message: msg3 };
10939
+ }
10940
+ return applyPatch(
10941
+ { council: { distinctness: mode } },
10942
+ (s) => `Brain council distinctness set to ${color14.cyan(s.council.distinctness)}${mode === "none" ? color14.dim(" \u2014 a non-diverse panel will no longer be reported") : ""}`
10943
+ );
10944
+ }
10945
+ if (COUNCIL_NUMERIC_OPS[op]) {
10946
+ const field = COUNCIL_NUMERIC_OPS[op];
10947
+ const raw = rest[1];
10948
+ if (!raw) {
10949
+ const msg3 = `Usage: /brain council ${op} <positive integer | default>`;
10950
+ opts.renderer.writeWarning(msg3);
10951
+ return { message: msg3 };
10952
+ }
10953
+ const value = raw.toLowerCase() === "default" ? null : Number(raw);
10954
+ return applyPatch({ council: { [field]: value } }, (s) => {
10955
+ const applied = s.council[field];
10956
+ return `Brain council ${op} set to ${color14.cyan(applied === void 0 ? "default" : String(applied))}`;
10957
+ });
10958
+ }
10959
+ const msg2 = `Unknown council subcommand: ${op}. Use on, off, minrisk, voters, personas, judge, quorum, approval, distinctness, timeout, concurrency, or judgetokens.`;
10861
10960
  opts.renderer.writeWarning(msg2);
10862
10961
  return { message: msg2 };
10863
10962
  }
@@ -16320,6 +16419,78 @@ async function fileExists(filePath) {
16320
16419
  }
16321
16420
  }
16322
16421
 
16422
+ // src/slash-commands/intake.ts
16423
+ import {
16424
+ AllowAllIntakeAuthorizer,
16425
+ RequirementIntakeService,
16426
+ RequirementIntakeStore
16427
+ } from "@wrongstack/requirement-intake";
16428
+ import { ensureProjectIdentity, resolveWstackPaths as resolveWstackPaths2 } from "@wrongstack/core/utils";
16429
+ var INTAKE_ACTOR = { id: "cli-operator", type: "user" };
16430
+ function lastUserPrompt(opts) {
16431
+ const ctx = opts.context;
16432
+ if (!ctx) return "";
16433
+ for (let index = ctx.messages.length - 1; index >= 0; index -= 1) {
16434
+ const message = ctx.messages[index];
16435
+ if (message?.role === "user" && typeof message.content === "string") {
16436
+ const trimmed = message.content.trim();
16437
+ if (trimmed.length > 0) return trimmed;
16438
+ }
16439
+ }
16440
+ return "";
16441
+ }
16442
+ function buildIntakeCommand(opts) {
16443
+ return {
16444
+ name: "intake",
16445
+ category: "Run",
16446
+ description: "Create and submit a requirement intake record from the current prompt (or given text)",
16447
+ argsHint: "[request text]",
16448
+ help: [
16449
+ "/intake [text] Create + submit a requirement intake record.",
16450
+ " No text \u2192 uses the most recent prompt in this session.",
16451
+ "",
16452
+ "The exact text is preserved verbatim as the record's original request;",
16453
+ "the record is stored under the project state dir and is ready for",
16454
+ "downstream modules (SDD interview kickoff, planning)."
16455
+ ].join("\n"),
16456
+ async run(args) {
16457
+ const text = args.trim() || lastUserPrompt(opts);
16458
+ if (!text) {
16459
+ return {
16460
+ message: "Nothing to intake \u2014 give the request as text or submit a prompt first.\nUsage: /intake <request text>"
16461
+ };
16462
+ }
16463
+ const identity = await ensureProjectIdentity(opts.projectRoot);
16464
+ const projectId = identity.identity.projectId;
16465
+ const baseDir = opts.paths?.projectRequirementIntakes ?? resolveWstackPaths2({ projectRoot: opts.projectRoot }).projectRequirementIntakes;
16466
+ const service = new RequirementIntakeService({
16467
+ store: new RequirementIntakeStore({ baseDir }),
16468
+ authorizer: new AllowAllIntakeAuthorizer()
16469
+ });
16470
+ const ctx = { ...INTAKE_ACTOR, projectId };
16471
+ const { record, created, idempotent } = await service.createIntake(
16472
+ { projectId, originalRequest: text, requestedBy: ctx.id },
16473
+ ctx
16474
+ );
16475
+ const { record: submitted } = await service.submitIntake(record.id, ctx);
16476
+ return {
16477
+ message: [
16478
+ idempotent ? "\u26A0 Already recorded \u2014 resubmitted the existing record:" : "\u{1F4E5} Requirement intake recorded:",
16479
+ ` id: ${submitted.id}`,
16480
+ ` title: ${submitted.title}`,
16481
+ ` type: ${submitted.requestType}`,
16482
+ ` status: ${submitted.status} (${created ? "new" : "existing"})`,
16483
+ ` stored: ${baseDir}`,
16484
+ "",
16485
+ "Use /sdd to turn this into a specification. Records live under the",
16486
+ "project state dir (see `stored` above) and are consumed by the",
16487
+ "WebUI requirement-intake API."
16488
+ ].join("\n")
16489
+ };
16490
+ }
16491
+ };
16492
+ }
16493
+
16323
16494
  // src/slash-commands/kanban.ts
16324
16495
  import {
16325
16496
  addCheckToTask,
@@ -18300,7 +18471,7 @@ function errorMessage(err) {
18300
18471
  }
18301
18472
 
18302
18473
  // src/slash-commands/memory.ts
18303
- import { toErrorMessage as toErrorMessage17 } from "@wrongstack/core/utils";
18474
+ import { toErrorMessage as toErrorMessage18 } from "@wrongstack/core/utils";
18304
18475
  import { getSageSurface as getSageSurface2 } from "@wrongstack/sage";
18305
18476
 
18306
18477
  // src/slash-commands/memory-compact.ts
@@ -18564,6 +18735,240 @@ function parseCompactEntries(raw) {
18564
18735
  return entries;
18565
18736
  }
18566
18737
 
18738
+ // src/slash-commands/memory-triage.ts
18739
+ import { toErrorMessage as toErrorMessage17 } from "@wrongstack/core/utils";
18740
+ import { runTriage, formatTriageReport } from "@wrongstack/sage";
18741
+ var DEFAULT_MAX_PHASE3 = 1e3;
18742
+ var DEFAULT_MAX_PHASE4_PAIRS = 50;
18743
+ async function runTriageCommand(opts, args) {
18744
+ const Sage = getSage(opts);
18745
+ if (!Sage) return { message: "No SAGE surface available." };
18746
+ let dryRun = true;
18747
+ let limit;
18748
+ let maxPhase3 = DEFAULT_MAX_PHASE3;
18749
+ let maxPhase4Pairs = DEFAULT_MAX_PHASE4_PAIRS;
18750
+ const flagErrors = [];
18751
+ for (let i = 0; i < args.length; i++) {
18752
+ const token = args[i];
18753
+ if (!token) continue;
18754
+ if (!token.startsWith("--")) continue;
18755
+ const name = token.slice(2).toLowerCase();
18756
+ const next = args[i + 1];
18757
+ switch (name) {
18758
+ case "dry-run":
18759
+ dryRun = true;
18760
+ break;
18761
+ case "apply":
18762
+ dryRun = false;
18763
+ break;
18764
+ case "limit": {
18765
+ if (next === void 0 || next.startsWith("--")) {
18766
+ flagErrors.push("--limit needs a value.");
18767
+ continue;
18768
+ }
18769
+ const parsed = Number.parseInt(next, 10);
18770
+ if (!Number.isFinite(parsed) || parsed < 1) {
18771
+ flagErrors.push(`--limit must be a positive integer (got "${next}").`);
18772
+ continue;
18773
+ }
18774
+ limit = Math.min(500, Math.max(1, parsed));
18775
+ i++;
18776
+ break;
18777
+ }
18778
+ case "max-phase3": {
18779
+ if (next === void 0 || next.startsWith("--")) {
18780
+ flagErrors.push("--max-phase3 needs a value.");
18781
+ continue;
18782
+ }
18783
+ const parsed = Number.parseInt(next, 10);
18784
+ if (!Number.isFinite(parsed) || parsed < 1) {
18785
+ flagErrors.push(`--max-phase3 must be a positive integer (got "${next}").`);
18786
+ continue;
18787
+ }
18788
+ maxPhase3 = parsed;
18789
+ i++;
18790
+ break;
18791
+ }
18792
+ case "max-phase4-pairs": {
18793
+ if (next === void 0 || next.startsWith("--")) {
18794
+ flagErrors.push("--max-phase4-pairs needs a value.");
18795
+ continue;
18796
+ }
18797
+ const parsed = Number.parseInt(next, 10);
18798
+ if (!Number.isFinite(parsed) || parsed < 1) {
18799
+ flagErrors.push(`--max-phase4-pairs must be a positive integer (got "${next}").`);
18800
+ continue;
18801
+ }
18802
+ maxPhase4Pairs = parsed;
18803
+ i++;
18804
+ break;
18805
+ }
18806
+ default:
18807
+ flagErrors.push(`Unknown flag "--${name}".`);
18808
+ }
18809
+ }
18810
+ if (flagErrors.length > 0) {
18811
+ return {
18812
+ message: `Cannot triage:
18813
+ - ${flagErrors.join("\n- ")}
18814
+
18815
+ Usage: /memory triage [--dry-run|--apply] [--limit N] [--max-phase3 N] [--max-phase4-pairs N]`
18816
+ };
18817
+ }
18818
+ const memories = await loadActiveMemories(Sage, limit);
18819
+ if (memories.length === 0) {
18820
+ return { message: "No active memories to triage." };
18821
+ }
18822
+ const provider = opts.llmProvider;
18823
+ const model = opts.llmModel ?? "";
18824
+ const callLlm = provider?.complete ? async (system, userPrompt) => {
18825
+ const signal = AbortSignal.timeout(3e4);
18826
+ const response = await provider.complete(
18827
+ {
18828
+ model,
18829
+ system: [{ type: "text", text: system }],
18830
+ messages: [{ role: "user", content: userPrompt }],
18831
+ maxTokens: 60,
18832
+ temperature: 0.1
18833
+ },
18834
+ { signal }
18835
+ );
18836
+ return response.content.filter((b) => b.type === "text").map((b) => b.text).join("").trim();
18837
+ } : async () => "3";
18838
+ let report;
18839
+ try {
18840
+ report = await runTriage(memories, callLlm, {
18841
+ dryRun,
18842
+ maxPhase3Calls: maxPhase3,
18843
+ maxPhase4Pairs,
18844
+ verbose: false
18845
+ });
18846
+ } catch (err) {
18847
+ return { message: `Triage failed: ${toErrorMessage17(err)}` };
18848
+ }
18849
+ if (!dryRun) {
18850
+ const applyReport = await applyDispatch(Sage, report);
18851
+ return {
18852
+ message: formatTriageReport(report) + "\n\n" + applyReport
18853
+ };
18854
+ }
18855
+ return { message: formatTriageReport(report) };
18856
+ }
18857
+ function getSage(opts) {
18858
+ if (!opts.memoryStore) return null;
18859
+ try {
18860
+ const { getSageSurface: getSageSurface3 } = __require("@wrongstack/sage");
18861
+ return getSageSurface3(opts.memoryStore);
18862
+ } catch {
18863
+ return null;
18864
+ }
18865
+ }
18866
+ async function loadActiveMemories(Sage, limit) {
18867
+ const all = [];
18868
+ let cursor;
18869
+ while (true) {
18870
+ const pageSize = limit ? Math.min(500, limit - all.length) : 500;
18871
+ if (pageSize <= 0) break;
18872
+ const page = await Sage.listSagePage({
18873
+ statuses: ["active", "stale"],
18874
+ limit: pageSize,
18875
+ cursor
18876
+ });
18877
+ all.push(...page.memories);
18878
+ if (limit && all.length >= limit) break;
18879
+ if (!page.nextCursor) break;
18880
+ cursor = page.nextCursor;
18881
+ }
18882
+ return limit ? all.slice(0, limit) : all;
18883
+ }
18884
+ async function applyDispatch(Sage, report) {
18885
+ const lines = ["## Apply Results", ""];
18886
+ let autoOk = 0;
18887
+ let autoFail = 0;
18888
+ for (const action of report.dispatch.autoApply) {
18889
+ const patch = {};
18890
+ for (const update of action.updates) {
18891
+ if (update.field === "status") {
18892
+ patch.status = update.value;
18893
+ } else if (update.field === "confidence") {
18894
+ patch.confidence = update.value;
18895
+ } else if (update.field === "importance") {
18896
+ patch.importance = update.value;
18897
+ }
18898
+ }
18899
+ try {
18900
+ await Sage.updateSage(action.memoryId, patch);
18901
+ autoOk++;
18902
+ } catch (err) {
18903
+ autoFail++;
18904
+ lines.push(` \u2717 Failed to update ${action.memoryId}: ${toErrorMessage17(err)}`);
18905
+ }
18906
+ }
18907
+ lines.push(`**Auto-apply:** ${autoOk} succeeded, ${autoFail} failed`);
18908
+ lines.push("");
18909
+ let mergeOk = 0;
18910
+ let mergeFail = 0;
18911
+ for (const merge of report.merges.merges) {
18912
+ try {
18913
+ await Sage.updateSage(merge.supersededId, { status: "superseded" });
18914
+ await Sage.updateSage(merge.keeperId, { supersedes: [merge.supersededId] });
18915
+ mergeOk++;
18916
+ } catch (err) {
18917
+ mergeFail++;
18918
+ lines.push(` \u2717 Failed to merge ${merge.supersededId} \u2192 ${merge.keeperId}: ${toErrorMessage17(err)}`);
18919
+ }
18920
+ }
18921
+ lines.push(`**Merges:** ${mergeOk} succeeded, ${mergeFail} failed`);
18922
+ lines.push("");
18923
+ const proposalResult = await fileProposals(Sage, report.dispatch.proposals);
18924
+ if (proposalResult.total > 0) {
18925
+ lines.push(
18926
+ `**Proposals:** ${proposalResult.filed} of ${proposalResult.total} filed via memory_candidates (use \`/memory candidates\` to accept/reject).`
18927
+ );
18928
+ }
18929
+ for (const failure of proposalResult.failures) {
18930
+ lines.push(` \u2717 Failed to file proposal for ${failure.memoryId}: ${failure.error}`);
18931
+ }
18932
+ return lines.join("\n");
18933
+ }
18934
+ async function fileProposals(Sage, proposals) {
18935
+ const inputs = [];
18936
+ const failures = [];
18937
+ let filed = 0;
18938
+ for (const proposal of proposals) {
18939
+ const input = {
18940
+ text: proposal.memoryText,
18941
+ targetMemoryId: proposal.memoryId,
18942
+ reviewReason: proposal.reason,
18943
+ suggestedAction: proposal.suggestedAction,
18944
+ kind: "memory_review",
18945
+ scope: "project",
18946
+ importance: 0.5,
18947
+ confidence: 0.9,
18948
+ tags: ["triage"],
18949
+ anchors: [],
18950
+ sources: [{ type: "project_instruction" }]
18951
+ };
18952
+ inputs.push(input);
18953
+ try {
18954
+ await Sage.createCandidate(input);
18955
+ filed++;
18956
+ } catch (err) {
18957
+ failures.push({
18958
+ memoryId: proposal.memoryId,
18959
+ error: toErrorMessage17(err)
18960
+ });
18961
+ }
18962
+ }
18963
+ return {
18964
+ filed,
18965
+ failed: failures.length,
18966
+ total: proposals.length,
18967
+ failures,
18968
+ inputs
18969
+ };
18970
+ }
18971
+
18567
18972
  // src/slash-commands/memory-formatters.ts
18568
18973
  function previewText(text, maxLen) {
18569
18974
  if (text.length <= maxLen) return text;
@@ -18918,7 +19323,7 @@ function buildMemoryCommand(opts) {
18918
19323
  return {
18919
19324
  name: "memory",
18920
19325
  category: "Inspect",
18921
- description: "Inspect or edit persistent memory: /memory [show|search|file|path|for-file|graph|gather|remember|update|delete|forget|hygiene|verify|candidates|audit|import-legacy|clear|compact|compact-log|stats|audience]",
19326
+ description: "Inspect or edit persistent memory: /memory [show|search|file|path|for-file|graph|gather|remember|update|delete|forget|hygiene|verify|candidates|triage|audit|import-legacy|clear|compact|compact-log|stats|audience]",
18922
19327
  async run(args) {
18923
19328
  const store = opts.memoryStore;
18924
19329
  if (!store) return { message: "No memory store configured." };
@@ -18981,7 +19386,7 @@ function buildMemoryCommand(opts) {
18981
19386
  message: `Remembered \`${memory.id}\` [${memory.kind}] ${memory.text}${tags}`
18982
19387
  };
18983
19388
  } catch (err) {
18984
- return { message: `Could not remember: ${toErrorMessage17(err)}` };
19389
+ return { message: `Could not remember: ${toErrorMessage18(err)}` };
18985
19390
  }
18986
19391
  }
18987
19392
  case "update":
@@ -19019,7 +19424,7 @@ function buildMemoryCommand(opts) {
19019
19424
  message: `Updated \`${memory.id}\` [${memory.kind}|${memory.status}] ${memory.text}`
19020
19425
  };
19021
19426
  } catch (err) {
19022
- return { message: `Could not update: ${toErrorMessage17(err)}` };
19427
+ return { message: `Could not update: ${toErrorMessage18(err)}` };
19023
19428
  }
19024
19429
  }
19025
19430
  case "delete":
@@ -19034,7 +19439,7 @@ function buildMemoryCommand(opts) {
19034
19439
  await Sage.deleteSage(id, reason, { force: true });
19035
19440
  return { message: `Deleted \`${id}\`.` };
19036
19441
  } catch (err) {
19037
- return { message: `Could not delete: ${toErrorMessage17(err)}` };
19442
+ return { message: `Could not delete: ${toErrorMessage18(err)}` };
19038
19443
  }
19039
19444
  }
19040
19445
  case "forget":
@@ -19253,7 +19658,7 @@ function buildMemoryCommand(opts) {
19253
19658
  }
19254
19659
  return { message: lines.join("\n") };
19255
19660
  } catch (err) {
19256
- return { message: `gather failed: ${toErrorMessage17(err)}` };
19661
+ return { message: `gather failed: ${toErrorMessage18(err)}` };
19257
19662
  }
19258
19663
  }
19259
19664
  case "verify": {
@@ -19289,6 +19694,10 @@ function buildMemoryCommand(opts) {
19289
19694
  message: formatCandidates(await Sage.listCandidates(action === "all"))
19290
19695
  };
19291
19696
  }
19697
+ case "triage": {
19698
+ if (!Sage) return requiresSage("triage");
19699
+ return runTriageCommand(opts, rest);
19700
+ }
19292
19701
  case "audit": {
19293
19702
  if (!Sage?.readAudit) return requiresSage("audit");
19294
19703
  return { message: formatAudit(await Sage.readAudit(50)) };
@@ -19344,7 +19753,7 @@ function buildMemoryCommand(opts) {
19344
19753
  File size reduced. Audit-logged as \`memory.log_compacted\`.`
19345
19754
  };
19346
19755
  } catch (err) {
19347
- return { message: `Compaction failed: ${toErrorMessage17(err)}` };
19756
+ return { message: `Compaction failed: ${toErrorMessage18(err)}` };
19348
19757
  }
19349
19758
  }
19350
19759
  case "stats": {
@@ -19404,6 +19813,7 @@ File size reduced. Audit-logged as \`memory.log_compacted\`.`
19404
19813
  "hygiene",
19405
19814
  "verify",
19406
19815
  "candidates",
19816
+ "triage",
19407
19817
  "audit",
19408
19818
  "import-legacy",
19409
19819
  "clear",
@@ -19571,7 +19981,7 @@ async function runAudienceMemory(store, rest) {
19571
19981
  message: `Remembered \`${memory.id}\` for ${formatAudienceSelector(memory.audience)}: ${memory.text}`
19572
19982
  };
19573
19983
  } catch (err) {
19574
- return { message: `Could not remember: ${toErrorMessage17(err)}` };
19984
+ return { message: `Could not remember: ${toErrorMessage18(err)}` };
19575
19985
  }
19576
19986
  }
19577
19987
  if (sub === "clear") {
@@ -19583,7 +19993,7 @@ async function runAudienceMemory(store, rest) {
19583
19993
  message: `Cleared audience scope from \`${id}\` \u2014 it is now general project memory.`
19584
19994
  };
19585
19995
  } catch (err) {
19586
- return { message: `Could not clear audience: ${toErrorMessage17(err)}` };
19996
+ return { message: `Could not clear audience: ${toErrorMessage18(err)}` };
19587
19997
  }
19588
19998
  }
19589
19999
  if (sub === "search" || sub === "find") {
@@ -20212,7 +20622,7 @@ function buildModelCapsCommand(opts) {
20212
20622
 
20213
20623
  // src/slash-commands/models.ts
20214
20624
  import * as fs15 from "node:fs/promises";
20215
- import { toErrorMessage as toErrorMessage18 } from "@wrongstack/core/utils";
20625
+ import { toErrorMessage as toErrorMessage19 } from "@wrongstack/core/utils";
20216
20626
  import { atomicWrite as atomicWrite7, color as color37 } from "@wrongstack/core/utils";
20217
20627
  import { ConfigError as ConfigError4, ToolValidationError as ToolValidationError4 } from "@wrongstack/core/types";
20218
20628
  import { decryptConfigSecrets as decryptConfigSecrets3, encryptConfigSecrets as encryptConfigSecrets3, noOpVault as noOpVault4 } from "@wrongstack/core/security";
@@ -20447,7 +20857,7 @@ function buildModelsCommand(opts) {
20447
20857
  };
20448
20858
  } catch (err) {
20449
20859
  return {
20450
- message: `${color37.red("models error")}: ${toErrorMessage18(err)}`
20860
+ message: `${color37.red("models error")}: ${toErrorMessage19(err)}`
20451
20861
  };
20452
20862
  }
20453
20863
  }
@@ -21296,7 +21706,7 @@ function buildPruneCommand(opts) {
21296
21706
  // src/slash-commands/refiner.ts
21297
21707
  import { color as color42 } from "@wrongstack/core/utils";
21298
21708
  import { noOpVault as noOpVault5 } from "@wrongstack/core/security";
21299
- import { toErrorMessage as toErrorMessage19 } from "@wrongstack/core/utils";
21709
+ import { toErrorMessage as toErrorMessage20 } from "@wrongstack/core/utils";
21300
21710
  function buildRefinerCommand(opts) {
21301
21711
  const help = [
21302
21712
  "Usage:",
@@ -21394,7 +21804,7 @@ function buildRefinerCommand(opts) {
21394
21804
  };
21395
21805
  } catch (err) {
21396
21806
  return {
21397
- message: `${color42.red("refiner error")}: ${toErrorMessage19(err)}`
21807
+ message: `${color42.red("refiner error")}: ${toErrorMessage20(err)}`
21398
21808
  };
21399
21809
  }
21400
21810
  }
@@ -21416,7 +21826,7 @@ function buildRefinerCommand(opts) {
21416
21826
  };
21417
21827
  } catch (err) {
21418
21828
  return {
21419
- message: `${color42.red("refiner error")}: ${toErrorMessage19(err)}`
21829
+ message: `${color42.red("refiner error")}: ${toErrorMessage20(err)}`
21420
21830
  };
21421
21831
  }
21422
21832
  }
@@ -22594,7 +23004,7 @@ ${sddHelp()}`
22594
23004
  // src/slash-commands/session.ts
22595
23005
  import { color as color43, isPidAlive } from "@wrongstack/core/utils";
22596
23006
  import { SessionRecovery } from "@wrongstack/core/storage";
22597
- import { toErrorMessage as toErrorMessage20 } from "@wrongstack/core/utils";
23007
+ import { toErrorMessage as toErrorMessage21 } from "@wrongstack/core/utils";
22598
23008
  function statusIcon2(status) {
22599
23009
  switch (status) {
22600
23010
  case "active":
@@ -22712,7 +23122,7 @@ function buildLoadCommand(opts) {
22712
23122
  message: name ? color43.green(`Renamed ${targetId} \u2192 "${name}"`) : color43.green(`Cleared name on ${targetId} (title: "${summary.title}")`)
22713
23123
  };
22714
23124
  } catch (err) {
22715
- return { message: color43.red(`Rename failed: ${toErrorMessage20(err)}`) };
23125
+ return { message: color43.red(`Rename failed: ${toErrorMessage21(err)}`) };
22716
23126
  }
22717
23127
  }
22718
23128
  if (first === "delete") {
@@ -22738,7 +23148,7 @@ function buildLoadCommand(opts) {
22738
23148
  await opts.sessionStore.delete(targetId);
22739
23149
  return { message: color43.green(`Deleted session ${targetId}`) };
22740
23150
  } catch (err) {
22741
- return { message: color43.red(`Delete failed: ${toErrorMessage20(err)}`) };
23151
+ return { message: color43.red(`Delete failed: ${toErrorMessage21(err)}`) };
22742
23152
  }
22743
23153
  }
22744
23154
  const showIncomplete = parts.includes("--incomplete") || parts.includes("-i");
@@ -23076,7 +23486,7 @@ async function killSession(sessionId, confirm) {
23076
23486
  } catch (err) {
23077
23487
  return {
23078
23488
  message: color43.red(
23079
- `Failed to kill session: ${toErrorMessage20(err)}`
23489
+ `Failed to kill session: ${toErrorMessage21(err)}`
23080
23490
  )
23081
23491
  };
23082
23492
  }
@@ -23084,7 +23494,7 @@ async function killSession(sessionId, confirm) {
23084
23494
 
23085
23495
  // src/slash-commands/setmodel.ts
23086
23496
  import * as fs16 from "node:fs/promises";
23087
- import { toErrorMessage as toErrorMessage21 } from "@wrongstack/core/utils";
23497
+ import { toErrorMessage as toErrorMessage22 } from "@wrongstack/core/utils";
23088
23498
  import { AGENT_CATALOG as AGENT_CATALOG2, AGENTS_BY_PHASE as AGENTS_BY_PHASE3 } from "@wrongstack/core/agent-catalog";
23089
23499
  import { atomicWrite as atomicWrite9, color as color44, expectDefined as expectDefined8 } from "@wrongstack/core/utils";
23090
23500
  import { ConfigError as ConfigError5 } from "@wrongstack/core/types";
@@ -23617,7 +24027,7 @@ function buildSetModelCommand(opts) {
23617
24027
  };
23618
24028
  } catch (err) {
23619
24029
  return {
23620
- message: `${color44.red("setmodel error")}: ${toErrorMessage21(err)}`
24030
+ message: `${color44.red("setmodel error")}: ${toErrorMessage22(err)}`
23621
24031
  };
23622
24032
  }
23623
24033
  }
@@ -23630,7 +24040,7 @@ import { access as access4 } from "node:fs/promises";
23630
24040
  import * as path25 from "node:path";
23631
24041
  import { color as color45 } from "@wrongstack/core/utils";
23632
24042
  import { parseNextSteps } from "@wrongstack/tools/next-steps";
23633
- import { toErrorMessage as toErrorMessage22 } from "@wrongstack/core/utils";
24043
+ import { toErrorMessage as toErrorMessage23 } from "@wrongstack/core/utils";
23634
24044
  function readGitStatus(projectRoot, includeBranch) {
23635
24045
  const args = ["status", "--short"];
23636
24046
  if (includeBranch) args.push("--branch");
@@ -23754,7 +24164,7 @@ function buildSuggestCommand(opts) {
23754
24164
  suggestCache = { suggestions, at: Date.now() };
23755
24165
  return { message: formatSuggestions(suggestions) };
23756
24166
  } catch (err) {
23757
- const msg = `Suggestion generation failed: ${toErrorMessage22(err)}`;
24167
+ const msg = `Suggestion generation failed: ${toErrorMessage23(err)}`;
23758
24168
  opts.renderer.writeWarning(msg);
23759
24169
  return { message: msg };
23760
24170
  }
@@ -24341,7 +24751,7 @@ import * as path26 from "node:path";
24341
24751
  import {
24342
24752
  color as color47,
24343
24753
  ensureProjectGitignore,
24344
- ensureProjectIdentity,
24754
+ ensureProjectIdentity as ensureProjectIdentity2,
24345
24755
  projectIdentityPath,
24346
24756
  readProjectIdentity,
24347
24757
  rekeyProjectIdentity
@@ -24477,7 +24887,7 @@ ${color47.dim(filePath)}`
24477
24887
  } : { message: `No committed project identity. Run ${color47.cyan("/project init")}.` };
24478
24888
  }
24479
24889
  if (action === "init") {
24480
- const result2 = await ensureProjectIdentity(root);
24890
+ const result2 = await ensureProjectIdentity2(root);
24481
24891
  await ensureProjectGitignore(root);
24482
24892
  return {
24483
24893
  message: result2.created ? `${color47.green("Created")} ${filePath}
@@ -25068,7 +25478,7 @@ function buildSecurityCommand(opts) {
25068
25478
  // src/slash-commands/settings.ts
25069
25479
  import { noOpVault as noOpVault9 } from "@wrongstack/core/security";
25070
25480
  import { resolveFleetChatVerbosity as resolveFleetChatVerbosity2 } from "@wrongstack/core/types";
25071
- import { color as color48, toErrorMessage as toErrorMessage23 } from "@wrongstack/core/utils";
25481
+ import { color as color48, toErrorMessage as toErrorMessage24 } from "@wrongstack/core/utils";
25072
25482
  import { getProcessRegistry } from "@wrongstack/tools";
25073
25483
 
25074
25484
  // src/utils/delay-format.ts
@@ -25983,7 +26393,7 @@ function buildSettingsCommand(opts) {
25983
26393
  };
25984
26394
  } catch (err) {
25985
26395
  return {
25986
- message: `${color48.red("Settings error")}: ${toErrorMessage23(err)}`
26396
+ message: `${color48.red("Settings error")}: ${toErrorMessage24(err)}`
25987
26397
  };
25988
26398
  }
25989
26399
  }
@@ -26273,7 +26683,7 @@ function parseFlags2(args) {
26273
26683
  }
26274
26684
 
26275
26685
  // src/slash-commands/spawn-agents.ts
26276
- import { toErrorMessage as toErrorMessage24 } from "@wrongstack/core/utils";
26686
+ import { toErrorMessage as toErrorMessage25 } from "@wrongstack/core/utils";
26277
26687
  function buildSpawnCommand(opts) {
26278
26688
  return {
26279
26689
  name: "spawn",
@@ -26310,7 +26720,7 @@ function buildSpawnCommand(opts) {
26310
26720
  const summary = Object.keys(parsed).length > 0 ? await opts.onSpawn(description, parsed) : await opts.onSpawn(description);
26311
26721
  return { message: summary };
26312
26722
  } catch (err) {
26313
- return { message: `Spawn failed: ${toErrorMessage24(err)}` };
26723
+ return { message: `Spawn failed: ${toErrorMessage25(err)}` };
26314
26724
  }
26315
26725
  }
26316
26726
  };
@@ -26918,7 +27328,7 @@ ${formatTaskProgress(file.tasks)}`;
26918
27328
  import * as fs18 from "node:fs/promises";
26919
27329
  import * as path28 from "node:path";
26920
27330
  import { color as color51 } from "@wrongstack/core/utils";
26921
- import { toErrorMessage as toErrorMessage25 } from "@wrongstack/core/utils";
27331
+ import { toErrorMessage as toErrorMessage26 } from "@wrongstack/core/utils";
26922
27332
  async function discoverPackageFiles(projectRoot) {
26923
27333
  const files = [];
26924
27334
  const rootPkg = path28.join(projectRoot, "package.json");
@@ -27126,7 +27536,7 @@ function buildTechStackCommand(opts) {
27126
27536
  message: `TechStack remediation finished: ${applied} applied, ${failed} failed, ${skipped} skipped.`
27127
27537
  };
27128
27538
  } catch (err) {
27129
- const msg = `TechStack remediation failed: ${toErrorMessage25(err)}`;
27539
+ const msg = `TechStack remediation failed: ${toErrorMessage26(err)}`;
27130
27540
  opts.renderer.writeWarning(msg);
27131
27541
  return { message: msg };
27132
27542
  } finally {
@@ -27145,7 +27555,7 @@ function buildTechStackCommand(opts) {
27145
27555
  message: `TechStack inventory complete: ${snapshot.workspaces.length} workspaces, ${snapshot.dependencies.length} dependencies (fingerprint: ${snapshot.fingerprint})`
27146
27556
  };
27147
27557
  } catch (err) {
27148
- const msg = `TechStack inventory failed: ${toErrorMessage25(err)}`;
27558
+ const msg = `TechStack inventory failed: ${toErrorMessage26(err)}`;
27149
27559
  opts.renderer.writeWarning(msg);
27150
27560
  return { message: msg };
27151
27561
  }
@@ -27160,7 +27570,7 @@ function buildTechStackCommand(opts) {
27160
27570
  );
27161
27571
  }
27162
27572
  } catch (err) {
27163
- discoveryNote = color51.red(`Could not scan for package files: ${toErrorMessage25(err)}`);
27573
+ discoveryNote = color51.red(`Could not scan for package files: ${toErrorMessage26(err)}`);
27164
27574
  }
27165
27575
  const task = buildTechStackTask({
27166
27576
  projectRoot: opts.projectRoot,
@@ -27200,7 +27610,7 @@ function buildTechStackCommand(opts) {
27200
27610
  });
27201
27611
  return { message: summary };
27202
27612
  } catch (err) {
27203
- const msg = `Tech stack scan failed: ${toErrorMessage25(err)}`;
27613
+ const msg = `Tech stack scan failed: ${toErrorMessage26(err)}`;
27204
27614
  opts.renderer.writeWarning(msg);
27205
27615
  return { message: msg };
27206
27616
  }
@@ -27210,7 +27620,7 @@ function buildTechStackCommand(opts) {
27210
27620
 
27211
27621
  // src/slash-commands/telegram-settings.ts
27212
27622
  import { color as color53 } from "@wrongstack/core/utils";
27213
- import { toErrorMessage as toErrorMessage26 } from "@wrongstack/core/utils";
27623
+ import { toErrorMessage as toErrorMessage27 } from "@wrongstack/core/utils";
27214
27624
 
27215
27625
  // src/slash-commands/telegram-setup.ts
27216
27626
  import { color as color52 } from "@wrongstack/core/utils";
@@ -27708,7 +28118,7 @@ function buildTelegramSettingsCommand(opts) {
27708
28118
  };
27709
28119
  } catch (err) {
27710
28120
  return {
27711
- message: `${color53.red("Settings error")}: ${toErrorMessage26(err)}`
28121
+ message: `${color53.red("Settings error")}: ${toErrorMessage27(err)}`
27712
28122
  };
27713
28123
  }
27714
28124
  }
@@ -27817,7 +28227,7 @@ function buildTodosCommand(opts) {
27817
28227
  // src/slash-commands/tool.ts
27818
28228
  import { color as color54, getToolDescriptionMode as getToolDescriptionMode2, getToolResultRenderMode, normalizeToolDescriptionMode, normalizeToolResultRenderMode, setToolResultRenderMode } from "@wrongstack/core/utils";
27819
28229
  import { noOpVault as noOpVault10 } from "@wrongstack/core/security";
27820
- import { toErrorMessage as toErrorMessage27 } from "@wrongstack/core/utils";
28230
+ import { toErrorMessage as toErrorMessage28 } from "@wrongstack/core/utils";
27821
28231
  function fit(text, width) {
27822
28232
  if (text.length <= width) return text.padEnd(width);
27823
28233
  return `${text.slice(0, Math.max(0, width - 3))}...`;
@@ -28051,7 +28461,7 @@ function buildToolCommand(opts) {
28051
28461
  try {
28052
28462
  return { message: await cmdEnableAll() };
28053
28463
  } catch (err) {
28054
- return { message: `${color54.red("Error")}: ${toErrorMessage27(err)}` };
28464
+ return { message: `${color54.red("Error")}: ${toErrorMessage28(err)}` };
28055
28465
  }
28056
28466
  }
28057
28467
  const name = parts[0] ?? "";
@@ -28065,7 +28475,7 @@ function buildToolCommand(opts) {
28065
28475
  for (const t of targets) results.push(await cmdDisable(t));
28066
28476
  return { message: results.join("\n") };
28067
28477
  } catch (err) {
28068
- return { message: `${color54.red("Error")}: ${toErrorMessage27(err)}` };
28478
+ return { message: `${color54.red("Error")}: ${toErrorMessage28(err)}` };
28069
28479
  }
28070
28480
  }
28071
28481
  if (sub === "enable") {
@@ -28077,7 +28487,7 @@ function buildToolCommand(opts) {
28077
28487
  for (const t of targets) results.push(await cmdEnable(t));
28078
28488
  return { message: results.join("\n") };
28079
28489
  } catch (err) {
28080
- return { message: `${color54.red("Error")}: ${toErrorMessage27(err)}` };
28490
+ return { message: `${color54.red("Error")}: ${toErrorMessage28(err)}` };
28081
28491
  }
28082
28492
  }
28083
28493
  const action = parts[1]?.toLowerCase();
@@ -28090,7 +28500,7 @@ function buildToolCommand(opts) {
28090
28500
  try {
28091
28501
  return { message: action === "disable" ? await cmdDisable(name) : await cmdEnable(name) };
28092
28502
  } catch (err) {
28093
- return { message: `${color54.red("Error")}: ${toErrorMessage27(err)}` };
28503
+ return { message: `${color54.red("Error")}: ${toErrorMessage28(err)}` };
28094
28504
  }
28095
28505
  }
28096
28506
  if (!opts.toolRegistry.get(name) && !opts.toolRegistry.isDisabled(name)) {
@@ -28130,7 +28540,7 @@ function buildToolCommand(opts) {
28130
28540
  };
28131
28541
  } catch (err) {
28132
28542
  return {
28133
- message: `${color54.red("Could not save tool setting")}: ${toErrorMessage27(err)}`
28543
+ message: `${color54.red("Could not save tool setting")}: ${toErrorMessage28(err)}`
28134
28544
  };
28135
28545
  }
28136
28546
  }
@@ -28150,7 +28560,7 @@ function buildToolCommand(opts) {
28150
28560
  };
28151
28561
  } catch (err) {
28152
28562
  return {
28153
- message: `${color54.red("Could not save tool setting")}: ${toErrorMessage27(err)}`
28563
+ message: `${color54.red("Could not save tool setting")}: ${toErrorMessage28(err)}`
28154
28564
  };
28155
28565
  }
28156
28566
  }
@@ -29047,7 +29457,7 @@ function summaryLine(findings, fixable, handoffs, power) {
29047
29457
  import * as fs20 from "node:fs/promises";
29048
29458
  import * as path30 from "node:path";
29049
29459
  import { color as color57 } from "@wrongstack/core/utils";
29050
- import { toErrorMessage as toErrorMessage28 } from "@wrongstack/core/utils";
29460
+ import { toErrorMessage as toErrorMessage29 } from "@wrongstack/core/utils";
29051
29461
  function buildWorkingDirCommand(_opts) {
29052
29462
  return {
29053
29463
  name: "working_dir",
@@ -29104,7 +29514,7 @@ function buildWorkingDirCommand(_opts) {
29104
29514
  ctx.setWorkingDir(resolved);
29105
29515
  } catch (err) {
29106
29516
  return {
29107
- message: color57.red(toErrorMessage28(err))
29517
+ message: color57.red(toErrorMessage29(err))
29108
29518
  };
29109
29519
  }
29110
29520
  const prevRel = path30.relative(ctx.projectRoot, previous) || ".";
@@ -29250,6 +29660,7 @@ function buildBuiltinSlashCommands(opts) {
29250
29660
  buildDesktopCommand(),
29251
29661
  buildWebuiCommand(),
29252
29662
  buildInitCommand(opts),
29663
+ buildIntakeCommand(opts),
29253
29664
  buildClearCommand(opts),
29254
29665
  buildInterruptCommand(opts),
29255
29666
  buildKanbanCommand(opts),
@@ -29993,7 +30404,7 @@ async function runInteractive(cliCtx) {
29993
30404
  onEvent: evOn
29994
30405
  });
29995
30406
  const savedProviderCfg = config.providers?.[config.provider];
29996
- const { execute } = await import("./execution-BY556FCF.js");
30407
+ const { execute } = await import("./execution-ANOYMWPO.js");
29997
30408
  const stopHeapWatchdog = startSharedHeapWatchdog({
29998
30409
  collectStats: () => {
29999
30410
  const hqQueue = hqPublisherRef.current?.getQueueStats();
@@ -30225,4 +30636,4 @@ export {
30225
30636
  CLI_VERSION,
30226
30637
  runInteractive
30227
30638
  };
30228
- //# sourceMappingURL=cli-main-ZDZMCVLM.js.map
30639
+ //# sourceMappingURL=cli-main-3MQDFSYK.js.map
@@ -1676,7 +1676,14 @@ function createSettingsAdapter(ctx) {
1676
1676
  }
1677
1677
 
1678
1678
  // src/brain-menu/panel-service.ts
1679
- var PERSONA_CYCLE = ["executor", "skeptic", "auditor"];
1679
+ import { BUILTIN_COUNCIL_PERSONAS } from "@wrongstack/core/execution";
1680
+ var PERSONA_CATALOG = BUILTIN_COUNCIL_PERSONAS.map((persona) => ({
1681
+ id: persona.id,
1682
+ name: persona.name,
1683
+ description: persona.description,
1684
+ defaultVeto: persona.defaultVeto
1685
+ }));
1686
+ var PERSONA_CYCLE = PERSONA_CATALOG.map((persona) => persona.id);
1680
1687
  function compactEntry(entry) {
1681
1688
  return entry.provider ? `${entry.provider}/${entry.model}` : entry.model;
1682
1689
  }
@@ -1715,6 +1722,13 @@ function createBrainPanelHost(deps) {
1715
1722
  weight: v.weight
1716
1723
  })),
1717
1724
  councilSeats: snap.councilLabels,
1725
+ personaCatalog: PERSONA_CATALOG.map((persona) => ({ ...persona })),
1726
+ councilQuorum: snap.council.quorum,
1727
+ councilApproval: snap.council.approval,
1728
+ councilDistinctness: snap.council.distinctness,
1729
+ councilPerCallTimeoutMs: snap.council.perCallTimeoutMs,
1730
+ councilMaxConcurrency: snap.council.maxConcurrency,
1731
+ councilJudgeMaxTokens: snap.council.judgeMaxTokens,
1718
1732
  // The EFFECTIVE judge, not `council.judge`. The configured one is
1719
1733
  // usually absent, so the panel used to read "auto" and say nothing
1720
1734
  // about who actually breaks the panel's ties — including when that is
@@ -1775,7 +1789,7 @@ function createBrainPanelHost(deps) {
1775
1789
  const voters = currentVoters();
1776
1790
  const voter = voters[index];
1777
1791
  if (!voter) return Promise.resolve("No such voter.");
1778
- const at = PERSONA_CYCLE.indexOf(voter.persona);
1792
+ const at = PERSONA_CYCLE.indexOf(voter.persona ?? "");
1779
1793
  voter.persona = PERSONA_CYCLE[(at + 1) % PERSONA_CYCLE.length];
1780
1794
  return applyVoters(voters);
1781
1795
  },
@@ -1788,6 +1802,14 @@ function createBrainPanelHost(deps) {
1788
1802
  },
1789
1803
  setJudge: (providerId, model) => apply({ council: { judge: `${providerId}/${model}` } }),
1790
1804
  clearJudge: () => apply({ council: { judge: null } }),
1805
+ // Resolution + budget knobs. `BrainCouncilPatch` accepts null for the
1806
+ // three integer fields, so `undefined` here really does clear to default.
1807
+ setCouncilQuorum: (fraction) => apply({ council: { quorum: fraction } }),
1808
+ setCouncilApproval: (fraction) => apply({ council: { approval: fraction } }),
1809
+ setCouncilDistinctness: (mode) => apply({ council: { distinctness: mode } }),
1810
+ setCouncilPerCallTimeout: (ms) => apply({ council: { perCallTimeoutMs: ms ?? null } }),
1811
+ setCouncilMaxConcurrency: (count) => apply({ council: { maxConcurrency: count ?? null } }),
1812
+ setCouncilJudgeMaxTokens: (tokens) => apply({ council: { judgeMaxTokens: tokens ?? null } }),
1791
1813
  setLedgerEnabled: (on) => apply({ ledger: { enabled: on } }),
1792
1814
  setAutoDeny: (count) => apply({ ledger: { autoDenyAfterFailures: count ?? null } }),
1793
1815
  setTerminalPolicy: (policy) => apply({ terminalPolicy: policy }),
@@ -2850,7 +2872,8 @@ import {
2850
2872
  function normalizeFileKeyForCitation(raw, cwd) {
2851
2873
  const forward = raw.replace(/\\/g, "/").replace(/^\.\//, "");
2852
2874
  const isAbsolute = forward.startsWith("/") || /^[a-zA-Z]:\//.test(forward);
2853
- const relative = isAbsolute ? path9.relative(cwd, forward).replace(/\\/g, "/").replace(/^\.\//, "") : forward;
2875
+ const pathMod = process.platform === "win32" ? path9.win32 : path9;
2876
+ const relative = isAbsolute ? pathMod.relative(cwd, forward).replace(/\\/g, "/").replace(/^\.\//, "") : forward;
2854
2877
  return process.platform === "win32" ? relative.toLowerCase() : relative;
2855
2878
  }
2856
2879
  function installChimeraReviewHandler({
@@ -4942,4 +4965,4 @@ export {
4942
4965
  execute,
4943
4966
  resolveReviewerFallbackModels
4944
4967
  };
4945
- //# sourceMappingURL=execution-BY556FCF.js.map
4968
+ //# sourceMappingURL=execution-ANOYMWPO.js.map
@@ -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-6TSAORAC.js");
39
+ const { startHqServer } = await import("./hq-server-RJLJJZU7.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;
@@ -544,4 +544,4 @@ export {
544
544
  hqCmd,
545
545
  resolveAuditActor
546
546
  };
547
- //# sourceMappingURL=hq-WO6CLUSI.js.map
547
+ //# sourceMappingURL=hq-BDO56CX6.js.map
@@ -15,7 +15,7 @@ import {
15
15
  readLocalSubagentTranscript,
16
16
  sanitizeApiError,
17
17
  startHqServer
18
- } from "./chunk-C5GH4YBL.js";
18
+ } from "./chunk-2SLSUDYS.js";
19
19
  import "./chunk-Q5GTM25S.js";
20
20
  import "./chunk-7OCVIDC7.js";
21
21
  export {
@@ -36,4 +36,4 @@ export {
36
36
  sanitizeApiError,
37
37
  startHqServer
38
38
  };
39
- //# sourceMappingURL=hq-server-6TSAORAC.js.map
39
+ //# sourceMappingURL=hq-server-RJLJJZU7.js.map
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  } from "./chunk-TGDHN4LM.js";
23
23
  import {
24
24
  DEFAULT_PORT
25
- } from "./chunk-C5GH4YBL.js";
25
+ } from "./chunk-2SLSUDYS.js";
26
26
  import "./chunk-Q5GTM25S.js";
27
27
  import {
28
28
  CLI_VERSION
@@ -1522,7 +1522,7 @@ var loaders = {
1522
1522
  quick: async () => (await import("./quick-VIO2BEHS.js")).quickCmd,
1523
1523
  bench: async () => (await import("./bench-7ZDWSNEF.js")).benchCmd,
1524
1524
  chronicle: async () => (await import("./chronicle-QQEQFSSE.js")).chronicleCmd,
1525
- hq: async () => (await import("./hq-WO6CLUSI.js")).hqCmd,
1525
+ hq: async () => (await import("./hq-BDO56CX6.js")).hqCmd,
1526
1526
  mailbox: async () => (await import("./mailbox-serve-DBOOOOWZ.js")).mailboxServeCmd,
1527
1527
  permissions: async () => (await import("./permissions-FNYLZIL4.js")).permissionsCmd,
1528
1528
  project: async () => (await import("./project-BLQ3ALVL.js")).projectCmd,
@@ -2159,7 +2159,7 @@ async function isPortInUse(host, port) {
2159
2159
  }
2160
2160
  async function handleHqShortCircuit(flags) {
2161
2161
  if (flags["hq"] !== true) return null;
2162
- const { startHqServer } = await import("./hq-server-6TSAORAC.js");
2162
+ const { startHqServer } = await import("./hq-server-RJLJJZU7.js");
2163
2163
  const tunnelRequested = flags["tunnel"] === true;
2164
2164
  const host = typeof flags["host"] === "string" ? flags["host"] : tunnelRequested ? "127.0.0.1" : HQ_CLI_DEFAULT_HOST;
2165
2165
  if (tunnelRequested && !isLoopbackHost(host)) {
@@ -3149,7 +3149,7 @@ async function initializeCli(argv) {
3149
3149
  async function main(argv) {
3150
3150
  const cliCtx = await initializeCli(argv);
3151
3151
  if (typeof cliCtx === "number") return cliCtx;
3152
- const { runInteractive } = await import("./cli-main-ZDZMCVLM.js");
3152
+ const { runInteractive } = await import("./cli-main-3MQDFSYK.js");
3153
3153
  return runInteractive(cliCtx);
3154
3154
  }
3155
3155
 
@@ -0,0 +1,4 @@
1
+ import type { SlashCommand } from '@wrongstack/core/types';
2
+ import type { SlashCommandContext } from './command-context.js';
3
+ export declare function buildIntakeCommand(opts: SlashCommandContext): SlashCommand;
4
+ //# sourceMappingURL=intake.d.ts.map
@@ -0,0 +1,47 @@
1
+ /**
2
+ * `/memory triage` slash command.
3
+ *
4
+ * Runs the SAGE Memory Triage pipeline (Phase 1 → 2 → 3 → 4 → 5):
5
+ * 1. preFilter → keep / discard / uncertain
6
+ * 2. valueScore → 0-100 numeric score
7
+ * 3. LLM triage → gray-zone evaluation (1-5)
8
+ * 4. merge detect → cluster + pair LLM comparison
9
+ * 5. dispatch → auto-apply updates + proposals
10
+ *
11
+ * Flags:
12
+ * --dry-run (default) Print what would happen. No state changes.
13
+ * --apply Execute auto-apply updates and file proposals.
14
+ * --limit N Max memories to process (default: all active).
15
+ * --max-phase3 N Max LLM calls for Phase 3 (default: 1000).
16
+ * --max-phase4-pairs N Max pairs for Phase 4 (default: 50).
17
+ *
18
+ * Transport: `opts.llmProvider` (Provider.complete) — same pattern as
19
+ * `/memory compact`. Falls back to a no-op LLM if the provider is absent
20
+ * (Phase 3 and 4 will degrade to skip-only; report is still useful).
21
+ */
22
+ import type { SageSurface, CreateCandidateInput } from '@wrongstack/sage';
23
+ import { type TriageReport } from '@wrongstack/sage';
24
+ import type { SlashCommandContext } from './command-context.js';
25
+ export declare function runTriageCommand(opts: SlashCommandContext, args: string[]): Promise<{
26
+ message: string;
27
+ }>;
28
+ export interface ProposalFileResult {
29
+ filed: number;
30
+ failed: number;
31
+ total: number;
32
+ failures: Array<{
33
+ memoryId: string;
34
+ error: string;
35
+ }>;
36
+ /** All inputs that were submitted to Sage.createCandidate, for test verification. */
37
+ inputs: CreateCandidateInput[];
38
+ }
39
+ /**
40
+ * File triage proposals as MemoryCandidates via the Sage surface.
41
+ *
42
+ * Exported for unit testing — the round-trip path through
43
+ * Sage.createCandidate is the key contract: every proposal must
44
+ * surface in `/memory candidates` for human review.
45
+ */
46
+ export declare function fileProposals(Sage: SageSurface, proposals: TriageReport['dispatch']['proposals']): Promise<ProposalFileResult>;
47
+ //# sourceMappingURL=memory-triage.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/cli",
3
- "version": "0.298.2",
3
+ "version": "0.299.0",
4
4
  "license": "MIT",
5
5
  "description": "WrongStack CLI — terminal AI coding agent with provider catalog from models.dev. Provides `wrongstack` and `wstack` binaries.",
6
6
  "keywords": [
@@ -42,29 +42,30 @@
42
42
  ],
43
43
  "dependencies": {
44
44
  "ws": "^8.21.1",
45
- "@wrongstack/bench": "0.298.2",
46
- "@wrongstack/acp": "0.298.2",
47
- "@wrongstack/kanban": "0.298.2",
48
- "@wrongstack/plug-lsp": "0.298.2",
49
- "@wrongstack/mcp": "0.298.2",
50
- "@wrongstack/plugins": "0.298.2",
51
- "@wrongstack/core": "0.298.2",
52
- "@wrongstack/sdd": "0.298.2",
53
- "@wrongstack/runtime": "0.298.2",
54
- "@wrongstack/providers": "0.298.2",
55
- "@wrongstack/security-scanner": "0.298.2",
56
- "@wrongstack/simpleui": "0.298.2",
57
- "@wrongstack/sage": "0.298.2",
58
- "@wrongstack/telegram": "0.298.2",
59
- "@wrongstack/techstack": "0.298.2",
60
- "@wrongstack/tools": "0.298.2",
61
- "@wrongstack/tui": "0.298.2",
62
- "@wrongstack/webui-hq": "0.298.2",
63
- "@wrongstack/webui-server": "0.298.2",
64
- "@wrongstack/webui": "0.298.2"
45
+ "@wrongstack/acp": "0.299.0",
46
+ "@wrongstack/core": "0.299.0",
47
+ "@wrongstack/kanban": "0.299.0",
48
+ "@wrongstack/bench": "0.299.0",
49
+ "@wrongstack/plug-lsp": "0.299.0",
50
+ "@wrongstack/plugins": "0.299.0",
51
+ "@wrongstack/runtime": "0.299.0",
52
+ "@wrongstack/providers": "0.299.0",
53
+ "@wrongstack/mcp": "0.299.0",
54
+ "@wrongstack/requirement-intake": "0.299.0",
55
+ "@wrongstack/sdd": "0.299.0",
56
+ "@wrongstack/sage": "0.299.0",
57
+ "@wrongstack/techstack": "0.299.0",
58
+ "@wrongstack/security-scanner": "0.299.0",
59
+ "@wrongstack/tools": "0.299.0",
60
+ "@wrongstack/simpleui": "0.299.0",
61
+ "@wrongstack/telegram": "0.299.0",
62
+ "@wrongstack/webui": "0.299.0",
63
+ "@wrongstack/tui": "0.299.0",
64
+ "@wrongstack/webui-server": "0.299.0",
65
+ "@wrongstack/webui-hq": "0.299.0"
65
66
  },
66
67
  "optionalDependencies": {
67
- "@wrongstack/desktop": "0.298.2"
68
+ "@wrongstack/desktop": "0.299.0"
68
69
  },
69
70
  "devDependencies": {
70
71
  "@types/node": "^26.1.2",