@wrongstack/cli 0.302.2 → 0.305.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 (35) hide show
  1. package/dist/{acp-XCE4JJF3.js → acp-5ZLGFHWP.js} +5 -2
  2. package/dist/boot/system-prompt-builder.d.ts +24 -1
  3. package/dist/boot/tui-project-switch.d.ts +1 -1
  4. package/dist/boot/tui-runtime-state.d.ts +14 -0
  5. package/dist/boot/tui-theme-adapter.d.ts +33 -0
  6. package/dist/{chunk-BXQG3H2Y.js → chunk-53STH6CX.js} +87 -13
  7. package/dist/{chunk-JW75HY4F.js → chunk-FKWHSFX4.js} +33 -4
  8. package/dist/{chunk-WOXUE2CN.js → chunk-RHLQT3EV.js} +12 -2
  9. package/dist/{cli-main-PJMDZHDK.js → cli-main-W3T56YCY.js} +1498 -891
  10. package/dist/execute-deps.d.ts +16 -0
  11. package/dist/{execution-LZRYRGDD.js → execution-YMBD7YWC.js} +149 -37
  12. package/dist/fleet/host-context.d.ts +11 -0
  13. package/dist/fleet/host-learning-tracker.d.ts +2 -2
  14. package/dist/fleet/host-learning.d.ts +12 -1
  15. package/dist/fleet/host-session-writer.d.ts +33 -0
  16. package/dist/fleet/host-subagent-factory.d.ts +1 -1
  17. package/dist/fleet/host.d.ts +32 -0
  18. package/dist/{hq-AHPAE7EN.js → hq-5G6D5YIU.js} +2 -2
  19. package/dist/{hq-server-KU2Z54QU.js → hq-server-IVZLVCFV.js} +2 -2
  20. package/dist/index.js +15 -58
  21. package/dist/{mailbox-serve-DBOOOOWZ.js → mailbox-serve-V3QIJBDH.js} +2 -2
  22. package/dist/{mcp-E5EBGOHF.js → mcp-MSARYOPN.js} +5 -2
  23. package/dist/replay-XU6ZD7GM.js +34 -0
  24. package/dist/slash-commands/agent-improve.d.ts +4 -1
  25. package/dist/slash-commands/memory-triage.d.ts +4 -17
  26. package/dist/slash-commands/theme.d.ts +6 -0
  27. package/dist/subcommands/handlers/update.d.ts +2 -0
  28. package/dist/{update-LTDJGAFU.js → update-VZSOZGYC.js} +2 -2
  29. package/dist/{webui-server-E3OPEKU5.js → webui-server-FTG7JAZU.js} +5 -5
  30. package/dist/wiring/domain-glossary.d.ts +58 -0
  31. package/dist/wiring/domain-terms-mirror.d.ts +52 -0
  32. package/dist/wiring/pipeline.d.ts +2 -0
  33. package/dist/wiring/replay.d.ts +11 -1
  34. package/dist/wiring/sage.d.ts +4 -16
  35. package/package.json +23 -23
@@ -48,6 +48,7 @@ export interface ToolPickerItem {
48
48
  owner: string;
49
49
  category: string;
50
50
  enabled: boolean;
51
+ exposure: 'direct' | 'lazy' | 'disabled';
51
52
  mutating: boolean;
52
53
  permission: string;
53
54
  descMode: 'extend' | 'simple';
@@ -89,6 +90,21 @@ export interface CoreDeps {
89
90
  positional: string[];
90
91
  slashRegistry: SlashCommandRegistry;
91
92
  tokenCounter: TokenCounter;
93
+ /**
94
+ * Forward-declared session ref owned by the host (cli-main). When an
95
+ * in-process `/resume` swaps the agent's active writer, the resume
96
+ * handler repoints `sessionRef.current` so provider-side
97
+ * `getSessionId: () => sessionRef.current?.id` callbacks and the
98
+ * record-mode `bindReplayToContainer` binding follow the resumed
99
+ * session instead of staying pinned to the boot session.
100
+ *
101
+ * Optional: hosts that don't need post-resume propagation (or tests
102
+ * that predate the refactor) can omit it; provider calls then stay
103
+ * pinned to the boot session — same behavior as before this existed.
104
+ */
105
+ sessionRef?: {
106
+ current: import('@wrongstack/core/types').SessionWriter | undefined;
107
+ } | undefined;
92
108
  /** Atomically move this process's SessionRegistry ownership after explicit resume. */
93
109
  activateSessionIdentity?: ((sessionId: string, target?: import('./wiring/session-registry.js').SessionIdentityTarget) => Promise<void>) | undefined;
94
110
  /**
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-V3XH6XBJ.js";
11
11
  import {
12
12
  startCliHqConnection
13
- } from "./chunk-JW75HY4F.js";
13
+ } from "./chunk-FKWHSFX4.js";
14
14
  import "./chunk-WHOIR577.js";
15
15
  import {
16
16
  advanceToNextTask,
@@ -25,17 +25,17 @@ import {
25
25
  trySaveSpecFromAIOutput,
26
26
  trySaveTasksFromAIOutput
27
27
  } from "./chunk-DGXNL7OT.js";
28
- import {
29
- WEBUI_SESSION_CHILD_CAPABILITIES,
30
- writeWebuiSessionChildError,
31
- writeWebuiSessionChildReady
32
- } from "./chunk-3JPTNADJ.js";
33
28
  import {
34
29
  theme
35
30
  } from "./chunk-QD544J2B.js";
36
31
  import {
37
32
  fmtTok
38
33
  } from "./chunk-TYO2OVAD.js";
34
+ import {
35
+ WEBUI_SESSION_CHILD_CAPABILITIES,
36
+ writeWebuiSessionChildError,
37
+ writeWebuiSessionChildReady
38
+ } from "./chunk-3JPTNADJ.js";
39
39
  import {
40
40
  resolveActiveApiKey
41
41
  } from "./chunk-SZ42FYPT.js";
@@ -52,7 +52,7 @@ import {
52
52
  import "./chunk-7OCVIDC7.js";
53
53
 
54
54
  // src/execution.ts
55
- import * as path12 from "node:path";
55
+ import * as path13 from "node:path";
56
56
  import { effectiveFallbackChain as effectiveFallbackChain2, setQueuedMessagesSnapshot as setQueuedMessagesSnapshot2 } from "@wrongstack/core/agent";
57
57
  import { attachTodosCheckpoint as attachTodosCheckpoint2 } from "@wrongstack/core/storage";
58
58
  import { normalizeTokenSavingTier as normalizeTokenSavingTier2 } from "@wrongstack/core/types";
@@ -203,7 +203,7 @@ async function runWebUIDispatch(ctx) {
203
203
  const isSimpleUi = !isSessionChild && flags["simpleui"] === true;
204
204
  agent.disableInteractiveConfirmation();
205
205
  renderer.setSilent(true);
206
- const { runWebUI } = await import("./webui-server-E3OPEKU5.js");
206
+ const { runWebUI } = await import("./webui-server-FTG7JAZU.js");
207
207
  const flagValue = (names) => {
208
208
  for (const name of names) {
209
209
  if (!Object.hasOwn(flags, name)) continue;
@@ -985,8 +985,13 @@ import {
985
985
  DefaultSystemPromptBuilder,
986
986
  setQueuedMessagesSnapshot
987
987
  } from "@wrongstack/core/agent";
988
+ import { TOKENS } from "@wrongstack/core/kernel";
988
989
  import { DefaultSessionStore } from "@wrongstack/core/storage";
989
- import { resolveWstackPaths, sessionScopedPath } from "@wrongstack/core/utils";
990
+ import {
991
+ activateProjectStateGuard,
992
+ resolveWstackPaths,
993
+ sessionScopedPath
994
+ } from "@wrongstack/core/utils";
990
995
  async function switchProjectInPlace(ctx, targetRoot, displayName) {
991
996
  const {
992
997
  state,
@@ -1064,6 +1069,7 @@ async function switchProjectInPlace(ctx, targetRoot, displayName) {
1064
1069
  });
1065
1070
  targetActivated = true;
1066
1071
  await state.activateSessionIdentity(nextWriter.id, target);
1072
+ await activateProjectStateGuard(resolved);
1067
1073
  } catch (err) {
1068
1074
  if (!targetActivated) await targetClaim?.cancel().catch(() => void 0);
1069
1075
  else if (oldWriter) {
@@ -1124,6 +1130,13 @@ async function switchProjectInPlace(ctx, targetRoot, displayName) {
1124
1130
  modeStore,
1125
1131
  modeId: modeId ?? "default",
1126
1132
  modePrompt: switchMode?.prompt ?? "",
1133
+ tokenSavingMode: config.features.tokenSavingMode,
1134
+ modelCapabilities: {
1135
+ maxContextTokens: context.provider.capabilities.maxContext,
1136
+ supportsTools: !!context.provider.capabilities.tools,
1137
+ supportsVision: !!context.provider.capabilities.vision,
1138
+ supportsReasoning: !!context.provider.capabilities.reasoning
1139
+ },
1127
1140
  instructionPaths: {
1128
1141
  globalDir: nextWpaths.globalInstructions,
1129
1142
  projectDir: nextWpaths.inProjectInstructions,
@@ -1138,6 +1151,11 @@ async function switchProjectInPlace(ctx, targetRoot, displayName) {
1138
1151
  provider: context.provider.id,
1139
1152
  model: context.model
1140
1153
  });
1154
+ if (agent.container.has(TOKENS.SystemPromptBuilder)) {
1155
+ agent.container.override(TOKENS.SystemPromptBuilder, () => switchBuilder, {
1156
+ owner: "tui-project-switch"
1157
+ });
1158
+ }
1141
1159
  } catch (err) {
1142
1160
  console.error(
1143
1161
  JSON.stringify({
@@ -1298,6 +1316,7 @@ async function resumeSession(ctx, sessionId) {
1298
1316
  throw err;
1299
1317
  }
1300
1318
  writerSwapped = true;
1319
+ state.sessionRef && (state.sessionRef.current = resumed.writer);
1301
1320
  await agent.ctx.flushConversationJournal().catch((err) => {
1302
1321
  console.error(
1303
1322
  JSON.stringify({
@@ -1851,6 +1870,57 @@ function createSettingsAdapter(ctx) {
1851
1870
  return { getSettings, saveSettings };
1852
1871
  }
1853
1872
 
1873
+ // src/boot/tui-theme-adapter.ts
1874
+ import * as fs4 from "node:fs/promises";
1875
+ import * as path9 from "node:path";
1876
+ import { atomicWrite as atomicWrite2 } from "@wrongstack/core/utils";
1877
+ var VALID_PRESETS = /* @__PURE__ */ new Set([
1878
+ "catppuccin",
1879
+ "tokyo-night",
1880
+ "nord",
1881
+ "cyberpunk",
1882
+ "dracula",
1883
+ "gruvbox-dark",
1884
+ "solarized-dark",
1885
+ "one-dark",
1886
+ "monokai",
1887
+ "rose-pine",
1888
+ "kanagawa",
1889
+ "ayu-dark",
1890
+ "everforest",
1891
+ "night-owl",
1892
+ "synthwave"
1893
+ ]);
1894
+ function createThemeAdapter({ configStore, wpaths }) {
1895
+ return {
1896
+ getThemePreset: () => {
1897
+ const live = configStore.get();
1898
+ const candidate = live.themePreset;
1899
+ return candidate && VALID_PRESETS.has(candidate) ? candidate : void 0;
1900
+ },
1901
+ saveThemePreset: async (preset) => {
1902
+ if (!VALID_PRESETS.has(preset)) {
1903
+ throw new Error(`Unknown theme preset: ${preset}`);
1904
+ }
1905
+ configStore.update({ themePreset: preset });
1906
+ const configPath = activeProfileConfigPath(wpaths, configStore.get());
1907
+ let parsed = {};
1908
+ try {
1909
+ const raw = await fs4.readFile(configPath, "utf8");
1910
+ parsed = JSON.parse(raw);
1911
+ } catch (err) {
1912
+ const e = err;
1913
+ if (e.code !== "ENOENT") {
1914
+ throw err;
1915
+ }
1916
+ }
1917
+ parsed.themePreset = preset;
1918
+ await fs4.mkdir(path9.dirname(configPath), { recursive: true });
1919
+ await atomicWrite2(configPath, JSON.stringify(parsed, null, 2), { mode: 384 });
1920
+ }
1921
+ };
1922
+ }
1923
+
1854
1924
  // src/brain-menu/panel-service.ts
1855
1925
  import { BUILTIN_COUNCIL_PERSONAS } from "@wrongstack/core/execution";
1856
1926
  var PERSONA_CATALOG = BUILTIN_COUNCIL_PERSONAS.map((persona) => ({
@@ -2268,7 +2338,9 @@ async function finalizeExecutionCleanup(input) {
2268
2338
 
2269
2339
  // src/execution-kanban-dispatch.ts
2270
2340
  import { randomUUID } from "node:crypto";
2341
+ import { missingRequiredRuntimeTools } from "@wrongstack/core/agent-catalog";
2271
2342
  import { WIDE_SUBAGENT_CAPABILITIES } from "@wrongstack/core/security";
2343
+ import { stripFrontmatter } from "@wrongstack/core/skills";
2272
2344
 
2273
2345
  // src/kanban-dispatch-route.ts
2274
2346
  import { fallbackProfileChain, parseModelRef as parseModelRef2 } from "@wrongstack/core/agent";
@@ -2307,21 +2379,43 @@ function createKanbanDispatchHandler({
2307
2379
  } = resolveKanbanDispatchRoute(config, spawnOpts);
2308
2380
  let agentDescription = description;
2309
2381
  if (spawnOpts?.skills?.length && skillLoader) {
2310
- const loaded = await Promise.all(
2311
- spawnOpts.skills.map(async (skillName) => {
2382
+ const availableToolNames = spawnOpts.tools;
2383
+ const loaded = [];
2384
+ for (const skillName of spawnOpts.skills) {
2385
+ try {
2312
2386
  const manifest = await skillLoader.find(skillName);
2313
- if (!manifest) throw new Error(`Kanban skill not found: ${skillName}`);
2314
- const body = await skillLoader.readBody(skillName);
2315
- return `## Required skill: ${skillName}
2387
+ if (!manifest) {
2388
+ process.emitWarning(`Kanban dispatch skill not found, skipped: ${skillName}`, {
2389
+ code: "WRONGSTACK_KANBAN_SKILL_SKIPPED"
2390
+ });
2391
+ continue;
2392
+ }
2393
+ if (availableToolNames !== void 0 && missingRequiredRuntimeTools(manifest.requiredTools, availableToolNames).length > 0) {
2394
+ process.emitWarning(
2395
+ `Kanban dispatch skill "${skillName}" needs tools this worker does not have, skipped.`,
2396
+ { code: "WRONGSTACK_KANBAN_SKILL_SKIPPED" }
2397
+ );
2398
+ continue;
2399
+ }
2400
+ const body = stripFrontmatter(await skillLoader.readBody(skillName)).trim();
2401
+ if (!body) continue;
2402
+ loaded.push(`## Required skill: ${skillName}
2316
2403
 
2317
- ${body}`;
2318
- })
2319
- );
2320
- agentDescription = `${description}
2404
+ ${body}`);
2405
+ } catch (err) {
2406
+ process.emitWarning(
2407
+ `Kanban dispatch skill "${skillName}" could not be loaded, skipped: ${err instanceof Error ? err.message : String(err)}`,
2408
+ { code: "WRONGSTACK_KANBAN_SKILL_SKIPPED" }
2409
+ );
2410
+ }
2411
+ }
2412
+ if (loaded.length > 0) {
2413
+ agentDescription = `${description}
2321
2414
 
2322
2415
  # Required agentic skill instructions
2323
2416
 
2324
2417
  ${loaded.join("\n\n")}`;
2418
+ }
2325
2419
  }
2326
2420
  void (async () => {
2327
2421
  const built = await sddSubagentFactory({
@@ -2664,7 +2758,7 @@ function createChimeraWorkRegistry() {
2664
2758
 
2665
2759
  // src/execution-chimera-cascade.ts
2666
2760
  import * as fsp from "node:fs/promises";
2667
- import * as path9 from "node:path";
2761
+ import * as path10 from "node:path";
2668
2762
  import { emitReviewIfChanged } from "@wrongstack/core/plugin";
2669
2763
 
2670
2764
  // src/chimera-review-task.ts
@@ -3027,7 +3121,7 @@ async function maybeReReviewCascade({
3027
3121
  const reReadFiles = [];
3028
3122
  for (const f of bundle.files) {
3029
3123
  try {
3030
- const absPath = path9.join(bundle.cwd, f.path);
3124
+ const absPath = path10.join(bundle.cwd, f.path);
3031
3125
  const content = await fsp.readFile(absPath, "utf8");
3032
3126
  reReadFiles.push({ path: f.path, status: "modified", content });
3033
3127
  } catch {
@@ -3096,7 +3190,7 @@ async function maybeReReviewCascade({
3096
3190
 
3097
3191
  // src/execution-chimera-review.ts
3098
3192
  import { randomUUID as randomUUID3 } from "node:crypto";
3099
- import path10 from "node:path";
3193
+ import path11 from "node:path";
3100
3194
  import { effectiveFallbackChain } from "@wrongstack/core/agent";
3101
3195
  import {
3102
3196
  CHIMERA_REVIEW_PROMPT,
@@ -3109,7 +3203,7 @@ import {
3109
3203
  function normalizeFileKeyForCitation(raw, cwd) {
3110
3204
  const forward = raw.replace(/\\/g, "/").replace(/^\.\//, "");
3111
3205
  const isAbsolute = forward.startsWith("/") || /^[a-zA-Z]:\//.test(forward);
3112
- const pathMod = process.platform === "win32" ? path10.win32 : path10;
3206
+ const pathMod = process.platform === "win32" ? path11.win32 : path11;
3113
3207
  const relative = isAbsolute ? pathMod.relative(cwd, forward).replace(/\\/g, "/").replace(/^\.\//, "") : forward;
3114
3208
  return process.platform === "win32" ? relative.toLowerCase() : relative;
3115
3209
  }
@@ -3307,7 +3401,7 @@ function installChimeraReviewHandler({
3307
3401
  const changedBasenameIndex = /* @__PURE__ */ new Map();
3308
3402
  for (const file of p.files) {
3309
3403
  const normalized = normalizeFileKeyForCitation(file.path, p.cwd);
3310
- const basename7 = path10.basename(file.path).toLowerCase();
3404
+ const basename7 = path11.basename(file.path).toLowerCase();
3311
3405
  const bucket = changedBasenameIndex.get(basename7);
3312
3406
  if (bucket) bucket.push(normalized);
3313
3407
  else changedBasenameIndex.set(basename7, [normalized]);
@@ -3315,7 +3409,7 @@ function installChimeraReviewHandler({
3315
3409
  const droppedCitations = citedFindings.filter((finding) => {
3316
3410
  const citedKey = normalizeFileKeyForCitation(finding.location.file, p.cwd);
3317
3411
  if (citedPaths.has(citedKey)) return false;
3318
- const basename7 = path10.basename(finding.location.file).toLowerCase();
3412
+ const basename7 = path11.basename(finding.location.file).toLowerCase();
3319
3413
  const bucket = changedBasenameIndex.get(basename7);
3320
3414
  return !(bucket && bucket.length === 1);
3321
3415
  });
@@ -3616,6 +3710,7 @@ import {
3616
3710
  } from "@wrongstack/core/utils";
3617
3711
  import { readClipboardImage, routeImagesForModel } from "@wrongstack/runtime";
3618
3712
  import { createAutoProceedLoopGuard } from "@wrongstack/tools/auto-proceed-loop-guard";
3713
+ import { todoTool } from "@wrongstack/tools/todo";
3619
3714
 
3620
3715
  // src/repl-auto-proceed.ts
3621
3716
  import { color as color6, estimateRequestTokensCalibrated } from "@wrongstack/core/utils";
@@ -3791,7 +3886,7 @@ async function runContextCompaction(opts, _signal) {
3791
3886
 
3792
3887
  // src/repl-client-registration.ts
3793
3888
  import * as crypto from "node:crypto";
3794
- import * as path11 from "node:path";
3889
+ import * as path12 from "node:path";
3795
3890
  import { getSharedProjectMailbox, resolveProjectDir } from "@wrongstack/core/coordination";
3796
3891
  import { wstackGlobalRoot } from "@wrongstack/core/utils";
3797
3892
  function registerReplClient(opts) {
@@ -3801,7 +3896,7 @@ function registerReplClient(opts) {
3801
3896
  const hqConnection = startCliHqConnection({
3802
3897
  clientKind: "repl",
3803
3898
  projectRoot: replProjectRoot,
3804
- projectName: path11.basename(replProjectRoot),
3899
+ projectName: path12.basename(replProjectRoot),
3805
3900
  appConfig: opts.appConfig,
3806
3901
  capabilities: ["telemetry.publish", "mailbox.summary"]
3807
3902
  });
@@ -3815,7 +3910,7 @@ function registerReplClient(opts) {
3815
3910
  clientMailbox.registerClient({
3816
3911
  clientId,
3817
3912
  sessionId: opts.getSessionId?.() ?? replProjectRoot,
3818
- name: `REPL [${path11.basename(replProjectRoot)}]`,
3913
+ name: `REPL [${path12.basename(replProjectRoot)}]`,
3819
3914
  source: "repl",
3820
3915
  pid: process.pid
3821
3916
  }).then(() => {
@@ -4117,10 +4212,14 @@ ${lines.join("\n")}
4117
4212
  if (resolved.source === "todo" && resolved.todoId) {
4118
4213
  const selected = todos.find((todo) => todo.id === resolved.todoId);
4119
4214
  if (selected?.status === "pending") {
4120
- opts.agent.ctx.state.replaceTodos(
4121
- todos.map(
4122
- (todo) => todo.id === resolved.todoId ? { ...todo, status: "in_progress" } : todo.status === "in_progress" ? { ...todo, status: "pending" } : todo
4123
- )
4215
+ await todoTool.execute(
4216
+ {
4217
+ todos: todos.map(
4218
+ (todo) => todo.id === resolved.todoId ? { ...todo, status: "in_progress" } : todo.status === "in_progress" ? { ...todo, status: "pending" } : todo
4219
+ )
4220
+ },
4221
+ opts.agent.ctx,
4222
+ { signal: AbortSignal.timeout(3e4) }
4124
4223
  );
4125
4224
  }
4126
4225
  }
@@ -4293,10 +4392,14 @@ ${color9.dim(taskList2)}
4293
4392
  if (resolved.source === "todo" && resolved.todoId) {
4294
4393
  const selected = opts.agent.ctx.todos.find((todo) => todo.id === resolved.todoId);
4295
4394
  if (selected?.status === "pending") {
4296
- opts.agent.ctx.state.replaceTodos(
4297
- opts.agent.ctx.todos.map(
4298
- (todo) => todo.id === resolved.todoId ? { ...todo, status: "in_progress" } : todo.status === "in_progress" ? { ...todo, status: "pending" } : todo
4299
- )
4395
+ await todoTool.execute(
4396
+ {
4397
+ todos: opts.agent.ctx.todos.map(
4398
+ (todo) => todo.id === resolved.todoId ? { ...todo, status: "in_progress" } : todo.status === "in_progress" ? { ...todo, status: "pending" } : todo
4399
+ )
4400
+ },
4401
+ opts.agent.ctx,
4402
+ { signal: AbortSignal.timeout(3e4) }
4300
4403
  );
4301
4404
  }
4302
4405
  }
@@ -4678,6 +4781,7 @@ async function execute(deps) {
4678
4781
  positional,
4679
4782
  slashRegistry,
4680
4783
  tokenCounter,
4784
+ sessionRef,
4681
4785
  activateSessionIdentity,
4682
4786
  updateInfo: initialUpdateInfo,
4683
4787
  webuiSessionChild
@@ -4874,6 +4978,12 @@ async function execute(deps) {
4874
4978
  activeSessionStore,
4875
4979
  activateSessionIdentity,
4876
4980
  detachActiveTodosCheckpoint,
4981
+ // Plumbed from cli-main.ts so `resumeSession` can repoint the ref
4982
+ // when an in-process `/resume` swaps the active writer. Optional
4983
+ // on TuiRuntimeState; tests/hosts that omit it revert to the
4984
+ // pre-refactor behavior where provider calls stay pinned to the
4985
+ // boot session.
4986
+ sessionRef,
4877
4987
  pendingProjectSwitch: null,
4878
4988
  autonomousCoordinator: null,
4879
4989
  coordinatorRun: null,
@@ -4988,6 +5098,8 @@ async function execute(deps) {
4988
5098
  fleetStreamController,
4989
5099
  applyLiveSettings
4990
5100
  }),
5101
+ ...createThemeAdapter({ configStore, wpaths }),
5102
+ configStore,
4991
5103
  effectiveMaxContext,
4992
5104
  titleAnimation: config.autonomy?.["terminalTitleAnimation"] ?? true,
4993
5105
  chime: config.autonomy?.["chime"] ?? false,
@@ -5191,7 +5303,7 @@ async function execute(deps) {
5191
5303
  attachments,
5192
5304
  effectiveMaxContext,
5193
5305
  getEffectiveMaxContext,
5194
- projectName: path12.basename(projectRoot) || void 0,
5306
+ projectName: path13.basename(projectRoot) || void 0,
5195
5307
  projectRoot,
5196
5308
  appConfig: config,
5197
5309
  getSessionId: () => agent.ctx.session?.id ?? session.id,
@@ -5250,4 +5362,4 @@ export {
5250
5362
  execute,
5251
5363
  resolveReviewerFallbackModels
5252
5364
  };
5253
- //# sourceMappingURL=execution-LZRYRGDD.js.map
5365
+ //# sourceMappingURL=execution-YMBD7YWC.js.map
@@ -1,5 +1,16 @@
1
1
  import type { SubagentConfig } from '@wrongstack/core/types';
2
2
  import type { MultiAgentDeps } from './host-types.js';
3
+ /** Skills whose bodies were requested but did not make it into the prompt. */
4
+ export interface SkillResolutionReport {
5
+ content: string;
6
+ selected: string[];
7
+ /** skill → why it was dropped, so the omission is never silent. */
8
+ dropped: Record<string, 'not-found' | 'missing-capability' | 'missing-tool' | 'budget' | 'empty'>;
9
+ /** Skills kept under budget by shortening their bundled body. */
10
+ trimmed: string[];
11
+ }
12
+ export declare function resolveHostSubagentSkillResolution(deps: MultiAgentDeps, roster: Record<string, SubagentConfig>, subCfg: SubagentConfig, availableToolNames?: readonly string[]): Promise<SkillResolutionReport>;
13
+ /** Backwards-compatible wrapper for callers that only need the prompt text. */
3
14
  export declare function resolveHostSubagentSkillContent(deps: MultiAgentDeps, roster: Record<string, SubagentConfig>, subCfg: SubagentConfig, availableToolNames?: readonly string[]): Promise<string>;
4
15
  export declare function retrieveHostSubagentMemory(deps: MultiAgentDeps, getLeaderMode: (() => string | undefined) | undefined, subCfg: SubagentConfig, taskContext?: Record<string, unknown>): Promise<string[]>;
5
16
  //# sourceMappingURL=host-context.d.ts.map
@@ -5,7 +5,7 @@ export declare class HostLearningRoleTracker {
5
5
  private readonly roles;
6
6
  private readonly accessOrder;
7
7
  constructor(maxEntries?: number);
8
- record(subagentId: string, role: string): void;
9
- capture(result: TaskResult, deps: Pick<MultiAgentDeps, 'container' | 'projectRoot'>): void;
8
+ record(subagentId: string, role: string, skills?: readonly string[]): void;
9
+ capture(result: TaskResult, deps: Pick<MultiAgentDeps, 'container' | 'projectRoot'>, onCaptured?: (role: string) => void): void;
10
10
  }
11
11
  //# sourceMappingURL=host-learning-tracker.d.ts.map
@@ -1,4 +1,15 @@
1
1
  import type { TaskResult } from '@wrongstack/core/types';
2
2
  import type { MultiAgentDeps } from './host-types.js';
3
- export declare function captureCompletedTaskLearningForHost(result: TaskResult, deps: Pick<MultiAgentDeps, 'container' | 'projectRoot'>, subagentLearningRoles: ReadonlyMap<string, string>): void;
3
+ /** What a spawn recorded about the agent that will produce this result. */
4
+ export interface LearningSubject {
5
+ role: string;
6
+ skills: readonly string[];
7
+ }
8
+ export declare function captureCompletedTaskLearningForHost(result: TaskResult, deps: Pick<MultiAgentDeps, 'container' | 'projectRoot'>, subjects: ReadonlyMap<string, LearningSubject>,
9
+ /**
10
+ * Called when a capture persisted at least one directive. The auto-optimize
11
+ * scheduler uses it as its trigger, so distillation follows learning without
12
+ * anyone pressing a button.
13
+ */
14
+ onCaptured?: (role: string) => void): void;
4
15
  //# sourceMappingURL=host-learning.d.ts.map
@@ -1,3 +1,36 @@
1
1
  import type { SessionWriter } from '@wrongstack/core/types';
2
+ /**
3
+ * Session writer for a subagent that has no journal of its own: its events are
4
+ * interleaved into the parent's JSONL.
5
+ *
6
+ * Lifecycle and rewind *control* stay no-ops — `writeCheckpoint`,
7
+ * `writeInFlightMarker`, `truncateToCheckpoint`, `clearSession` and `close`
8
+ * belong to the parent, and a subagent driving them would corrupt the parent's
9
+ * checkpoint chain and crash-recovery markers.
10
+ *
11
+ * Rewind *evidence* is a different thing and is forwarded. A subagent's file
12
+ * mutations are real edits to the user's working tree, made under the parent
13
+ * prompt that spawned it; dropping their `file_snapshot` records left `/rewind`
14
+ * quietly unable to undo them, with nothing in the transcript saying so. The
15
+ * parent writer files them against its own `activePromptIndex`, which is
16
+ * exactly the prompt a user rewinding this work would target.
17
+ */
2
18
  export declare function createParentSubagentSessionWriter(parentSession: SessionWriter): SessionWriter;
19
+ /**
20
+ * Wrap a subagent's own `SessionWriter` so its file mutations are ALSO recorded
21
+ * against the parent session.
22
+ *
23
+ * When a subagent gets a real per-subagent JSONL, its `file_snapshot` events
24
+ * land in that file — under the director run directory, which
25
+ * `DefaultSessionRewinder` never reads: it resolves one path, the session being
26
+ * rewound. So the richer, "proper" subagent-session path was the one that lost
27
+ * rewind coverage entirely, while the fallback shim above at least had the
28
+ * parent handle in reach.
29
+ *
30
+ * Recording to the parent as well keeps one authority for undo (the session the
31
+ * user actually rewinds) without taking anything away from the subagent's own
32
+ * transcript, which still receives every other event including the conversation
33
+ * that produced the edit.
34
+ */
35
+ export declare function withParentFileSnapshots(subagentSession: SessionWriter, parentSession: SessionWriter): SessionWriter;
3
36
  //# sourceMappingURL=host-session-writer.d.ts.map
@@ -9,7 +9,7 @@ export interface HostSubagentFactoryContext {
9
9
  sessionFactory?: DirectorSessionFactory | undefined;
10
10
  filterTools: (allow?: string[]) => Tool[];
11
11
  mailboxProjectDir: () => string;
12
- recordLearningRole: (subagentId: string, role: string) => void;
12
+ recordLearningRole: (subagentId: string, role: string, skills?: readonly string[]) => void;
13
13
  subagentToolRegistry: (allow?: string[]) => ToolRegistry;
14
14
  }
15
15
  export declare function createHostSubagentFactory(config: Config, host: HostSubagentFactoryContext): AgentFactory;
@@ -39,6 +39,9 @@ export declare class MultiAgentHost {
39
39
  * Bounded to 20 entries with LRU eviction to prevent unbounded memory growth. */
40
40
  private readonly acpRunnerCache;
41
41
  private readonly learningRoles;
42
+ /** Background distillation of captured learning into per-skill addenda. */
43
+ private learningOptimizer;
44
+ private learningSwept;
42
45
  /** Adaptive concurrency controller — created in buildDirector() when config has
43
46
  * adaptiveConcurrency.enabled = true. Monitors FleetBus for 429 errors and
44
47
  * automatically adjusts maxConcurrent to prevent rate limiting. */
@@ -202,6 +205,35 @@ export declare class MultiAgentHost {
202
205
  private emitLifecycleCompleted;
203
206
  private recordLearningRole;
204
207
  private captureCompletedTaskLearning;
208
+ /**
209
+ * Lazily built so a session that never spawns a subagent pays nothing, and
210
+ * so the policy is read from the live config rather than frozen at boot.
211
+ * Returns null when auto-optimization is switched off.
212
+ */
213
+ private getLearningOptimizer;
214
+ /**
215
+ * Break a dispatch tie with a model when the keyword heuristic is ambiguous.
216
+ *
217
+ * Routes through the `dispatcher` model-matrix slot when one is configured —
218
+ * picking a role is a short classification, so it belongs on a cheap fast
219
+ * model rather than the leader's — and the session default otherwise.
220
+ * Returning `null` on any failure leaves the dispatcher on its heuristic
221
+ * result, so routing degrades rather than breaking a spawn.
222
+ */
223
+ private classifyDispatch;
224
+ /**
225
+ * Resolve a model for the distillation pass. Uses the `memory-curator` slot
226
+ * of the model matrix when one is configured — curating learned knowledge is
227
+ * exactly that role's job — and the session default otherwise. A failure
228
+ * here is not fatal: the pass still writes the deterministic skill addenda.
229
+ */
230
+ private resolveOptimizerLlm;
231
+ /**
232
+ * Evaluate every role with learning data once per session. Without it, a
233
+ * role that became eligible before this session would wait for its next
234
+ * capture — which for a rarely-used role can be never.
235
+ */
236
+ private sweepLearningOptimization;
205
237
  status(): FleetHostStatus;
206
238
  /**
207
239
  * Read-only concurrency + lifetime spawn budget for `/fleet status` and
@@ -47,7 +47,7 @@ var hqCmd = async (args, deps) => {
47
47
  return 1;
48
48
  };
49
49
  async function startServer(deps) {
50
- const { startHqServer } = await import("./hq-server-KU2Z54QU.js");
50
+ const { startHqServer } = await import("./hq-server-IVZLVCFV.js");
51
51
  const dataDir = resolveDataDir(deps);
52
52
  const flags = deps.flags ?? {};
53
53
  const host = typeof flags["host"] === "string" ? flags["host"] : HQ_CLI_DEFAULT_HOST;
@@ -612,4 +612,4 @@ export {
612
612
  hqCmd,
613
613
  resolveAuditActor
614
614
  };
615
- //# sourceMappingURL=hq-AHPAE7EN.js.map
615
+ //# sourceMappingURL=hq-5G6D5YIU.js.map
@@ -15,7 +15,7 @@ import {
15
15
  readLocalSubagentTranscript,
16
16
  sanitizeApiError,
17
17
  startHqServer
18
- } from "./chunk-WOXUE2CN.js";
18
+ } from "./chunk-RHLQT3EV.js";
19
19
  import "./chunk-KE7E7DPX.js";
20
20
  import "./chunk-Q5GTM25S.js";
21
21
  import "./chunk-7OCVIDC7.js";
@@ -37,4 +37,4 @@ export {
37
37
  sanitizeApiError,
38
38
  startHqServer
39
39
  };
40
- //# sourceMappingURL=hq-server-KU2Z54QU.js.map
40
+ //# sourceMappingURL=hq-server-IVZLVCFV.js.map