crosscheck-mcp 0.2.30 → 0.2.31

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/embed.cjs CHANGED
@@ -54,11 +54,35 @@ module.exports = __toCommonJS(embed_exports);
54
54
  var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
55
55
  var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
56
56
 
57
- // src/entrypoints/embed.ts
58
- var import_node_async_hooks3 = require("async_hooks");
59
- var import_node_fs16 = require("fs");
60
- var import_node_path15 = __toESM(require("path"), 1);
61
- var import_node_url2 = require("url");
57
+ // src/core/retarget.ts
58
+ function retargetProvider(p, newModel, fallbackModels) {
59
+ const chain = fallbackModels && fallbackModels.length > 0 ? fallbackModels : void 0;
60
+ if (p.model === newModel && chain === void 0) return p;
61
+ return {
62
+ name: p.name,
63
+ model: newModel,
64
+ ...chain ? { fallbackModels: chain } : {},
65
+ send: (args) => p.send({ ...args, modelOverride: newModel })
66
+ };
67
+ }
68
+ async function loadProviderWeights(storage, names) {
69
+ const out = {};
70
+ for (const raw of names) {
71
+ const name = raw.toLowerCase();
72
+ if (name in out) continue;
73
+ const row = await storage.getProviderStats(name);
74
+ if (!row) {
75
+ out[name] = 0.5;
76
+ continue;
77
+ }
78
+ const total = row.wins + row.losses + row.abstains;
79
+ out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
80
+ }
81
+ return out;
82
+ }
83
+
84
+ // src/core/super-mode.ts
85
+ var import_node_async_hooks = require("async_hooks");
62
86
 
63
87
  // src/core/pricing.ts
64
88
  var import_node_fs = require("fs");
@@ -1348,12 +1372,238 @@ function makeGeminiProvider(model, apiKey, opts, fallbackModels = []) {
1348
1372
  };
1349
1373
  }
1350
1374
 
1375
+ // src/core/reasoning-upgrade.ts
1376
+ var UPGRADE_MODEL = "claude-fable-5";
1377
+ var UPGRADE_LABEL = "Fable 5";
1378
+ function isUpgradeRequested(args) {
1379
+ if (args["upgrade"] === true) return true;
1380
+ const reasoning = args["reasoning"];
1381
+ if (typeof reasoning === "string" && reasoning.trim().toLowerCase() === "max") {
1382
+ return true;
1383
+ }
1384
+ const model = args["model"];
1385
+ if (typeof model === "string") {
1386
+ const m = model.trim().toLowerCase();
1387
+ if (m === "fable5" || m === "fable-5" || m === "fable" || m === UPGRADE_MODEL) {
1388
+ return true;
1389
+ }
1390
+ }
1391
+ return false;
1392
+ }
1393
+ var HEAVY_TERMS = [
1394
+ /\bprov(e|ing|en)\b|\bproof\b|\btheorem\b|\bderiv(e|ation)\b/i,
1395
+ /\barchitect(ure|ing)?\b|\bdesign\s+(a|the|an)\b|\bhigh[-\s]?level\s+design\b/i,
1396
+ /\btrade[-\s]?offs?\b|\bpros?\s+and\s+cons?\b|\bweigh\b/i,
1397
+ /\boptimi[sz]e\b|\bcomplexity\b|\bO\(|\balgorithm(ic)?\b/i,
1398
+ /\brace\s+condition\b|\bconcurren(t|cy)\b|\bdeadlock\b|\bdistributed\b|\bconsensus\b/i,
1399
+ /\broot[-\s]?cause\b|\bdebug(ging)?\b|\bwhy\s+(does|is|would|might)\b/i,
1400
+ /\bsecurity\b|\bthreat\s+model\b|\bexploit\b|\bvulnerabilit(y|ies)\b/i,
1401
+ /\bmigrat(e|ion)\b|\brefactor\b|\bre[-\s]?architect\b/i,
1402
+ /\binvariant\b|\bcorrectness\b|\bedge\s+cases?\b|\bformal(ly)?\b/i,
1403
+ /\bstrateg(y|ic|ies)\b|\bplan\s+(the|a|an|out)\b|\broadmap\b/i,
1404
+ /\breason\s+(through|about)\b|\bstep[-\s]?by[-\s]?step\b|\bthink\s+deeply\b/i,
1405
+ /\bnp[-\s]?(hard|complete)\b|\bgraph\b|\bstate\s+machine\b|\bconstraint\b/i
1406
+ ];
1407
+ var DIFFICULTY_TERMS = /\b(hard|tricky|subtle|complex|complicated|non[-\s]?trivial|thorny|gnarly|difficult|deep)\b/i;
1408
+ function assessReasoning(text) {
1409
+ const s = typeof text === "string" ? text : "";
1410
+ const signals = [];
1411
+ let score = 0;
1412
+ let termHits = 0;
1413
+ for (const re of HEAVY_TERMS) {
1414
+ if (re.test(s)) termHits += 1;
1415
+ }
1416
+ if (termHits > 0) {
1417
+ const add = Math.min(termHits, 4);
1418
+ score += add;
1419
+ signals.push(`${termHits} reasoning cue${termHits === 1 ? "" : "s"}`);
1420
+ }
1421
+ if (DIFFICULTY_TERMS.test(s)) {
1422
+ score += 1;
1423
+ signals.push("explicit difficulty marker");
1424
+ }
1425
+ const words = s.split(/\s+/).filter(Boolean).length;
1426
+ if (words >= 120) {
1427
+ score += 2;
1428
+ signals.push("long, detailed prompt");
1429
+ } else if (words >= 50) {
1430
+ score += 1;
1431
+ signals.push("substantial prompt");
1432
+ }
1433
+ const constraints = (s.match(/\b(must|should|need to|ensure|require[ds]?|constraint)\b/gi) ?? []).length + (s.match(/(^|\n)\s*(?:[-*]|\d+[.)])\s+/g) ?? []).length;
1434
+ if (constraints >= 3) {
1435
+ score += 1;
1436
+ signals.push(`${constraints} constraints/steps`);
1437
+ }
1438
+ const heavy = score >= 3;
1439
+ const reason = heavy ? `Task looks reasoning-heavy (${signals.join(", ") || "score " + score}).` : `Task looks routine (score ${score}).`;
1440
+ return { heavy, score, reason, signals };
1441
+ }
1442
+ function buildSuggestedUpgrade(toolName, assessment) {
1443
+ return {
1444
+ model: UPGRADE_MODEL,
1445
+ label: UPGRADE_LABEL,
1446
+ reason: assessment.reason,
1447
+ how: `re-run \`${toolName}\` with reasoning:"max" (or upgrade:true) to route the lead reasoning seat to ${UPGRADE_LABEL}, and \u2014 when an OpenAI key is configured \u2014 add an OpenAI second-opinion pass that ${UPGRADE_LABEL} weighs before finalizing (the result names which model ran). Other panel seats stay on their normal models.`
1448
+ };
1449
+ }
1450
+
1451
+ // src/core/super-mode.ts
1452
+ var SUPER_MODELS = {
1453
+ // Fable 5.1 verified present on the account; 5 as the fallback for accounts
1454
+ // that haven't been granted 5.1 yet.
1455
+ anthropic: ["claude-fable-5-1", UPGRADE_MODEL],
1456
+ // Astra is live (verified against the account's model list 2026-09-23).
1457
+ // The fallback is named explicitly rather than reusing CO_REASON_MODEL:
1458
+ // that constant means "OpenAI's top reasoning model" for the max-reasoning
1459
+ // co-reasoner, which is a different question from "what should super fall
1460
+ // back to". Sharing the symbol silently coupled two unrelated decisions.
1461
+ openai: ["gpt-6-astra", "gpt-6-sol"],
1462
+ // Explicitly left as-is per product decision. Note grok-4-latest is not in
1463
+ // xAI's published model list, but it resolves in practice — real panel calls
1464
+ // on it succeeded as recently as 2026-08-30 — so it is an unlisted alias
1465
+ // rather than a dead id.
1466
+ xai: [DEFAULT_MODELS["xai"]],
1467
+ // 3.8 Flash is now BOTH the everyday default and the super pick for Gemini,
1468
+ // so this can no longer borrow DEFAULT_MODELS as its fallback — that would
1469
+ // repeat the same model and retry one that just failed. Named explicitly.
1470
+ gemini: ["gemini-3.8-flash", "gemini-3.1-pro-preview"],
1471
+ kimi: [DEFAULT_MODELS["kimi"]],
1472
+ qwen: [DEFAULT_MODELS["qwen"]]
1473
+ };
1474
+ var SUPER_ENV_VARS = {
1475
+ anthropic: "ANTHROPIC_SUPER_MODEL",
1476
+ openai: "OPENAI_SUPER_MODEL",
1477
+ xai: "XAI_SUPER_MODEL",
1478
+ gemini: "GEMINI_SUPER_MODEL",
1479
+ kimi: "KIMI_SUPER_MODEL",
1480
+ qwen: "QWEN_SUPER_MODEL",
1481
+ mistral: "MISTRAL_SUPER_MODEL",
1482
+ groq: "GROQ_SUPER_MODEL",
1483
+ deepseek: "DEEPSEEK_SUPER_MODEL"
1484
+ };
1485
+ function parseChain(v) {
1486
+ if (!v) return [];
1487
+ const out = [];
1488
+ for (const raw of v.split(",")) {
1489
+ const m = raw.trim();
1490
+ if (m && !out.includes(m)) out.push(m);
1491
+ }
1492
+ return out;
1493
+ }
1494
+ var superEnvStore = new import_node_async_hooks.AsyncLocalStorage();
1495
+ function withSuperEnv(env, fn) {
1496
+ return superEnvStore.run(env, fn);
1497
+ }
1498
+ var currentEnv = () => superEnvStore.getStore() ?? process.env;
1499
+ function superChainFor(provider, env = currentEnv()) {
1500
+ const varName = SUPER_ENV_VARS[provider];
1501
+ const user = varName ? parseChain(env[varName]) : [];
1502
+ if (user.length > 0) {
1503
+ const fallbacks = varName ? parseChain(env[`${varName}_FALLBACKS`]) : [];
1504
+ return [...user, ...fallbacks.filter((m) => !user.includes(m))];
1505
+ }
1506
+ return SUPER_MODELS[provider] ?? [];
1507
+ }
1508
+ function superPrimary(provider, env = currentEnv()) {
1509
+ return superChainFor(provider, env)[0];
1510
+ }
1511
+ var SUPER_PROVIDER_NAMES = Object.keys(SUPER_MODELS);
1512
+ function isSuperRequested(args) {
1513
+ return args["super"] === true;
1514
+ }
1515
+ function retargetForSuper(p, env = currentEnv()) {
1516
+ const chain = superChainFor(p.name, env);
1517
+ if (chain.length === 0) return p;
1518
+ const [primary, ...fallbacks] = chain;
1519
+ return retargetProvider(p, primary, fallbacks);
1520
+ }
1521
+ var SUPER_BUDGET_MULTIPLIER = 3;
1522
+ var SUPER_BUDGET_CEILING = 16e3;
1523
+ function superBudget(base) {
1524
+ return Math.min(base * SUPER_BUDGET_MULTIPLIER, SUPER_BUDGET_CEILING);
1525
+ }
1526
+
1527
+ // src/core/moderator.ts
1528
+ var DEFAULT_MODERATOR = "anthropic";
1529
+ var PROVIDER_ALIASES = {
1530
+ grok: "xai",
1531
+ claude: "anthropic",
1532
+ google: "gemini",
1533
+ chatgpt: "openai",
1534
+ gpt: "openai",
1535
+ moonshot: "kimi"
1536
+ };
1537
+ function parseModeratorSpec(raw) {
1538
+ if (typeof raw !== "string") return null;
1539
+ const s = raw.trim();
1540
+ if (!s) return null;
1541
+ const i = s.indexOf(":");
1542
+ const providerPart = (i < 0 ? s : s.slice(0, i)).trim().toLowerCase();
1543
+ const modelPart = i < 0 ? "" : s.slice(i + 1).trim();
1544
+ if (!providerPart) return null;
1545
+ return {
1546
+ provider: PROVIDER_ALIASES[providerPart] ?? providerPart,
1547
+ model: modelPart || null
1548
+ };
1549
+ }
1550
+ function moderatorProviderName(raw, fallback = DEFAULT_MODERATOR) {
1551
+ return (parseModeratorSpec(raw) ?? parseModeratorSpec(fallback))?.provider ?? DEFAULT_MODERATOR;
1552
+ }
1553
+ function pickModeratorSpec(...candidates) {
1554
+ for (const c of candidates) {
1555
+ const s = parseModeratorSpec(c);
1556
+ if (s) return s;
1557
+ }
1558
+ return { provider: DEFAULT_MODERATOR, model: null };
1559
+ }
1560
+ function resolveModerator(providers, ...candidates) {
1561
+ const spec = pickModeratorSpec(...candidates);
1562
+ const base = providers[spec.provider];
1563
+ if (!base) return { provider: void 0, spec, pinned: false };
1564
+ if (!spec.model) return { provider: base, spec, pinned: false };
1565
+ return { provider: retargetProvider(base, spec.model), spec, pinned: true };
1566
+ }
1567
+ function synthesisSeat(o) {
1568
+ const { moderator, pinned, superRequested, upgradeRequested } = o;
1569
+ const wantsMore = superRequested || upgradeRequested;
1570
+ if (!wantsMore) return { provider: moderator, upgraded: false, label: null, skipReason: null };
1571
+ if (pinned) {
1572
+ return {
1573
+ provider: moderator,
1574
+ upgraded: false,
1575
+ label: null,
1576
+ skipReason: `the moderator is pinned to ${moderator.name}:${moderator.model}; a pinned model is used as-is.`
1577
+ };
1578
+ }
1579
+ if (moderator.name === "anthropic" && !superRequested) {
1580
+ return { provider: retargetProvider(moderator, UPGRADE_MODEL), upgraded: true, label: UPGRADE_LABEL, skipReason: null };
1581
+ }
1582
+ if (superChainFor(moderator.name).length === 0) {
1583
+ return {
1584
+ provider: moderator,
1585
+ upgraded: false,
1586
+ label: null,
1587
+ skipReason: `no flagship model is known for ${moderator.name}; the moderator ran at ${moderator.model}.`
1588
+ };
1589
+ }
1590
+ const seat = retargetForSuper(moderator);
1591
+ const label = moderator.name === "anthropic" ? UPGRADE_LABEL : seat.model;
1592
+ return { provider: seat, upgraded: true, label, skipReason: null };
1593
+ }
1594
+
1595
+ // src/entrypoints/embed.ts
1596
+ var import_node_async_hooks3 = require("async_hooks");
1597
+ var import_node_fs16 = require("fs");
1598
+ var import_node_path15 = __toESM(require("path"), 1);
1599
+ var import_node_url2 = require("url");
1600
+
1351
1601
  // src/tools/index.ts
1352
1602
  var import_zod = require("zod");
1353
1603
 
1354
1604
  // src/server-meta.ts
1355
1605
  var SERVER_NAME = "crosscheck-agent";
1356
- var SERVER_VERSION = true ? "0.2.30" : "0.0.0-dev";
1606
+ var SERVER_VERSION = true ? "0.2.31" : "0.0.0-dev";
1357
1607
 
1358
1608
  // src/tools/audit.ts
1359
1609
  var import_node_fs4 = require("fs");
@@ -2036,33 +2286,6 @@ function operatorCeiling(purpose, provider) {
2036
2286
  return null;
2037
2287
  }
2038
2288
 
2039
- // src/core/retarget.ts
2040
- function retargetProvider(p, newModel, fallbackModels) {
2041
- const chain = fallbackModels && fallbackModels.length > 0 ? fallbackModels : void 0;
2042
- if (p.model === newModel && chain === void 0) return p;
2043
- return {
2044
- name: p.name,
2045
- model: newModel,
2046
- ...chain ? { fallbackModels: chain } : {},
2047
- send: (args) => p.send({ ...args, modelOverride: newModel })
2048
- };
2049
- }
2050
- async function loadProviderWeights(storage, names) {
2051
- const out = {};
2052
- for (const raw of names) {
2053
- const name = raw.toLowerCase();
2054
- if (name in out) continue;
2055
- const row = await storage.getProviderStats(name);
2056
- if (!row) {
2057
- out[name] = 0.5;
2058
- continue;
2059
- }
2060
- const total = row.wins + row.losses + row.abstains;
2061
- out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
2062
- }
2063
- return out;
2064
- }
2065
-
2066
2289
  // src/core/model-fallback.ts
2067
2290
  async function sendWithModelFallback(provider, args) {
2068
2291
  const chain = [provider.model, ...provider.fallbackModels ?? []];
@@ -3129,82 +3352,6 @@ function selectForDifficulty(args) {
3129
3352
  };
3130
3353
  }
3131
3354
 
3132
- // src/core/reasoning-upgrade.ts
3133
- var UPGRADE_MODEL = "claude-fable-5";
3134
- var UPGRADE_LABEL = "Fable 5";
3135
- function isUpgradeRequested(args) {
3136
- if (args["upgrade"] === true) return true;
3137
- const reasoning = args["reasoning"];
3138
- if (typeof reasoning === "string" && reasoning.trim().toLowerCase() === "max") {
3139
- return true;
3140
- }
3141
- const model = args["model"];
3142
- if (typeof model === "string") {
3143
- const m = model.trim().toLowerCase();
3144
- if (m === "fable5" || m === "fable-5" || m === "fable" || m === UPGRADE_MODEL) {
3145
- return true;
3146
- }
3147
- }
3148
- return false;
3149
- }
3150
- var HEAVY_TERMS = [
3151
- /\bprov(e|ing|en)\b|\bproof\b|\btheorem\b|\bderiv(e|ation)\b/i,
3152
- /\barchitect(ure|ing)?\b|\bdesign\s+(a|the|an)\b|\bhigh[-\s]?level\s+design\b/i,
3153
- /\btrade[-\s]?offs?\b|\bpros?\s+and\s+cons?\b|\bweigh\b/i,
3154
- /\boptimi[sz]e\b|\bcomplexity\b|\bO\(|\balgorithm(ic)?\b/i,
3155
- /\brace\s+condition\b|\bconcurren(t|cy)\b|\bdeadlock\b|\bdistributed\b|\bconsensus\b/i,
3156
- /\broot[-\s]?cause\b|\bdebug(ging)?\b|\bwhy\s+(does|is|would|might)\b/i,
3157
- /\bsecurity\b|\bthreat\s+model\b|\bexploit\b|\bvulnerabilit(y|ies)\b/i,
3158
- /\bmigrat(e|ion)\b|\brefactor\b|\bre[-\s]?architect\b/i,
3159
- /\binvariant\b|\bcorrectness\b|\bedge\s+cases?\b|\bformal(ly)?\b/i,
3160
- /\bstrateg(y|ic|ies)\b|\bplan\s+(the|a|an|out)\b|\broadmap\b/i,
3161
- /\breason\s+(through|about)\b|\bstep[-\s]?by[-\s]?step\b|\bthink\s+deeply\b/i,
3162
- /\bnp[-\s]?(hard|complete)\b|\bgraph\b|\bstate\s+machine\b|\bconstraint\b/i
3163
- ];
3164
- var DIFFICULTY_TERMS = /\b(hard|tricky|subtle|complex|complicated|non[-\s]?trivial|thorny|gnarly|difficult|deep)\b/i;
3165
- function assessReasoning(text) {
3166
- const s = typeof text === "string" ? text : "";
3167
- const signals = [];
3168
- let score = 0;
3169
- let termHits = 0;
3170
- for (const re of HEAVY_TERMS) {
3171
- if (re.test(s)) termHits += 1;
3172
- }
3173
- if (termHits > 0) {
3174
- const add = Math.min(termHits, 4);
3175
- score += add;
3176
- signals.push(`${termHits} reasoning cue${termHits === 1 ? "" : "s"}`);
3177
- }
3178
- if (DIFFICULTY_TERMS.test(s)) {
3179
- score += 1;
3180
- signals.push("explicit difficulty marker");
3181
- }
3182
- const words = s.split(/\s+/).filter(Boolean).length;
3183
- if (words >= 120) {
3184
- score += 2;
3185
- signals.push("long, detailed prompt");
3186
- } else if (words >= 50) {
3187
- score += 1;
3188
- signals.push("substantial prompt");
3189
- }
3190
- const constraints = (s.match(/\b(must|should|need to|ensure|require[ds]?|constraint)\b/gi) ?? []).length + (s.match(/(^|\n)\s*(?:[-*]|\d+[.)])\s+/g) ?? []).length;
3191
- if (constraints >= 3) {
3192
- score += 1;
3193
- signals.push(`${constraints} constraints/steps`);
3194
- }
3195
- const heavy = score >= 3;
3196
- const reason = heavy ? `Task looks reasoning-heavy (${signals.join(", ") || "score " + score}).` : `Task looks routine (score ${score}).`;
3197
- return { heavy, score, reason, signals };
3198
- }
3199
- function buildSuggestedUpgrade(toolName, assessment) {
3200
- return {
3201
- model: UPGRADE_MODEL,
3202
- label: UPGRADE_LABEL,
3203
- reason: assessment.reason,
3204
- how: `re-run \`${toolName}\` with reasoning:"max" (or upgrade:true) to route the lead reasoning seat to ${UPGRADE_LABEL}, and \u2014 when an OpenAI key is configured \u2014 add an OpenAI second-opinion pass that ${UPGRADE_LABEL} weighs before finalizing (the result names which model ran). Other panel seats stay on their normal models.`
3205
- };
3206
- }
3207
-
3208
3355
  // src/tools/audit.ts
3209
3356
  var AUDIT_OBVIOUS_FAILURE_HIGH = 0.3;
3210
3357
  var AUDIT_OBVIOUS_FAILURE_MED = 0.2;
@@ -3502,9 +3649,14 @@ ${rubricText}`;
3502
3649
  );
3503
3650
  }
3504
3651
  const upgradeRequested = isUpgradeRequested(args);
3505
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
3506
- const auditorForCall = upgraded ? retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL) : auditor;
3507
- const coProvider = pickCoReasoner(opts.providers, upgradeRequested);
3652
+ const seat = synthesisSeat({ moderator: auditor, pinned: false, superRequested: false, upgradeRequested });
3653
+ const upgraded = seat.upgraded;
3654
+ const auditorForCall = seat.provider;
3655
+ const coProvider = pickCoReasoner(
3656
+ opts.providers,
3657
+ upgradeRequested,
3658
+ auditorForCall.name === "openai" ? auditorForCall.model : void 0
3659
+ );
3508
3660
  const coReason = await coReasonThenInject({
3509
3661
  provider: coProvider,
3510
3662
  messages: msgs,
@@ -3587,15 +3739,16 @@ ${rubricText}`;
3587
3739
  if (upgraded) {
3588
3740
  result["reasoning_upgrade"] = {
3589
3741
  applied: true,
3590
- model: UPGRADE_MODEL,
3591
- label: UPGRADE_LABEL,
3742
+ provider: auditorForCall.name,
3743
+ model: auditorForCall.model,
3744
+ label: seat.label ?? auditorForCall.model,
3592
3745
  seat: "auditor",
3593
3746
  ...coReasoned ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
3594
3747
  };
3595
3748
  } else if (upgradeRequested) {
3596
3749
  result["reasoning_upgrade"] = {
3597
3750
  applied: false,
3598
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
3751
+ reason: seat.skipReason ?? "the auditor couldn't be upgraded."
3599
3752
  };
3600
3753
  } else {
3601
3754
  const assessment = assessReasoning(
@@ -3678,7 +3831,7 @@ function pickAuditor(providers, exclude, explicit, moderatorName, allowlist, che
3678
3831
  }
3679
3832
  }
3680
3833
  }
3681
- const mod = providers[moderatorName.toLowerCase()];
3834
+ const mod = resolveModerator(providers, moderatorName).provider;
3682
3835
  if (mod !== void 0 && !exclude.has(mod.name) && (allowlist === null || allowlist.includes(mod.name))) {
3683
3836
  return { auditor: mod, reason: null };
3684
3837
  }
@@ -3997,9 +4150,7 @@ function pickPanelJudge(providers, moderatorName, pricing) {
3997
4150
  return entry.model && entry.model !== base.model ? retargetProvider(base, entry.model) : base;
3998
4151
  }
3999
4152
  }
4000
- const mod = providers[moderatorName.toLowerCase()];
4001
- if (mod !== void 0) return mod;
4002
- return null;
4153
+ return resolveModerator(providers, moderatorName).provider ?? null;
4003
4154
  }
4004
4155
  var AGREEMENT_CHECK_SCHEMA = {
4005
4156
  type: "object",
@@ -4316,83 +4467,6 @@ async function pickAutoPanel(storage, purpose, n, providers, allowlist) {
4316
4467
  return { providers: ranked.slice(0, Math.max(1, n)), coldStart: true };
4317
4468
  }
4318
4469
 
4319
- // src/core/super-mode.ts
4320
- var import_node_async_hooks = require("async_hooks");
4321
- var SUPER_MODELS = {
4322
- // Fable 5.1 verified present on the account; 5 as the fallback for accounts
4323
- // that haven't been granted 5.1 yet.
4324
- anthropic: ["claude-fable-5-1", UPGRADE_MODEL],
4325
- // Astra is live (verified against the account's model list 2026-09-23).
4326
- // The fallback is named explicitly rather than reusing CO_REASON_MODEL:
4327
- // that constant means "OpenAI's top reasoning model" for the max-reasoning
4328
- // co-reasoner, which is a different question from "what should super fall
4329
- // back to". Sharing the symbol silently coupled two unrelated decisions.
4330
- openai: ["gpt-6-astra", "gpt-6-sol"],
4331
- // Explicitly left as-is per product decision. Note grok-4-latest is not in
4332
- // xAI's published model list, but it resolves in practice — real panel calls
4333
- // on it succeeded as recently as 2026-08-30 — so it is an unlisted alias
4334
- // rather than a dead id.
4335
- xai: [DEFAULT_MODELS["xai"]],
4336
- // 3.8 Flash is now BOTH the everyday default and the super pick for Gemini,
4337
- // so this can no longer borrow DEFAULT_MODELS as its fallback — that would
4338
- // repeat the same model and retry one that just failed. Named explicitly.
4339
- gemini: ["gemini-3.8-flash", "gemini-3.1-pro-preview"],
4340
- kimi: [DEFAULT_MODELS["kimi"]],
4341
- qwen: [DEFAULT_MODELS["qwen"]]
4342
- };
4343
- var SUPER_ENV_VARS = {
4344
- anthropic: "ANTHROPIC_SUPER_MODEL",
4345
- openai: "OPENAI_SUPER_MODEL",
4346
- xai: "XAI_SUPER_MODEL",
4347
- gemini: "GEMINI_SUPER_MODEL",
4348
- kimi: "KIMI_SUPER_MODEL",
4349
- qwen: "QWEN_SUPER_MODEL",
4350
- mistral: "MISTRAL_SUPER_MODEL",
4351
- groq: "GROQ_SUPER_MODEL",
4352
- deepseek: "DEEPSEEK_SUPER_MODEL"
4353
- };
4354
- function parseChain(v) {
4355
- if (!v) return [];
4356
- const out = [];
4357
- for (const raw of v.split(",")) {
4358
- const m = raw.trim();
4359
- if (m && !out.includes(m)) out.push(m);
4360
- }
4361
- return out;
4362
- }
4363
- var superEnvStore = new import_node_async_hooks.AsyncLocalStorage();
4364
- function withSuperEnv(env, fn) {
4365
- return superEnvStore.run(env, fn);
4366
- }
4367
- var currentEnv = () => superEnvStore.getStore() ?? process.env;
4368
- function superChainFor(provider, env = currentEnv()) {
4369
- const varName = SUPER_ENV_VARS[provider];
4370
- const user = varName ? parseChain(env[varName]) : [];
4371
- if (user.length > 0) {
4372
- const fallbacks = varName ? parseChain(env[`${varName}_FALLBACKS`]) : [];
4373
- return [...user, ...fallbacks.filter((m) => !user.includes(m))];
4374
- }
4375
- return SUPER_MODELS[provider] ?? [];
4376
- }
4377
- function superPrimary(provider, env = currentEnv()) {
4378
- return superChainFor(provider, env)[0];
4379
- }
4380
- var SUPER_PROVIDER_NAMES = Object.keys(SUPER_MODELS);
4381
- function isSuperRequested(args) {
4382
- return args["super"] === true;
4383
- }
4384
- function retargetForSuper(p, env = currentEnv()) {
4385
- const chain = superChainFor(p.name, env);
4386
- if (chain.length === 0) return p;
4387
- const [primary, ...fallbacks] = chain;
4388
- return retargetProvider(p, primary, fallbacks);
4389
- }
4390
- var SUPER_BUDGET_MULTIPLIER = 3;
4391
- var SUPER_BUDGET_CEILING = 16e3;
4392
- function superBudget(base) {
4393
- return Math.min(base * SUPER_BUDGET_MULTIPLIER, SUPER_BUDGET_CEILING);
4394
- }
4395
-
4396
4470
  // src/core/session-memory.ts
4397
4471
  var SESSION_MEMORY_DEFAULT_LIMIT = 50;
4398
4472
  var SESSION_MEMORY_INJECT_BUDGET_CHARS = 4e3;
@@ -6756,8 +6830,9 @@ async function runOrchestrate(args, opts) {
6756
6830
  blocked
6757
6831
  };
6758
6832
  }
6759
- const moderatorName = typeof args["moderator"] === "string" && args["moderator"] ? args["moderator"] : opts.moderator ?? "anthropic";
6760
- const moderator = opts.providers[moderatorName.toLowerCase()] ?? selected[0];
6833
+ const resolvedModerator = resolveModerator(opts.providers, args["moderator"], opts.moderator);
6834
+ const moderator = resolvedModerator.provider ?? selected[0];
6835
+ const moderatorChosen = resolvedModerator.provider !== void 0 && (resolvedModerator.pinned || parseModeratorSpec(args["moderator"]) !== null);
6761
6836
  const failFast = boolArg2(args["fail_fast"], false);
6762
6837
  const planOnly = boolArg2(args["plan_only"], false);
6763
6838
  const cheapMode = boolArg2(args["cheap_mode"], opts.ctx?.cheap_mode ?? false);
@@ -7241,11 +7316,17 @@ ${recombineSys}` : recombineSys },
7241
7316
  let synthProvider = moderator;
7242
7317
  let synthModel = null;
7243
7318
  const upgradeRequested = isUpgradeRequested(args);
7244
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
7319
+ const seat = synthesisSeat({
7320
+ moderator,
7321
+ pinned: resolvedModerator.provider !== void 0 && resolvedModerator.pinned,
7322
+ superRequested: false,
7323
+ upgradeRequested
7324
+ });
7325
+ const upgraded = seat.upgraded;
7245
7326
  if (upgraded) {
7246
- synthProvider = retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL);
7247
- synthModel = `anthropic/${UPGRADE_MODEL}`;
7248
- } else if (cheapMode && opts.pricing) {
7327
+ synthProvider = seat.provider;
7328
+ synthModel = `${seat.provider.name}/${seat.provider.model}`;
7329
+ } else if (cheapMode && opts.pricing && !moderatorChosen) {
7249
7330
  const pick = selectForDifficulty({
7250
7331
  pricing: opts.pricing,
7251
7332
  tier: "med",
@@ -7271,7 +7352,11 @@ ${recombineSys}` : recombineSys },
7271
7352
  provider: synthProvider.name
7272
7353
  } }
7273
7354
  );
7274
- const coProvider = pickCoReasoner(opts.providers, upgradeRequested);
7355
+ const coProvider = pickCoReasoner(
7356
+ opts.providers,
7357
+ upgradeRequested,
7358
+ synthProvider.name === "openai" ? synthProvider.model : void 0
7359
+ );
7275
7360
  const coReason = await coReasonThenInject({
7276
7361
  provider: coProvider,
7277
7362
  messages: recMsgs,
@@ -7321,7 +7406,8 @@ ${recombineSys}` : recombineSys },
7321
7406
  missing,
7322
7407
  partial,
7323
7408
  fail_fast: failFast,
7324
- cheap_mode: cheapMode
7409
+ cheap_mode: cheapMode,
7410
+ moderator: { provider: moderator.name, model: moderator.model }
7325
7411
  };
7326
7412
  if (personaMeta.used !== null) result["persona"] = personaMeta;
7327
7413
  if (synthModel) result["synth_model"] = synthModel;
@@ -7329,15 +7415,16 @@ ${recombineSys}` : recombineSys },
7329
7415
  if (upgraded) {
7330
7416
  result["reasoning_upgrade"] = {
7331
7417
  applied: true,
7332
- model: UPGRADE_MODEL,
7333
- label: UPGRADE_LABEL,
7418
+ provider: seat.provider.name,
7419
+ model: seat.provider.model,
7420
+ label: seat.label ?? seat.provider.model,
7334
7421
  seat: "recombine/synthesis",
7335
7422
  ...coReasoned ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
7336
7423
  };
7337
7424
  } else if (upgradeRequested) {
7338
7425
  result["reasoning_upgrade"] = {
7339
7426
  applied: false,
7340
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
7427
+ reason: seat.skipReason ?? "the moderator couldn't be upgraded."
7341
7428
  };
7342
7429
  } else if (goal) {
7343
7430
  const assessment = assessReasoning(goal);
@@ -8519,13 +8606,24 @@ async function runCoordinate(args, opts) {
8519
8606
  };
8520
8607
  }
8521
8608
  const proposerName = typeof args["proposer"] === "string" && args["proposer"] ? args["proposer"] : selected[0].name;
8522
- const synthName = typeof args["synthesizer"] === "string" && args["synthesizer"] ? args["synthesizer"] : typeof args["moderator"] === "string" && args["moderator"] ? args["moderator"] : opts.moderator ?? "anthropic";
8609
+ const resolvedSynth = resolveModerator(
8610
+ opts.providers,
8611
+ args["synthesizer"],
8612
+ args["moderator"],
8613
+ opts.moderator
8614
+ );
8523
8615
  const proposer = opts.providers[proposerName.toLowerCase()] ?? selected[0];
8524
- const synth = opts.providers[synthName.toLowerCase()] ?? proposer;
8616
+ const synth = resolvedSynth.provider ?? proposer;
8525
8617
  const proposerForCall = superRequested ? retargetForSuper(proposer) : proposer;
8526
8618
  const upgradeRequested = isUpgradeRequested(args) || superRequested;
8527
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
8528
- const synthForCall = superRequested && opts.providers["anthropic"] ? retargetForSuper(opts.providers["anthropic"]) : upgraded ? retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL) : synth;
8619
+ const seat = synthesisSeat({
8620
+ moderator: synth,
8621
+ pinned: resolvedSynth.provider !== void 0 && resolvedSynth.pinned,
8622
+ superRequested,
8623
+ upgradeRequested
8624
+ });
8625
+ const upgraded = seat.upgraded;
8626
+ const synthForCall = seat.provider;
8529
8627
  let critics = [];
8530
8628
  if (Array.isArray(args["critics"])) {
8531
8629
  for (const n of args["critics"]) {
@@ -8676,7 +8774,11 @@ CRITIQUES:
8676
8774
  ${critiqueBlock}`
8677
8775
  }
8678
8776
  ];
8679
- const coProvider = pickCoReasoner(opts.providers, upgradeRequested);
8777
+ const coProvider = pickCoReasoner(
8778
+ opts.providers,
8779
+ upgradeRequested,
8780
+ synthForCall.name === "openai" ? synthForCall.model : void 0
8781
+ );
8680
8782
  const coReason = await coReasonThenInject({
8681
8783
  provider: coProvider,
8682
8784
  messages: synthMessages,
@@ -8721,7 +8823,8 @@ ${critiqueBlock}`
8721
8823
  critique_answers: scannedCritiques,
8722
8824
  critique_structured: critiqueStructured,
8723
8825
  synthesis_answer: synthAns,
8724
- synthesis_structured: synthObj
8826
+ synthesis_structured: synthObj,
8827
+ moderator: { provider: synthForCall.name, model: synthForCall.model }
8725
8828
  };
8726
8829
  if (synthErrs.length > 0) result["synthesis_errors"] = synthErrs;
8727
8830
  if (canaryLeaks.length > 0) result["canary_leaks"] = canaryLeaks;
@@ -8739,15 +8842,16 @@ ${critiqueBlock}`
8739
8842
  result["reasoning_upgrade"] = {
8740
8843
  applied: true,
8741
8844
  // The model that actually ran the seat, not the one we'd have picked.
8742
- model: superRequested ? superPrimary("anthropic") ?? UPGRADE_MODEL : UPGRADE_MODEL,
8743
- label: UPGRADE_LABEL,
8845
+ provider: synthForCall.name,
8846
+ model: synthForCall.model,
8847
+ label: seat.label ?? synthForCall.model,
8744
8848
  seat: "synthesizer",
8745
8849
  ...coReasonAns ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
8746
8850
  };
8747
8851
  } else if (upgradeRequested) {
8748
8852
  result["reasoning_upgrade"] = {
8749
8853
  applied: false,
8750
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
8854
+ reason: seat.skipReason ?? "the synthesizer couldn't be upgraded."
8751
8855
  };
8752
8856
  } else {
8753
8857
  const assessment = assessReasoning(topic);
@@ -9353,10 +9457,12 @@ async function runDebate(args, opts) {
9353
9457
  const earlyStopThreshold = Number.isFinite(earlyStopThresholdRaw) ? earlyStopThresholdRaw : 0.7;
9354
9458
  let earlyStoppedRound = null;
9355
9459
  let agreementBlock = null;
9356
- const moderatorName = typeof args["moderator"] === "string" && args["moderator"] ? args["moderator"] : opts.moderator ?? "anthropic";
9357
- const moderator = opts.providers[moderatorName.toLowerCase()] ?? selected[0];
9460
+ const resolvedModerator = resolveModerator(opts.providers, args["moderator"], opts.moderator);
9461
+ const moderatorName = resolvedModerator.spec.provider;
9462
+ const moderator = resolvedModerator.provider ?? selected[0];
9358
9463
  const upgradeRequested = isUpgradeRequested(args) || superRequested;
9359
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
9464
+ const seat = moderator ? synthesisSeat({ moderator, pinned: resolvedModerator.pinned, superRequested, upgradeRequested }) : null;
9465
+ const upgraded = seat?.upgraded ?? false;
9360
9466
  for (let rnd = 1; rnd <= maxRounds; rnd++) {
9361
9467
  const roundMessages = [
9362
9468
  {
@@ -9441,8 +9547,10 @@ ${prior.body}` });
9441
9547
  let personaMeta = null;
9442
9548
  let coReasoned = false;
9443
9549
  let coReasonerModel = null;
9550
+ let moderatorUsed = null;
9444
9551
  if (moderator) {
9445
- const synthProvider = superRequested && opts.providers["anthropic"] ? retargetForSuper(opts.providers["anthropic"]) : upgraded ? retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL) : moderator;
9552
+ const synthProvider = seat?.provider ?? moderator;
9553
+ moderatorUsed = { provider: synthProvider.name, model: synthProvider.model };
9446
9554
  const condensed = buildModeratorTranscript(transcript);
9447
9555
  const persona = buildPersonaInjection({
9448
9556
  toolName: "debate",
@@ -9468,7 +9576,7 @@ TRANSCRIPT:
9468
9576
  ${condensed}`
9469
9577
  }
9470
9578
  ];
9471
- const openAiSeat = selected.find((p) => p.name === "openai")?.model;
9579
+ const openAiSeat = synthProvider.name === "openai" ? synthProvider.model : selected.find((p) => p.name === "openai")?.model;
9472
9580
  const coProvider = pickCoReasoner(opts.providers, upgradeRequested, openAiSeat);
9473
9581
  const coReason = await coReasonThenInject({
9474
9582
  provider: coProvider,
@@ -9528,20 +9636,22 @@ ${condensed}`
9528
9636
  transcript,
9529
9637
  synthesis
9530
9638
  };
9639
+ if (moderatorUsed) result["moderator"] = moderatorUsed;
9531
9640
  if (personaMeta && personaMeta.used !== null) result["persona"] = personaMeta;
9532
9641
  if (compactionMeta) result["round_compaction"] = compactionMeta;
9533
9642
  if (upgraded) {
9534
9643
  result["reasoning_upgrade"] = {
9535
9644
  applied: true,
9536
- model: superRequested ? superPrimary("anthropic") ?? UPGRADE_MODEL : UPGRADE_MODEL,
9537
- label: UPGRADE_LABEL,
9645
+ provider: seat.provider.name,
9646
+ model: seat.provider.model,
9647
+ label: seat.label ?? seat.provider.model,
9538
9648
  seat: "moderator synthesis",
9539
9649
  ...coReasoned && coReasonerModel ? { co_reasoner: { model: coReasonerModel, label: coReasonLabel(coReasonerModel) } } : {}
9540
9650
  };
9541
9651
  } else if (upgradeRequested) {
9542
9652
  result["reasoning_upgrade"] = {
9543
9653
  applied: false,
9544
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
9654
+ reason: seat?.skipReason ?? "no moderator was available for the upgrade."
9545
9655
  };
9546
9656
  } else {
9547
9657
  const assessment = assessReasoning(topic);
@@ -10170,12 +10280,25 @@ function runListProviders(args, opts) {
10170
10280
  }
10171
10281
  const result = {
10172
10282
  providers,
10173
- moderator_default: opts.moderatorDefault ?? "anthropic",
10283
+ moderator_default: opts.moderatorDefault ?? DEFAULT_MODERATOR,
10284
+ // Who will actually moderate right now: the configured spec if that
10285
+ // provider has a key, else the first available (what the tools fall to).
10286
+ moderator: describeModerator(opts.providers ?? {}, opts.moderatorDefault),
10174
10287
  usage_hint: USAGE_HINT
10175
10288
  };
10176
10289
  if (superRequested) result["super_mode"] = true;
10177
10290
  return result;
10178
10291
  }
10292
+ function describeModerator(providers, spec) {
10293
+ const r = resolveModerator(providers, spec);
10294
+ const p = r.provider ?? Object.values(providers)[0];
10295
+ return {
10296
+ spec: spec ?? DEFAULT_MODERATOR,
10297
+ provider: p?.name ?? null,
10298
+ model: p?.model ?? null,
10299
+ available: r.provider !== void 0
10300
+ };
10301
+ }
10179
10302
 
10180
10303
  // src/tools/models.ts
10181
10304
  var KNOWN_PROVIDERS7 = [
@@ -11725,7 +11848,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
11725
11848
  var DEFAULT_PACKAGE = "crosscheck-cli";
11726
11849
  var FETCH_TIMEOUT_MS = 3e3;
11727
11850
  function engineVersion() {
11728
- return true ? "0.2.30" : "0.0.0-dev";
11851
+ return true ? "0.2.31" : "0.0.0-dev";
11729
11852
  }
11730
11853
  function defaultUpdateCachePath() {
11731
11854
  const base = process.env["CROSSCHECK_DATA_DIR"] || import_node_path12.default.join(import_node_os2.default.homedir() || import_node_os2.default.tmpdir(), ".crosscheck");
@@ -12170,7 +12293,7 @@ var HELP_CATALOG = [
12170
12293
  primaryArg: "topic",
12171
12294
  summary: "Bounded multi-round debate between models, ending in a synthesized recommendation.",
12172
12295
  example: "xc debate should we adopt a monorepo?",
12173
- detail: `The panel argues across a few rounds \u2014 each model sees the others' prior turns, concedes or pushes back \u2014 then a moderator synthesizes a single grounded recommendation. Supports reasoning:"max" to bump the moderator synthesis seat to Fable 5. Great for contentious or trade-off-heavy calls.`
12296
+ detail: `The panel argues across a few rounds \u2014 each model sees the others' prior turns, concedes or pushes back \u2014 then a moderator synthesizes a single grounded recommendation. Any model can moderate \u2014 pass moderator:"openai" or an exact model (moderator:"openai:gpt-6-sol"), or set a default with \`crosscheck moderator set\`. reasoning:"max" bumps the moderator's synthesis to its provider's flagship (Fable 5 for Anthropic). Great for contentious or trade-off-heavy calls.`
12174
12297
  },
12175
12298
  {
12176
12299
  name: "coordinate",
@@ -12413,6 +12536,10 @@ function renderHelpCommand(entry) {
12413
12536
  }
12414
12537
 
12415
12538
  // src/tools/index.ts
12539
+ var MODERATOR_PARAM = {
12540
+ type: "string",
12541
+ description: 'Who moderates (synthesises) this run: a provider ("openai") or an exact model ("openai:gpt-6-sol"). Any configured provider works. Defaults to the configured moderator (CROSSCHECK_MODERATOR / config `moderator`).'
12542
+ };
12416
12543
  function defineTool(opts) {
12417
12544
  return {
12418
12545
  name: opts.name,
@@ -12647,7 +12774,7 @@ function createTool(o, toolName, cheapDefault) {
12647
12774
  instruction: { type: "string" },
12648
12775
  documents: { type: "array", items: { type: "string" } },
12649
12776
  providers: { type: "array", items: { type: "string" } },
12650
- moderator: { type: "string" },
12777
+ moderator: MODERATOR_PARAM,
12651
12778
  constraints: { type: "string" },
12652
12779
  target_path: { type: "string" },
12653
12780
  session_id: { type: "string" },
@@ -12688,7 +12815,7 @@ function orchestrateTool(providers, allowlist, moderator, storage, pricing, brea
12688
12815
  dag: { type: "object", additionalProperties: true },
12689
12816
  context: { type: "string" },
12690
12817
  providers: { type: "array", items: { type: "string" } },
12691
- moderator: { type: "string" },
12818
+ moderator: MODERATOR_PARAM,
12692
12819
  fail_fast: { type: "boolean" },
12693
12820
  plan_only: { type: "boolean" },
12694
12821
  cheap_mode: { type: "boolean" },
@@ -13067,7 +13194,7 @@ function planTool(providers, allowlist, storage, breakers, transcriptsDir, repoR
13067
13194
  constraints: { type: "string" },
13068
13195
  context: { type: "string" },
13069
13196
  providers: { type: "array", items: { type: "string" } },
13070
- moderator: { type: "string" },
13197
+ moderator: MODERATOR_PARAM,
13071
13198
  session_id: { type: "string" },
13072
13199
  structured: { type: "boolean" },
13073
13200
  mode: { type: "string", enum: ["fast", "thorough"] },
@@ -13129,7 +13256,7 @@ function coordinateTool(providers, allowlist, innerCallers, storage, breakers, t
13129
13256
  providers: { type: "array", items: { type: "string" } },
13130
13257
  proposer: { type: "string" },
13131
13258
  synthesizer: { type: "string" },
13132
- moderator: { type: "string" },
13259
+ moderator: MODERATOR_PARAM,
13133
13260
  critics: { type: "array", items: { type: "string" } },
13134
13261
  session_id: { type: "string" },
13135
13262
  untrusted_input: { type: "boolean" },
@@ -13162,7 +13289,7 @@ function debateTool(providers, allowlist, innerCallers, storage, pricing, breake
13162
13289
  context: { type: "string" },
13163
13290
  max_rounds: { type: "integer", minimum: 1 },
13164
13291
  providers: { type: "array", items: { type: "string" } },
13165
- moderator: { type: "string" },
13292
+ moderator: MODERATOR_PARAM,
13166
13293
  session_id: { type: "string" },
13167
13294
  reasoning: { type: "string", enum: ["default", "max"], description: '"max" bumps the moderator-synthesis seat to Fable 5 (premium thinking model); the debaters stay on their normal models. Omit for the Opus 4.8 default.' },
13168
13295
  structured: { type: "boolean" },
@@ -13707,8 +13834,8 @@ function createEmbeddedEngine(opts) {
13707
13834
  const built = Object.fromEntries(
13708
13835
  Object.entries(buildProviders({ env, pricing })).map(([name, p]) => [name, withActivity(p)])
13709
13836
  );
13710
- const moderator = opts.moderator ?? "anthropic";
13711
- const tools = registerCoreTools({ providers: built, pricing });
13837
+ const moderator = opts.moderator ?? DEFAULT_MODERATOR;
13838
+ const tools = registerCoreTools({ providers: built, pricing, moderatorDefault: moderator });
13712
13839
  const providerIds = Object.keys(built).map(vaultId).sort();
13713
13840
  const models = {};
13714
13841
  for (const [name, p] of Object.entries(built)) models[vaultId(name)] = p.model;
@@ -13739,7 +13866,8 @@ function createEmbeddedEngine(opts) {
13739
13866
  nextCallId,
13740
13867
  skipped: /* @__PURE__ */ new Set(),
13741
13868
  inflight: /* @__PURE__ */ new Map(),
13742
- moderator: vaultId(moderator),
13869
+ // The provider half of a `provider:model` spec, as the vault spells it.
13870
+ moderator: vaultId(moderatorProviderName(moderator)),
13743
13871
  ...runOpts.callTimeoutMs && runOpts.callTimeoutMs > 0 ? { callTimeoutMs: runOpts.callTimeoutMs } : {},
13744
13872
  ...runOpts.images?.length ? { images: runOpts.images.filter((i) => IMAGE_MEDIA_TYPES.includes(i.mediaType) && typeof i.data === "string" && i.data.length > 0).slice(0, MAX_RUN_IMAGES) } : {}
13745
13873
  };