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.
@@ -1293,6 +1293,11 @@ function reasoningSharesOutputBudget(provider) {
1293
1293
 
1294
1294
  // src/providers/types.ts
1295
1295
  init_cjs_shims();
1296
+ var IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
1297
+ function imagesOf(m) {
1298
+ if (m.role !== "user" || !Array.isArray(m.images)) return [];
1299
+ return m.images.filter((i) => Boolean(i) && typeof i.data === "string" && i.data.length > 0 && IMAGE_MEDIA_TYPES.includes(i.mediaType));
1300
+ }
1296
1301
  var ProviderError = class extends Error {
1297
1302
  name = "ProviderError";
1298
1303
  kind;
@@ -1692,7 +1697,12 @@ function buildAnthropicRequest(opts) {
1692
1697
  }
1693
1698
  continue;
1694
1699
  }
1695
- convo.push({ role: m.role, content: typeof m.content === "string" ? m.content : "" });
1700
+ const text = typeof m.content === "string" ? m.content : "";
1701
+ const images = imagesOf(m);
1702
+ convo.push(images.length ? { role: m.role, content: [
1703
+ ...images.map((i) => ({ type: "image", source: { type: "base64", media_type: i.mediaType, data: i.data } })),
1704
+ { type: "text", text }
1705
+ ] } : { role: m.role, content: text });
1696
1706
  }
1697
1707
  const body = {
1698
1708
  model: opts.model,
@@ -1933,7 +1943,10 @@ function buildGeminiRequest(opts) {
1933
1943
  const role = m.role === "user" ? "user" : "model";
1934
1944
  contents.push({
1935
1945
  role,
1936
- parts: [{ text: typeof m.content === "string" ? m.content : "" }]
1946
+ parts: [
1947
+ ...imagesOf(m).map((i) => ({ inline_data: { mime_type: i.mediaType, data: i.data } })),
1948
+ { text: typeof m.content === "string" ? m.content : "" }
1949
+ ]
1937
1950
  });
1938
1951
  }
1939
1952
  const body = {
@@ -2102,10 +2115,11 @@ function supportsNativeJsonSchema(provider, model) {
2102
2115
  return true;
2103
2116
  }
2104
2117
  function buildOpenAICompatibleRequest(opts) {
2105
- const msgs = opts.messages.map((m) => ({
2106
- role: m.role,
2107
- content: typeof m.content === "string" ? m.content : ""
2108
- }));
2118
+ const msgs = opts.messages.map((m) => {
2119
+ const text = typeof m.content === "string" ? m.content : "";
2120
+ const images = imagesOf(m);
2121
+ 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 };
2122
+ });
2109
2123
  const body = {
2110
2124
  model: opts.model,
2111
2125
  messages: msgs
@@ -2427,7 +2441,7 @@ function makeGeminiProvider(model, apiKey, opts, fallbackModels = []) {
2427
2441
  // src/server.ts
2428
2442
  init_cjs_shims();
2429
2443
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
2430
- var import_types9 = require("@modelcontextprotocol/sdk/types.js");
2444
+ var import_types12 = require("@modelcontextprotocol/sdk/types.js");
2431
2445
 
2432
2446
  // src/instructions.ts
2433
2447
  init_cjs_shims();
@@ -2478,6 +2492,7 @@ Routing rules:
2478
2492
  2. If the next token does NOT match a tool name (or there is no next token), default to \`mcp__crosscheck__${DEFAULT_TOOL}\` and pass the entire message tail (everything after \`xc\`/\`XC\`) as \`question\`.
2479
2493
  3. Preserve the user's wording verbatim in the argument \u2014 do not paraphrase, summarize, or "clean up" the prompt before forwarding.
2480
2494
  4. If the user message is exactly \`xc\` or \`XC\` with no tail, ask them what they want crosscheck to do; do not invent a question.
2495
+ 5. If the user names a moderator ("let Gemini moderate", "moderator gpt", "moderated by openai:gpt-6-sol"), ALSO pass it as \`moderator\` \u2014 a provider or \`provider:model\` \u2014 on tools that accept one (debate, plan, coordinate, orchestrate, create). Any provider can moderate. Still forward the wording verbatim.
2481
2496
 
2482
2497
  Routable tool names: ${ROUTABLE_TOOLS.join(", ")}.
2483
2498
 
@@ -2515,7 +2530,7 @@ var import_zod = require("zod");
2515
2530
  // src/server-meta.ts
2516
2531
  init_cjs_shims();
2517
2532
  var SERVER_NAME = "crosscheck-agent";
2518
- var SERVER_VERSION = true ? "0.2.29" : "0.0.0-dev";
2533
+ var SERVER_VERSION = true ? "0.2.31" : "0.0.0-dev";
2519
2534
 
2520
2535
  // src/tools/audit.ts
2521
2536
  init_cjs_shims();
@@ -4149,6 +4164,231 @@ function numberOrNull(v) {
4149
4164
  // src/tools/audit.ts
4150
4165
  var import_node_perf_hooks = require("perf_hooks");
4151
4166
 
4167
+ // src/core/moderator.ts
4168
+ init_cjs_shims();
4169
+
4170
+ // src/core/super-mode.ts
4171
+ init_cjs_shims();
4172
+ var import_node_async_hooks = require("async_hooks");
4173
+
4174
+ // src/core/reasoning-upgrade.ts
4175
+ init_cjs_shims();
4176
+ var UPGRADE_MODEL = "claude-fable-5";
4177
+ var UPGRADE_LABEL = "Fable 5";
4178
+ function isUpgradeRequested(args) {
4179
+ if (args["upgrade"] === true) return true;
4180
+ const reasoning = args["reasoning"];
4181
+ if (typeof reasoning === "string" && reasoning.trim().toLowerCase() === "max") {
4182
+ return true;
4183
+ }
4184
+ const model = args["model"];
4185
+ if (typeof model === "string") {
4186
+ const m = model.trim().toLowerCase();
4187
+ if (m === "fable5" || m === "fable-5" || m === "fable" || m === UPGRADE_MODEL) {
4188
+ return true;
4189
+ }
4190
+ }
4191
+ return false;
4192
+ }
4193
+ var HEAVY_TERMS = [
4194
+ /\bprov(e|ing|en)\b|\bproof\b|\btheorem\b|\bderiv(e|ation)\b/i,
4195
+ /\barchitect(ure|ing)?\b|\bdesign\s+(a|the|an)\b|\bhigh[-\s]?level\s+design\b/i,
4196
+ /\btrade[-\s]?offs?\b|\bpros?\s+and\s+cons?\b|\bweigh\b/i,
4197
+ /\boptimi[sz]e\b|\bcomplexity\b|\bO\(|\balgorithm(ic)?\b/i,
4198
+ /\brace\s+condition\b|\bconcurren(t|cy)\b|\bdeadlock\b|\bdistributed\b|\bconsensus\b/i,
4199
+ /\broot[-\s]?cause\b|\bdebug(ging)?\b|\bwhy\s+(does|is|would|might)\b/i,
4200
+ /\bsecurity\b|\bthreat\s+model\b|\bexploit\b|\bvulnerabilit(y|ies)\b/i,
4201
+ /\bmigrat(e|ion)\b|\brefactor\b|\bre[-\s]?architect\b/i,
4202
+ /\binvariant\b|\bcorrectness\b|\bedge\s+cases?\b|\bformal(ly)?\b/i,
4203
+ /\bstrateg(y|ic|ies)\b|\bplan\s+(the|a|an|out)\b|\broadmap\b/i,
4204
+ /\breason\s+(through|about)\b|\bstep[-\s]?by[-\s]?step\b|\bthink\s+deeply\b/i,
4205
+ /\bnp[-\s]?(hard|complete)\b|\bgraph\b|\bstate\s+machine\b|\bconstraint\b/i
4206
+ ];
4207
+ var DIFFICULTY_TERMS = /\b(hard|tricky|subtle|complex|complicated|non[-\s]?trivial|thorny|gnarly|difficult|deep)\b/i;
4208
+ function assessReasoning(text) {
4209
+ const s = typeof text === "string" ? text : "";
4210
+ const signals = [];
4211
+ let score = 0;
4212
+ let termHits = 0;
4213
+ for (const re of HEAVY_TERMS) {
4214
+ if (re.test(s)) termHits += 1;
4215
+ }
4216
+ if (termHits > 0) {
4217
+ const add = Math.min(termHits, 4);
4218
+ score += add;
4219
+ signals.push(`${termHits} reasoning cue${termHits === 1 ? "" : "s"}`);
4220
+ }
4221
+ if (DIFFICULTY_TERMS.test(s)) {
4222
+ score += 1;
4223
+ signals.push("explicit difficulty marker");
4224
+ }
4225
+ const words = s.split(/\s+/).filter(Boolean).length;
4226
+ if (words >= 120) {
4227
+ score += 2;
4228
+ signals.push("long, detailed prompt");
4229
+ } else if (words >= 50) {
4230
+ score += 1;
4231
+ signals.push("substantial prompt");
4232
+ }
4233
+ const constraints = (s.match(/\b(must|should|need to|ensure|require[ds]?|constraint)\b/gi) ?? []).length + (s.match(/(^|\n)\s*(?:[-*]|\d+[.)])\s+/g) ?? []).length;
4234
+ if (constraints >= 3) {
4235
+ score += 1;
4236
+ signals.push(`${constraints} constraints/steps`);
4237
+ }
4238
+ const heavy = score >= 3;
4239
+ const reason = heavy ? `Task looks reasoning-heavy (${signals.join(", ") || "score " + score}).` : `Task looks routine (score ${score}).`;
4240
+ return { heavy, score, reason, signals };
4241
+ }
4242
+ function buildSuggestedUpgrade(toolName, assessment) {
4243
+ return {
4244
+ model: UPGRADE_MODEL,
4245
+ label: UPGRADE_LABEL,
4246
+ reason: assessment.reason,
4247
+ 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.`
4248
+ };
4249
+ }
4250
+
4251
+ // src/core/super-mode.ts
4252
+ var SUPER_MODELS = {
4253
+ // Fable 5.1 verified present on the account; 5 as the fallback for accounts
4254
+ // that haven't been granted 5.1 yet.
4255
+ anthropic: ["claude-fable-5-1", UPGRADE_MODEL],
4256
+ // Astra is live (verified against the account's model list 2026-09-23).
4257
+ // The fallback is named explicitly rather than reusing CO_REASON_MODEL:
4258
+ // that constant means "OpenAI's top reasoning model" for the max-reasoning
4259
+ // co-reasoner, which is a different question from "what should super fall
4260
+ // back to". Sharing the symbol silently coupled two unrelated decisions.
4261
+ openai: ["gpt-6-astra", "gpt-6-sol"],
4262
+ // Explicitly left as-is per product decision. Note grok-4-latest is not in
4263
+ // xAI's published model list, but it resolves in practice — real panel calls
4264
+ // on it succeeded as recently as 2026-08-30 — so it is an unlisted alias
4265
+ // rather than a dead id.
4266
+ xai: [DEFAULT_MODELS["xai"]],
4267
+ // 3.8 Flash is now BOTH the everyday default and the super pick for Gemini,
4268
+ // so this can no longer borrow DEFAULT_MODELS as its fallback — that would
4269
+ // repeat the same model and retry one that just failed. Named explicitly.
4270
+ gemini: ["gemini-3.8-flash", "gemini-3.1-pro-preview"],
4271
+ kimi: [DEFAULT_MODELS["kimi"]],
4272
+ qwen: [DEFAULT_MODELS["qwen"]]
4273
+ };
4274
+ var SUPER_ENV_VARS = {
4275
+ anthropic: "ANTHROPIC_SUPER_MODEL",
4276
+ openai: "OPENAI_SUPER_MODEL",
4277
+ xai: "XAI_SUPER_MODEL",
4278
+ gemini: "GEMINI_SUPER_MODEL",
4279
+ kimi: "KIMI_SUPER_MODEL",
4280
+ qwen: "QWEN_SUPER_MODEL",
4281
+ mistral: "MISTRAL_SUPER_MODEL",
4282
+ groq: "GROQ_SUPER_MODEL",
4283
+ deepseek: "DEEPSEEK_SUPER_MODEL"
4284
+ };
4285
+ function parseChain(v) {
4286
+ if (!v) return [];
4287
+ const out = [];
4288
+ for (const raw of v.split(",")) {
4289
+ const m = raw.trim();
4290
+ if (m && !out.includes(m)) out.push(m);
4291
+ }
4292
+ return out;
4293
+ }
4294
+ var superEnvStore = new import_node_async_hooks.AsyncLocalStorage();
4295
+ var currentEnv = () => superEnvStore.getStore() ?? process.env;
4296
+ function superChainFor(provider, env = currentEnv()) {
4297
+ const varName = SUPER_ENV_VARS[provider];
4298
+ const user = varName ? parseChain(env[varName]) : [];
4299
+ if (user.length > 0) {
4300
+ const fallbacks = varName ? parseChain(env[`${varName}_FALLBACKS`]) : [];
4301
+ return [...user, ...fallbacks.filter((m) => !user.includes(m))];
4302
+ }
4303
+ return SUPER_MODELS[provider] ?? [];
4304
+ }
4305
+ function superPrimary(provider, env = currentEnv()) {
4306
+ return superChainFor(provider, env)[0];
4307
+ }
4308
+ var SUPER_PROVIDER_NAMES = Object.keys(SUPER_MODELS);
4309
+ function isSuperRequested(args) {
4310
+ return args["super"] === true;
4311
+ }
4312
+ function retargetForSuper(p, env = currentEnv()) {
4313
+ const chain = superChainFor(p.name, env);
4314
+ if (chain.length === 0) return p;
4315
+ const [primary, ...fallbacks] = chain;
4316
+ return retargetProvider(p, primary, fallbacks);
4317
+ }
4318
+ var SUPER_BUDGET_MULTIPLIER = 3;
4319
+ var SUPER_BUDGET_CEILING = 16e3;
4320
+ function superBudget(base) {
4321
+ return Math.min(base * SUPER_BUDGET_MULTIPLIER, SUPER_BUDGET_CEILING);
4322
+ }
4323
+
4324
+ // src/core/moderator.ts
4325
+ var DEFAULT_MODERATOR = "anthropic";
4326
+ var PROVIDER_ALIASES = {
4327
+ grok: "xai",
4328
+ claude: "anthropic",
4329
+ google: "gemini",
4330
+ chatgpt: "openai",
4331
+ gpt: "openai",
4332
+ moonshot: "kimi"
4333
+ };
4334
+ function parseModeratorSpec(raw) {
4335
+ if (typeof raw !== "string") return null;
4336
+ const s = raw.trim();
4337
+ if (!s) return null;
4338
+ const i = s.indexOf(":");
4339
+ const providerPart = (i < 0 ? s : s.slice(0, i)).trim().toLowerCase();
4340
+ const modelPart = i < 0 ? "" : s.slice(i + 1).trim();
4341
+ if (!providerPart) return null;
4342
+ return {
4343
+ provider: PROVIDER_ALIASES[providerPart] ?? providerPart,
4344
+ model: modelPart || null
4345
+ };
4346
+ }
4347
+ function formatModeratorSpec(spec) {
4348
+ return spec.model ? `${spec.provider}:${spec.model}` : spec.provider;
4349
+ }
4350
+ function pickModeratorSpec(...candidates) {
4351
+ for (const c of candidates) {
4352
+ const s = parseModeratorSpec(c);
4353
+ if (s) return s;
4354
+ }
4355
+ return { provider: DEFAULT_MODERATOR, model: null };
4356
+ }
4357
+ function resolveModerator(providers, ...candidates) {
4358
+ const spec = pickModeratorSpec(...candidates);
4359
+ const base = providers[spec.provider];
4360
+ if (!base) return { provider: void 0, spec, pinned: false };
4361
+ if (!spec.model) return { provider: base, spec, pinned: false };
4362
+ return { provider: retargetProvider(base, spec.model), spec, pinned: true };
4363
+ }
4364
+ function synthesisSeat(o) {
4365
+ const { moderator, pinned, superRequested, upgradeRequested } = o;
4366
+ const wantsMore = superRequested || upgradeRequested;
4367
+ if (!wantsMore) return { provider: moderator, upgraded: false, label: null, skipReason: null };
4368
+ if (pinned) {
4369
+ return {
4370
+ provider: moderator,
4371
+ upgraded: false,
4372
+ label: null,
4373
+ skipReason: `the moderator is pinned to ${moderator.name}:${moderator.model}; a pinned model is used as-is.`
4374
+ };
4375
+ }
4376
+ if (moderator.name === "anthropic" && !superRequested) {
4377
+ return { provider: retargetProvider(moderator, UPGRADE_MODEL), upgraded: true, label: UPGRADE_LABEL, skipReason: null };
4378
+ }
4379
+ if (superChainFor(moderator.name).length === 0) {
4380
+ return {
4381
+ provider: moderator,
4382
+ upgraded: false,
4383
+ label: null,
4384
+ skipReason: `no flagship model is known for ${moderator.name}; the moderator ran at ${moderator.model}.`
4385
+ };
4386
+ }
4387
+ const seat = retargetForSuper(moderator);
4388
+ const label = moderator.name === "anthropic" ? UPGRADE_LABEL : seat.model;
4389
+ return { provider: seat, upgraded: true, label, skipReason: null };
4390
+ }
4391
+
4152
4392
  // src/core/co-reason.ts
4153
4393
  init_cjs_shims();
4154
4394
  var CO_REASON_MODEL = "gpt-6-sol";
@@ -4327,83 +4567,6 @@ function selectForDifficulty(args) {
4327
4567
  };
4328
4568
  }
4329
4569
 
4330
- // src/core/reasoning-upgrade.ts
4331
- init_cjs_shims();
4332
- var UPGRADE_MODEL = "claude-fable-5";
4333
- var UPGRADE_LABEL = "Fable 5";
4334
- function isUpgradeRequested(args) {
4335
- if (args["upgrade"] === true) return true;
4336
- const reasoning = args["reasoning"];
4337
- if (typeof reasoning === "string" && reasoning.trim().toLowerCase() === "max") {
4338
- return true;
4339
- }
4340
- const model = args["model"];
4341
- if (typeof model === "string") {
4342
- const m = model.trim().toLowerCase();
4343
- if (m === "fable5" || m === "fable-5" || m === "fable" || m === UPGRADE_MODEL) {
4344
- return true;
4345
- }
4346
- }
4347
- return false;
4348
- }
4349
- var HEAVY_TERMS = [
4350
- /\bprov(e|ing|en)\b|\bproof\b|\btheorem\b|\bderiv(e|ation)\b/i,
4351
- /\barchitect(ure|ing)?\b|\bdesign\s+(a|the|an)\b|\bhigh[-\s]?level\s+design\b/i,
4352
- /\btrade[-\s]?offs?\b|\bpros?\s+and\s+cons?\b|\bweigh\b/i,
4353
- /\boptimi[sz]e\b|\bcomplexity\b|\bO\(|\balgorithm(ic)?\b/i,
4354
- /\brace\s+condition\b|\bconcurren(t|cy)\b|\bdeadlock\b|\bdistributed\b|\bconsensus\b/i,
4355
- /\broot[-\s]?cause\b|\bdebug(ging)?\b|\bwhy\s+(does|is|would|might)\b/i,
4356
- /\bsecurity\b|\bthreat\s+model\b|\bexploit\b|\bvulnerabilit(y|ies)\b/i,
4357
- /\bmigrat(e|ion)\b|\brefactor\b|\bre[-\s]?architect\b/i,
4358
- /\binvariant\b|\bcorrectness\b|\bedge\s+cases?\b|\bformal(ly)?\b/i,
4359
- /\bstrateg(y|ic|ies)\b|\bplan\s+(the|a|an|out)\b|\broadmap\b/i,
4360
- /\breason\s+(through|about)\b|\bstep[-\s]?by[-\s]?step\b|\bthink\s+deeply\b/i,
4361
- /\bnp[-\s]?(hard|complete)\b|\bgraph\b|\bstate\s+machine\b|\bconstraint\b/i
4362
- ];
4363
- var DIFFICULTY_TERMS = /\b(hard|tricky|subtle|complex|complicated|non[-\s]?trivial|thorny|gnarly|difficult|deep)\b/i;
4364
- function assessReasoning(text) {
4365
- const s = typeof text === "string" ? text : "";
4366
- const signals = [];
4367
- let score = 0;
4368
- let termHits = 0;
4369
- for (const re of HEAVY_TERMS) {
4370
- if (re.test(s)) termHits += 1;
4371
- }
4372
- if (termHits > 0) {
4373
- const add = Math.min(termHits, 4);
4374
- score += add;
4375
- signals.push(`${termHits} reasoning cue${termHits === 1 ? "" : "s"}`);
4376
- }
4377
- if (DIFFICULTY_TERMS.test(s)) {
4378
- score += 1;
4379
- signals.push("explicit difficulty marker");
4380
- }
4381
- const words = s.split(/\s+/).filter(Boolean).length;
4382
- if (words >= 120) {
4383
- score += 2;
4384
- signals.push("long, detailed prompt");
4385
- } else if (words >= 50) {
4386
- score += 1;
4387
- signals.push("substantial prompt");
4388
- }
4389
- const constraints = (s.match(/\b(must|should|need to|ensure|require[ds]?|constraint)\b/gi) ?? []).length + (s.match(/(^|\n)\s*(?:[-*]|\d+[.)])\s+/g) ?? []).length;
4390
- if (constraints >= 3) {
4391
- score += 1;
4392
- signals.push(`${constraints} constraints/steps`);
4393
- }
4394
- const heavy = score >= 3;
4395
- const reason = heavy ? `Task looks reasoning-heavy (${signals.join(", ") || "score " + score}).` : `Task looks routine (score ${score}).`;
4396
- return { heavy, score, reason, signals };
4397
- }
4398
- function buildSuggestedUpgrade(toolName, assessment) {
4399
- return {
4400
- model: UPGRADE_MODEL,
4401
- label: UPGRADE_LABEL,
4402
- reason: assessment.reason,
4403
- 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.`
4404
- };
4405
- }
4406
-
4407
4570
  // src/tools/audit.ts
4408
4571
  var AUDIT_OBVIOUS_FAILURE_HIGH = 0.3;
4409
4572
  var AUDIT_OBVIOUS_FAILURE_MED = 0.2;
@@ -4701,9 +4864,14 @@ ${rubricText}`;
4701
4864
  );
4702
4865
  }
4703
4866
  const upgradeRequested = isUpgradeRequested(args);
4704
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
4705
- const auditorForCall = upgraded ? retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL) : auditor;
4706
- const coProvider = pickCoReasoner(opts.providers, upgradeRequested);
4867
+ const seat = synthesisSeat({ moderator: auditor, pinned: false, superRequested: false, upgradeRequested });
4868
+ const upgraded = seat.upgraded;
4869
+ const auditorForCall = seat.provider;
4870
+ const coProvider = pickCoReasoner(
4871
+ opts.providers,
4872
+ upgradeRequested,
4873
+ auditorForCall.name === "openai" ? auditorForCall.model : void 0
4874
+ );
4707
4875
  const coReason = await coReasonThenInject({
4708
4876
  provider: coProvider,
4709
4877
  messages: msgs,
@@ -4786,15 +4954,16 @@ ${rubricText}`;
4786
4954
  if (upgraded) {
4787
4955
  result["reasoning_upgrade"] = {
4788
4956
  applied: true,
4789
- model: UPGRADE_MODEL,
4790
- label: UPGRADE_LABEL,
4957
+ provider: auditorForCall.name,
4958
+ model: auditorForCall.model,
4959
+ label: seat.label ?? auditorForCall.model,
4791
4960
  seat: "auditor",
4792
4961
  ...coReasoned ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
4793
4962
  };
4794
4963
  } else if (upgradeRequested) {
4795
4964
  result["reasoning_upgrade"] = {
4796
4965
  applied: false,
4797
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
4966
+ reason: seat.skipReason ?? "the auditor couldn't be upgraded."
4798
4967
  };
4799
4968
  } else {
4800
4969
  const assessment = assessReasoning(
@@ -4877,7 +5046,7 @@ function pickAuditor(providers, exclude, explicit, moderatorName, allowlist, che
4877
5046
  }
4878
5047
  }
4879
5048
  }
4880
- const mod = providers[moderatorName.toLowerCase()];
5049
+ const mod = resolveModerator(providers, moderatorName).provider;
4881
5050
  if (mod !== void 0 && !exclude.has(mod.name) && (allowlist === null || allowlist.includes(mod.name))) {
4882
5051
  return { auditor: mod, reason: null };
4883
5052
  }
@@ -5202,9 +5371,7 @@ function pickPanelJudge(providers, moderatorName, pricing) {
5202
5371
  return entry.model && entry.model !== base.model ? retargetProvider(base, entry.model) : base;
5203
5372
  }
5204
5373
  }
5205
- const mod = providers[moderatorName.toLowerCase()];
5206
- if (mod !== void 0) return mod;
5207
- return null;
5374
+ return resolveModerator(providers, moderatorName).provider ?? null;
5208
5375
  }
5209
5376
  var AGREEMENT_CHECK_SCHEMA = {
5210
5377
  type: "object",
@@ -5522,78 +5689,6 @@ async function pickAutoPanel(storage, purpose, n, providers, allowlist) {
5522
5689
  return { providers: ranked.slice(0, Math.max(1, n)), coldStart: true };
5523
5690
  }
5524
5691
 
5525
- // src/core/super-mode.ts
5526
- init_cjs_shims();
5527
- var SUPER_MODELS = {
5528
- // Fable 5.1 verified present on the account; 5 as the fallback for accounts
5529
- // that haven't been granted 5.1 yet.
5530
- anthropic: ["claude-fable-5-1", UPGRADE_MODEL],
5531
- // Astra is live (verified against the account's model list 2026-09-23).
5532
- // The fallback is named explicitly rather than reusing CO_REASON_MODEL:
5533
- // that constant means "OpenAI's top reasoning model" for the max-reasoning
5534
- // co-reasoner, which is a different question from "what should super fall
5535
- // back to". Sharing the symbol silently coupled two unrelated decisions.
5536
- openai: ["gpt-6-astra", "gpt-6-sol"],
5537
- // Explicitly left as-is per product decision. Note grok-4-latest is not in
5538
- // xAI's published model list, but it resolves in practice — real panel calls
5539
- // on it succeeded as recently as 2026-08-30 — so it is an unlisted alias
5540
- // rather than a dead id.
5541
- xai: [DEFAULT_MODELS["xai"]],
5542
- // 3.8 Flash is now BOTH the everyday default and the super pick for Gemini,
5543
- // so this can no longer borrow DEFAULT_MODELS as its fallback — that would
5544
- // repeat the same model and retry one that just failed. Named explicitly.
5545
- gemini: ["gemini-3.8-flash", "gemini-3.1-pro-preview"],
5546
- kimi: [DEFAULT_MODELS["kimi"]],
5547
- qwen: [DEFAULT_MODELS["qwen"]]
5548
- };
5549
- var SUPER_ENV_VARS = {
5550
- anthropic: "ANTHROPIC_SUPER_MODEL",
5551
- openai: "OPENAI_SUPER_MODEL",
5552
- xai: "XAI_SUPER_MODEL",
5553
- gemini: "GEMINI_SUPER_MODEL",
5554
- kimi: "KIMI_SUPER_MODEL",
5555
- qwen: "QWEN_SUPER_MODEL",
5556
- mistral: "MISTRAL_SUPER_MODEL",
5557
- groq: "GROQ_SUPER_MODEL",
5558
- deepseek: "DEEPSEEK_SUPER_MODEL"
5559
- };
5560
- function parseChain(v) {
5561
- if (!v) return [];
5562
- const out = [];
5563
- for (const raw of v.split(",")) {
5564
- const m = raw.trim();
5565
- if (m && !out.includes(m)) out.push(m);
5566
- }
5567
- return out;
5568
- }
5569
- function superChainFor(provider, env = process.env) {
5570
- const varName = SUPER_ENV_VARS[provider];
5571
- const user = varName ? parseChain(env[varName]) : [];
5572
- if (user.length > 0) {
5573
- const fallbacks = varName ? parseChain(env[`${varName}_FALLBACKS`]) : [];
5574
- return [...user, ...fallbacks.filter((m) => !user.includes(m))];
5575
- }
5576
- return SUPER_MODELS[provider] ?? [];
5577
- }
5578
- function superPrimary(provider, env = process.env) {
5579
- return superChainFor(provider, env)[0];
5580
- }
5581
- var SUPER_PROVIDER_NAMES = Object.keys(SUPER_MODELS);
5582
- function isSuperRequested(args) {
5583
- return args["super"] === true;
5584
- }
5585
- function retargetForSuper(p, env = process.env) {
5586
- const chain = superChainFor(p.name, env);
5587
- if (chain.length === 0) return p;
5588
- const [primary, ...fallbacks] = chain;
5589
- return retargetProvider(p, primary, fallbacks);
5590
- }
5591
- var SUPER_BUDGET_MULTIPLIER = 3;
5592
- var SUPER_BUDGET_CEILING = 16e3;
5593
- function superBudget(base) {
5594
- return Math.min(base * SUPER_BUDGET_MULTIPLIER, SUPER_BUDGET_CEILING);
5595
- }
5596
-
5597
5692
  // src/core/session-memory.ts
5598
5693
  init_cjs_shims();
5599
5694
  var SESSION_MEMORY_DEFAULT_LIMIT = 50;
@@ -7663,9 +7758,9 @@ var import_node_perf_hooks6 = require("perf_hooks");
7663
7758
 
7664
7759
  // src/core/progress.ts
7665
7760
  init_cjs_shims();
7666
- var import_node_async_hooks = require("async_hooks");
7761
+ var import_node_async_hooks2 = require("async_hooks");
7667
7762
  var import_node_perf_hooks5 = require("perf_hooks");
7668
- var progressStorage = new import_node_async_hooks.AsyncLocalStorage();
7763
+ var progressStorage = new import_node_async_hooks2.AsyncLocalStorage();
7669
7764
  async function withProgress(token, sendNotification, fn) {
7670
7765
  if (token === void 0 || token === null) {
7671
7766
  return fn();
@@ -7969,8 +8064,9 @@ async function runOrchestrate(args, opts) {
7969
8064
  blocked
7970
8065
  };
7971
8066
  }
7972
- const moderatorName = typeof args["moderator"] === "string" && args["moderator"] ? args["moderator"] : opts.moderator ?? "anthropic";
7973
- const moderator = opts.providers[moderatorName.toLowerCase()] ?? selected[0];
8067
+ const resolvedModerator = resolveModerator(opts.providers, args["moderator"], opts.moderator);
8068
+ const moderator = resolvedModerator.provider ?? selected[0];
8069
+ const moderatorChosen = resolvedModerator.provider !== void 0 && (resolvedModerator.pinned || parseModeratorSpec(args["moderator"]) !== null);
7974
8070
  const failFast = boolArg2(args["fail_fast"], false);
7975
8071
  const planOnly = boolArg2(args["plan_only"], false);
7976
8072
  const cheapMode = boolArg2(args["cheap_mode"], opts.ctx?.cheap_mode ?? false);
@@ -8454,11 +8550,17 @@ ${recombineSys}` : recombineSys },
8454
8550
  let synthProvider = moderator;
8455
8551
  let synthModel = null;
8456
8552
  const upgradeRequested = isUpgradeRequested(args);
8457
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
8553
+ const seat = synthesisSeat({
8554
+ moderator,
8555
+ pinned: resolvedModerator.provider !== void 0 && resolvedModerator.pinned,
8556
+ superRequested: false,
8557
+ upgradeRequested
8558
+ });
8559
+ const upgraded = seat.upgraded;
8458
8560
  if (upgraded) {
8459
- synthProvider = retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL);
8460
- synthModel = `anthropic/${UPGRADE_MODEL}`;
8461
- } else if (cheapMode && opts.pricing) {
8561
+ synthProvider = seat.provider;
8562
+ synthModel = `${seat.provider.name}/${seat.provider.model}`;
8563
+ } else if (cheapMode && opts.pricing && !moderatorChosen) {
8462
8564
  const pick = selectForDifficulty({
8463
8565
  pricing: opts.pricing,
8464
8566
  tier: "med",
@@ -8484,7 +8586,11 @@ ${recombineSys}` : recombineSys },
8484
8586
  provider: synthProvider.name
8485
8587
  } }
8486
8588
  );
8487
- const coProvider = pickCoReasoner(opts.providers, upgradeRequested);
8589
+ const coProvider = pickCoReasoner(
8590
+ opts.providers,
8591
+ upgradeRequested,
8592
+ synthProvider.name === "openai" ? synthProvider.model : void 0
8593
+ );
8488
8594
  const coReason = await coReasonThenInject({
8489
8595
  provider: coProvider,
8490
8596
  messages: recMsgs,
@@ -8534,7 +8640,8 @@ ${recombineSys}` : recombineSys },
8534
8640
  missing,
8535
8641
  partial,
8536
8642
  fail_fast: failFast,
8537
- cheap_mode: cheapMode
8643
+ cheap_mode: cheapMode,
8644
+ moderator: { provider: moderator.name, model: moderator.model }
8538
8645
  };
8539
8646
  if (personaMeta.used !== null) result["persona"] = personaMeta;
8540
8647
  if (synthModel) result["synth_model"] = synthModel;
@@ -8542,15 +8649,16 @@ ${recombineSys}` : recombineSys },
8542
8649
  if (upgraded) {
8543
8650
  result["reasoning_upgrade"] = {
8544
8651
  applied: true,
8545
- model: UPGRADE_MODEL,
8546
- label: UPGRADE_LABEL,
8652
+ provider: seat.provider.name,
8653
+ model: seat.provider.model,
8654
+ label: seat.label ?? seat.provider.model,
8547
8655
  seat: "recombine/synthesis",
8548
8656
  ...coReasoned ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
8549
8657
  };
8550
8658
  } else if (upgradeRequested) {
8551
8659
  result["reasoning_upgrade"] = {
8552
8660
  applied: false,
8553
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
8661
+ reason: seat.skipReason ?? "the moderator couldn't be upgraded."
8554
8662
  };
8555
8663
  } else if (goal) {
8556
8664
  const assessment = assessReasoning(goal);
@@ -9735,13 +9843,24 @@ async function runCoordinate(args, opts) {
9735
9843
  };
9736
9844
  }
9737
9845
  const proposerName = typeof args["proposer"] === "string" && args["proposer"] ? args["proposer"] : selected[0].name;
9738
- const synthName = typeof args["synthesizer"] === "string" && args["synthesizer"] ? args["synthesizer"] : typeof args["moderator"] === "string" && args["moderator"] ? args["moderator"] : opts.moderator ?? "anthropic";
9846
+ const resolvedSynth = resolveModerator(
9847
+ opts.providers,
9848
+ args["synthesizer"],
9849
+ args["moderator"],
9850
+ opts.moderator
9851
+ );
9739
9852
  const proposer = opts.providers[proposerName.toLowerCase()] ?? selected[0];
9740
- const synth = opts.providers[synthName.toLowerCase()] ?? proposer;
9853
+ const synth = resolvedSynth.provider ?? proposer;
9741
9854
  const proposerForCall = superRequested ? retargetForSuper(proposer) : proposer;
9742
9855
  const upgradeRequested = isUpgradeRequested(args) || superRequested;
9743
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
9744
- const synthForCall = superRequested && opts.providers["anthropic"] ? retargetForSuper(opts.providers["anthropic"]) : upgraded ? retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL) : synth;
9856
+ const seat = synthesisSeat({
9857
+ moderator: synth,
9858
+ pinned: resolvedSynth.provider !== void 0 && resolvedSynth.pinned,
9859
+ superRequested,
9860
+ upgradeRequested
9861
+ });
9862
+ const upgraded = seat.upgraded;
9863
+ const synthForCall = seat.provider;
9745
9864
  let critics = [];
9746
9865
  if (Array.isArray(args["critics"])) {
9747
9866
  for (const n of args["critics"]) {
@@ -9892,7 +10011,11 @@ CRITIQUES:
9892
10011
  ${critiqueBlock}`
9893
10012
  }
9894
10013
  ];
9895
- const coProvider = pickCoReasoner(opts.providers, upgradeRequested);
10014
+ const coProvider = pickCoReasoner(
10015
+ opts.providers,
10016
+ upgradeRequested,
10017
+ synthForCall.name === "openai" ? synthForCall.model : void 0
10018
+ );
9896
10019
  const coReason = await coReasonThenInject({
9897
10020
  provider: coProvider,
9898
10021
  messages: synthMessages,
@@ -9937,7 +10060,8 @@ ${critiqueBlock}`
9937
10060
  critique_answers: scannedCritiques,
9938
10061
  critique_structured: critiqueStructured,
9939
10062
  synthesis_answer: synthAns,
9940
- synthesis_structured: synthObj
10063
+ synthesis_structured: synthObj,
10064
+ moderator: { provider: synthForCall.name, model: synthForCall.model }
9941
10065
  };
9942
10066
  if (synthErrs.length > 0) result["synthesis_errors"] = synthErrs;
9943
10067
  if (canaryLeaks.length > 0) result["canary_leaks"] = canaryLeaks;
@@ -9955,15 +10079,16 @@ ${critiqueBlock}`
9955
10079
  result["reasoning_upgrade"] = {
9956
10080
  applied: true,
9957
10081
  // The model that actually ran the seat, not the one we'd have picked.
9958
- model: superRequested ? superPrimary("anthropic") ?? UPGRADE_MODEL : UPGRADE_MODEL,
9959
- label: UPGRADE_LABEL,
10082
+ provider: synthForCall.name,
10083
+ model: synthForCall.model,
10084
+ label: seat.label ?? synthForCall.model,
9960
10085
  seat: "synthesizer",
9961
10086
  ...coReasonAns ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
9962
10087
  };
9963
10088
  } else if (upgradeRequested) {
9964
10089
  result["reasoning_upgrade"] = {
9965
10090
  applied: false,
9966
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
10091
+ reason: seat.skipReason ?? "the synthesizer couldn't be upgraded."
9967
10092
  };
9968
10093
  } else {
9969
10094
  const assessment = assessReasoning(topic);
@@ -10574,10 +10699,12 @@ async function runDebate(args, opts) {
10574
10699
  const earlyStopThreshold = Number.isFinite(earlyStopThresholdRaw) ? earlyStopThresholdRaw : 0.7;
10575
10700
  let earlyStoppedRound = null;
10576
10701
  let agreementBlock = null;
10577
- const moderatorName = typeof args["moderator"] === "string" && args["moderator"] ? args["moderator"] : opts.moderator ?? "anthropic";
10578
- const moderator = opts.providers[moderatorName.toLowerCase()] ?? selected[0];
10702
+ const resolvedModerator = resolveModerator(opts.providers, args["moderator"], opts.moderator);
10703
+ const moderatorName = resolvedModerator.spec.provider;
10704
+ const moderator = resolvedModerator.provider ?? selected[0];
10579
10705
  const upgradeRequested = isUpgradeRequested(args) || superRequested;
10580
- const upgraded = upgradeRequested && opts.providers["anthropic"] !== void 0;
10706
+ const seat = moderator ? synthesisSeat({ moderator, pinned: resolvedModerator.pinned, superRequested, upgradeRequested }) : null;
10707
+ const upgraded = seat?.upgraded ?? false;
10581
10708
  for (let rnd = 1; rnd <= maxRounds; rnd++) {
10582
10709
  const roundMessages = [
10583
10710
  {
@@ -10662,8 +10789,10 @@ ${prior.body}` });
10662
10789
  let personaMeta = null;
10663
10790
  let coReasoned = false;
10664
10791
  let coReasonerModel = null;
10792
+ let moderatorUsed = null;
10665
10793
  if (moderator) {
10666
- const synthProvider = superRequested && opts.providers["anthropic"] ? retargetForSuper(opts.providers["anthropic"]) : upgraded ? retargetProvider(opts.providers["anthropic"], UPGRADE_MODEL) : moderator;
10794
+ const synthProvider = seat?.provider ?? moderator;
10795
+ moderatorUsed = { provider: synthProvider.name, model: synthProvider.model };
10667
10796
  const condensed = buildModeratorTranscript(transcript);
10668
10797
  const persona = buildPersonaInjection({
10669
10798
  toolName: "debate",
@@ -10689,7 +10818,7 @@ TRANSCRIPT:
10689
10818
  ${condensed}`
10690
10819
  }
10691
10820
  ];
10692
- const openAiSeat = selected.find((p) => p.name === "openai")?.model;
10821
+ const openAiSeat = synthProvider.name === "openai" ? synthProvider.model : selected.find((p) => p.name === "openai")?.model;
10693
10822
  const coProvider = pickCoReasoner(opts.providers, upgradeRequested, openAiSeat);
10694
10823
  const coReason = await coReasonThenInject({
10695
10824
  provider: coProvider,
@@ -10749,20 +10878,22 @@ ${condensed}`
10749
10878
  transcript,
10750
10879
  synthesis
10751
10880
  };
10881
+ if (moderatorUsed) result["moderator"] = moderatorUsed;
10752
10882
  if (personaMeta && personaMeta.used !== null) result["persona"] = personaMeta;
10753
10883
  if (compactionMeta) result["round_compaction"] = compactionMeta;
10754
10884
  if (upgraded) {
10755
10885
  result["reasoning_upgrade"] = {
10756
10886
  applied: true,
10757
- model: superRequested ? superPrimary("anthropic") ?? UPGRADE_MODEL : UPGRADE_MODEL,
10758
- label: UPGRADE_LABEL,
10887
+ provider: seat.provider.name,
10888
+ model: seat.provider.model,
10889
+ label: seat.label ?? seat.provider.model,
10759
10890
  seat: "moderator synthesis",
10760
10891
  ...coReasoned && coReasonerModel ? { co_reasoner: { model: coReasonerModel, label: coReasonLabel(coReasonerModel) } } : {}
10761
10892
  };
10762
10893
  } else if (upgradeRequested) {
10763
10894
  result["reasoning_upgrade"] = {
10764
10895
  applied: false,
10765
- reason: `no Anthropic key available \u2014 the ${UPGRADE_LABEL} upgrade needs it.`
10896
+ reason: seat?.skipReason ?? "no moderator was available for the upgrade."
10766
10897
  };
10767
10898
  } else {
10768
10899
  const assessment = assessReasoning(topic);
@@ -11394,12 +11525,25 @@ function runListProviders(args, opts) {
11394
11525
  }
11395
11526
  const result = {
11396
11527
  providers,
11397
- moderator_default: opts.moderatorDefault ?? "anthropic",
11528
+ moderator_default: opts.moderatorDefault ?? DEFAULT_MODERATOR,
11529
+ // Who will actually moderate right now: the configured spec if that
11530
+ // provider has a key, else the first available (what the tools fall to).
11531
+ moderator: describeModerator(opts.providers ?? {}, opts.moderatorDefault),
11398
11532
  usage_hint: USAGE_HINT
11399
11533
  };
11400
11534
  if (superRequested) result["super_mode"] = true;
11401
11535
  return result;
11402
11536
  }
11537
+ function describeModerator(providers, spec) {
11538
+ const r = resolveModerator(providers, spec);
11539
+ const p = r.provider ?? Object.values(providers)[0];
11540
+ return {
11541
+ spec: spec ?? DEFAULT_MODERATOR,
11542
+ provider: p?.name ?? null,
11543
+ model: p?.model ?? null,
11544
+ available: r.provider !== void 0
11545
+ };
11546
+ }
11403
11547
 
11404
11548
  // src/tools/models.ts
11405
11549
  init_cjs_shims();
@@ -12960,7 +13104,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
12960
13104
  var DEFAULT_PACKAGE = "crosscheck-cli";
12961
13105
  var FETCH_TIMEOUT_MS = 3e3;
12962
13106
  function engineVersion() {
12963
- return true ? "0.2.29" : "0.0.0-dev";
13107
+ return true ? "0.2.31" : "0.0.0-dev";
12964
13108
  }
12965
13109
  function defaultUpdateCachePath() {
12966
13110
  const base = process.env["CROSSCHECK_DATA_DIR"] || import_node_path13.default.join(import_node_os2.default.homedir() || import_node_os2.default.tmpdir(), ".crosscheck");
@@ -13407,7 +13551,7 @@ var HELP_CATALOG = [
13407
13551
  primaryArg: "topic",
13408
13552
  summary: "Bounded multi-round debate between models, ending in a synthesized recommendation.",
13409
13553
  example: "xc debate should we adopt a monorepo?",
13410
- 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.`
13554
+ 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.`
13411
13555
  },
13412
13556
  {
13413
13557
  name: "coordinate",
@@ -13650,6 +13794,10 @@ function renderHelpCommand(entry) {
13650
13794
  }
13651
13795
 
13652
13796
  // src/tools/index.ts
13797
+ var MODERATOR_PARAM = {
13798
+ type: "string",
13799
+ 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`).'
13800
+ };
13653
13801
  function defineTool(opts) {
13654
13802
  return {
13655
13803
  name: opts.name,
@@ -13884,7 +14032,7 @@ function createTool(o, toolName, cheapDefault) {
13884
14032
  instruction: { type: "string" },
13885
14033
  documents: { type: "array", items: { type: "string" } },
13886
14034
  providers: { type: "array", items: { type: "string" } },
13887
- moderator: { type: "string" },
14035
+ moderator: MODERATOR_PARAM,
13888
14036
  constraints: { type: "string" },
13889
14037
  target_path: { type: "string" },
13890
14038
  session_id: { type: "string" },
@@ -13925,7 +14073,7 @@ function orchestrateTool(providers, allowlist, moderator, storage, pricing, brea
13925
14073
  dag: { type: "object", additionalProperties: true },
13926
14074
  context: { type: "string" },
13927
14075
  providers: { type: "array", items: { type: "string" } },
13928
- moderator: { type: "string" },
14076
+ moderator: MODERATOR_PARAM,
13929
14077
  fail_fast: { type: "boolean" },
13930
14078
  plan_only: { type: "boolean" },
13931
14079
  cheap_mode: { type: "boolean" },
@@ -14304,7 +14452,7 @@ function planTool(providers, allowlist, storage, breakers, transcriptsDir, repoR
14304
14452
  constraints: { type: "string" },
14305
14453
  context: { type: "string" },
14306
14454
  providers: { type: "array", items: { type: "string" } },
14307
- moderator: { type: "string" },
14455
+ moderator: MODERATOR_PARAM,
14308
14456
  session_id: { type: "string" },
14309
14457
  structured: { type: "boolean" },
14310
14458
  mode: { type: "string", enum: ["fast", "thorough"] },
@@ -14366,7 +14514,7 @@ function coordinateTool(providers, allowlist, innerCallers, storage, breakers, t
14366
14514
  providers: { type: "array", items: { type: "string" } },
14367
14515
  proposer: { type: "string" },
14368
14516
  synthesizer: { type: "string" },
14369
- moderator: { type: "string" },
14517
+ moderator: MODERATOR_PARAM,
14370
14518
  critics: { type: "array", items: { type: "string" } },
14371
14519
  session_id: { type: "string" },
14372
14520
  untrusted_input: { type: "boolean" },
@@ -14399,7 +14547,7 @@ function debateTool(providers, allowlist, innerCallers, storage, pricing, breake
14399
14547
  context: { type: "string" },
14400
14548
  max_rounds: { type: "integer", minimum: 1 },
14401
14549
  providers: { type: "array", items: { type: "string" } },
14402
- moderator: { type: "string" },
14550
+ moderator: MODERATOR_PARAM,
14403
14551
  session_id: { type: "string" },
14404
14552
  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.' },
14405
14553
  structured: { type: "boolean" },
@@ -15171,14 +15319,14 @@ function createServer(opts = {}) {
15171
15319
  });
15172
15320
  };
15173
15321
  const tools = buildToolRegistry(opts);
15174
- server.setRequestHandler(import_types9.ListToolsRequestSchema, async () => ({
15322
+ server.setRequestHandler(import_types12.ListToolsRequestSchema, async () => ({
15175
15323
  tools: Array.from(tools.values()).map((t) => ({
15176
15324
  name: t.name,
15177
15325
  description: t.description,
15178
15326
  inputSchema: t.inputSchema
15179
15327
  }))
15180
15328
  }));
15181
- server.setRequestHandler(import_types9.CallToolRequestSchema, async (req) => {
15329
+ server.setRequestHandler(import_types12.CallToolRequestSchema, async (req) => {
15182
15330
  const name = req.params.name;
15183
15331
  const tool = tools.get(name);
15184
15332
  if (!tool) {
@@ -15402,8 +15550,8 @@ function mapConfig(raw, sourcePath) {
15402
15550
  if (pa === null) result.providerAllowlist = null;
15403
15551
  else if (strArr(pa) !== void 0) result.providerAllowlist = strArr(pa);
15404
15552
  }
15405
- const moderator = str(raw["moderator"]);
15406
- if (moderator) result.moderatorDefault = moderator.toLowerCase();
15553
+ const moderator = parseModeratorSpec(raw["moderator"]);
15554
+ if (moderator) result.moderatorDefault = formatModeratorSpec(moderator);
15407
15555
  const bench = asObj(raw["bench"]);
15408
15556
  const goldens = bench ? str(bench["goldens_dir"]) : void 0;
15409
15557
  if (goldens) result.benchGoldensDir = goldens;
@@ -15607,7 +15755,9 @@ async function main() {
15607
15755
  if (configFile?.providerAllowlist !== void 0) {
15608
15756
  serverOpts.providerAllowlist = configFile.providerAllowlist;
15609
15757
  }
15610
- if (configFile?.moderatorDefault) serverOpts.moderatorDefault = configFile.moderatorDefault;
15758
+ const envModerator = parseModeratorSpec(process.env["CROSSCHECK_MODERATOR"]);
15759
+ const moderatorDefault = envModerator ? formatModeratorSpec(envModerator) : configFile?.moderatorDefault;
15760
+ if (moderatorDefault) serverOpts.moderatorDefault = moderatorDefault;
15611
15761
  if (configFile?.benchGoldensDir) serverOpts.benchGoldensDir = configFile.benchGoldensDir;
15612
15762
  if (configFile?.eventsPath) serverOpts.eventsPath = configFile.eventsPath;
15613
15763
  await connectAndServe(transport, serverOpts);