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.js CHANGED
@@ -6,11 +6,35 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  throw Error('Dynamic require of "' + x + '" is not supported');
7
7
  });
8
8
 
9
- // src/entrypoints/embed.ts
10
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
11
- import { existsSync as existsSync11 } from "fs";
12
- import path12 from "path";
13
- import { fileURLToPath as fileURLToPath2 } from "url";
9
+ // src/core/retarget.ts
10
+ function retargetProvider(p, newModel, fallbackModels) {
11
+ const chain = fallbackModels && fallbackModels.length > 0 ? fallbackModels : void 0;
12
+ if (p.model === newModel && chain === void 0) return p;
13
+ return {
14
+ name: p.name,
15
+ model: newModel,
16
+ ...chain ? { fallbackModels: chain } : {},
17
+ send: (args) => p.send({ ...args, modelOverride: newModel })
18
+ };
19
+ }
20
+ async function loadProviderWeights(storage, names) {
21
+ const out = {};
22
+ for (const raw of names) {
23
+ const name = raw.toLowerCase();
24
+ if (name in out) continue;
25
+ const row = await storage.getProviderStats(name);
26
+ if (!row) {
27
+ out[name] = 0.5;
28
+ continue;
29
+ }
30
+ const total = row.wins + row.losses + row.abstains;
31
+ out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
32
+ }
33
+ return out;
34
+ }
35
+
36
+ // src/core/super-mode.ts
37
+ import { AsyncLocalStorage } from "async_hooks";
14
38
 
15
39
  // src/core/pricing.ts
16
40
  import { readFileSync } from "fs";
@@ -179,6 +203,11 @@ function reasoningSharesOutputBudget(provider) {
179
203
  }
180
204
 
181
205
  // src/providers/types.ts
206
+ var IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
207
+ function imagesOf(m) {
208
+ if (m.role !== "user" || !Array.isArray(m.images)) return [];
209
+ return m.images.filter((i) => Boolean(i) && typeof i.data === "string" && i.data.length > 0 && IMAGE_MEDIA_TYPES.includes(i.mediaType));
210
+ }
182
211
  var ProviderError = class extends Error {
183
212
  name = "ProviderError";
184
213
  kind;
@@ -567,7 +596,12 @@ function buildAnthropicRequest(opts) {
567
596
  }
568
597
  continue;
569
598
  }
570
- convo.push({ role: m.role, content: typeof m.content === "string" ? m.content : "" });
599
+ const text = typeof m.content === "string" ? m.content : "";
600
+ const images = imagesOf(m);
601
+ convo.push(images.length ? { role: m.role, content: [
602
+ ...images.map((i) => ({ type: "image", source: { type: "base64", media_type: i.mediaType, data: i.data } })),
603
+ { type: "text", text }
604
+ ] } : { role: m.role, content: text });
571
605
  }
572
606
  const body = {
573
607
  model: opts.model,
@@ -807,7 +841,10 @@ function buildGeminiRequest(opts) {
807
841
  const role = m.role === "user" ? "user" : "model";
808
842
  contents.push({
809
843
  role,
810
- parts: [{ text: typeof m.content === "string" ? m.content : "" }]
844
+ parts: [
845
+ ...imagesOf(m).map((i) => ({ inline_data: { mime_type: i.mediaType, data: i.data } })),
846
+ { text: typeof m.content === "string" ? m.content : "" }
847
+ ]
811
848
  });
812
849
  }
813
850
  const body = {
@@ -975,10 +1012,11 @@ function supportsNativeJsonSchema(provider, model) {
975
1012
  return true;
976
1013
  }
977
1014
  function buildOpenAICompatibleRequest(opts) {
978
- const msgs = opts.messages.map((m) => ({
979
- role: m.role,
980
- content: typeof m.content === "string" ? m.content : ""
981
- }));
1015
+ const msgs = opts.messages.map((m) => {
1016
+ const text = typeof m.content === "string" ? m.content : "";
1017
+ const images = imagesOf(m);
1018
+ 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 };
1019
+ });
982
1020
  const body = {
983
1021
  model: opts.model,
984
1022
  messages: msgs
@@ -1286,12 +1324,238 @@ function makeGeminiProvider(model, apiKey, opts, fallbackModels = []) {
1286
1324
  };
1287
1325
  }
1288
1326
 
1327
+ // src/core/reasoning-upgrade.ts
1328
+ var UPGRADE_MODEL = "claude-fable-5";
1329
+ var UPGRADE_LABEL = "Fable 5";
1330
+ function isUpgradeRequested(args) {
1331
+ if (args["upgrade"] === true) return true;
1332
+ const reasoning = args["reasoning"];
1333
+ if (typeof reasoning === "string" && reasoning.trim().toLowerCase() === "max") {
1334
+ return true;
1335
+ }
1336
+ const model = args["model"];
1337
+ if (typeof model === "string") {
1338
+ const m = model.trim().toLowerCase();
1339
+ if (m === "fable5" || m === "fable-5" || m === "fable" || m === UPGRADE_MODEL) {
1340
+ return true;
1341
+ }
1342
+ }
1343
+ return false;
1344
+ }
1345
+ var HEAVY_TERMS = [
1346
+ /\bprov(e|ing|en)\b|\bproof\b|\btheorem\b|\bderiv(e|ation)\b/i,
1347
+ /\barchitect(ure|ing)?\b|\bdesign\s+(a|the|an)\b|\bhigh[-\s]?level\s+design\b/i,
1348
+ /\btrade[-\s]?offs?\b|\bpros?\s+and\s+cons?\b|\bweigh\b/i,
1349
+ /\boptimi[sz]e\b|\bcomplexity\b|\bO\(|\balgorithm(ic)?\b/i,
1350
+ /\brace\s+condition\b|\bconcurren(t|cy)\b|\bdeadlock\b|\bdistributed\b|\bconsensus\b/i,
1351
+ /\broot[-\s]?cause\b|\bdebug(ging)?\b|\bwhy\s+(does|is|would|might)\b/i,
1352
+ /\bsecurity\b|\bthreat\s+model\b|\bexploit\b|\bvulnerabilit(y|ies)\b/i,
1353
+ /\bmigrat(e|ion)\b|\brefactor\b|\bre[-\s]?architect\b/i,
1354
+ /\binvariant\b|\bcorrectness\b|\bedge\s+cases?\b|\bformal(ly)?\b/i,
1355
+ /\bstrateg(y|ic|ies)\b|\bplan\s+(the|a|an|out)\b|\broadmap\b/i,
1356
+ /\breason\s+(through|about)\b|\bstep[-\s]?by[-\s]?step\b|\bthink\s+deeply\b/i,
1357
+ /\bnp[-\s]?(hard|complete)\b|\bgraph\b|\bstate\s+machine\b|\bconstraint\b/i
1358
+ ];
1359
+ var DIFFICULTY_TERMS = /\b(hard|tricky|subtle|complex|complicated|non[-\s]?trivial|thorny|gnarly|difficult|deep)\b/i;
1360
+ function assessReasoning(text) {
1361
+ const s = typeof text === "string" ? text : "";
1362
+ const signals = [];
1363
+ let score = 0;
1364
+ let termHits = 0;
1365
+ for (const re of HEAVY_TERMS) {
1366
+ if (re.test(s)) termHits += 1;
1367
+ }
1368
+ if (termHits > 0) {
1369
+ const add = Math.min(termHits, 4);
1370
+ score += add;
1371
+ signals.push(`${termHits} reasoning cue${termHits === 1 ? "" : "s"}`);
1372
+ }
1373
+ if (DIFFICULTY_TERMS.test(s)) {
1374
+ score += 1;
1375
+ signals.push("explicit difficulty marker");
1376
+ }
1377
+ const words = s.split(/\s+/).filter(Boolean).length;
1378
+ if (words >= 120) {
1379
+ score += 2;
1380
+ signals.push("long, detailed prompt");
1381
+ } else if (words >= 50) {
1382
+ score += 1;
1383
+ signals.push("substantial prompt");
1384
+ }
1385
+ const constraints = (s.match(/\b(must|should|need to|ensure|require[ds]?|constraint)\b/gi) ?? []).length + (s.match(/(^|\n)\s*(?:[-*]|\d+[.)])\s+/g) ?? []).length;
1386
+ if (constraints >= 3) {
1387
+ score += 1;
1388
+ signals.push(`${constraints} constraints/steps`);
1389
+ }
1390
+ const heavy = score >= 3;
1391
+ const reason = heavy ? `Task looks reasoning-heavy (${signals.join(", ") || "score " + score}).` : `Task looks routine (score ${score}).`;
1392
+ return { heavy, score, reason, signals };
1393
+ }
1394
+ function buildSuggestedUpgrade(toolName, assessment) {
1395
+ return {
1396
+ model: UPGRADE_MODEL,
1397
+ label: UPGRADE_LABEL,
1398
+ reason: assessment.reason,
1399
+ 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.`
1400
+ };
1401
+ }
1402
+
1403
+ // src/core/super-mode.ts
1404
+ var SUPER_MODELS = {
1405
+ // Fable 5.1 verified present on the account; 5 as the fallback for accounts
1406
+ // that haven't been granted 5.1 yet.
1407
+ anthropic: ["claude-fable-5-1", UPGRADE_MODEL],
1408
+ // Astra is live (verified against the account's model list 2026-09-23).
1409
+ // The fallback is named explicitly rather than reusing CO_REASON_MODEL:
1410
+ // that constant means "OpenAI's top reasoning model" for the max-reasoning
1411
+ // co-reasoner, which is a different question from "what should super fall
1412
+ // back to". Sharing the symbol silently coupled two unrelated decisions.
1413
+ openai: ["gpt-6-astra", "gpt-6-sol"],
1414
+ // Explicitly left as-is per product decision. Note grok-4-latest is not in
1415
+ // xAI's published model list, but it resolves in practice — real panel calls
1416
+ // on it succeeded as recently as 2026-08-30 — so it is an unlisted alias
1417
+ // rather than a dead id.
1418
+ xai: [DEFAULT_MODELS["xai"]],
1419
+ // 3.8 Flash is now BOTH the everyday default and the super pick for Gemini,
1420
+ // so this can no longer borrow DEFAULT_MODELS as its fallback — that would
1421
+ // repeat the same model and retry one that just failed. Named explicitly.
1422
+ gemini: ["gemini-3.8-flash", "gemini-3.1-pro-preview"],
1423
+ kimi: [DEFAULT_MODELS["kimi"]],
1424
+ qwen: [DEFAULT_MODELS["qwen"]]
1425
+ };
1426
+ var SUPER_ENV_VARS = {
1427
+ anthropic: "ANTHROPIC_SUPER_MODEL",
1428
+ openai: "OPENAI_SUPER_MODEL",
1429
+ xai: "XAI_SUPER_MODEL",
1430
+ gemini: "GEMINI_SUPER_MODEL",
1431
+ kimi: "KIMI_SUPER_MODEL",
1432
+ qwen: "QWEN_SUPER_MODEL",
1433
+ mistral: "MISTRAL_SUPER_MODEL",
1434
+ groq: "GROQ_SUPER_MODEL",
1435
+ deepseek: "DEEPSEEK_SUPER_MODEL"
1436
+ };
1437
+ function parseChain(v) {
1438
+ if (!v) return [];
1439
+ const out = [];
1440
+ for (const raw of v.split(",")) {
1441
+ const m = raw.trim();
1442
+ if (m && !out.includes(m)) out.push(m);
1443
+ }
1444
+ return out;
1445
+ }
1446
+ var superEnvStore = new AsyncLocalStorage();
1447
+ function withSuperEnv(env, fn) {
1448
+ return superEnvStore.run(env, fn);
1449
+ }
1450
+ var currentEnv = () => superEnvStore.getStore() ?? process.env;
1451
+ function superChainFor(provider, env = currentEnv()) {
1452
+ const varName = SUPER_ENV_VARS[provider];
1453
+ const user = varName ? parseChain(env[varName]) : [];
1454
+ if (user.length > 0) {
1455
+ const fallbacks = varName ? parseChain(env[`${varName}_FALLBACKS`]) : [];
1456
+ return [...user, ...fallbacks.filter((m) => !user.includes(m))];
1457
+ }
1458
+ return SUPER_MODELS[provider] ?? [];
1459
+ }
1460
+ function superPrimary(provider, env = currentEnv()) {
1461
+ return superChainFor(provider, env)[0];
1462
+ }
1463
+ var SUPER_PROVIDER_NAMES = Object.keys(SUPER_MODELS);
1464
+ function isSuperRequested(args) {
1465
+ return args["super"] === true;
1466
+ }
1467
+ function retargetForSuper(p, env = currentEnv()) {
1468
+ const chain = superChainFor(p.name, env);
1469
+ if (chain.length === 0) return p;
1470
+ const [primary, ...fallbacks] = chain;
1471
+ return retargetProvider(p, primary, fallbacks);
1472
+ }
1473
+ var SUPER_BUDGET_MULTIPLIER = 3;
1474
+ var SUPER_BUDGET_CEILING = 16e3;
1475
+ function superBudget(base) {
1476
+ return Math.min(base * SUPER_BUDGET_MULTIPLIER, SUPER_BUDGET_CEILING);
1477
+ }
1478
+
1479
+ // src/core/moderator.ts
1480
+ var DEFAULT_MODERATOR = "anthropic";
1481
+ var PROVIDER_ALIASES = {
1482
+ grok: "xai",
1483
+ claude: "anthropic",
1484
+ google: "gemini",
1485
+ chatgpt: "openai",
1486
+ gpt: "openai",
1487
+ moonshot: "kimi"
1488
+ };
1489
+ function parseModeratorSpec(raw) {
1490
+ if (typeof raw !== "string") return null;
1491
+ const s = raw.trim();
1492
+ if (!s) return null;
1493
+ const i = s.indexOf(":");
1494
+ const providerPart = (i < 0 ? s : s.slice(0, i)).trim().toLowerCase();
1495
+ const modelPart = i < 0 ? "" : s.slice(i + 1).trim();
1496
+ if (!providerPart) return null;
1497
+ return {
1498
+ provider: PROVIDER_ALIASES[providerPart] ?? providerPart,
1499
+ model: modelPart || null
1500
+ };
1501
+ }
1502
+ function moderatorProviderName(raw, fallback = DEFAULT_MODERATOR) {
1503
+ return (parseModeratorSpec(raw) ?? parseModeratorSpec(fallback))?.provider ?? DEFAULT_MODERATOR;
1504
+ }
1505
+ function pickModeratorSpec(...candidates) {
1506
+ for (const c of candidates) {
1507
+ const s = parseModeratorSpec(c);
1508
+ if (s) return s;
1509
+ }
1510
+ return { provider: DEFAULT_MODERATOR, model: null };
1511
+ }
1512
+ function resolveModerator(providers, ...candidates) {
1513
+ const spec = pickModeratorSpec(...candidates);
1514
+ const base = providers[spec.provider];
1515
+ if (!base) return { provider: void 0, spec, pinned: false };
1516
+ if (!spec.model) return { provider: base, spec, pinned: false };
1517
+ return { provider: retargetProvider(base, spec.model), spec, pinned: true };
1518
+ }
1519
+ function synthesisSeat(o) {
1520
+ const { moderator, pinned, superRequested, upgradeRequested } = o;
1521
+ const wantsMore = superRequested || upgradeRequested;
1522
+ if (!wantsMore) return { provider: moderator, upgraded: false, label: null, skipReason: null };
1523
+ if (pinned) {
1524
+ return {
1525
+ provider: moderator,
1526
+ upgraded: false,
1527
+ label: null,
1528
+ skipReason: `the moderator is pinned to ${moderator.name}:${moderator.model}; a pinned model is used as-is.`
1529
+ };
1530
+ }
1531
+ if (moderator.name === "anthropic" && !superRequested) {
1532
+ return { provider: retargetProvider(moderator, UPGRADE_MODEL), upgraded: true, label: UPGRADE_LABEL, skipReason: null };
1533
+ }
1534
+ if (superChainFor(moderator.name).length === 0) {
1535
+ return {
1536
+ provider: moderator,
1537
+ upgraded: false,
1538
+ label: null,
1539
+ skipReason: `no flagship model is known for ${moderator.name}; the moderator ran at ${moderator.model}.`
1540
+ };
1541
+ }
1542
+ const seat = retargetForSuper(moderator);
1543
+ const label = moderator.name === "anthropic" ? UPGRADE_LABEL : seat.model;
1544
+ return { provider: seat, upgraded: true, label, skipReason: null };
1545
+ }
1546
+
1547
+ // src/entrypoints/embed.ts
1548
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
1549
+ import { existsSync as existsSync11 } from "fs";
1550
+ import path12 from "path";
1551
+ import { fileURLToPath as fileURLToPath2 } from "url";
1552
+
1289
1553
  // src/tools/index.ts
1290
1554
  import { z } from "zod";
1291
1555
 
1292
1556
  // src/server-meta.ts
1293
1557
  var SERVER_NAME = "crosscheck-agent";
1294
- var SERVER_VERSION = true ? "0.2.29" : "0.0.0-dev";
1558
+ var SERVER_VERSION = true ? "0.2.31" : "0.0.0-dev";
1295
1559
 
1296
1560
  // src/tools/audit.ts
1297
1561
  import { readdirSync, readFileSync as readFileSync3, statSync } from "fs";
@@ -1974,33 +2238,6 @@ function operatorCeiling(purpose, provider) {
1974
2238
  return null;
1975
2239
  }
1976
2240
 
1977
- // src/core/retarget.ts
1978
- function retargetProvider(p, newModel, fallbackModels) {
1979
- const chain = fallbackModels && fallbackModels.length > 0 ? fallbackModels : void 0;
1980
- if (p.model === newModel && chain === void 0) return p;
1981
- return {
1982
- name: p.name,
1983
- model: newModel,
1984
- ...chain ? { fallbackModels: chain } : {},
1985
- send: (args) => p.send({ ...args, modelOverride: newModel })
1986
- };
1987
- }
1988
- async function loadProviderWeights(storage, names) {
1989
- const out = {};
1990
- for (const raw of names) {
1991
- const name = raw.toLowerCase();
1992
- if (name in out) continue;
1993
- const row = await storage.getProviderStats(name);
1994
- if (!row) {
1995
- out[name] = 0.5;
1996
- continue;
1997
- }
1998
- const total = row.wins + row.losses + row.abstains;
1999
- out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
2000
- }
2001
- return out;
2002
- }
2003
-
2004
2241
  // src/core/model-fallback.ts
2005
2242
  async function sendWithModelFallback(provider, args) {
2006
2243
  const chain = [provider.model, ...provider.fallbackModels ?? []];
@@ -3067,82 +3304,6 @@ function selectForDifficulty(args) {
3067
3304
  };
3068
3305
  }
3069
3306
 
3070
- // src/core/reasoning-upgrade.ts
3071
- var UPGRADE_MODEL = "claude-fable-5";
3072
- var UPGRADE_LABEL = "Fable 5";
3073
- function isUpgradeRequested(args) {
3074
- if (args["upgrade"] === true) return true;
3075
- const reasoning = args["reasoning"];
3076
- if (typeof reasoning === "string" && reasoning.trim().toLowerCase() === "max") {
3077
- return true;
3078
- }
3079
- const model = args["model"];
3080
- if (typeof model === "string") {
3081
- const m = model.trim().toLowerCase();
3082
- if (m === "fable5" || m === "fable-5" || m === "fable" || m === UPGRADE_MODEL) {
3083
- return true;
3084
- }
3085
- }
3086
- return false;
3087
- }
3088
- var HEAVY_TERMS = [
3089
- /\bprov(e|ing|en)\b|\bproof\b|\btheorem\b|\bderiv(e|ation)\b/i,
3090
- /\barchitect(ure|ing)?\b|\bdesign\s+(a|the|an)\b|\bhigh[-\s]?level\s+design\b/i,
3091
- /\btrade[-\s]?offs?\b|\bpros?\s+and\s+cons?\b|\bweigh\b/i,
3092
- /\boptimi[sz]e\b|\bcomplexity\b|\bO\(|\balgorithm(ic)?\b/i,
3093
- /\brace\s+condition\b|\bconcurren(t|cy)\b|\bdeadlock\b|\bdistributed\b|\bconsensus\b/i,
3094
- /\broot[-\s]?cause\b|\bdebug(ging)?\b|\bwhy\s+(does|is|would|might)\b/i,
3095
- /\bsecurity\b|\bthreat\s+model\b|\bexploit\b|\bvulnerabilit(y|ies)\b/i,
3096
- /\bmigrat(e|ion)\b|\brefactor\b|\bre[-\s]?architect\b/i,
3097
- /\binvariant\b|\bcorrectness\b|\bedge\s+cases?\b|\bformal(ly)?\b/i,
3098
- /\bstrateg(y|ic|ies)\b|\bplan\s+(the|a|an|out)\b|\broadmap\b/i,
3099
- /\breason\s+(through|about)\b|\bstep[-\s]?by[-\s]?step\b|\bthink\s+deeply\b/i,
3100
- /\bnp[-\s]?(hard|complete)\b|\bgraph\b|\bstate\s+machine\b|\bconstraint\b/i
3101
- ];
3102
- var DIFFICULTY_TERMS = /\b(hard|tricky|subtle|complex|complicated|non[-\s]?trivial|thorny|gnarly|difficult|deep)\b/i;
3103
- function assessReasoning(text) {
3104
- const s = typeof text === "string" ? text : "";
3105
- const signals = [];
3106
- let score = 0;
3107
- let termHits = 0;
3108
- for (const re of HEAVY_TERMS) {
3109
- if (re.test(s)) termHits += 1;
3110
- }
3111
- if (termHits > 0) {
3112
- const add = Math.min(termHits, 4);
3113
- score += add;
3114
- signals.push(`${termHits} reasoning cue${termHits === 1 ? "" : "s"}`);
3115
- }
3116
- if (DIFFICULTY_TERMS.test(s)) {
3117
- score += 1;
3118
- signals.push("explicit difficulty marker");
3119
- }
3120
- const words = s.split(/\s+/).filter(Boolean).length;
3121
- if (words >= 120) {
3122
- score += 2;
3123
- signals.push("long, detailed prompt");
3124
- } else if (words >= 50) {
3125
- score += 1;
3126
- signals.push("substantial prompt");
3127
- }
3128
- const constraints = (s.match(/\b(must|should|need to|ensure|require[ds]?|constraint)\b/gi) ?? []).length + (s.match(/(^|\n)\s*(?:[-*]|\d+[.)])\s+/g) ?? []).length;
3129
- if (constraints >= 3) {
3130
- score += 1;
3131
- signals.push(`${constraints} constraints/steps`);
3132
- }
3133
- const heavy = score >= 3;
3134
- const reason = heavy ? `Task looks reasoning-heavy (${signals.join(", ") || "score " + score}).` : `Task looks routine (score ${score}).`;
3135
- return { heavy, score, reason, signals };
3136
- }
3137
- function buildSuggestedUpgrade(toolName, assessment) {
3138
- return {
3139
- model: UPGRADE_MODEL,
3140
- label: UPGRADE_LABEL,
3141
- reason: assessment.reason,
3142
- 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.`
3143
- };
3144
- }
3145
-
3146
3307
  // src/tools/audit.ts
3147
3308
  var AUDIT_OBVIOUS_FAILURE_HIGH = 0.3;
3148
3309
  var AUDIT_OBVIOUS_FAILURE_MED = 0.2;
@@ -3440,9 +3601,14 @@ ${rubricText}`;
3440
3601
  );
3441
3602
  }
3442
3603
  const upgradeRequested = isUpgradeRequested(args);
3443
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
3444
- const auditorForCall = upgraded ? retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL) : auditor;
3445
- const coProvider = pickCoReasoner(opts.providers, upgradeRequested);
3604
+ const seat = synthesisSeat({ moderator: auditor, pinned: false, superRequested: false, upgradeRequested });
3605
+ const upgraded = seat.upgraded;
3606
+ const auditorForCall = seat.provider;
3607
+ const coProvider = pickCoReasoner(
3608
+ opts.providers,
3609
+ upgradeRequested,
3610
+ auditorForCall.name === "openai" ? auditorForCall.model : void 0
3611
+ );
3446
3612
  const coReason = await coReasonThenInject({
3447
3613
  provider: coProvider,
3448
3614
  messages: msgs,
@@ -3525,15 +3691,16 @@ ${rubricText}`;
3525
3691
  if (upgraded) {
3526
3692
  result["reasoning_upgrade"] = {
3527
3693
  applied: true,
3528
- model: UPGRADE_MODEL,
3529
- label: UPGRADE_LABEL,
3694
+ provider: auditorForCall.name,
3695
+ model: auditorForCall.model,
3696
+ label: seat.label ?? auditorForCall.model,
3530
3697
  seat: "auditor",
3531
3698
  ...coReasoned ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
3532
3699
  };
3533
3700
  } else if (upgradeRequested) {
3534
3701
  result["reasoning_upgrade"] = {
3535
3702
  applied: false,
3536
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
3703
+ reason: seat.skipReason ?? "the auditor couldn't be upgraded."
3537
3704
  };
3538
3705
  } else {
3539
3706
  const assessment = assessReasoning(
@@ -3616,7 +3783,7 @@ function pickAuditor(providers, exclude, explicit, moderatorName, allowlist, che
3616
3783
  }
3617
3784
  }
3618
3785
  }
3619
- const mod = providers[moderatorName.toLowerCase()];
3786
+ const mod = resolveModerator(providers, moderatorName).provider;
3620
3787
  if (mod !== void 0 && !exclude.has(mod.name) && (allowlist === null || allowlist.includes(mod.name))) {
3621
3788
  return { auditor: mod, reason: null };
3622
3789
  }
@@ -3935,9 +4102,7 @@ function pickPanelJudge(providers, moderatorName, pricing) {
3935
4102
  return entry.model && entry.model !== base.model ? retargetProvider(base, entry.model) : base;
3936
4103
  }
3937
4104
  }
3938
- const mod = providers[moderatorName.toLowerCase()];
3939
- if (mod !== void 0) return mod;
3940
- return null;
4105
+ return resolveModerator(providers, moderatorName).provider ?? null;
3941
4106
  }
3942
4107
  var AGREEMENT_CHECK_SCHEMA = {
3943
4108
  type: "object",
@@ -4254,77 +4419,6 @@ async function pickAutoPanel(storage, purpose, n, providers, allowlist) {
4254
4419
  return { providers: ranked.slice(0, Math.max(1, n)), coldStart: true };
4255
4420
  }
4256
4421
 
4257
- // src/core/super-mode.ts
4258
- var SUPER_MODELS = {
4259
- // Fable 5.1 verified present on the account; 5 as the fallback for accounts
4260
- // that haven't been granted 5.1 yet.
4261
- anthropic: ["claude-fable-5-1", UPGRADE_MODEL],
4262
- // Astra is live (verified against the account's model list 2026-09-23).
4263
- // The fallback is named explicitly rather than reusing CO_REASON_MODEL:
4264
- // that constant means "OpenAI's top reasoning model" for the max-reasoning
4265
- // co-reasoner, which is a different question from "what should super fall
4266
- // back to". Sharing the symbol silently coupled two unrelated decisions.
4267
- openai: ["gpt-6-astra", "gpt-6-sol"],
4268
- // Explicitly left as-is per product decision. Note grok-4-latest is not in
4269
- // xAI's published model list, but it resolves in practice — real panel calls
4270
- // on it succeeded as recently as 2026-08-30 — so it is an unlisted alias
4271
- // rather than a dead id.
4272
- xai: [DEFAULT_MODELS["xai"]],
4273
- // 3.8 Flash is now BOTH the everyday default and the super pick for Gemini,
4274
- // so this can no longer borrow DEFAULT_MODELS as its fallback — that would
4275
- // repeat the same model and retry one that just failed. Named explicitly.
4276
- gemini: ["gemini-3.8-flash", "gemini-3.1-pro-preview"],
4277
- kimi: [DEFAULT_MODELS["kimi"]],
4278
- qwen: [DEFAULT_MODELS["qwen"]]
4279
- };
4280
- var SUPER_ENV_VARS = {
4281
- anthropic: "ANTHROPIC_SUPER_MODEL",
4282
- openai: "OPENAI_SUPER_MODEL",
4283
- xai: "XAI_SUPER_MODEL",
4284
- gemini: "GEMINI_SUPER_MODEL",
4285
- kimi: "KIMI_SUPER_MODEL",
4286
- qwen: "QWEN_SUPER_MODEL",
4287
- mistral: "MISTRAL_SUPER_MODEL",
4288
- groq: "GROQ_SUPER_MODEL",
4289
- deepseek: "DEEPSEEK_SUPER_MODEL"
4290
- };
4291
- function parseChain(v) {
4292
- if (!v) return [];
4293
- const out = [];
4294
- for (const raw of v.split(",")) {
4295
- const m = raw.trim();
4296
- if (m && !out.includes(m)) out.push(m);
4297
- }
4298
- return out;
4299
- }
4300
- function superChainFor(provider, env = process.env) {
4301
- const varName = SUPER_ENV_VARS[provider];
4302
- const user = varName ? parseChain(env[varName]) : [];
4303
- if (user.length > 0) {
4304
- const fallbacks = varName ? parseChain(env[`${varName}_FALLBACKS`]) : [];
4305
- return [...user, ...fallbacks.filter((m) => !user.includes(m))];
4306
- }
4307
- return SUPER_MODELS[provider] ?? [];
4308
- }
4309
- function superPrimary(provider, env = process.env) {
4310
- return superChainFor(provider, env)[0];
4311
- }
4312
- var SUPER_PROVIDER_NAMES = Object.keys(SUPER_MODELS);
4313
- function isSuperRequested(args) {
4314
- return args["super"] === true;
4315
- }
4316
- function retargetForSuper(p, env = process.env) {
4317
- const chain = superChainFor(p.name, env);
4318
- if (chain.length === 0) return p;
4319
- const [primary, ...fallbacks] = chain;
4320
- return retargetProvider(p, primary, fallbacks);
4321
- }
4322
- var SUPER_BUDGET_MULTIPLIER = 3;
4323
- var SUPER_BUDGET_CEILING = 16e3;
4324
- function superBudget(base) {
4325
- return Math.min(base * SUPER_BUDGET_MULTIPLIER, SUPER_BUDGET_CEILING);
4326
- }
4327
-
4328
4422
  // src/core/session-memory.ts
4329
4423
  var SESSION_MEMORY_DEFAULT_LIMIT = 50;
4330
4424
  var SESSION_MEMORY_INJECT_BUDGET_CHARS = 4e3;
@@ -6391,9 +6485,9 @@ import path6 from "path";
6391
6485
  import { performance as performance7 } from "perf_hooks";
6392
6486
 
6393
6487
  // src/core/progress.ts
6394
- import { AsyncLocalStorage } from "async_hooks";
6488
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
6395
6489
  import { performance as performance6 } from "perf_hooks";
6396
- var progressStorage = new AsyncLocalStorage();
6490
+ var progressStorage = new AsyncLocalStorage2();
6397
6491
  async function withProgress(token, sendNotification, fn) {
6398
6492
  if (token === void 0 || token === null) {
6399
6493
  return fn();
@@ -6705,8 +6799,9 @@ async function runOrchestrate(args, opts) {
6705
6799
  blocked
6706
6800
  };
6707
6801
  }
6708
- const moderatorName = typeof args["moderator"] === "string" && args["moderator"] ? args["moderator"] : opts.moderator ?? "anthropic";
6709
- const moderator = opts.providers[moderatorName.toLowerCase()] ?? selected[0];
6802
+ const resolvedModerator = resolveModerator(opts.providers, args["moderator"], opts.moderator);
6803
+ const moderator = resolvedModerator.provider ?? selected[0];
6804
+ const moderatorChosen = resolvedModerator.provider !== void 0 && (resolvedModerator.pinned || parseModeratorSpec(args["moderator"]) !== null);
6710
6805
  const failFast = boolArg2(args["fail_fast"], false);
6711
6806
  const planOnly = boolArg2(args["plan_only"], false);
6712
6807
  const cheapMode = boolArg2(args["cheap_mode"], opts.ctx?.cheap_mode ?? false);
@@ -7190,11 +7285,17 @@ ${recombineSys}` : recombineSys },
7190
7285
  let synthProvider = moderator;
7191
7286
  let synthModel = null;
7192
7287
  const upgradeRequested = isUpgradeRequested(args);
7193
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
7288
+ const seat = synthesisSeat({
7289
+ moderator,
7290
+ pinned: resolvedModerator.provider !== void 0 && resolvedModerator.pinned,
7291
+ superRequested: false,
7292
+ upgradeRequested
7293
+ });
7294
+ const upgraded = seat.upgraded;
7194
7295
  if (upgraded) {
7195
- synthProvider = retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL);
7196
- synthModel = `anthropic/${UPGRADE_MODEL}`;
7197
- } else if (cheapMode && opts.pricing) {
7296
+ synthProvider = seat.provider;
7297
+ synthModel = `${seat.provider.name}/${seat.provider.model}`;
7298
+ } else if (cheapMode && opts.pricing && !moderatorChosen) {
7198
7299
  const pick = selectForDifficulty({
7199
7300
  pricing: opts.pricing,
7200
7301
  tier: "med",
@@ -7220,7 +7321,11 @@ ${recombineSys}` : recombineSys },
7220
7321
  provider: synthProvider.name
7221
7322
  } }
7222
7323
  );
7223
- const coProvider = pickCoReasoner(opts.providers, upgradeRequested);
7324
+ const coProvider = pickCoReasoner(
7325
+ opts.providers,
7326
+ upgradeRequested,
7327
+ synthProvider.name === "openai" ? synthProvider.model : void 0
7328
+ );
7224
7329
  const coReason = await coReasonThenInject({
7225
7330
  provider: coProvider,
7226
7331
  messages: recMsgs,
@@ -7270,7 +7375,8 @@ ${recombineSys}` : recombineSys },
7270
7375
  missing,
7271
7376
  partial,
7272
7377
  fail_fast: failFast,
7273
- cheap_mode: cheapMode
7378
+ cheap_mode: cheapMode,
7379
+ moderator: { provider: moderator.name, model: moderator.model }
7274
7380
  };
7275
7381
  if (personaMeta.used !== null) result["persona"] = personaMeta;
7276
7382
  if (synthModel) result["synth_model"] = synthModel;
@@ -7278,15 +7384,16 @@ ${recombineSys}` : recombineSys },
7278
7384
  if (upgraded) {
7279
7385
  result["reasoning_upgrade"] = {
7280
7386
  applied: true,
7281
- model: UPGRADE_MODEL,
7282
- label: UPGRADE_LABEL,
7387
+ provider: seat.provider.name,
7388
+ model: seat.provider.model,
7389
+ label: seat.label ?? seat.provider.model,
7283
7390
  seat: "recombine/synthesis",
7284
7391
  ...coReasoned ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
7285
7392
  };
7286
7393
  } else if (upgradeRequested) {
7287
7394
  result["reasoning_upgrade"] = {
7288
7395
  applied: false,
7289
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
7396
+ reason: seat.skipReason ?? "the moderator couldn't be upgraded."
7290
7397
  };
7291
7398
  } else if (goal) {
7292
7399
  const assessment = assessReasoning(goal);
@@ -8468,13 +8575,24 @@ async function runCoordinate(args, opts) {
8468
8575
  };
8469
8576
  }
8470
8577
  const proposerName = typeof args["proposer"] === "string" && args["proposer"] ? args["proposer"] : selected[0].name;
8471
- const synthName = typeof args["synthesizer"] === "string" && args["synthesizer"] ? args["synthesizer"] : typeof args["moderator"] === "string" && args["moderator"] ? args["moderator"] : opts.moderator ?? "anthropic";
8578
+ const resolvedSynth = resolveModerator(
8579
+ opts.providers,
8580
+ args["synthesizer"],
8581
+ args["moderator"],
8582
+ opts.moderator
8583
+ );
8472
8584
  const proposer = opts.providers[proposerName.toLowerCase()] ?? selected[0];
8473
- const synth = opts.providers[synthName.toLowerCase()] ?? proposer;
8585
+ const synth = resolvedSynth.provider ?? proposer;
8474
8586
  const proposerForCall = superRequested ? retargetForSuper(proposer) : proposer;
8475
8587
  const upgradeRequested = isUpgradeRequested(args) || superRequested;
8476
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
8477
- const synthForCall = superRequested && opts.providers["anthropic"] ? retargetForSuper(opts.providers["anthropic"]) : upgraded ? retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL) : synth;
8588
+ const seat = synthesisSeat({
8589
+ moderator: synth,
8590
+ pinned: resolvedSynth.provider !== void 0 && resolvedSynth.pinned,
8591
+ superRequested,
8592
+ upgradeRequested
8593
+ });
8594
+ const upgraded = seat.upgraded;
8595
+ const synthForCall = seat.provider;
8478
8596
  let critics = [];
8479
8597
  if (Array.isArray(args["critics"])) {
8480
8598
  for (const n of args["critics"]) {
@@ -8625,7 +8743,11 @@ CRITIQUES:
8625
8743
  ${critiqueBlock}`
8626
8744
  }
8627
8745
  ];
8628
- const coProvider = pickCoReasoner(opts.providers, upgradeRequested);
8746
+ const coProvider = pickCoReasoner(
8747
+ opts.providers,
8748
+ upgradeRequested,
8749
+ synthForCall.name === "openai" ? synthForCall.model : void 0
8750
+ );
8629
8751
  const coReason = await coReasonThenInject({
8630
8752
  provider: coProvider,
8631
8753
  messages: synthMessages,
@@ -8670,7 +8792,8 @@ ${critiqueBlock}`
8670
8792
  critique_answers: scannedCritiques,
8671
8793
  critique_structured: critiqueStructured,
8672
8794
  synthesis_answer: synthAns,
8673
- synthesis_structured: synthObj
8795
+ synthesis_structured: synthObj,
8796
+ moderator: { provider: synthForCall.name, model: synthForCall.model }
8674
8797
  };
8675
8798
  if (synthErrs.length > 0) result["synthesis_errors"] = synthErrs;
8676
8799
  if (canaryLeaks.length > 0) result["canary_leaks"] = canaryLeaks;
@@ -8688,15 +8811,16 @@ ${critiqueBlock}`
8688
8811
  result["reasoning_upgrade"] = {
8689
8812
  applied: true,
8690
8813
  // The model that actually ran the seat, not the one we'd have picked.
8691
- model: superRequested ? superPrimary("anthropic") ?? UPGRADE_MODEL : UPGRADE_MODEL,
8692
- label: UPGRADE_LABEL,
8814
+ provider: synthForCall.name,
8815
+ model: synthForCall.model,
8816
+ label: seat.label ?? synthForCall.model,
8693
8817
  seat: "synthesizer",
8694
8818
  ...coReasonAns ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
8695
8819
  };
8696
8820
  } else if (upgradeRequested) {
8697
8821
  result["reasoning_upgrade"] = {
8698
8822
  applied: false,
8699
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
8823
+ reason: seat.skipReason ?? "the synthesizer couldn't be upgraded."
8700
8824
  };
8701
8825
  } else {
8702
8826
  const assessment = assessReasoning(topic);
@@ -9302,10 +9426,12 @@ async function runDebate(args, opts) {
9302
9426
  const earlyStopThreshold = Number.isFinite(earlyStopThresholdRaw) ? earlyStopThresholdRaw : 0.7;
9303
9427
  let earlyStoppedRound = null;
9304
9428
  let agreementBlock = null;
9305
- const moderatorName = typeof args["moderator"] === "string" && args["moderator"] ? args["moderator"] : opts.moderator ?? "anthropic";
9306
- const moderator = opts.providers[moderatorName.toLowerCase()] ?? selected[0];
9429
+ const resolvedModerator = resolveModerator(opts.providers, args["moderator"], opts.moderator);
9430
+ const moderatorName = resolvedModerator.spec.provider;
9431
+ const moderator = resolvedModerator.provider ?? selected[0];
9307
9432
  const upgradeRequested = isUpgradeRequested(args) || superRequested;
9308
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
9433
+ const seat = moderator ? synthesisSeat({ moderator, pinned: resolvedModerator.pinned, superRequested, upgradeRequested }) : null;
9434
+ const upgraded = seat?.upgraded ?? false;
9309
9435
  for (let rnd = 1; rnd <= maxRounds; rnd++) {
9310
9436
  const roundMessages = [
9311
9437
  {
@@ -9390,8 +9516,10 @@ ${prior.body}` });
9390
9516
  let personaMeta = null;
9391
9517
  let coReasoned = false;
9392
9518
  let coReasonerModel = null;
9519
+ let moderatorUsed = null;
9393
9520
  if (moderator) {
9394
- const synthProvider = superRequested && opts.providers["anthropic"] ? retargetForSuper(opts.providers["anthropic"]) : upgraded ? retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL) : moderator;
9521
+ const synthProvider = seat?.provider ?? moderator;
9522
+ moderatorUsed = { provider: synthProvider.name, model: synthProvider.model };
9395
9523
  const condensed = buildModeratorTranscript(transcript);
9396
9524
  const persona = buildPersonaInjection({
9397
9525
  toolName: "debate",
@@ -9417,7 +9545,7 @@ TRANSCRIPT:
9417
9545
  ${condensed}`
9418
9546
  }
9419
9547
  ];
9420
- const openAiSeat = selected.find((p) => p.name === "openai")?.model;
9548
+ const openAiSeat = synthProvider.name === "openai" ? synthProvider.model : selected.find((p) => p.name === "openai")?.model;
9421
9549
  const coProvider = pickCoReasoner(opts.providers, upgradeRequested, openAiSeat);
9422
9550
  const coReason = await coReasonThenInject({
9423
9551
  provider: coProvider,
@@ -9477,20 +9605,22 @@ ${condensed}`
9477
9605
  transcript,
9478
9606
  synthesis
9479
9607
  };
9608
+ if (moderatorUsed) result["moderator"] = moderatorUsed;
9480
9609
  if (personaMeta && personaMeta.used !== null) result["persona"] = personaMeta;
9481
9610
  if (compactionMeta) result["round_compaction"] = compactionMeta;
9482
9611
  if (upgraded) {
9483
9612
  result["reasoning_upgrade"] = {
9484
9613
  applied: true,
9485
- model: superRequested ? superPrimary("anthropic") ?? UPGRADE_MODEL : UPGRADE_MODEL,
9486
- label: UPGRADE_LABEL,
9614
+ provider: seat.provider.name,
9615
+ model: seat.provider.model,
9616
+ label: seat.label ?? seat.provider.model,
9487
9617
  seat: "moderator synthesis",
9488
9618
  ...coReasoned && coReasonerModel ? { co_reasoner: { model: coReasonerModel, label: coReasonLabel(coReasonerModel) } } : {}
9489
9619
  };
9490
9620
  } else if (upgradeRequested) {
9491
9621
  result["reasoning_upgrade"] = {
9492
9622
  applied: false,
9493
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
9623
+ reason: seat?.skipReason ?? "no moderator was available for the upgrade."
9494
9624
  };
9495
9625
  } else {
9496
9626
  const assessment = assessReasoning(topic);
@@ -10119,12 +10249,25 @@ function runListProviders(args, opts) {
10119
10249
  }
10120
10250
  const result = {
10121
10251
  providers,
10122
- moderator_default: opts.moderatorDefault ?? "anthropic",
10252
+ moderator_default: opts.moderatorDefault ?? DEFAULT_MODERATOR,
10253
+ // Who will actually moderate right now: the configured spec if that
10254
+ // provider has a key, else the first available (what the tools fall to).
10255
+ moderator: describeModerator(opts.providers ?? {}, opts.moderatorDefault),
10123
10256
  usage_hint: USAGE_HINT
10124
10257
  };
10125
10258
  if (superRequested) result["super_mode"] = true;
10126
10259
  return result;
10127
10260
  }
10261
+ function describeModerator(providers, spec) {
10262
+ const r = resolveModerator(providers, spec);
10263
+ const p = r.provider ?? Object.values(providers)[0];
10264
+ return {
10265
+ spec: spec ?? DEFAULT_MODERATOR,
10266
+ provider: p?.name ?? null,
10267
+ model: p?.model ?? null,
10268
+ available: r.provider !== void 0
10269
+ };
10270
+ }
10128
10271
 
10129
10272
  // src/tools/models.ts
10130
10273
  var KNOWN_PROVIDERS7 = [
@@ -11674,7 +11817,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
11674
11817
  var DEFAULT_PACKAGE = "crosscheck-cli";
11675
11818
  var FETCH_TIMEOUT_MS = 3e3;
11676
11819
  function engineVersion() {
11677
- return true ? "0.2.29" : "0.0.0-dev";
11820
+ return true ? "0.2.31" : "0.0.0-dev";
11678
11821
  }
11679
11822
  function defaultUpdateCachePath() {
11680
11823
  const base = process.env["CROSSCHECK_DATA_DIR"] || path9.join(os.homedir() || os.tmpdir(), ".crosscheck");
@@ -12119,7 +12262,7 @@ var HELP_CATALOG = [
12119
12262
  primaryArg: "topic",
12120
12263
  summary: "Bounded multi-round debate between models, ending in a synthesized recommendation.",
12121
12264
  example: "xc debate should we adopt a monorepo?",
12122
- 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.`
12265
+ 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.`
12123
12266
  },
12124
12267
  {
12125
12268
  name: "coordinate",
@@ -12362,6 +12505,10 @@ function renderHelpCommand(entry) {
12362
12505
  }
12363
12506
 
12364
12507
  // src/tools/index.ts
12508
+ var MODERATOR_PARAM = {
12509
+ type: "string",
12510
+ 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`).'
12511
+ };
12365
12512
  function defineTool(opts) {
12366
12513
  return {
12367
12514
  name: opts.name,
@@ -12596,7 +12743,7 @@ function createTool(o, toolName, cheapDefault) {
12596
12743
  instruction: { type: "string" },
12597
12744
  documents: { type: "array", items: { type: "string" } },
12598
12745
  providers: { type: "array", items: { type: "string" } },
12599
- moderator: { type: "string" },
12746
+ moderator: MODERATOR_PARAM,
12600
12747
  constraints: { type: "string" },
12601
12748
  target_path: { type: "string" },
12602
12749
  session_id: { type: "string" },
@@ -12637,7 +12784,7 @@ function orchestrateTool(providers, allowlist, moderator, storage, pricing, brea
12637
12784
  dag: { type: "object", additionalProperties: true },
12638
12785
  context: { type: "string" },
12639
12786
  providers: { type: "array", items: { type: "string" } },
12640
- moderator: { type: "string" },
12787
+ moderator: MODERATOR_PARAM,
12641
12788
  fail_fast: { type: "boolean" },
12642
12789
  plan_only: { type: "boolean" },
12643
12790
  cheap_mode: { type: "boolean" },
@@ -13016,7 +13163,7 @@ function planTool(providers, allowlist, storage, breakers, transcriptsDir, repoR
13016
13163
  constraints: { type: "string" },
13017
13164
  context: { type: "string" },
13018
13165
  providers: { type: "array", items: { type: "string" } },
13019
- moderator: { type: "string" },
13166
+ moderator: MODERATOR_PARAM,
13020
13167
  session_id: { type: "string" },
13021
13168
  structured: { type: "boolean" },
13022
13169
  mode: { type: "string", enum: ["fast", "thorough"] },
@@ -13078,7 +13225,7 @@ function coordinateTool(providers, allowlist, innerCallers, storage, breakers, t
13078
13225
  providers: { type: "array", items: { type: "string" } },
13079
13226
  proposer: { type: "string" },
13080
13227
  synthesizer: { type: "string" },
13081
- moderator: { type: "string" },
13228
+ moderator: MODERATOR_PARAM,
13082
13229
  critics: { type: "array", items: { type: "string" } },
13083
13230
  session_id: { type: "string" },
13084
13231
  untrusted_input: { type: "boolean" },
@@ -13111,7 +13258,7 @@ function debateTool(providers, allowlist, innerCallers, storage, pricing, breake
13111
13258
  context: { type: "string" },
13112
13259
  max_rounds: { type: "integer", minimum: 1 },
13113
13260
  providers: { type: "array", items: { type: "string" } },
13114
- moderator: { type: "string" },
13261
+ moderator: MODERATOR_PARAM,
13115
13262
  session_id: { type: "string" },
13116
13263
  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.' },
13117
13264
  structured: { type: "boolean" },
@@ -13467,7 +13614,33 @@ function vaultId(engineName) {
13467
13614
  }
13468
13615
  var ACTIVITY_TEXT_CAP = 8e3;
13469
13616
  var ASKED_CAP = 280;
13470
- var runContext = new AsyncLocalStorage2();
13617
+ var VISION_PROVIDERS = ["anthropic", "openai", "gemini", "grok"];
13618
+ var NO_IMAGE_PURPOSES = /* @__PURE__ */ new Set(["early_stop", "extract", "canary", "classify"]);
13619
+ var MAX_RUN_IMAGES = 8;
13620
+ function withRunImages(args, provider, images) {
13621
+ if (!images?.length || NO_IMAGE_PURPOSES.has(args.purpose ?? "")) return args;
13622
+ const msgs = [...args.messages];
13623
+ let idx = -1;
13624
+ for (let i = msgs.length - 1; i >= 0; i--) if (msgs[i]?.role === "user") {
13625
+ idx = i;
13626
+ break;
13627
+ }
13628
+ if (idx < 0) return args;
13629
+ const m = msgs[idx];
13630
+ if (VISION_PROVIDERS.includes(provider)) {
13631
+ msgs[idx] = { ...m, images: [...m.images ?? [], ...images] };
13632
+ } else {
13633
+ const n = images.length;
13634
+ msgs[idx] = {
13635
+ ...m,
13636
+ content: `${m.content}
13637
+
13638
+ [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"}.]`
13639
+ };
13640
+ }
13641
+ return { ...args, messages: msgs };
13642
+ }
13643
+ var runContext = new AsyncLocalStorage3();
13471
13644
  var SYNTHESIS_PURPOSES = /* @__PURE__ */ new Set(["synth", "moderator", "recombine", "judge", "unify"]);
13472
13645
  function exemptFromSkip(ctx, provider, purpose) {
13473
13646
  return provider === ctx.moderator && SYNTHESIS_PURPOSES.has(purpose);
@@ -13522,7 +13695,7 @@ function withActivity(p) {
13522
13695
  if (!stopped && controller.signal.reason === "skip") stopped = "user";
13523
13696
  }, { once: true });
13524
13697
  try {
13525
- const res = await send({ ...args, signal: controller.signal });
13698
+ const res = await send({ ...withRunImages(args, provider, ctx.images), signal: controller.signal });
13526
13699
  const text = typeof res.text === "string" ? res.text : "";
13527
13700
  ctx.emit({
13528
13701
  type: "call.done",
@@ -13617,6 +13790,12 @@ function createEmbeddedEngine(opts) {
13617
13790
  const varName = MODEL_ENV[provider.trim().toLowerCase()];
13618
13791
  if (varName && model) env[varName] = model;
13619
13792
  }
13793
+ const superEnv = {};
13794
+ for (const [provider, model] of Object.entries(opts.superModels ?? {})) {
13795
+ const p = provider.trim().toLowerCase();
13796
+ const varName = SUPER_ENV_VARS[p === "grok" ? "xai" : p];
13797
+ if (varName && model) superEnv[varName] = model;
13798
+ }
13620
13799
  const pricingPath = [opts.pricingPath, bundledPricingPath()].find(
13621
13800
  (p) => Boolean(p) && existsSync11(p)
13622
13801
  );
@@ -13624,8 +13803,8 @@ function createEmbeddedEngine(opts) {
13624
13803
  const built = Object.fromEntries(
13625
13804
  Object.entries(buildProviders({ env, pricing })).map(([name, p]) => [name, withActivity(p)])
13626
13805
  );
13627
- const moderator = opts.moderator ?? "anthropic";
13628
- const tools = registerCoreTools({ providers: built, pricing });
13806
+ const moderator = opts.moderator ?? DEFAULT_MODERATOR;
13807
+ const tools = registerCoreTools({ providers: built, pricing, moderatorDefault: moderator });
13629
13808
  const providerIds = Object.keys(built).map(vaultId).sort();
13630
13809
  const models = {};
13631
13810
  for (const [name, p] of Object.entries(built)) models[vaultId(name)] = p.model;
@@ -13656,14 +13835,16 @@ function createEmbeddedEngine(opts) {
13656
13835
  nextCallId,
13657
13836
  skipped: /* @__PURE__ */ new Set(),
13658
13837
  inflight: /* @__PURE__ */ new Map(),
13659
- moderator: vaultId(moderator),
13660
- ...runOpts.callTimeoutMs && runOpts.callTimeoutMs > 0 ? { callTimeoutMs: runOpts.callTimeoutMs } : {}
13838
+ // The provider half of a `provider:model` spec, as the vault spells it.
13839
+ moderator: vaultId(moderatorProviderName(moderator)),
13840
+ ...runOpts.callTimeoutMs && runOpts.callTimeoutMs > 0 ? { callTimeoutMs: runOpts.callTimeoutMs } : {},
13841
+ ...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) } : {}
13661
13842
  };
13662
13843
  liveRuns.set(runId, ctx);
13663
13844
  try {
13664
13845
  return await runContext.run(ctx, () => withProgress(runId, (n) => {
13665
13846
  if (n.params.message) emit({ type: "progress", runId, at: Date.now(), message: n.params.message });
13666
- }, () => runInner(name, args, runId, started, emit)));
13847
+ }, () => withSuperEnv(superEnv, () => runInner(name, args, runId, started, emit))));
13667
13848
  } finally {
13668
13849
  liveRuns.delete(runId);
13669
13850
  }
@@ -13821,12 +14002,17 @@ export {
13821
14002
  DEFAULT_EXCLUDED_PROVIDERS,
13822
14003
  DEFAULT_FALLBACKS,
13823
14004
  DEFAULT_MODELS,
14005
+ IMAGE_MEDIA_TYPES,
14006
+ MAX_RUN_IMAGES,
14007
+ SUPER_MODELS,
14008
+ VISION_PROVIDERS,
13824
14009
  KEY_ENV as __KEY_ENV,
13825
14010
  MODEL_ENV as __MODEL_ENV,
13826
14011
  __test_internals,
13827
14012
  createEmbeddedEngine,
13828
14013
  isAlive,
13829
14014
  listProcesses,
13830
- processRegistryPath
14015
+ processRegistryPath,
14016
+ withRunImages
13831
14017
  };
13832
14018
  //# sourceMappingURL=embed.js.map