crosscheck-mcp 0.2.29 → 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
@@ -35,13 +35,18 @@ __export(embed_exports, {
35
35
  DEFAULT_EXCLUDED_PROVIDERS: () => DEFAULT_EXCLUDED_PROVIDERS,
36
36
  DEFAULT_FALLBACKS: () => DEFAULT_FALLBACKS,
37
37
  DEFAULT_MODELS: () => DEFAULT_MODELS,
38
+ IMAGE_MEDIA_TYPES: () => IMAGE_MEDIA_TYPES,
39
+ MAX_RUN_IMAGES: () => MAX_RUN_IMAGES,
40
+ SUPER_MODELS: () => SUPER_MODELS,
41
+ VISION_PROVIDERS: () => VISION_PROVIDERS,
38
42
  __KEY_ENV: () => KEY_ENV,
39
43
  __MODEL_ENV: () => MODEL_ENV,
40
44
  __test_internals: () => __test_internals,
41
45
  createEmbeddedEngine: () => createEmbeddedEngine,
42
46
  isAlive: () => isAlive,
43
47
  listProcesses: () => listProcesses,
44
- processRegistryPath: () => processRegistryPath
48
+ processRegistryPath: () => processRegistryPath,
49
+ withRunImages: () => withRunImages
45
50
  });
46
51
  module.exports = __toCommonJS(embed_exports);
47
52
 
@@ -49,11 +54,35 @@ module.exports = __toCommonJS(embed_exports);
49
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;
50
55
  var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
51
56
 
52
- // src/entrypoints/embed.ts
53
- var import_node_async_hooks2 = require("async_hooks");
54
- var import_node_fs16 = require("fs");
55
- var import_node_path15 = __toESM(require("path"), 1);
56
- 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");
57
86
 
58
87
  // src/core/pricing.ts
59
88
  var import_node_fs = require("fs");
@@ -222,6 +251,11 @@ function reasoningSharesOutputBudget(provider) {
222
251
  }
223
252
 
224
253
  // src/providers/types.ts
254
+ var IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
255
+ function imagesOf(m) {
256
+ if (m.role !== "user" || !Array.isArray(m.images)) return [];
257
+ return m.images.filter((i) => Boolean(i) && typeof i.data === "string" && i.data.length > 0 && IMAGE_MEDIA_TYPES.includes(i.mediaType));
258
+ }
225
259
  var ProviderError = class extends Error {
226
260
  name = "ProviderError";
227
261
  kind;
@@ -610,7 +644,12 @@ function buildAnthropicRequest(opts) {
610
644
  }
611
645
  continue;
612
646
  }
613
- convo.push({ role: m.role, content: typeof m.content === "string" ? m.content : "" });
647
+ const text = typeof m.content === "string" ? m.content : "";
648
+ const images = imagesOf(m);
649
+ convo.push(images.length ? { role: m.role, content: [
650
+ ...images.map((i) => ({ type: "image", source: { type: "base64", media_type: i.mediaType, data: i.data } })),
651
+ { type: "text", text }
652
+ ] } : { role: m.role, content: text });
614
653
  }
615
654
  const body = {
616
655
  model: opts.model,
@@ -850,7 +889,10 @@ function buildGeminiRequest(opts) {
850
889
  const role = m.role === "user" ? "user" : "model";
851
890
  contents.push({
852
891
  role,
853
- parts: [{ text: typeof m.content === "string" ? m.content : "" }]
892
+ parts: [
893
+ ...imagesOf(m).map((i) => ({ inline_data: { mime_type: i.mediaType, data: i.data } })),
894
+ { text: typeof m.content === "string" ? m.content : "" }
895
+ ]
854
896
  });
855
897
  }
856
898
  const body = {
@@ -1018,10 +1060,11 @@ function supportsNativeJsonSchema(provider, model) {
1018
1060
  return true;
1019
1061
  }
1020
1062
  function buildOpenAICompatibleRequest(opts) {
1021
- const msgs = opts.messages.map((m) => ({
1022
- role: m.role,
1023
- content: typeof m.content === "string" ? m.content : ""
1024
- }));
1063
+ const msgs = opts.messages.map((m) => {
1064
+ const text = typeof m.content === "string" ? m.content : "";
1065
+ const images = imagesOf(m);
1066
+ return images.length ? { role: m.role, content: [{ type: "text", text }, ...images.map((i) => ({ type: "image_url", image_url: { url: `data:${i.mediaType};base64,${i.data}` } }))] } : { role: m.role, content: text };
1067
+ });
1025
1068
  const body = {
1026
1069
  model: opts.model,
1027
1070
  messages: msgs
@@ -1329,12 +1372,238 @@ function makeGeminiProvider(model, apiKey, opts, fallbackModels = []) {
1329
1372
  };
1330
1373
  }
1331
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
+
1332
1601
  // src/tools/index.ts
1333
1602
  var import_zod = require("zod");
1334
1603
 
1335
1604
  // src/server-meta.ts
1336
1605
  var SERVER_NAME = "crosscheck-agent";
1337
- var SERVER_VERSION = true ? "0.2.29" : "0.0.0-dev";
1606
+ var SERVER_VERSION = true ? "0.2.31" : "0.0.0-dev";
1338
1607
 
1339
1608
  // src/tools/audit.ts
1340
1609
  var import_node_fs4 = require("fs");
@@ -2017,33 +2286,6 @@ function operatorCeiling(purpose, provider) {
2017
2286
  return null;
2018
2287
  }
2019
2288
 
2020
- // src/core/retarget.ts
2021
- function retargetProvider(p, newModel, fallbackModels) {
2022
- const chain = fallbackModels && fallbackModels.length > 0 ? fallbackModels : void 0;
2023
- if (p.model === newModel && chain === void 0) return p;
2024
- return {
2025
- name: p.name,
2026
- model: newModel,
2027
- ...chain ? { fallbackModels: chain } : {},
2028
- send: (args) => p.send({ ...args, modelOverride: newModel })
2029
- };
2030
- }
2031
- async function loadProviderWeights(storage, names) {
2032
- const out = {};
2033
- for (const raw of names) {
2034
- const name = raw.toLowerCase();
2035
- if (name in out) continue;
2036
- const row = await storage.getProviderStats(name);
2037
- if (!row) {
2038
- out[name] = 0.5;
2039
- continue;
2040
- }
2041
- const total = row.wins + row.losses + row.abstains;
2042
- out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
2043
- }
2044
- return out;
2045
- }
2046
-
2047
2289
  // src/core/model-fallback.ts
2048
2290
  async function sendWithModelFallback(provider, args) {
2049
2291
  const chain = [provider.model, ...provider.fallbackModels ?? []];
@@ -3110,82 +3352,6 @@ function selectForDifficulty(args) {
3110
3352
  };
3111
3353
  }
3112
3354
 
3113
- // src/core/reasoning-upgrade.ts
3114
- var UPGRADE_MODEL = "claude-fable-5";
3115
- var UPGRADE_LABEL = "Fable 5";
3116
- function isUpgradeRequested(args) {
3117
- if (args["upgrade"] === true) return true;
3118
- const reasoning = args["reasoning"];
3119
- if (typeof reasoning === "string" && reasoning.trim().toLowerCase() === "max") {
3120
- return true;
3121
- }
3122
- const model = args["model"];
3123
- if (typeof model === "string") {
3124
- const m = model.trim().toLowerCase();
3125
- if (m === "fable5" || m === "fable-5" || m === "fable" || m === UPGRADE_MODEL) {
3126
- return true;
3127
- }
3128
- }
3129
- return false;
3130
- }
3131
- var HEAVY_TERMS = [
3132
- /\bprov(e|ing|en)\b|\bproof\b|\btheorem\b|\bderiv(e|ation)\b/i,
3133
- /\barchitect(ure|ing)?\b|\bdesign\s+(a|the|an)\b|\bhigh[-\s]?level\s+design\b/i,
3134
- /\btrade[-\s]?offs?\b|\bpros?\s+and\s+cons?\b|\bweigh\b/i,
3135
- /\boptimi[sz]e\b|\bcomplexity\b|\bO\(|\balgorithm(ic)?\b/i,
3136
- /\brace\s+condition\b|\bconcurren(t|cy)\b|\bdeadlock\b|\bdistributed\b|\bconsensus\b/i,
3137
- /\broot[-\s]?cause\b|\bdebug(ging)?\b|\bwhy\s+(does|is|would|might)\b/i,
3138
- /\bsecurity\b|\bthreat\s+model\b|\bexploit\b|\bvulnerabilit(y|ies)\b/i,
3139
- /\bmigrat(e|ion)\b|\brefactor\b|\bre[-\s]?architect\b/i,
3140
- /\binvariant\b|\bcorrectness\b|\bedge\s+cases?\b|\bformal(ly)?\b/i,
3141
- /\bstrateg(y|ic|ies)\b|\bplan\s+(the|a|an|out)\b|\broadmap\b/i,
3142
- /\breason\s+(through|about)\b|\bstep[-\s]?by[-\s]?step\b|\bthink\s+deeply\b/i,
3143
- /\bnp[-\s]?(hard|complete)\b|\bgraph\b|\bstate\s+machine\b|\bconstraint\b/i
3144
- ];
3145
- var DIFFICULTY_TERMS = /\b(hard|tricky|subtle|complex|complicated|non[-\s]?trivial|thorny|gnarly|difficult|deep)\b/i;
3146
- function assessReasoning(text) {
3147
- const s = typeof text === "string" ? text : "";
3148
- const signals = [];
3149
- let score = 0;
3150
- let termHits = 0;
3151
- for (const re of HEAVY_TERMS) {
3152
- if (re.test(s)) termHits += 1;
3153
- }
3154
- if (termHits > 0) {
3155
- const add = Math.min(termHits, 4);
3156
- score += add;
3157
- signals.push(`${termHits} reasoning cue${termHits === 1 ? "" : "s"}`);
3158
- }
3159
- if (DIFFICULTY_TERMS.test(s)) {
3160
- score += 1;
3161
- signals.push("explicit difficulty marker");
3162
- }
3163
- const words = s.split(/\s+/).filter(Boolean).length;
3164
- if (words >= 120) {
3165
- score += 2;
3166
- signals.push("long, detailed prompt");
3167
- } else if (words >= 50) {
3168
- score += 1;
3169
- signals.push("substantial prompt");
3170
- }
3171
- const constraints = (s.match(/\b(must|should|need to|ensure|require[ds]?|constraint)\b/gi) ?? []).length + (s.match(/(^|\n)\s*(?:[-*]|\d+[.)])\s+/g) ?? []).length;
3172
- if (constraints >= 3) {
3173
- score += 1;
3174
- signals.push(`${constraints} constraints/steps`);
3175
- }
3176
- const heavy = score >= 3;
3177
- const reason = heavy ? `Task looks reasoning-heavy (${signals.join(", ") || "score " + score}).` : `Task looks routine (score ${score}).`;
3178
- return { heavy, score, reason, signals };
3179
- }
3180
- function buildSuggestedUpgrade(toolName, assessment) {
3181
- return {
3182
- model: UPGRADE_MODEL,
3183
- label: UPGRADE_LABEL,
3184
- reason: assessment.reason,
3185
- 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.`
3186
- };
3187
- }
3188
-
3189
3355
  // src/tools/audit.ts
3190
3356
  var AUDIT_OBVIOUS_FAILURE_HIGH = 0.3;
3191
3357
  var AUDIT_OBVIOUS_FAILURE_MED = 0.2;
@@ -3483,9 +3649,14 @@ ${rubricText}`;
3483
3649
  );
3484
3650
  }
3485
3651
  const upgradeRequested = isUpgradeRequested(args);
3486
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
3487
- const auditorForCall = upgraded ? retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL) : auditor;
3488
- 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
+ );
3489
3660
  const coReason = await coReasonThenInject({
3490
3661
  provider: coProvider,
3491
3662
  messages: msgs,
@@ -3568,15 +3739,16 @@ ${rubricText}`;
3568
3739
  if (upgraded) {
3569
3740
  result["reasoning_upgrade"] = {
3570
3741
  applied: true,
3571
- model: UPGRADE_MODEL,
3572
- label: UPGRADE_LABEL,
3742
+ provider: auditorForCall.name,
3743
+ model: auditorForCall.model,
3744
+ label: seat.label ?? auditorForCall.model,
3573
3745
  seat: "auditor",
3574
3746
  ...coReasoned ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
3575
3747
  };
3576
3748
  } else if (upgradeRequested) {
3577
3749
  result["reasoning_upgrade"] = {
3578
3750
  applied: false,
3579
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
3751
+ reason: seat.skipReason ?? "the auditor couldn't be upgraded."
3580
3752
  };
3581
3753
  } else {
3582
3754
  const assessment = assessReasoning(
@@ -3659,7 +3831,7 @@ function pickAuditor(providers, exclude, explicit, moderatorName, allowlist, che
3659
3831
  }
3660
3832
  }
3661
3833
  }
3662
- const mod = providers[moderatorName.toLowerCase()];
3834
+ const mod = resolveModerator(providers, moderatorName).provider;
3663
3835
  if (mod !== void 0 && !exclude.has(mod.name) && (allowlist === null || allowlist.includes(mod.name))) {
3664
3836
  return { auditor: mod, reason: null };
3665
3837
  }
@@ -3978,9 +4150,7 @@ function pickPanelJudge(providers, moderatorName, pricing) {
3978
4150
  return entry.model && entry.model !== base.model ? retargetProvider(base, entry.model) : base;
3979
4151
  }
3980
4152
  }
3981
- const mod = providers[moderatorName.toLowerCase()];
3982
- if (mod !== void 0) return mod;
3983
- return null;
4153
+ return resolveModerator(providers, moderatorName).provider ?? null;
3984
4154
  }
3985
4155
  var AGREEMENT_CHECK_SCHEMA = {
3986
4156
  type: "object",
@@ -4297,77 +4467,6 @@ async function pickAutoPanel(storage, purpose, n, providers, allowlist) {
4297
4467
  return { providers: ranked.slice(0, Math.max(1, n)), coldStart: true };
4298
4468
  }
4299
4469
 
4300
- // src/core/super-mode.ts
4301
- var SUPER_MODELS = {
4302
- // Fable 5.1 verified present on the account; 5 as the fallback for accounts
4303
- // that haven't been granted 5.1 yet.
4304
- anthropic: ["claude-fable-5-1", UPGRADE_MODEL],
4305
- // Astra is live (verified against the account's model list 2026-09-23).
4306
- // The fallback is named explicitly rather than reusing CO_REASON_MODEL:
4307
- // that constant means "OpenAI's top reasoning model" for the max-reasoning
4308
- // co-reasoner, which is a different question from "what should super fall
4309
- // back to". Sharing the symbol silently coupled two unrelated decisions.
4310
- openai: ["gpt-6-astra", "gpt-6-sol"],
4311
- // Explicitly left as-is per product decision. Note grok-4-latest is not in
4312
- // xAI's published model list, but it resolves in practice — real panel calls
4313
- // on it succeeded as recently as 2026-08-30 — so it is an unlisted alias
4314
- // rather than a dead id.
4315
- xai: [DEFAULT_MODELS["xai"]],
4316
- // 3.8 Flash is now BOTH the everyday default and the super pick for Gemini,
4317
- // so this can no longer borrow DEFAULT_MODELS as its fallback — that would
4318
- // repeat the same model and retry one that just failed. Named explicitly.
4319
- gemini: ["gemini-3.8-flash", "gemini-3.1-pro-preview"],
4320
- kimi: [DEFAULT_MODELS["kimi"]],
4321
- qwen: [DEFAULT_MODELS["qwen"]]
4322
- };
4323
- var SUPER_ENV_VARS = {
4324
- anthropic: "ANTHROPIC_SUPER_MODEL",
4325
- openai: "OPENAI_SUPER_MODEL",
4326
- xai: "XAI_SUPER_MODEL",
4327
- gemini: "GEMINI_SUPER_MODEL",
4328
- kimi: "KIMI_SUPER_MODEL",
4329
- qwen: "QWEN_SUPER_MODEL",
4330
- mistral: "MISTRAL_SUPER_MODEL",
4331
- groq: "GROQ_SUPER_MODEL",
4332
- deepseek: "DEEPSEEK_SUPER_MODEL"
4333
- };
4334
- function parseChain(v) {
4335
- if (!v) return [];
4336
- const out = [];
4337
- for (const raw of v.split(",")) {
4338
- const m = raw.trim();
4339
- if (m && !out.includes(m)) out.push(m);
4340
- }
4341
- return out;
4342
- }
4343
- function superChainFor(provider, env = process.env) {
4344
- const varName = SUPER_ENV_VARS[provider];
4345
- const user = varName ? parseChain(env[varName]) : [];
4346
- if (user.length > 0) {
4347
- const fallbacks = varName ? parseChain(env[`${varName}_FALLBACKS`]) : [];
4348
- return [...user, ...fallbacks.filter((m) => !user.includes(m))];
4349
- }
4350
- return SUPER_MODELS[provider] ?? [];
4351
- }
4352
- function superPrimary(provider, env = process.env) {
4353
- return superChainFor(provider, env)[0];
4354
- }
4355
- var SUPER_PROVIDER_NAMES = Object.keys(SUPER_MODELS);
4356
- function isSuperRequested(args) {
4357
- return args["super"] === true;
4358
- }
4359
- function retargetForSuper(p, env = process.env) {
4360
- const chain = superChainFor(p.name, env);
4361
- if (chain.length === 0) return p;
4362
- const [primary, ...fallbacks] = chain;
4363
- return retargetProvider(p, primary, fallbacks);
4364
- }
4365
- var SUPER_BUDGET_MULTIPLIER = 3;
4366
- var SUPER_BUDGET_CEILING = 16e3;
4367
- function superBudget(base) {
4368
- return Math.min(base * SUPER_BUDGET_MULTIPLIER, SUPER_BUDGET_CEILING);
4369
- }
4370
-
4371
4470
  // src/core/session-memory.ts
4372
4471
  var SESSION_MEMORY_DEFAULT_LIMIT = 50;
4373
4472
  var SESSION_MEMORY_INJECT_BUDGET_CHARS = 4e3;
@@ -6427,9 +6526,9 @@ var import_node_path9 = __toESM(require("path"), 1);
6427
6526
  var import_node_perf_hooks6 = require("perf_hooks");
6428
6527
 
6429
6528
  // src/core/progress.ts
6430
- var import_node_async_hooks = require("async_hooks");
6529
+ var import_node_async_hooks2 = require("async_hooks");
6431
6530
  var import_node_perf_hooks5 = require("perf_hooks");
6432
- var progressStorage = new import_node_async_hooks.AsyncLocalStorage();
6531
+ var progressStorage = new import_node_async_hooks2.AsyncLocalStorage();
6433
6532
  async function withProgress(token, sendNotification, fn) {
6434
6533
  if (token === void 0 || token === null) {
6435
6534
  return fn();
@@ -6731,8 +6830,9 @@ async function runOrchestrate(args, opts) {
6731
6830
  blocked
6732
6831
  };
6733
6832
  }
6734
- const moderatorName = typeof args["moderator"] === "string" && args["moderator"] ? args["moderator"] : opts.moderator ?? "anthropic";
6735
- 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);
6736
6836
  const failFast = boolArg2(args["fail_fast"], false);
6737
6837
  const planOnly = boolArg2(args["plan_only"], false);
6738
6838
  const cheapMode = boolArg2(args["cheap_mode"], opts.ctx?.cheap_mode ?? false);
@@ -7216,11 +7316,17 @@ ${recombineSys}` : recombineSys },
7216
7316
  let synthProvider = moderator;
7217
7317
  let synthModel = null;
7218
7318
  const upgradeRequested = isUpgradeRequested(args);
7219
- 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;
7220
7326
  if (upgraded) {
7221
- synthProvider = retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL);
7222
- synthModel = `anthropic/${UPGRADE_MODEL}`;
7223
- } else if (cheapMode && opts.pricing) {
7327
+ synthProvider = seat.provider;
7328
+ synthModel = `${seat.provider.name}/${seat.provider.model}`;
7329
+ } else if (cheapMode && opts.pricing && !moderatorChosen) {
7224
7330
  const pick = selectForDifficulty({
7225
7331
  pricing: opts.pricing,
7226
7332
  tier: "med",
@@ -7246,7 +7352,11 @@ ${recombineSys}` : recombineSys },
7246
7352
  provider: synthProvider.name
7247
7353
  } }
7248
7354
  );
7249
- const coProvider = pickCoReasoner(opts.providers, upgradeRequested);
7355
+ const coProvider = pickCoReasoner(
7356
+ opts.providers,
7357
+ upgradeRequested,
7358
+ synthProvider.name === "openai" ? synthProvider.model : void 0
7359
+ );
7250
7360
  const coReason = await coReasonThenInject({
7251
7361
  provider: coProvider,
7252
7362
  messages: recMsgs,
@@ -7296,7 +7406,8 @@ ${recombineSys}` : recombineSys },
7296
7406
  missing,
7297
7407
  partial,
7298
7408
  fail_fast: failFast,
7299
- cheap_mode: cheapMode
7409
+ cheap_mode: cheapMode,
7410
+ moderator: { provider: moderator.name, model: moderator.model }
7300
7411
  };
7301
7412
  if (personaMeta.used !== null) result["persona"] = personaMeta;
7302
7413
  if (synthModel) result["synth_model"] = synthModel;
@@ -7304,15 +7415,16 @@ ${recombineSys}` : recombineSys },
7304
7415
  if (upgraded) {
7305
7416
  result["reasoning_upgrade"] = {
7306
7417
  applied: true,
7307
- model: UPGRADE_MODEL,
7308
- label: UPGRADE_LABEL,
7418
+ provider: seat.provider.name,
7419
+ model: seat.provider.model,
7420
+ label: seat.label ?? seat.provider.model,
7309
7421
  seat: "recombine/synthesis",
7310
7422
  ...coReasoned ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
7311
7423
  };
7312
7424
  } else if (upgradeRequested) {
7313
7425
  result["reasoning_upgrade"] = {
7314
7426
  applied: false,
7315
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
7427
+ reason: seat.skipReason ?? "the moderator couldn't be upgraded."
7316
7428
  };
7317
7429
  } else if (goal) {
7318
7430
  const assessment = assessReasoning(goal);
@@ -8494,13 +8606,24 @@ async function runCoordinate(args, opts) {
8494
8606
  };
8495
8607
  }
8496
8608
  const proposerName = typeof args["proposer"] === "string" && args["proposer"] ? args["proposer"] : selected[0].name;
8497
- 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
+ );
8498
8615
  const proposer = opts.providers[proposerName.toLowerCase()] ?? selected[0];
8499
- const synth = opts.providers[synthName.toLowerCase()] ?? proposer;
8616
+ const synth = resolvedSynth.provider ?? proposer;
8500
8617
  const proposerForCall = superRequested ? retargetForSuper(proposer) : proposer;
8501
8618
  const upgradeRequested = isUpgradeRequested(args) || superRequested;
8502
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
8503
- 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;
8504
8627
  let critics = [];
8505
8628
  if (Array.isArray(args["critics"])) {
8506
8629
  for (const n of args["critics"]) {
@@ -8651,7 +8774,11 @@ CRITIQUES:
8651
8774
  ${critiqueBlock}`
8652
8775
  }
8653
8776
  ];
8654
- const coProvider = pickCoReasoner(opts.providers, upgradeRequested);
8777
+ const coProvider = pickCoReasoner(
8778
+ opts.providers,
8779
+ upgradeRequested,
8780
+ synthForCall.name === "openai" ? synthForCall.model : void 0
8781
+ );
8655
8782
  const coReason = await coReasonThenInject({
8656
8783
  provider: coProvider,
8657
8784
  messages: synthMessages,
@@ -8696,7 +8823,8 @@ ${critiqueBlock}`
8696
8823
  critique_answers: scannedCritiques,
8697
8824
  critique_structured: critiqueStructured,
8698
8825
  synthesis_answer: synthAns,
8699
- synthesis_structured: synthObj
8826
+ synthesis_structured: synthObj,
8827
+ moderator: { provider: synthForCall.name, model: synthForCall.model }
8700
8828
  };
8701
8829
  if (synthErrs.length > 0) result["synthesis_errors"] = synthErrs;
8702
8830
  if (canaryLeaks.length > 0) result["canary_leaks"] = canaryLeaks;
@@ -8714,15 +8842,16 @@ ${critiqueBlock}`
8714
8842
  result["reasoning_upgrade"] = {
8715
8843
  applied: true,
8716
8844
  // The model that actually ran the seat, not the one we'd have picked.
8717
- model: superRequested ? superPrimary("anthropic") ?? UPGRADE_MODEL : UPGRADE_MODEL,
8718
- label: UPGRADE_LABEL,
8845
+ provider: synthForCall.name,
8846
+ model: synthForCall.model,
8847
+ label: seat.label ?? synthForCall.model,
8719
8848
  seat: "synthesizer",
8720
8849
  ...coReasonAns ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
8721
8850
  };
8722
8851
  } else if (upgradeRequested) {
8723
8852
  result["reasoning_upgrade"] = {
8724
8853
  applied: false,
8725
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
8854
+ reason: seat.skipReason ?? "the synthesizer couldn't be upgraded."
8726
8855
  };
8727
8856
  } else {
8728
8857
  const assessment = assessReasoning(topic);
@@ -9328,10 +9457,12 @@ async function runDebate(args, opts) {
9328
9457
  const earlyStopThreshold = Number.isFinite(earlyStopThresholdRaw) ? earlyStopThresholdRaw : 0.7;
9329
9458
  let earlyStoppedRound = null;
9330
9459
  let agreementBlock = null;
9331
- const moderatorName = typeof args["moderator"] === "string" && args["moderator"] ? args["moderator"] : opts.moderator ?? "anthropic";
9332
- 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];
9333
9463
  const upgradeRequested = isUpgradeRequested(args) || superRequested;
9334
- 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;
9335
9466
  for (let rnd = 1; rnd <= maxRounds; rnd++) {
9336
9467
  const roundMessages = [
9337
9468
  {
@@ -9416,8 +9547,10 @@ ${prior.body}` });
9416
9547
  let personaMeta = null;
9417
9548
  let coReasoned = false;
9418
9549
  let coReasonerModel = null;
9550
+ let moderatorUsed = null;
9419
9551
  if (moderator) {
9420
- 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 };
9421
9554
  const condensed = buildModeratorTranscript(transcript);
9422
9555
  const persona = buildPersonaInjection({
9423
9556
  toolName: "debate",
@@ -9443,7 +9576,7 @@ TRANSCRIPT:
9443
9576
  ${condensed}`
9444
9577
  }
9445
9578
  ];
9446
- const openAiSeat = selected.find((p) => p.name === "openai")?.model;
9579
+ const openAiSeat = synthProvider.name === "openai" ? synthProvider.model : selected.find((p) => p.name === "openai")?.model;
9447
9580
  const coProvider = pickCoReasoner(opts.providers, upgradeRequested, openAiSeat);
9448
9581
  const coReason = await coReasonThenInject({
9449
9582
  provider: coProvider,
@@ -9503,20 +9636,22 @@ ${condensed}`
9503
9636
  transcript,
9504
9637
  synthesis
9505
9638
  };
9639
+ if (moderatorUsed) result["moderator"] = moderatorUsed;
9506
9640
  if (personaMeta && personaMeta.used !== null) result["persona"] = personaMeta;
9507
9641
  if (compactionMeta) result["round_compaction"] = compactionMeta;
9508
9642
  if (upgraded) {
9509
9643
  result["reasoning_upgrade"] = {
9510
9644
  applied: true,
9511
- model: superRequested ? superPrimary("anthropic") ?? UPGRADE_MODEL : UPGRADE_MODEL,
9512
- label: UPGRADE_LABEL,
9645
+ provider: seat.provider.name,
9646
+ model: seat.provider.model,
9647
+ label: seat.label ?? seat.provider.model,
9513
9648
  seat: "moderator synthesis",
9514
9649
  ...coReasoned && coReasonerModel ? { co_reasoner: { model: coReasonerModel, label: coReasonLabel(coReasonerModel) } } : {}
9515
9650
  };
9516
9651
  } else if (upgradeRequested) {
9517
9652
  result["reasoning_upgrade"] = {
9518
9653
  applied: false,
9519
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
9654
+ reason: seat?.skipReason ?? "no moderator was available for the upgrade."
9520
9655
  };
9521
9656
  } else {
9522
9657
  const assessment = assessReasoning(topic);
@@ -10145,12 +10280,25 @@ function runListProviders(args, opts) {
10145
10280
  }
10146
10281
  const result = {
10147
10282
  providers,
10148
- 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),
10149
10287
  usage_hint: USAGE_HINT
10150
10288
  };
10151
10289
  if (superRequested) result["super_mode"] = true;
10152
10290
  return result;
10153
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
+ }
10154
10302
 
10155
10303
  // src/tools/models.ts
10156
10304
  var KNOWN_PROVIDERS7 = [
@@ -11700,7 +11848,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
11700
11848
  var DEFAULT_PACKAGE = "crosscheck-cli";
11701
11849
  var FETCH_TIMEOUT_MS = 3e3;
11702
11850
  function engineVersion() {
11703
- return true ? "0.2.29" : "0.0.0-dev";
11851
+ return true ? "0.2.31" : "0.0.0-dev";
11704
11852
  }
11705
11853
  function defaultUpdateCachePath() {
11706
11854
  const base = process.env["CROSSCHECK_DATA_DIR"] || import_node_path12.default.join(import_node_os2.default.homedir() || import_node_os2.default.tmpdir(), ".crosscheck");
@@ -12145,7 +12293,7 @@ var HELP_CATALOG = [
12145
12293
  primaryArg: "topic",
12146
12294
  summary: "Bounded multi-round debate between models, ending in a synthesized recommendation.",
12147
12295
  example: "xc debate should we adopt a monorepo?",
12148
- 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.`
12149
12297
  },
12150
12298
  {
12151
12299
  name: "coordinate",
@@ -12388,6 +12536,10 @@ function renderHelpCommand(entry) {
12388
12536
  }
12389
12537
 
12390
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
+ };
12391
12543
  function defineTool(opts) {
12392
12544
  return {
12393
12545
  name: opts.name,
@@ -12622,7 +12774,7 @@ function createTool(o, toolName, cheapDefault) {
12622
12774
  instruction: { type: "string" },
12623
12775
  documents: { type: "array", items: { type: "string" } },
12624
12776
  providers: { type: "array", items: { type: "string" } },
12625
- moderator: { type: "string" },
12777
+ moderator: MODERATOR_PARAM,
12626
12778
  constraints: { type: "string" },
12627
12779
  target_path: { type: "string" },
12628
12780
  session_id: { type: "string" },
@@ -12663,7 +12815,7 @@ function orchestrateTool(providers, allowlist, moderator, storage, pricing, brea
12663
12815
  dag: { type: "object", additionalProperties: true },
12664
12816
  context: { type: "string" },
12665
12817
  providers: { type: "array", items: { type: "string" } },
12666
- moderator: { type: "string" },
12818
+ moderator: MODERATOR_PARAM,
12667
12819
  fail_fast: { type: "boolean" },
12668
12820
  plan_only: { type: "boolean" },
12669
12821
  cheap_mode: { type: "boolean" },
@@ -13042,7 +13194,7 @@ function planTool(providers, allowlist, storage, breakers, transcriptsDir, repoR
13042
13194
  constraints: { type: "string" },
13043
13195
  context: { type: "string" },
13044
13196
  providers: { type: "array", items: { type: "string" } },
13045
- moderator: { type: "string" },
13197
+ moderator: MODERATOR_PARAM,
13046
13198
  session_id: { type: "string" },
13047
13199
  structured: { type: "boolean" },
13048
13200
  mode: { type: "string", enum: ["fast", "thorough"] },
@@ -13104,7 +13256,7 @@ function coordinateTool(providers, allowlist, innerCallers, storage, breakers, t
13104
13256
  providers: { type: "array", items: { type: "string" } },
13105
13257
  proposer: { type: "string" },
13106
13258
  synthesizer: { type: "string" },
13107
- moderator: { type: "string" },
13259
+ moderator: MODERATOR_PARAM,
13108
13260
  critics: { type: "array", items: { type: "string" } },
13109
13261
  session_id: { type: "string" },
13110
13262
  untrusted_input: { type: "boolean" },
@@ -13137,7 +13289,7 @@ function debateTool(providers, allowlist, innerCallers, storage, pricing, breake
13137
13289
  context: { type: "string" },
13138
13290
  max_rounds: { type: "integer", minimum: 1 },
13139
13291
  providers: { type: "array", items: { type: "string" } },
13140
- moderator: { type: "string" },
13292
+ moderator: MODERATOR_PARAM,
13141
13293
  session_id: { type: "string" },
13142
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.' },
13143
13295
  structured: { type: "boolean" },
@@ -13493,7 +13645,33 @@ function vaultId(engineName) {
13493
13645
  }
13494
13646
  var ACTIVITY_TEXT_CAP = 8e3;
13495
13647
  var ASKED_CAP = 280;
13496
- var runContext = new import_node_async_hooks2.AsyncLocalStorage();
13648
+ var VISION_PROVIDERS = ["anthropic", "openai", "gemini", "grok"];
13649
+ var NO_IMAGE_PURPOSES = /* @__PURE__ */ new Set(["early_stop", "extract", "canary", "classify"]);
13650
+ var MAX_RUN_IMAGES = 8;
13651
+ function withRunImages(args, provider, images) {
13652
+ if (!images?.length || NO_IMAGE_PURPOSES.has(args.purpose ?? "")) return args;
13653
+ const msgs = [...args.messages];
13654
+ let idx = -1;
13655
+ for (let i = msgs.length - 1; i >= 0; i--) if (msgs[i]?.role === "user") {
13656
+ idx = i;
13657
+ break;
13658
+ }
13659
+ if (idx < 0) return args;
13660
+ const m = msgs[idx];
13661
+ if (VISION_PROVIDERS.includes(provider)) {
13662
+ msgs[idx] = { ...m, images: [...m.images ?? [], ...images] };
13663
+ } else {
13664
+ const n = images.length;
13665
+ msgs[idx] = {
13666
+ ...m,
13667
+ content: `${m.content}
13668
+
13669
+ [The user also attached ${n} image${n === 1 ? "" : "s"} that this model can't see. Don't guess at ${n === 1 ? "its" : "their"} contents \u2014 work from the text, and say so if the answer depends on ${n === 1 ? "it" : "them"}.]`
13670
+ };
13671
+ }
13672
+ return { ...args, messages: msgs };
13673
+ }
13674
+ var runContext = new import_node_async_hooks3.AsyncLocalStorage();
13497
13675
  var SYNTHESIS_PURPOSES = /* @__PURE__ */ new Set(["synth", "moderator", "recombine", "judge", "unify"]);
13498
13676
  function exemptFromSkip(ctx, provider, purpose) {
13499
13677
  return provider === ctx.moderator && SYNTHESIS_PURPOSES.has(purpose);
@@ -13548,7 +13726,7 @@ function withActivity(p) {
13548
13726
  if (!stopped && controller.signal.reason === "skip") stopped = "user";
13549
13727
  }, { once: true });
13550
13728
  try {
13551
- const res = await send({ ...args, signal: controller.signal });
13729
+ const res = await send({ ...withRunImages(args, provider, ctx.images), signal: controller.signal });
13552
13730
  const text = typeof res.text === "string" ? res.text : "";
13553
13731
  ctx.emit({
13554
13732
  type: "call.done",
@@ -13643,6 +13821,12 @@ function createEmbeddedEngine(opts) {
13643
13821
  const varName = MODEL_ENV[provider.trim().toLowerCase()];
13644
13822
  if (varName && model) env[varName] = model;
13645
13823
  }
13824
+ const superEnv = {};
13825
+ for (const [provider, model] of Object.entries(opts.superModels ?? {})) {
13826
+ const p = provider.trim().toLowerCase();
13827
+ const varName = SUPER_ENV_VARS[p === "grok" ? "xai" : p];
13828
+ if (varName && model) superEnv[varName] = model;
13829
+ }
13646
13830
  const pricingPath = [opts.pricingPath, bundledPricingPath()].find(
13647
13831
  (p) => Boolean(p) && (0, import_node_fs16.existsSync)(p)
13648
13832
  );
@@ -13650,8 +13834,8 @@ function createEmbeddedEngine(opts) {
13650
13834
  const built = Object.fromEntries(
13651
13835
  Object.entries(buildProviders({ env, pricing })).map(([name, p]) => [name, withActivity(p)])
13652
13836
  );
13653
- const moderator = opts.moderator ?? "anthropic";
13654
- const tools = registerCoreTools({ providers: built, pricing });
13837
+ const moderator = opts.moderator ?? DEFAULT_MODERATOR;
13838
+ const tools = registerCoreTools({ providers: built, pricing, moderatorDefault: moderator });
13655
13839
  const providerIds = Object.keys(built).map(vaultId).sort();
13656
13840
  const models = {};
13657
13841
  for (const [name, p] of Object.entries(built)) models[vaultId(name)] = p.model;
@@ -13682,14 +13866,16 @@ function createEmbeddedEngine(opts) {
13682
13866
  nextCallId,
13683
13867
  skipped: /* @__PURE__ */ new Set(),
13684
13868
  inflight: /* @__PURE__ */ new Map(),
13685
- moderator: vaultId(moderator),
13686
- ...runOpts.callTimeoutMs && runOpts.callTimeoutMs > 0 ? { callTimeoutMs: runOpts.callTimeoutMs } : {}
13869
+ // The provider half of a `provider:model` spec, as the vault spells it.
13870
+ moderator: vaultId(moderatorProviderName(moderator)),
13871
+ ...runOpts.callTimeoutMs && runOpts.callTimeoutMs > 0 ? { callTimeoutMs: runOpts.callTimeoutMs } : {},
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) } : {}
13687
13873
  };
13688
13874
  liveRuns.set(runId, ctx);
13689
13875
  try {
13690
13876
  return await runContext.run(ctx, () => withProgress(runId, (n) => {
13691
13877
  if (n.params.message) emit({ type: "progress", runId, at: Date.now(), message: n.params.message });
13692
- }, () => runInner(name, args, runId, started, emit)));
13878
+ }, () => withSuperEnv(superEnv, () => runInner(name, args, runId, started, emit))));
13693
13879
  } finally {
13694
13880
  liveRuns.delete(runId);
13695
13881
  }
@@ -13848,12 +14034,17 @@ var __test_internals = { usageOf, textOf, errorOf, vaultId };
13848
14034
  DEFAULT_EXCLUDED_PROVIDERS,
13849
14035
  DEFAULT_FALLBACKS,
13850
14036
  DEFAULT_MODELS,
14037
+ IMAGE_MEDIA_TYPES,
14038
+ MAX_RUN_IMAGES,
14039
+ SUPER_MODELS,
14040
+ VISION_PROVIDERS,
13851
14041
  __KEY_ENV,
13852
14042
  __MODEL_ENV,
13853
14043
  __test_internals,
13854
14044
  createEmbeddedEngine,
13855
14045
  isAlive,
13856
14046
  listProcesses,
13857
- processRegistryPath
14047
+ processRegistryPath,
14048
+ withRunImages
13858
14049
  });
13859
14050
  //# sourceMappingURL=embed.cjs.map