@wrongstack/cli 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,17 +1,19 @@
1
1
  #!/usr/bin/env node
2
- import { color, DefaultPathResolver, Container, DefaultConfigStore, TOKENS, DefaultSecretScrubber, DefaultRetryPolicy, DefaultErrorHandler, DefaultTokenCounter, DefaultModeStore, DefaultSessionStore, DefaultMemoryStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultPermissionPolicy, HybridCompactor, ProviderRegistry, ToolRegistry, createContextManagerTool, EventBus, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, Agent, SlashCommandRegistry, loadPlugins, createDelegateTool, FLEET_ROSTER, DefaultLogger, DefaultModelsRegistry, makeDirectorSessionFactory, Director, DefaultMultiAgentCoordinator, makeAgentSubagentRunner, resolveWstackPaths, DefaultSecretVault, migratePlaintextSecrets, DefaultConfigLoader, DefaultSessionReader, atomicWrite, AutoApprovePermissionPolicy, formatTodosList, emptyPlan, clearPlan, savePlan, removePlanItem, formatPlan, setPlanItemStatus, addPlanItem, InputBuilder, decryptConfigSecrets, encryptConfigSecrets, DefaultPluginAPI } from '@wrongstack/core';
2
+ import { color, allServers, DefaultPathResolver, TOKENS, DefaultSystemPromptBuilder, ToolRegistry, createContextManagerTool, EventBus, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, SlashCommandRegistry, loadPlugins, createDelegateTool, FLEET_ROSTER, DefaultLogger, DefaultModelsRegistry, DefaultSessionStore, DefaultSkillLoader, ProviderRegistry, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, Agent, makeDirectorSessionFactory, Director, DefaultMultiAgentCoordinator, makeAgentSubagentRunner, resolveWstackPaths, DefaultSecretVault, migratePlaintextSecrets, DefaultConfigLoader, DefaultSessionReader, atomicWrite, AutoApprovePermissionPolicy, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, resolveContextWindowPolicy, formatTodosList, emptyPlan, clearPlan, savePlan, removePlanItem, formatPlan, setPlanItemStatus, addPlanItem, InputBuilder, decryptConfigSecrets, encryptConfigSecrets, DefaultPluginAPI } from '@wrongstack/core';
3
3
  import * as fs3 from 'fs/promises';
4
4
  import { WebSocketServer, WebSocket } from 'ws';
5
5
  import { writeFileSync } from 'fs';
6
6
  import { createRequire } from 'module';
7
- import * as path13 from 'path';
7
+ import * as path14 from 'path';
8
8
  import { MCPRegistry } from '@wrongstack/mcp';
9
9
  import { buildProviderFactoriesFromRegistry, makeProviderFromConfig, capabilitiesFor } from '@wrongstack/providers';
10
+ import { createDefaultContainer, routeImagesForModel, readClipboardImage } from '@wrongstack/runtime';
10
11
  import { rememberTool, forgetTool } from '@wrongstack/tools';
11
12
  import { builtinToolsPack } from '@wrongstack/tools/pack';
12
13
  import * as os3 from 'os';
13
14
  import * as readline from 'readline';
14
15
  import { randomUUID } from 'crypto';
16
+ import { createToolVisionAdapters } from '@wrongstack/runtime/vision';
15
17
 
16
18
  var __defProp = Object.defineProperty;
17
19
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -568,6 +570,216 @@ var init_plugin_api_factory = __esm({
568
570
  "src/plugin-api-factory.ts"() {
569
571
  }
570
572
  });
573
+ async function setupProvider(params) {
574
+ const { config, modelsRegistry, logger } = params;
575
+ const savedProviderCfg = config.providers?.[config.provider];
576
+ let resolvedProvider = await modelsRegistry.getProvider(config.provider).catch(() => void 0);
577
+ if (!resolvedProvider && savedProviderCfg?.type && savedProviderCfg.type !== config.provider) {
578
+ resolvedProvider = await modelsRegistry.getProvider(savedProviderCfg.type).catch(() => void 0);
579
+ }
580
+ if (!resolvedProvider) {
581
+ if (!savedProviderCfg?.family) {
582
+ logger.warn(
583
+ `Provider "${config.provider}" not found in models.dev. Continuing with raw config.`
584
+ );
585
+ }
586
+ } else if (resolvedProvider.family === "unsupported" && !savedProviderCfg?.family) {
587
+ throw Object.assign(
588
+ new Error(
589
+ `Provider "${config.provider}" uses an unsupported wire family (${resolvedProvider.npm}). Install a plugin to enable it, or pick a different provider.`
590
+ ),
591
+ { code: "UNSUPPORTED_PROVIDER" }
592
+ );
593
+ }
594
+ const providerRegistry = new ProviderRegistry();
595
+ if (config.features.modelsRegistry) {
596
+ try {
597
+ const factories = await buildProviderFactoriesFromRegistry({
598
+ registry: modelsRegistry,
599
+ log: logger
600
+ });
601
+ for (const f of factories) providerRegistry.register(f);
602
+ } catch (err) {
603
+ throw new Error(
604
+ `Failed to load models.dev registry: ${err instanceof Error ? err.message : err}
605
+ Try \`wstack models refresh\` once you have network access, or run with --no-features.`
606
+ );
607
+ }
608
+ }
609
+ const providerConfig = config.providers?.[config.provider] ?? {
610
+ type: config.provider,
611
+ apiKey: config.apiKey,
612
+ baseUrl: config.baseUrl
613
+ };
614
+ let provider;
615
+ try {
616
+ const cfgWithType = { ...providerConfig, type: config.provider };
617
+ if (config.features.modelsRegistry && providerRegistry.has(config.provider)) {
618
+ provider = providerRegistry.create(cfgWithType);
619
+ } else {
620
+ provider = makeProviderFromConfig(config.provider, cfgWithType);
621
+ }
622
+ } catch (err) {
623
+ throw new Error(
624
+ `Failed to create provider: ${err instanceof Error ? err.message : err}`
625
+ );
626
+ }
627
+ return { resolvedProvider, provider, providerRegistry };
628
+ }
629
+ async function setupSession(params) {
630
+ const { config, wpaths, projectRoot, cwd, sessionStore, systemPrompt, provider, tokenCounter, renderer, flags, onRecovery } = params;
631
+ let resumeId = typeof flags["resume"] === "string" ? flags["resume"] : void 0;
632
+ const recoveryLock = new RecoveryLock({ dir: wpaths.projectSessions, sessionStore });
633
+ if (!resumeId && !flags["no-recovery"]) {
634
+ const abandoned = await recoveryLock.checkAbandoned();
635
+ if (abandoned && abandoned.messageCount > 0) {
636
+ const choice = await onRecovery(abandoned, !!flags["recover"]);
637
+ if (choice === "resume") resumeId = abandoned.sessionId;
638
+ else if (choice === "delete") {
639
+ await sessionStore.delete(abandoned.sessionId).catch(() => void 0);
640
+ await recoveryLock.clear();
641
+ } else await recoveryLock.clear();
642
+ } else if (abandoned) {
643
+ await sessionStore.delete(abandoned.sessionId).catch(() => void 0);
644
+ await recoveryLock.clear();
645
+ }
646
+ }
647
+ let session;
648
+ let restoredMessages = [];
649
+ if (resumeId) {
650
+ try {
651
+ const resumed = await sessionStore.resume(resumeId);
652
+ session = resumed.writer;
653
+ restoredMessages = resumed.data.messages;
654
+ renderer.writeInfo(`Resumed session ${resumed.data.metadata.id} \u2014 ${restoredMessages.length} messages, ${resumed.data.usage.input + resumed.data.usage.output} tokens used previously.`);
655
+ } catch (err) {
656
+ renderer.writeError(`Resume failed: ${err instanceof Error ? err.message : String(err)}`);
657
+ throw Object.assign(new Error("RESUME_FAILED"), { exitCode: 2 });
658
+ }
659
+ } else {
660
+ session = await sessionStore.create({ id: "", title: "", model: config.model, provider: config.provider });
661
+ }
662
+ const sessionRef = { current: session };
663
+ await recoveryLock.write(session.id).catch(() => void 0);
664
+ const attachments = new DefaultAttachmentStore({ spoolDir: path14.join(wpaths.projectSessions, session.id, "attachments") });
665
+ const queueStore = new QueueStore({ dir: path14.join(wpaths.projectSessions, session.id) });
666
+ const ctxSignal = new AbortController().signal;
667
+ const context = new Context({ systemPrompt, provider, session, signal: ctxSignal, tokenCounter, cwd, projectRoot, model: config.model });
668
+ if (restoredMessages.length > 0) context.state.replaceMessages(restoredMessages);
669
+ const todosCheckpointPath = path14.join(wpaths.projectSessions, `${session.id}.todos.json`);
670
+ if (resumeId) {
671
+ try {
672
+ const restoredTodos = await loadTodosCheckpoint(todosCheckpointPath);
673
+ if (restoredTodos && restoredTodos.length > 0) {
674
+ context.state.replaceTodos(restoredTodos);
675
+ renderer.writeInfo(`Restored ${restoredTodos.length} todo${restoredTodos.length === 1 ? "" : "s"} from previous run.`);
676
+ }
677
+ } catch {
678
+ }
679
+ }
680
+ const detachTodosCheckpoint = attachTodosCheckpoint(context.state, todosCheckpointPath, session.id);
681
+ const planPath = path14.join(wpaths.projectSessions, `${session.id}.plan.json`);
682
+ context.state.setMeta("plan.path", planPath);
683
+ if (resumeId) {
684
+ try {
685
+ const fleetRoot = path14.join(wpaths.projectSessions, session.id);
686
+ const dirState = await loadDirectorState(path14.join(fleetRoot, "director-state.json"));
687
+ if (dirState) {
688
+ const tCounts = {};
689
+ for (const t of dirState.tasks) tCounts[t.status] = (tCounts[t.status] ?? 0) + 1;
690
+ const summary = Object.entries(tCounts).map(([k, v]) => `${v} ${k}`).join(", ");
691
+ renderer.writeInfo(`Prior fleet state: ${dirState.subagents.length} subagent${dirState.subagents.length === 1 ? "" : "s"}, tasks ${summary || "(none)"}.`);
692
+ }
693
+ } catch {
694
+ }
695
+ try {
696
+ const plan = await loadPlan(planPath);
697
+ if (plan && plan.items.length > 0) {
698
+ const open = plan.items.filter((p) => p.status !== "done").length;
699
+ const done = plan.items.length - open;
700
+ renderer.writeInfo(`Plan: ${plan.items.length} item${plan.items.length === 1 ? "" : "s"} (${open} open, ${done} done). Use /plan to review.`);
701
+ }
702
+ } catch {
703
+ }
704
+ }
705
+ return { session, sessionRef, context, restoredMessages, attachments, recoveryLock, queueStore, planPath, detachTodosCheckpoint };
706
+ }
707
+ function setupPipelines(params) {
708
+ const { events, logger } = params;
709
+ const pipelines = createDefaultPipelines();
710
+ const installBoundary = (p) => {
711
+ p.setErrorHandler((ev) => {
712
+ const fromPlugin = !!ev.owner && ev.owner !== "core";
713
+ logger.error(
714
+ `Pipeline middleware "${ev.middleware}" crashed (owner=${ev.owner ?? "unknown"}); ${fromPlugin ? "swallowed" : "rethrown"}`,
715
+ ev.err
716
+ );
717
+ events.emit("error", {
718
+ err: ev.err instanceof Error ? ev.err : new Error(String(ev.err)),
719
+ phase: `pipeline:${ev.middleware}`
720
+ });
721
+ return fromPlugin ? "swallow" : "rethrow";
722
+ });
723
+ };
724
+ installBoundary(pipelines.request);
725
+ installBoundary(pipelines.response);
726
+ installBoundary(pipelines.toolCall);
727
+ installBoundary(pipelines.userInput);
728
+ installBoundary(pipelines.assistantOutput);
729
+ installBoundary(pipelines.contextWindow);
730
+ return pipelines;
731
+ }
732
+ async function setupCompaction(params) {
733
+ const { compactor, events, modelsRegistry, context, config, provider, pipelines } = params;
734
+ const resolvedCaps = await capabilitiesFor(modelsRegistry, provider.id, context.model).catch(() => void 0);
735
+ const effectiveMaxContext = config.context.effectiveMaxContext ?? resolvedCaps?.maxContext ?? provider.capabilities.maxContext;
736
+ if (config.context.autoCompact !== false) {
737
+ const autoCompactor = new AutoCompactionMiddleware(
738
+ compactor,
739
+ effectiveMaxContext,
740
+ (ctx) => {
741
+ let total = 0;
742
+ for (const m of ctx.messages) {
743
+ if (typeof m.content === "string") {
744
+ total += Math.ceil(m.content.length / 4);
745
+ } else if (Array.isArray(m.content)) {
746
+ for (const b of m.content) {
747
+ if (b.type === "text") {
748
+ total += Math.ceil(b.text.length / 4);
749
+ } else if (b.type === "tool_use" || b.type === "tool_result") {
750
+ total += Math.ceil(JSON.stringify(b).length / 4);
751
+ }
752
+ }
753
+ }
754
+ }
755
+ return total;
756
+ },
757
+ {
758
+ warn: config.context.warnThreshold,
759
+ soft: config.context.softThreshold,
760
+ hard: config.context.hardThreshold
761
+ },
762
+ { aggressiveOn: "soft", failureMode: "throw_on_hard", events }
763
+ );
764
+ pipelines.contextWindow.use({ name: "AutoCompaction", handler: autoCompactor.handler() });
765
+ }
766
+ return effectiveMaxContext;
767
+ }
768
+ function createAgent(params) {
769
+ return new Agent({
770
+ container: params.container,
771
+ tools: params.tools,
772
+ providers: params.providers,
773
+ events: params.events,
774
+ pipelines: params.pipelines,
775
+ context: params.context,
776
+ maxIterations: params.config.tools.maxIterations,
777
+ iterationTimeoutMs: params.config.tools.iterationTimeoutMs,
778
+ executionStrategy: params.config.tools.defaultExecutionStrategy,
779
+ perIterationOutputCapBytes: params.config.tools.perIterationOutputCapBytes,
780
+ confirmAwaiter: params.confirmAwaiter
781
+ });
782
+ }
571
783
 
572
784
  // src/arg-parser.ts
573
785
  var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
@@ -693,7 +905,7 @@ function parseSpawnFlags(input) {
693
905
  return { description: rest.trim(), opts };
694
906
  }
695
907
  async function bootConfig(flags) {
696
- const cwd = typeof flags["cwd"] === "string" ? path13.resolve(flags["cwd"]) : process.cwd();
908
+ const cwd = typeof flags["cwd"] === "string" ? path14.resolve(flags["cwd"]) : process.cwd();
697
909
  const pathResolver = new DefaultPathResolver(cwd);
698
910
  const projectRoot = pathResolver.projectRoot;
699
911
  const userHome = os3.homedir();
@@ -760,7 +972,7 @@ var ReadlineInputReader = class {
760
972
  history = [];
761
973
  pending = false;
762
974
  constructor(opts = {}) {
763
- this.historyFile = opts.historyFile ?? path13.join(os3.homedir(), ".wrongstack", "history");
975
+ this.historyFile = opts.historyFile ?? path14.join(os3.homedir(), ".wrongstack", "history");
764
976
  }
765
977
  async loadHistory() {
766
978
  try {
@@ -772,7 +984,7 @@ var ReadlineInputReader = class {
772
984
  }
773
985
  async saveHistory() {
774
986
  try {
775
- await fs3.mkdir(path13.dirname(this.historyFile), { recursive: true });
987
+ await fs3.mkdir(path14.dirname(this.historyFile), { recursive: true });
776
988
  await fs3.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
777
989
  } catch {
778
990
  }
@@ -1228,7 +1440,7 @@ async function saveToGlobalConfig(configPath, provider, model) {
1228
1440
  async function detectProjectFacts(root) {
1229
1441
  const facts = { hints: [] };
1230
1442
  try {
1231
- const pkg = JSON.parse(await fs3.readFile(path13.join(root, "package.json"), "utf8"));
1443
+ const pkg = JSON.parse(await fs3.readFile(path14.join(root, "package.json"), "utf8"));
1232
1444
  const scripts = pkg.scripts ?? {};
1233
1445
  const pm = (pkg.packageManager ?? "npm").split("@")[0] ?? "npm";
1234
1446
  if (scripts["build"]) facts.build = `${pm} run build`;
@@ -1240,28 +1452,28 @@ async function detectProjectFacts(root) {
1240
1452
  } catch {
1241
1453
  }
1242
1454
  try {
1243
- await fs3.access(path13.join(root, "pyproject.toml"));
1455
+ await fs3.access(path14.join(root, "pyproject.toml"));
1244
1456
  facts.test ??= "pytest";
1245
1457
  facts.lint ??= "ruff check .";
1246
1458
  facts.hints.push("pyproject.toml");
1247
1459
  } catch {
1248
1460
  }
1249
1461
  try {
1250
- await fs3.access(path13.join(root, "go.mod"));
1462
+ await fs3.access(path14.join(root, "go.mod"));
1251
1463
  facts.build ??= "go build ./...";
1252
1464
  facts.test ??= "go test ./...";
1253
1465
  facts.hints.push("go.mod");
1254
1466
  } catch {
1255
1467
  }
1256
1468
  try {
1257
- await fs3.access(path13.join(root, "Cargo.toml"));
1469
+ await fs3.access(path14.join(root, "Cargo.toml"));
1258
1470
  facts.build ??= "cargo build";
1259
1471
  facts.test ??= "cargo test";
1260
1472
  facts.hints.push("Cargo.toml");
1261
1473
  } catch {
1262
1474
  }
1263
1475
  try {
1264
- await fs3.access(path13.join(root, "Makefile"));
1476
+ await fs3.access(path14.join(root, "Makefile"));
1265
1477
  facts.build ??= "make";
1266
1478
  facts.test ??= "make test";
1267
1479
  facts.hints.push("Makefile");
@@ -1398,7 +1610,9 @@ function buildCompactCommand(opts) {
1398
1610
  }
1399
1611
  const aggressive = args.trim() === "aggressive";
1400
1612
  const report = await opts.compactor.compact(ctx, { aggressive });
1401
- const msg = `Compaction: ${report.before} \u2192 ${report.after} tokens (${report.reductions.map((r) => `${r.phase}: ${r.saved}`).join(", ")})`;
1613
+ const reductions = report.reductions.map((r) => `${r.phase}: ${r.saved}`).join(", ");
1614
+ const repaired = report.repaired ? `; repaired ${report.repaired.removedToolUses.length} tool_use, ${report.repaired.removedToolResults.length} tool_result, ${report.repaired.removedMessages} empty messages` : "";
1615
+ const msg = `Compaction: ${report.before} -> ${report.after} tokens (${reductions})${repaired}`;
1402
1616
  opts.renderer.writeInfo(msg);
1403
1617
  return { message: msg };
1404
1618
  }
@@ -1412,15 +1626,68 @@ function buildContextCommand(opts) {
1412
1626
  help: [
1413
1627
  "Usage:",
1414
1628
  " /context Show counts: messages, est. tokens, tool calls, todos, read files.",
1415
- " /context detail As above, plus model, cwd, projectRoot, and the file list."
1629
+ " /context detail As above, plus model, cwd, projectRoot, and the file list.",
1630
+ " /context repair Repair orphan tool_use/tool_result blocks after manual compaction.",
1631
+ " /context mode List context-window modes.",
1632
+ " /context mode <id> Switch context-window mode for this session."
1416
1633
  ].join("\n"),
1417
1634
  async run(args, ctx) {
1635
+ const trimmed = args.trim();
1636
+ if (trimmed === "mode" || trimmed === "modes") {
1637
+ const active = readPolicy(ctx)?.id ?? "balanced";
1638
+ const msg2 = `${color.bold("Context Window Modes")}
1639
+ ${formatContextWindowModeList(active)}`;
1640
+ opts.renderer.write(`${msg2}
1641
+ `);
1642
+ return { message: msg2 };
1643
+ }
1644
+ if (trimmed === "repair") {
1645
+ const before = ctx.messages.length;
1646
+ const repaired = repairToolUseAdjacency(ctx.messages);
1647
+ if (repaired.report.changed) {
1648
+ ctx.state.replaceMessages(repaired.messages);
1649
+ }
1650
+ const msg2 = repaired.report.changed ? [
1651
+ `${color.green("Context repaired")}`,
1652
+ ` messages: ${before} -> ${ctx.messages.length}`,
1653
+ ` tool_use: removed ${repaired.report.removedToolUses.length}`,
1654
+ ` tool_result: removed ${repaired.report.removedToolResults.length}`,
1655
+ ` empty msgs: removed ${repaired.report.removedMessages}`
1656
+ ].join("\n") : "Context repair: no orphan tool_use/tool_result blocks found.";
1657
+ opts.renderer.write(`${msg2}
1658
+ `);
1659
+ return { message: msg2 };
1660
+ }
1661
+ if (trimmed.startsWith("mode ")) {
1662
+ const id = trimmed.slice("mode ".length).trim();
1663
+ const mode = getContextWindowMode(id);
1664
+ if (!mode) {
1665
+ const msg3 = `Unknown context mode "${id}". Use /context mode to list modes.`;
1666
+ opts.renderer.write(`${color.red(msg3)}
1667
+ `);
1668
+ return { message: msg3 };
1669
+ }
1670
+ const policy2 = resolveContextWindowPolicy({}, mode.id);
1671
+ ctx.meta["contextWindowMode"] = policy2.id;
1672
+ ctx.meta["contextWindowPolicy"] = policy2;
1673
+ const msg2 = [
1674
+ `${color.green("Context mode set:")} ${policy2.id} (${policy2.name})`,
1675
+ ` thresholds: warn ${pct(policy2.thresholds.warn)}, soft ${pct(policy2.thresholds.soft)}, hard ${pct(policy2.thresholds.hard)}`,
1676
+ ` preserve: last ${policy2.preserveK} user/assistant messages`,
1677
+ ` elide: old tool results >= ${policy2.eliseThreshold.toLocaleString()} tokens`
1678
+ ].join("\n");
1679
+ opts.renderer.write(`${msg2}
1680
+ `);
1681
+ return { message: msg2 };
1682
+ }
1418
1683
  const messages = ctx.messages;
1419
- const detailed = args.trim() === "detail";
1684
+ const detailed = trimmed === "detail";
1685
+ const policy = readPolicy(ctx);
1420
1686
  const lines = [
1421
1687
  `${color.bold("Context Window")}`,
1422
1688
  ` messages: ${messages.length} total (${countTurnPairs(messages)} user+assistant pairs)`,
1423
- ` tokens (\u2248): ${estimateTokens(messages).toLocaleString()} (chars \xF7 4 estimate)`,
1689
+ ` tokens (est): ${estimateTokens(messages).toLocaleString()} (chars / 4 estimate)`,
1690
+ ` mode: ${policy ? `${policy.id} (${policy.name})` : "balanced"}`,
1424
1691
  ` system prompt: ${ctx.systemPrompt.length} block${ctx.systemPrompt.length !== 1 ? "s" : ""}`,
1425
1692
  ` tools: ${countToolUses(messages)} calls made, ${countToolResults(messages)} results in history`,
1426
1693
  ` read files: ${ctx.readFiles.size} files`,
@@ -1428,6 +1695,7 @@ function buildContextCommand(opts) {
1428
1695
  ];
1429
1696
  if (detailed) {
1430
1697
  lines.push(
1698
+ ` thresholds: warn ${pct(policy?.thresholds.warn ?? 0.6)}, soft ${pct(policy?.thresholds.soft ?? 0.75)}, hard ${pct(policy?.thresholds.hard ?? 0.9)}`,
1431
1699
  ` model: ${ctx.model}`,
1432
1700
  ` cwd: ${ctx.cwd}`,
1433
1701
  ` projectRoot: ${ctx.projectRoot}`,
@@ -1442,6 +1710,13 @@ function buildContextCommand(opts) {
1442
1710
  }
1443
1711
  };
1444
1712
  }
1713
+ function readPolicy(ctx) {
1714
+ const policy = ctx.meta?.["contextWindowPolicy"];
1715
+ return policy && typeof policy === "object" ? policy : null;
1716
+ }
1717
+ function pct(n) {
1718
+ return `${Math.round(n * 100)}%`;
1719
+ }
1445
1720
 
1446
1721
  // src/slash-commands/diag-stats.ts
1447
1722
  function buildDiagCommand(opts) {
@@ -1643,8 +1918,8 @@ function buildInitCommand(opts) {
1643
1918
  description: "Scaffold .wrongstack/AGENTS.md in the current project.",
1644
1919
  async run(args, ctx) {
1645
1920
  const force = args.trim() === "--force";
1646
- const dir = path13.join(ctx.projectRoot, ".wrongstack");
1647
- const file = path13.join(dir, "AGENTS.md");
1921
+ const dir = path14.join(ctx.projectRoot, ".wrongstack");
1922
+ const file = path14.join(dir, "AGENTS.md");
1648
1923
  try {
1649
1924
  await fs3.access(file);
1650
1925
  if (!force) {
@@ -2043,13 +2318,13 @@ var MANIFESTS = [
2043
2318
  ];
2044
2319
  async function detectProjectKind(projectRoot) {
2045
2320
  try {
2046
- await fs3.access(path13.join(projectRoot, ".wrongstack", "AGENTS.md"));
2321
+ await fs3.access(path14.join(projectRoot, ".wrongstack", "AGENTS.md"));
2047
2322
  return "initialized";
2048
2323
  } catch {
2049
2324
  }
2050
2325
  for (const m of MANIFESTS) {
2051
2326
  try {
2052
- await fs3.access(path13.join(projectRoot, m));
2327
+ await fs3.access(path14.join(projectRoot, m));
2053
2328
  return "project";
2054
2329
  } catch {
2055
2330
  }
@@ -2057,8 +2332,8 @@ async function detectProjectKind(projectRoot) {
2057
2332
  return "empty";
2058
2333
  }
2059
2334
  async function scaffoldAgentsMd(projectRoot) {
2060
- const dir = path13.join(projectRoot, ".wrongstack");
2061
- const file = path13.join(dir, "AGENTS.md");
2335
+ const dir = path14.join(projectRoot, ".wrongstack");
2336
+ const file = path14.join(dir, "AGENTS.md");
2062
2337
  const facts = await detectProjectFacts(projectRoot);
2063
2338
  const body = renderAgentsTemplate(facts);
2064
2339
  await fs3.mkdir(dir, { recursive: true });
@@ -2071,7 +2346,7 @@ async function runProjectCheck(opts) {
2071
2346
  if (kind === "initialized") {
2072
2347
  renderer.write(
2073
2348
  `
2074
- ${color.green("\u2713")} Project initialized ${color.dim(`(${path13.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
2349
+ ${color.green("\u2713")} Project initialized ${color.dim(`(${path14.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
2075
2350
  `
2076
2351
  );
2077
2352
  return true;
@@ -2367,14 +2642,14 @@ function summarize(value, name) {
2367
2642
  if (typeof v === "object" && v !== null) {
2368
2643
  const o = v;
2369
2644
  if (name === "edit") {
2370
- const path14 = typeof o["path"] === "string" ? o["path"] : "";
2645
+ const path15 = typeof o["path"] === "string" ? o["path"] : "";
2371
2646
  const reps = typeof o["replacements"] === "number" ? o["replacements"] : 0;
2372
- return `${path14} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
2647
+ return `${path15} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
2373
2648
  }
2374
2649
  if (name === "write") {
2375
- const path14 = typeof o["path"] === "string" ? o["path"] : "";
2650
+ const path15 = typeof o["path"] === "string" ? o["path"] : "";
2376
2651
  const bytes = typeof o["bytes"] === "number" ? o["bytes"] : void 0;
2377
- return bytes !== void 0 ? `${path14} ${bytes}B` : path14;
2652
+ return bytes !== void 0 ? `${path15} ${bytes}B` : path15;
2378
2653
  }
2379
2654
  if (typeof o["count"] === "number") {
2380
2655
  return `${o["count"]} match${o["count"] === 1 ? "" : "es"}`;
@@ -3137,7 +3412,7 @@ var doctorCmd = async (_args, deps) => {
3137
3412
  }
3138
3413
  try {
3139
3414
  await fs3.mkdir(deps.paths.projectSessions, { recursive: true });
3140
- const probe = path13.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
3415
+ const probe = path14.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
3141
3416
  await fs3.writeFile(probe, "");
3142
3417
  await fs3.unlink(probe);
3143
3418
  checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
@@ -3240,8 +3515,8 @@ var exportCmd = async (args, deps) => {
3240
3515
  return 1;
3241
3516
  }
3242
3517
  if (output) {
3243
- await fs3.mkdir(path13.dirname(path13.resolve(deps.cwd, output)), { recursive: true });
3244
- await fs3.writeFile(path13.resolve(deps.cwd, output), rendered, "utf8");
3518
+ await fs3.mkdir(path14.dirname(path14.resolve(deps.cwd, output)), { recursive: true });
3519
+ await fs3.writeFile(path14.resolve(deps.cwd, output), rendered, "utf8");
3245
3520
  deps.renderer.write(`Wrote ${rendered.length} bytes to ${output}
3246
3521
  `);
3247
3522
  } else {
@@ -3302,8 +3577,8 @@ var initCmd = async (_args, deps) => {
3302
3577
  const config = { version: 1, provider: providerId, model: modelId };
3303
3578
  if (apiKey) config.apiKey = apiKey;
3304
3579
  await atomicWrite(deps.paths.globalConfig, JSON.stringify(config, null, 2));
3305
- await fs3.mkdir(path13.join(deps.projectRoot, ".wrongstack"), { recursive: true });
3306
- const agentsFile = path13.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
3580
+ await fs3.mkdir(path14.join(deps.projectRoot, ".wrongstack"), { recursive: true });
3581
+ const agentsFile = path14.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
3307
3582
  try {
3308
3583
  await fs3.access(agentsFile);
3309
3584
  } catch {
@@ -3317,86 +3592,7 @@ var initCmd = async (_args, deps) => {
3317
3592
  deps.renderer.writeInfo('Try: wstack "<task>" or wstack');
3318
3593
  return 0;
3319
3594
  };
3320
- var BUILT_IN_MCP = {
3321
- filesystem: {
3322
- name: "filesystem",
3323
- transport: "stdio",
3324
- command: "npx",
3325
- args: ["-y", "@modelcontextprotocol/server-filesystem", "."],
3326
- permission: "confirm",
3327
- description: "Read, write, and navigate the local filesystem"
3328
- },
3329
- github: {
3330
- name: "github",
3331
- transport: "stdio",
3332
- command: "npx",
3333
- args: ["-y", "@modelcontextprotocol/server-github"],
3334
- permission: "confirm",
3335
- description: "GitHub API \u2014 issues, PRs, repos, search"
3336
- },
3337
- context7: {
3338
- name: "context7",
3339
- transport: "streamable-http",
3340
- url: "https://server.context7.ai/mcp",
3341
- permission: "confirm",
3342
- description: "Codebase-aware documentation and Q&A"
3343
- },
3344
- "brave-search": {
3345
- name: "brave-search",
3346
- transport: "stdio",
3347
- command: "npx",
3348
- args: ["-y", "@modelcontextprotocol/server-brave-search"],
3349
- permission: "confirm",
3350
- description: "Web search (Brave)"
3351
- },
3352
- block: {
3353
- name: "block",
3354
- transport: "stdio",
3355
- command: "npx",
3356
- args: ["-y", "@modelcontextprotocol/server-block"],
3357
- permission: "confirm",
3358
- description: "Postgres database via SQL"
3359
- },
3360
- everart: {
3361
- name: "everart",
3362
- transport: "stdio",
3363
- command: "npx",
3364
- args: ["-y", "@modelcontextprotocol/server-everart"],
3365
- permission: "confirm",
3366
- description: "AI image generation"
3367
- },
3368
- slack: {
3369
- name: "slack",
3370
- transport: "stdio",
3371
- command: "npx",
3372
- args: ["-y", "@modelcontextprotocol/server-slack"],
3373
- permission: "confirm",
3374
- description: "Slack messaging & channels"
3375
- },
3376
- aws: {
3377
- name: "aws",
3378
- transport: "stdio",
3379
- command: "npx",
3380
- args: ["-y", "@modelcontextprotocol/server-aws"],
3381
- permission: "confirm",
3382
- description: "AWS \u2014 EC2, S3, Lambda, IAM"
3383
- },
3384
- "google-maps": {
3385
- name: "google-maps",
3386
- transport: "stdio",
3387
- command: "npx",
3388
- args: ["-y", "@modelcontextprotocol/server-google-maps"],
3389
- permission: "confirm",
3390
- description: "Google Maps \u2014 directions, geocoding, places"
3391
- },
3392
- sentinel: {
3393
- name: "sentinel",
3394
- transport: "streamable-http",
3395
- url: "https://mcp.sentinel.ai",
3396
- permission: "deny",
3397
- description: "Security vulnerability scanning"
3398
- }
3399
- };
3595
+ var BUILT_IN_MCP = allServers();
3400
3596
  var mcpCmd = async (args, deps) => {
3401
3597
  const sub = args[0];
3402
3598
  if (!sub || sub === "list") {
@@ -3459,7 +3655,7 @@ async function addMcpServer(args, deps) {
3459
3655
  return 1;
3460
3656
  }
3461
3657
  const serverCfg = { ...factory };
3462
- if (!enable) serverCfg.enabled = false;
3658
+ serverCfg.enabled = enable;
3463
3659
  let existing = {};
3464
3660
  try {
3465
3661
  existing = JSON.parse(await fs3.readFile(deps.paths.globalConfig, "utf8"));
@@ -3531,7 +3727,7 @@ var usageCmd = async (_args, deps) => {
3531
3727
  return 0;
3532
3728
  };
3533
3729
  var projectsCmd = async (_args, deps) => {
3534
- const projectsRoot = path13.join(deps.paths.globalRoot, "projects");
3730
+ const projectsRoot = path14.join(deps.paths.globalRoot, "projects");
3535
3731
  try {
3536
3732
  const entries = await fs3.readdir(projectsRoot);
3537
3733
  if (entries.length === 0) {
@@ -3541,7 +3737,7 @@ var projectsCmd = async (_args, deps) => {
3541
3737
  for (const hash of entries) {
3542
3738
  try {
3543
3739
  const meta = JSON.parse(
3544
- await fs3.readFile(path13.join(projectsRoot, hash, "meta.json"), "utf8")
3740
+ await fs3.readFile(path14.join(projectsRoot, hash, "meta.json"), "utf8")
3545
3741
  );
3546
3742
  deps.renderer.write(
3547
3743
  ` ${color.dim(hash)} ${color.dim(meta.lastSeen ?? "")} ${meta.root ?? "?"}
@@ -3839,7 +4035,7 @@ function resolveBundledSkillsDir() {
3839
4035
  try {
3840
4036
  const req2 = createRequire(import.meta.url);
3841
4037
  const corePkg = req2.resolve("@wrongstack/core/package.json");
3842
- return path13.join(path13.dirname(corePkg), "skills");
4038
+ return path14.join(path14.dirname(corePkg), "skills");
3843
4039
  } catch {
3844
4040
  return void 0;
3845
4041
  }
@@ -4008,6 +4204,10 @@ async function runRepl(opts) {
4008
4204
  continue;
4009
4205
  }
4010
4206
  interrupts = 0;
4207
+ if (trimmed === "/image" || trimmed === "/paste-image" || raw === "\x1Bv") {
4208
+ await pasteClipboardImage(builder, opts);
4209
+ continue;
4210
+ }
4011
4211
  if (trimmed.startsWith("/")) {
4012
4212
  try {
4013
4213
  const res = await opts.slashRegistry.dispatch(trimmed, opts.agent.ctx);
@@ -4032,7 +4232,23 @@ async function runRepl(opts) {
4032
4232
  const startedAt = Date.now();
4033
4233
  const before = opts.tokenCounter?.total();
4034
4234
  const costBefore = opts.tokenCounter?.estimateCost().total ?? 0;
4035
- const result = await opts.agent.run(blocks, { signal: runCtrl.signal });
4235
+ const routed = blocks.some((block) => block.type === "image") ? await routeImagesForModel(blocks, {
4236
+ supportsVision: opts.supportsVision ? await opts.supportsVision() : opts.agent.ctx.provider.capabilities.vision,
4237
+ adapters: opts.visionAdapters ?? [],
4238
+ ctx: opts.agent.ctx,
4239
+ signal: runCtrl.signal,
4240
+ providerId: opts.agent.ctx.provider.id,
4241
+ model: opts.agent.ctx.model
4242
+ }) : { blocks, route: "none", convertedImages: 0 };
4243
+ if (routed.route === "adapter") {
4244
+ opts.renderer.write(
4245
+ color.dim(
4246
+ ` \u21B3 image analyzed via ${routed.adapterName ?? "vision adapter"} (${routed.convertedImages} image${routed.convertedImages === 1 ? "" : "s"})
4247
+ `
4248
+ )
4249
+ );
4250
+ }
4251
+ const result = await opts.agent.run(routed.blocks, { signal: runCtrl.signal });
4036
4252
  if (result.status === "aborted") {
4037
4253
  opts.renderer.writeWarning("Aborted.");
4038
4254
  } else if (result.status === "failed") {
@@ -4071,6 +4287,23 @@ ${color.dim(
4071
4287
  });
4072
4288
  }
4073
4289
  }
4290
+ async function pasteClipboardImage(builder, opts) {
4291
+ try {
4292
+ const img = await readClipboardImage();
4293
+ if (!img) {
4294
+ opts.renderer.write(color.dim(" no image on clipboard\n"));
4295
+ return;
4296
+ }
4297
+ const placeholder = await builder.appendImage(img.base64, img.mediaType);
4298
+ const kb = (img.bytes / 1024).toFixed(0);
4299
+ opts.renderer.write(color.dim(` \u21B3 ${placeholder} (PNG ${kb}KB)
4300
+ `));
4301
+ } catch (err) {
4302
+ opts.renderer.writeError(
4303
+ `Clipboard image error: ${err instanceof Error ? err.message : String(err)}`
4304
+ );
4305
+ }
4306
+ }
4074
4307
  async function readPossiblyMultiline(opts) {
4075
4308
  const firstPrompt = theme2.primary("\u203A ");
4076
4309
  const contPrompt = color.dim("\xB7 ");
@@ -4096,9 +4329,9 @@ var FILLED = "\u2588";
4096
4329
  var EMPTY = "\u2591";
4097
4330
  function renderContextChip(used, max) {
4098
4331
  const ratio = Math.max(0, Math.min(1, used / max));
4099
- const pct = Math.round(ratio * 100);
4332
+ const pct2 = Math.round(ratio * 100);
4100
4333
  const bar = renderProgress(ratio, 6);
4101
- return `${bar} ${pct}% (${fmtTok(used)}/${fmtTok(max)})`;
4334
+ return `${bar} ${pct2}% (${fmtTok(used)}/${fmtTok(max)})`;
4102
4335
  }
4103
4336
  function renderProgress(ratio, width) {
4104
4337
  const clamped = Math.max(0, Math.min(1, ratio));
@@ -4142,6 +4375,7 @@ async function execute(deps) {
4142
4375
  queueStore,
4143
4376
  context,
4144
4377
  stats,
4378
+ detachTodosCheckpoint,
4145
4379
  savedProviderCfg,
4146
4380
  resolvedProvider,
4147
4381
  getPickableProviders,
@@ -4152,6 +4386,15 @@ async function execute(deps) {
4152
4386
  } = deps;
4153
4387
  let code = 0;
4154
4388
  try {
4389
+ const visionAdapters = () => createToolVisionAdapters(agent.tools);
4390
+ const supportsVision = async () => {
4391
+ try {
4392
+ const caps = await capabilitiesFor(modelsRegistry, context.provider.id, context.model);
4393
+ return caps.vision;
4394
+ } catch {
4395
+ return context.provider.capabilities.vision;
4396
+ }
4397
+ };
4155
4398
  const promptFlag = typeof flags["prompt"] === "string" ? flags["prompt"] : void 0;
4156
4399
  if (promptFlag) {
4157
4400
  positional.unshift(promptFlag);
@@ -4236,6 +4479,8 @@ async function execute(deps) {
4236
4479
  slashRegistry,
4237
4480
  attachments,
4238
4481
  tokenCounter,
4482
+ visionAdapters,
4483
+ supportsVision,
4239
4484
  model: context.model,
4240
4485
  banner: !flags["no-banner"],
4241
4486
  queueStore,
@@ -4287,9 +4532,11 @@ async function execute(deps) {
4287
4532
  reader,
4288
4533
  slashRegistry,
4289
4534
  tokenCounter,
4535
+ visionAdapters,
4536
+ supportsVision,
4290
4537
  attachments,
4291
4538
  effectiveMaxContext,
4292
- projectName: path13.basename(projectRoot) || void 0
4539
+ projectName: path14.basename(projectRoot) || void 0
4293
4540
  });
4294
4541
  await webuiPromise;
4295
4542
  } else {
@@ -4299,13 +4546,16 @@ async function execute(deps) {
4299
4546
  reader,
4300
4547
  slashRegistry,
4301
4548
  tokenCounter,
4549
+ visionAdapters,
4550
+ supportsVision,
4302
4551
  attachments,
4303
4552
  effectiveMaxContext,
4304
- projectName: path13.basename(projectRoot) || void 0
4553
+ projectName: path14.basename(projectRoot) || void 0
4305
4554
  });
4306
4555
  }
4307
4556
  } finally {
4308
4557
  stats.render(renderer);
4558
+ await Promise.resolve(detachTodosCheckpoint?.()).catch(() => void 0);
4309
4559
  await mcpRegistry.stopAll();
4310
4560
  await session.append({
4311
4561
  type: "session_end",
@@ -4558,7 +4808,7 @@ var MultiAgentHost = class {
4558
4808
  model: opts?.model,
4559
4809
  tools: opts?.tools
4560
4810
  };
4561
- const transcriptPath = this.sessionFactory ? path13.join(this.sessionFactory.dir, `${subagentConfig.name}.jsonl`) : void 0;
4811
+ const transcriptPath = this.sessionFactory ? path14.join(this.sessionFactory.dir, `${subagentConfig.name}.jsonl`) : void 0;
4562
4812
  if (this.director) {
4563
4813
  const subagentId = await this.director.spawn(subagentConfig);
4564
4814
  const taskId2 = randomUUID();
@@ -4710,16 +4960,16 @@ var MultiAgentHost = class {
4710
4960
  }
4711
4961
  this.opts.directorMode = true;
4712
4962
  if (this.opts.fleetRoot && !this.opts.manifestPath) {
4713
- this.opts.manifestPath = path13.join(this.opts.fleetRoot, "fleet.json");
4963
+ this.opts.manifestPath = path14.join(this.opts.fleetRoot, "fleet.json");
4714
4964
  }
4715
4965
  if (this.opts.fleetRoot && !this.opts.sharedScratchpadPath) {
4716
- this.opts.sharedScratchpadPath = path13.join(this.opts.fleetRoot, "shared");
4966
+ this.opts.sharedScratchpadPath = path14.join(this.opts.fleetRoot, "shared");
4717
4967
  }
4718
4968
  if (this.opts.fleetRoot && !this.opts.sessionsRoot) {
4719
- this.opts.sessionsRoot = path13.join(this.opts.fleetRoot, "subagents");
4969
+ this.opts.sessionsRoot = path14.join(this.opts.fleetRoot, "subagents");
4720
4970
  }
4721
4971
  if (this.opts.fleetRoot && !this.opts.stateCheckpointPath) {
4722
- this.opts.stateCheckpointPath = path13.join(this.opts.fleetRoot, "director-state.json");
4972
+ this.opts.stateCheckpointPath = path14.join(this.opts.fleetRoot, "director-state.json");
4723
4973
  }
4724
4974
  await this.ensureDirector();
4725
4975
  return this.director ?? null;
@@ -4839,11 +5089,11 @@ var SessionStats = class {
4839
5089
  if (e.name === "bash") this.bashCommands++;
4840
5090
  else if (e.name === "fetch") this.fetches++;
4841
5091
  if (!e.ok) return;
4842
- const path14 = typeof input?.path === "string" ? input.path : void 0;
4843
- if (e.name === "read" && path14) this.readPaths.add(path14);
4844
- else if (e.name === "edit" && path14) this.editedPaths.add(path14);
4845
- else if (e.name === "write" && path14) {
4846
- this.writtenPaths.add(path14);
5092
+ const path15 = typeof input?.path === "string" ? input.path : void 0;
5093
+ if (e.name === "read" && path15) this.readPaths.add(path15);
5094
+ else if (e.name === "edit" && path15) this.editedPaths.add(path15);
5095
+ else if (e.name === "write" && path15) {
5096
+ this.writtenPaths.add(path15);
4847
5097
  const content = typeof input?.content === "string" ? input.content : "";
4848
5098
  this.bytesWritten += Buffer.byteLength(content, "utf8");
4849
5099
  }
@@ -4880,9 +5130,9 @@ var SessionStats = class {
4880
5130
  );
4881
5131
  const cache = this.tokenCounter.cacheStats();
4882
5132
  if (cache.readTokens > 0 || cache.writeTokens > 0) {
4883
- const pct = (cache.hitRatio * 100).toFixed(1);
5133
+ const pct2 = (cache.hitRatio * 100).toFixed(1);
4884
5134
  lines.push(
4885
- ` Prompt cache: ${pct}% hit ${color.dim(`(${fmtTok(cache.readTokens)} read / ${fmtTok(cache.writeTokens)} write)`)}`
5135
+ ` Prompt cache: ${pct2}% hit ${color.dim(`(${fmtTok(cache.readTokens)} read / ${fmtTok(cache.writeTokens)} write)`)}`
4886
5136
  );
4887
5137
  }
4888
5138
  if (cost.total > 0) {
@@ -5009,10 +5259,10 @@ var Spinner = class {
5009
5259
  };
5010
5260
  function renderContextChip2(ctx) {
5011
5261
  const ratio = Math.max(0, Math.min(1, ctx.used / ctx.max));
5012
- const pct = Math.round(ratio * 100);
5262
+ const pct2 = Math.round(ratio * 100);
5013
5263
  const chipColor = ratio >= 0.85 ? color.red : ratio >= 0.65 ? color.yellow : color.cyan;
5014
5264
  const bar = renderProgress2(ratio, 8);
5015
- return color.dim("ctx ") + chipColor(bar) + chipColor(` ${pct}%`) + color.dim(` (${fmtTok(ctx.used)}/${fmtTok(ctx.max)})`);
5265
+ return color.dim("ctx ") + chipColor(bar) + chipColor(` ${pct2}%`) + color.dim(` (${fmtTok(ctx.used)}/${fmtTok(ctx.max)})`);
5016
5266
  }
5017
5267
  function renderProgress2(ratio, width) {
5018
5268
  const clamped = Math.max(0, Math.min(1, ratio));
@@ -5026,7 +5276,7 @@ function resolveBundledSkillsDir2() {
5026
5276
  try {
5027
5277
  const req2 = createRequire(import.meta.url);
5028
5278
  const corePkg = req2.resolve("@wrongstack/core/package.json");
5029
- return path13.join(path13.dirname(corePkg), "skills");
5279
+ return path14.join(path14.dirname(corePkg), "skills");
5030
5280
  } catch {
5031
5281
  return void 0;
5032
5282
  }
@@ -5049,52 +5299,35 @@ async function main(argv) {
5049
5299
  logger
5050
5300
  } = ctx;
5051
5301
  const pathResolver = new DefaultPathResolver(cwd);
5052
- const savedProviderCfg = config.providers?.[config.provider];
5053
- let resolvedProvider = await modelsRegistry.getProvider(config.provider).catch(() => void 0);
5054
- if (!resolvedProvider && savedProviderCfg?.type && savedProviderCfg.type !== config.provider) {
5055
- resolvedProvider = await modelsRegistry.getProvider(savedProviderCfg.type).catch(() => void 0);
5056
- }
5057
- if (!resolvedProvider) {
5058
- if (!savedProviderCfg?.family) {
5059
- logger.warn(
5060
- `Provider "${config.provider}" not found in models.dev. Continuing with raw config.`
5061
- );
5062
- }
5063
- } else if (resolvedProvider.family === "unsupported" && !savedProviderCfg?.family) {
5064
- process.stderr.write(
5065
- `Provider "${config.provider}" uses an unsupported wire family (${resolvedProvider.npm}). Install a plugin to enable it, or pick a different provider.
5066
- `
5067
- );
5302
+ const container = createDefaultContainer({
5303
+ config,
5304
+ wpaths,
5305
+ logger,
5306
+ modelsRegistry,
5307
+ permission: { yolo: config.yolo, promptDelegate: makePromptDelegate(reader) },
5308
+ compactor: { preserveK: config.context.preserveK, eliseThreshold: config.context.eliseThreshold },
5309
+ bundledSkillsDir: config.features.skills ? resolveBundledSkillsDir2() : void 0
5310
+ });
5311
+ const configStore = container.resolve(TOKENS.ConfigStore);
5312
+ container.bind(TOKENS.PathResolver, () => pathResolver);
5313
+ container.bind(TOKENS.Renderer, () => renderer);
5314
+ container.bind(TOKENS.InputReader, () => reader);
5315
+ const modeStore = container.resolve(TOKENS.ModeStore);
5316
+ const activeMode = await modeStore.getActiveMode();
5317
+ let resolvedProvider;
5318
+ let providerRegistry;
5319
+ let provider;
5320
+ try {
5321
+ const result = await setupProvider({ config, modelsRegistry, logger });
5322
+ resolvedProvider = result.resolvedProvider;
5323
+ providerRegistry = result.providerRegistry;
5324
+ provider = result.provider;
5325
+ } catch (err) {
5326
+ process.stderr.write(`${err instanceof Error ? err.message : err}
5327
+ `);
5068
5328
  await reader.close();
5069
5329
  return 2;
5070
5330
  }
5071
- const container = new Container();
5072
- const configStore = new DefaultConfigStore(config);
5073
- container.bind(TOKENS.ConfigStore, () => configStore);
5074
- container.bind(TOKENS.Logger, () => logger);
5075
- container.bind(TOKENS.PathResolver, () => pathResolver);
5076
- container.bind(TOKENS.SecretScrubber, () => new DefaultSecretScrubber());
5077
- container.bind(TOKENS.RetryPolicy, () => new DefaultRetryPolicy());
5078
- container.bind(TOKENS.ErrorHandler, () => new DefaultErrorHandler());
5079
- container.bind(TOKENS.ModelsRegistry, () => modelsRegistry);
5080
- container.bind(
5081
- TOKENS.TokenCounter,
5082
- () => new DefaultTokenCounter({ registry: modelsRegistry, providerId: config.provider })
5083
- );
5084
- const modeStore = new DefaultModeStore({ directory: wpaths.configDir });
5085
- container.bind(TOKENS.ModeStore, () => modeStore);
5086
- container.bind(
5087
- TOKENS.SessionStore,
5088
- () => new DefaultSessionStore({ dir: wpaths.projectSessions })
5089
- );
5090
- const memoryStore = new DefaultMemoryStore({ paths: wpaths });
5091
- container.bind(TOKENS.MemoryStore, () => memoryStore);
5092
- const skillLoader = new DefaultSkillLoader({
5093
- paths: wpaths,
5094
- bundledDir: config.features.skills ? resolveBundledSkillsDir2() : void 0
5095
- });
5096
- container.bind(TOKENS.SkillLoader, () => skillLoader);
5097
- const activeMode = await modeStore.getActiveMode();
5098
5331
  const modeId = activeMode?.id ?? "default";
5099
5332
  const modePrompt = activeMode?.prompt ?? "";
5100
5333
  const resolvedModel = await modelsRegistry.getModel(config.provider, config.model);
@@ -5104,6 +5337,8 @@ async function main(argv) {
5104
5337
  supportsVision: resolvedModel.capabilities.vision,
5105
5338
  supportsReasoning: resolvedModel.capabilities.reasoning
5106
5339
  } : void 0;
5340
+ const memoryStore = container.resolve(TOKENS.MemoryStore);
5341
+ const skillLoader = container.resolve(TOKENS.SkillLoader);
5107
5342
  const sessionRef = {};
5108
5343
  container.bind(
5109
5344
  TOKENS.SystemPromptBuilder,
@@ -5114,46 +5349,9 @@ async function main(argv) {
5114
5349
  modeId,
5115
5350
  modePrompt,
5116
5351
  modelCapabilities,
5117
- // Reads the ref each time returns undefined until the session
5118
- // is created, then resolves to the per-session plan JSON path.
5119
- planPath: () => sessionRef.current ? path13.join(wpaths.projectSessions, `${sessionRef.current.id}.plan.json`) : void 0
5120
- })
5121
- );
5122
- container.bind(TOKENS.Renderer, () => renderer);
5123
- container.bind(TOKENS.InputReader, () => reader);
5124
- container.bind(
5125
- TOKENS.PermissionPolicy,
5126
- () => new DefaultPermissionPolicy({
5127
- trustFile: wpaths.projectTrust,
5128
- yolo: config.yolo,
5129
- promptDelegate: makePromptDelegate(reader)
5130
- })
5131
- );
5132
- container.bind(
5133
- TOKENS.Compactor,
5134
- () => new HybridCompactor({
5135
- preserveK: config.context.preserveK,
5136
- eliseThreshold: config.context.eliseThreshold
5352
+ planPath: () => sessionRef.current ? path14.join(wpaths.projectSessions, `${sessionRef.current.id}.plan.json`) : void 0
5137
5353
  })
5138
5354
  );
5139
- const providerRegistry = new ProviderRegistry();
5140
- if (config.features.modelsRegistry) {
5141
- try {
5142
- const factories = await buildProviderFactoriesFromRegistry({
5143
- registry: modelsRegistry,
5144
- log: logger
5145
- });
5146
- for (const f of factories) providerRegistry.register(f);
5147
- } catch (err) {
5148
- process.stderr.write(
5149
- `Failed to load models.dev registry: ${err instanceof Error ? err.message : err}
5150
- Try \`wstack models refresh\` once you have network access, or run with --no-features.
5151
- `
5152
- );
5153
- await reader.close();
5154
- return 2;
5155
- }
5156
- }
5157
5355
  const toolRegistry = new ToolRegistry();
5158
5356
  toolRegistry.registerAllOrThrow([...builtinToolsPack.tools ?? []], builtinToolsPack.name);
5159
5357
  toolRegistry.registerDefault(
@@ -5196,7 +5394,7 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5196
5394
  const dumpMetrics = () => {
5197
5395
  if (!metricsSink) return;
5198
5396
  try {
5199
- const out = path13.join(wpaths.projectSessions, "metrics.json");
5397
+ const out = path14.join(wpaths.projectSessions, "metrics.json");
5200
5398
  const snap = metricsSink.snapshot();
5201
5399
  writeFileSync(out, JSON.stringify(snap, null, 2));
5202
5400
  } catch {
@@ -5281,27 +5479,6 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5281
5479
  process.stderr.write(color.red(` \u2717 ${p.description}
5282
5480
  `));
5283
5481
  });
5284
- const providerConfig = config.providers?.[config.provider] ?? {
5285
- type: config.provider,
5286
- apiKey: config.apiKey,
5287
- baseUrl: config.baseUrl
5288
- };
5289
- let provider;
5290
- try {
5291
- const cfgWithType = { ...providerConfig, type: config.provider };
5292
- if (config.features.modelsRegistry && providerRegistry.has(config.provider)) {
5293
- provider = providerRegistry.create(cfgWithType);
5294
- } else {
5295
- provider = makeProviderFromConfig(config.provider, cfgWithType);
5296
- }
5297
- } catch (err) {
5298
- process.stderr.write(
5299
- `Failed to create provider: ${err instanceof Error ? err.message : err}
5300
- `
5301
- );
5302
- await reader.close();
5303
- return 2;
5304
- }
5305
5482
  const promptBuilder = container.resolve(TOKENS.SystemPromptBuilder);
5306
5483
  const systemPrompt = await promptBuilder.build({
5307
5484
  cwd,
@@ -5311,59 +5488,29 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5311
5488
  model: config.model
5312
5489
  });
5313
5490
  const sessionStore = container.resolve(TOKENS.SessionStore);
5314
- let resumeId = typeof flags["resume"] === "string" ? flags["resume"] : void 0;
5315
- const recoveryLock = new RecoveryLock({
5316
- dir: wpaths.projectSessions,
5317
- sessionStore
5491
+ const tokenCounter = container.resolve(TOKENS.TokenCounter);
5492
+ const sessResult = await setupSession({
5493
+ config: { model: config.model, provider: config.provider },
5494
+ wpaths,
5495
+ projectRoot,
5496
+ cwd,
5497
+ sessionStore,
5498
+ systemPrompt,
5499
+ provider,
5500
+ tokenCounter,
5501
+ renderer,
5502
+ flags,
5503
+ onRecovery: (abandoned, autoRecover) => promptRecovery(reader, renderer, abandoned, autoRecover)
5318
5504
  });
5319
- if (!resumeId && !flags["no-recovery"]) {
5320
- const abandoned = await recoveryLock.checkAbandoned();
5321
- if (abandoned && abandoned.messageCount > 0) {
5322
- const choice = await promptRecovery(reader, renderer, abandoned, !!flags["recover"]);
5323
- if (choice === "resume") {
5324
- resumeId = abandoned.sessionId;
5325
- } else if (choice === "delete") {
5326
- await sessionStore.delete(abandoned.sessionId).catch(() => void 0);
5327
- await recoveryLock.clear();
5328
- } else {
5329
- await recoveryLock.clear();
5330
- }
5331
- } else if (abandoned) {
5332
- await sessionStore.delete(abandoned.sessionId).catch(() => void 0);
5333
- await recoveryLock.clear();
5334
- }
5335
- }
5336
- let session;
5337
- let restoredMessages = [];
5338
- if (resumeId) {
5339
- try {
5340
- const resumed = await sessionStore.resume(resumeId);
5341
- session = resumed.writer;
5342
- restoredMessages = resumed.data.messages;
5343
- renderer.writeInfo(
5344
- `Resumed session ${resumed.data.metadata.id} \u2014 ${restoredMessages.length} messages, ${resumed.data.usage.input + resumed.data.usage.output} tokens used previously.`
5345
- );
5346
- } catch (err) {
5347
- renderer.writeError(`Resume failed: ${err instanceof Error ? err.message : String(err)}`);
5348
- return 2;
5349
- }
5350
- } else {
5351
- session = await sessionStore.create({
5352
- id: "",
5353
- title: "",
5354
- model: config.model,
5355
- provider: config.provider
5356
- });
5357
- }
5505
+ const session = sessResult.session;
5358
5506
  sessionRef.current = session;
5359
- await recoveryLock.write(session.id).catch(() => void 0);
5360
- const attachments = new DefaultAttachmentStore({
5361
- spoolDir: path13.join(wpaths.projectSessions, session.id, "attachments")
5362
- });
5363
- const queueStore = new QueueStore({
5364
- dir: path13.join(wpaths.projectSessions, session.id)
5365
- });
5366
- const tokenCounter = container.resolve(TOKENS.TokenCounter);
5507
+ sessResult.restoredMessages;
5508
+ const context = sessResult.context;
5509
+ const attachments = sessResult.attachments;
5510
+ const recoveryLock = sessResult.recoveryLock;
5511
+ const queueStore = sessResult.queueStore;
5512
+ const planPath = sessResult.planPath;
5513
+ const detachTodosCheckpoint = sessResult.detachTodosCheckpoint;
5367
5514
  const stats = new SessionStats(events, tokenCounter);
5368
5515
  const errorRing = [];
5369
5516
  events.on("error", (e) => {
@@ -5373,150 +5520,15 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5373
5520
  errorRing.push({ ts: (/* @__PURE__ */ new Date()).toISOString(), phase: e.phase, code, message });
5374
5521
  if (errorRing.length > 5) errorRing.shift();
5375
5522
  });
5376
- const ctxSignal = new AbortController().signal;
5377
- const context = new Context({
5378
- systemPrompt,
5379
- provider,
5380
- session,
5381
- signal: ctxSignal,
5382
- tokenCounter,
5383
- cwd,
5384
- projectRoot,
5385
- model: config.model
5386
- });
5387
- if (restoredMessages.length > 0) {
5388
- context.state.replaceMessages(restoredMessages);
5389
- }
5390
- const todosCheckpointPath = path13.join(wpaths.projectSessions, `${session.id}.todos.json`);
5391
- if (resumeId) {
5392
- try {
5393
- const restoredTodos = await loadTodosCheckpoint(todosCheckpointPath);
5394
- if (restoredTodos && restoredTodos.length > 0) {
5395
- context.state.replaceTodos(restoredTodos);
5396
- renderer.writeInfo(
5397
- `Restored ${restoredTodos.length} todo${restoredTodos.length === 1 ? "" : "s"} from previous run.`
5398
- );
5399
- }
5400
- } catch {
5401
- }
5402
- }
5403
- attachTodosCheckpoint(
5404
- context.state,
5405
- todosCheckpointPath,
5406
- session.id
5407
- );
5408
- const planPath = path13.join(wpaths.projectSessions, `${session.id}.plan.json`);
5409
- context.state.setMeta("plan.path", planPath);
5410
- if (resumeId) {
5411
- try {
5412
- const fleetRoot2 = path13.join(wpaths.projectSessions, session.id);
5413
- const dirState = await loadDirectorState(path13.join(fleetRoot2, "director-state.json"));
5414
- if (dirState) {
5415
- const tCounts = {};
5416
- for (const t of dirState.tasks) {
5417
- tCounts[t.status] = (tCounts[t.status] ?? 0) + 1;
5418
- }
5419
- const summary = Object.entries(tCounts).map(([k, v]) => `${v} ${k}`).join(", ");
5420
- renderer.writeInfo(
5421
- `Prior fleet state: ${dirState.subagents.length} subagent${dirState.subagents.length === 1 ? "" : "s"}, tasks ${summary || "(none)"}.`
5422
- );
5423
- }
5424
- } catch {
5425
- }
5426
- try {
5427
- const plan = await loadPlan(planPath);
5428
- if (plan && plan.items.length > 0) {
5429
- const open = plan.items.filter((p) => p.status !== "done").length;
5430
- const done = plan.items.length - open;
5431
- renderer.writeInfo(
5432
- `Plan: ${plan.items.length} item${plan.items.length === 1 ? "" : "s"} (${open} open, ${done} done). Use /plan to review.`
5433
- );
5434
- }
5435
- } catch {
5436
- }
5437
- }
5438
- const pipelines = createDefaultPipelines();
5439
- const installBoundary = (p) => {
5440
- p.setErrorHandler((ev) => {
5441
- const fromPlugin = !!ev.owner && ev.owner !== "core";
5442
- logger.error(
5443
- `Pipeline middleware "${ev.middleware}" crashed (owner=${ev.owner ?? "unknown"}); ${fromPlugin ? "swallowed" : "rethrown"}`,
5444
- ev.err
5445
- );
5446
- events.emit("error", {
5447
- err: ev.err instanceof Error ? ev.err : new Error(String(ev.err)),
5448
- phase: `pipeline:${ev.middleware}`
5449
- });
5450
- return fromPlugin ? "swallow" : "rethrow";
5451
- });
5452
- };
5453
- installBoundary(pipelines.request);
5454
- installBoundary(pipelines.response);
5455
- installBoundary(pipelines.toolCall);
5456
- installBoundary(pipelines.userInput);
5457
- installBoundary(pipelines.assistantOutput);
5458
- installBoundary(pipelines.contextWindow);
5523
+ const pipelines = setupPipelines({ events, logger });
5459
5524
  const compactor = container.resolve(TOKENS.Compactor);
5460
- const resolvedCaps = await capabilitiesFor(modelsRegistry, config.provider, context.model).catch(
5461
- () => void 0
5462
- );
5463
- const effectiveMaxContext = config.context.effectiveMaxContext ?? resolvedCaps?.maxContext ?? provider.capabilities.maxContext;
5525
+ const effectiveMaxContext = await setupCompaction({ compactor, events, modelsRegistry, context, config, provider, pipelines });
5464
5526
  const updateSpinnerContext = () => {
5465
5527
  if (effectiveMaxContext > 0 && lastInputTokens > 0) {
5466
5528
  spinner.setContext({ used: lastInputTokens, max: effectiveMaxContext });
5467
- } else {
5468
- spinner.setContext(void 0);
5469
- }
5529
+ } else spinner.setContext(void 0);
5470
5530
  };
5471
- if (config.context.autoCompact !== false) {
5472
- const autoCompactor = new AutoCompactionMiddleware(
5473
- compactor,
5474
- effectiveMaxContext,
5475
- (ctx2) => {
5476
- const msgs = ctx2.messages;
5477
- let total = 0;
5478
- for (const m of msgs) {
5479
- if (typeof m.content === "string") total += Math.ceil(m.content.length / 4);
5480
- else if (Array.isArray(m.content)) {
5481
- for (const b of m.content) {
5482
- if (b.type === "text") total += Math.ceil(b.text.length / 4);
5483
- else if (b.type === "tool_use" || b.type === "tool_result") {
5484
- total += Math.ceil(JSON.stringify(b).length / 4);
5485
- }
5486
- }
5487
- }
5488
- }
5489
- return total;
5490
- },
5491
- {
5492
- warn: config.context.warnThreshold,
5493
- soft: config.context.softThreshold,
5494
- hard: config.context.hardThreshold
5495
- },
5496
- {
5497
- aggressiveOn: "soft",
5498
- failureMode: "throw_on_hard",
5499
- events
5500
- }
5501
- );
5502
- pipelines.contextWindow.use({
5503
- name: "AutoCompaction",
5504
- handler: autoCompactor.handler()
5505
- });
5506
- }
5507
- const agent = new Agent({
5508
- container,
5509
- tools: toolRegistry,
5510
- providers: providerRegistry,
5511
- events,
5512
- pipelines,
5513
- context,
5514
- maxIterations: config.tools.maxIterations,
5515
- iterationTimeoutMs: config.tools.iterationTimeoutMs,
5516
- executionStrategy: config.tools.defaultExecutionStrategy,
5517
- perIterationOutputCapBytes: config.tools.perIterationOutputCapBytes,
5518
- confirmAwaiter: makeConfirmAwaiter(reader)
5519
- });
5531
+ const agent = createAgent({ container, tools: toolRegistry, providers: providerRegistry, events, pipelines, context, config, confirmAwaiter: makeConfirmAwaiter(reader) });
5520
5532
  const mcpRegistry = new MCPRegistry({ toolRegistry, events, log: logger });
5521
5533
  if (config.features.mcp) {
5522
5534
  for (const cfg of Object.values(config.mcpServers ?? {})) {
@@ -5589,12 +5601,12 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5589
5601
  };
5590
5602
  const directorMode = flags["director"] === true;
5591
5603
  let director = null;
5592
- const fleetRoot = directorMode ? path13.join(wpaths.projectSessions, session.id) : void 0;
5593
- const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path13.join(fleetRoot, "fleet.json") : void 0;
5594
- const sharedScratchpadPath = directorMode ? path13.join(fleetRoot, "shared") : void 0;
5595
- const subagentSessionsRoot = directorMode ? path13.join(fleetRoot, "subagents") : void 0;
5596
- const stateCheckpointPath = directorMode ? path13.join(fleetRoot, "director-state.json") : void 0;
5597
- const fleetRootForPromotion = path13.join(wpaths.projectSessions, session.id);
5604
+ const fleetRoot = directorMode ? path14.join(wpaths.projectSessions, session.id) : void 0;
5605
+ const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path14.join(fleetRoot, "fleet.json") : void 0;
5606
+ const sharedScratchpadPath = directorMode ? path14.join(fleetRoot, "shared") : void 0;
5607
+ const subagentSessionsRoot = directorMode ? path14.join(fleetRoot, "subagents") : void 0;
5608
+ const stateCheckpointPath = directorMode ? path14.join(fleetRoot, "director-state.json") : void 0;
5609
+ const fleetRootForPromotion = path14.join(wpaths.projectSessions, session.id);
5598
5610
  const multiAgentHost = new MultiAgentHost(
5599
5611
  {
5600
5612
  container,
@@ -5746,7 +5758,7 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5746
5758
  return `Unknown fleet action: ${action}`;
5747
5759
  },
5748
5760
  onFleetLog: async (subagentId, mode) => {
5749
- const subagentsRoot = path13.join(fleetRootForPromotion, "subagents");
5761
+ const subagentsRoot = path14.join(fleetRootForPromotion, "subagents");
5750
5762
  let runDirs;
5751
5763
  try {
5752
5764
  runDirs = await fs3.readdir(subagentsRoot);
@@ -5755,7 +5767,7 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5755
5767
  }
5756
5768
  const found = [];
5757
5769
  for (const runId of runDirs) {
5758
- const runDir = path13.join(subagentsRoot, runId);
5770
+ const runDir = path14.join(subagentsRoot, runId);
5759
5771
  let files;
5760
5772
  try {
5761
5773
  files = await fs3.readdir(runDir);
@@ -5764,7 +5776,7 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5764
5776
  }
5765
5777
  for (const f of files) {
5766
5778
  if (!f.endsWith(".jsonl")) continue;
5767
- const full = path13.join(runDir, f);
5779
+ const full = path14.join(runDir, f);
5768
5780
  try {
5769
5781
  const stat2 = await fs3.stat(full);
5770
5782
  found.push({
@@ -5861,7 +5873,7 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5861
5873
  }
5862
5874
  const dir = await multiAgentHost.ensureDirector();
5863
5875
  if (!dir) return "Director is not available.";
5864
- const dirStatePath = path13.join(fleetRootForPromotion, "director-state.json");
5876
+ const dirStatePath = path14.join(fleetRootForPromotion, "director-state.json");
5865
5877
  const prior = await loadDirectorState(dirStatePath);
5866
5878
  if (!prior) {
5867
5879
  return "No prior director-state.json found \u2014 nothing to retry.";
@@ -5932,9 +5944,9 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5932
5944
  for (const tool of director2.tools(FLEET_ROSTER)) {
5933
5945
  toolRegistry.register(tool);
5934
5946
  }
5935
- const mp = path13.join(fleetRootForPromotion, "fleet.json");
5936
- const sp = path13.join(fleetRootForPromotion, "shared");
5937
- const ss = path13.join(fleetRootForPromotion, "subagents");
5947
+ const mp = path14.join(fleetRootForPromotion, "fleet.json");
5948
+ const sp = path14.join(fleetRootForPromotion, "shared");
5949
+ const ss = path14.join(fleetRootForPromotion, "subagents");
5938
5950
  const lines = [
5939
5951
  `${color.green("\u2713")} Promoted to director mode.`,
5940
5952
  ` Roster: ${Object.keys(FLEET_ROSTER).join(", ")}`,
@@ -5977,6 +5989,7 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5977
5989
  onStats: () => stats.format()
5978
5990
  });
5979
5991
  for (const cmd of slashCmds) slashRegistry.register(cmd);
5992
+ const savedProviderCfg = config.providers?.[config.provider];
5980
5993
  return execute({
5981
5994
  agent,
5982
5995
  events,
@@ -5998,6 +6011,7 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5998
6011
  queueStore,
5999
6012
  context,
6000
6013
  stats,
6014
+ detachTodosCheckpoint,
6001
6015
  savedProviderCfg,
6002
6016
  resolvedProvider: resolvedProvider ?? void 0,
6003
6017
  getPickableProviders: () => buildPickableProviders(modelsRegistry, config),