crosscheck-mcp 0.2.28 → 0.2.30

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.
@@ -611,6 +611,11 @@ function reasoningSharesOutputBudget(provider) {
611
611
  }
612
612
 
613
613
  // src/providers/types.ts
614
+ var IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
615
+ function imagesOf(m) {
616
+ if (m.role !== "user" || !Array.isArray(m.images)) return [];
617
+ return m.images.filter((i) => Boolean(i) && typeof i.data === "string" && i.data.length > 0 && IMAGE_MEDIA_TYPES.includes(i.mediaType));
618
+ }
614
619
  var ProviderError = class extends Error {
615
620
  name = "ProviderError";
616
621
  kind;
@@ -924,6 +929,52 @@ function rejectsDisabledThinking(model) {
924
929
  const m = model.toLowerCase();
925
930
  return ADAPTIVE_THINKING_PREFIXES.some((p) => m.startsWith(p));
926
931
  }
932
+ var NO_FORCED_TOOL_PREFIXES = [
933
+ "claude-fable-5-1",
934
+ "claude-mythos-5-1",
935
+ "claude-opus-5-5",
936
+ "claude-sonnet-5-5"
937
+ ];
938
+ function rejectsForcedToolUse(model) {
939
+ const m = model.toLowerCase();
940
+ return NO_FORCED_TOOL_PREFIXES.some((p) => m.startsWith(p));
941
+ }
942
+ var UNSUPPORTED_SCHEMA_KEYS = /* @__PURE__ */ new Set([
943
+ "minimum",
944
+ "maximum",
945
+ "exclusiveMinimum",
946
+ "exclusiveMaximum",
947
+ "multipleOf",
948
+ "minLength",
949
+ "maxLength",
950
+ "pattern",
951
+ "minItems",
952
+ "maxItems",
953
+ "uniqueItems",
954
+ "minProperties",
955
+ "maxProperties",
956
+ "$schema"
957
+ ]);
958
+ function toStructuredOutputSchema(schema) {
959
+ const walk = (node) => {
960
+ if (Array.isArray(node)) return node.map(walk);
961
+ if (!node || typeof node !== "object") return node;
962
+ const out = {};
963
+ for (const [k, v] of Object.entries(node)) {
964
+ if (UNSUPPORTED_SCHEMA_KEYS.has(k)) continue;
965
+ if (k === "properties" && v && typeof v === "object") {
966
+ out[k] = Object.fromEntries(Object.entries(v).map(([pk, pv]) => [pk, walk(pv)]));
967
+ } else if (["items", "anyOf", "allOf", "oneOf", "not", "$defs", "definitions"].includes(k)) {
968
+ out[k] = k === "$defs" || k === "definitions" ? Object.fromEntries(Object.entries(v ?? {}).map(([dk, dv]) => [dk, walk(dv)])) : walk(v);
969
+ } else {
970
+ out[k] = v;
971
+ }
972
+ }
973
+ if (out["type"] === "object" || "properties" in out) out["additionalProperties"] = false;
974
+ return out;
975
+ };
976
+ return walk(schema);
977
+ }
927
978
  var ANTHROPIC_STRUCTURED_TOOL_NAME = "structured_output";
928
979
  function applyPromptCaching(body, env = process.env) {
929
980
  const flag = (env["CROSSCHECK_ANTHROPIC_PROMPT_CACHE"] ?? "").trim();
@@ -953,7 +1004,12 @@ function buildAnthropicRequest(opts) {
953
1004
  }
954
1005
  continue;
955
1006
  }
956
- convo.push({ role: m.role, content: typeof m.content === "string" ? m.content : "" });
1007
+ const text = typeof m.content === "string" ? m.content : "";
1008
+ const images = imagesOf(m);
1009
+ convo.push(images.length ? { role: m.role, content: [
1010
+ ...images.map((i) => ({ type: "image", source: { type: "base64", media_type: i.mediaType, data: i.data } })),
1011
+ { type: "text", text }
1012
+ ] } : { role: m.role, content: text });
957
1013
  }
958
1014
  const body = {
959
1015
  model: opts.model,
@@ -975,7 +1031,12 @@ function buildAnthropicRequest(opts) {
975
1031
  body.thinking = { type: "disabled" };
976
1032
  }
977
1033
  }
978
- if (opts.jsonSchema) {
1034
+ if (opts.jsonSchema && rejectsForcedToolUse(opts.model)) {
1035
+ body.output_config = {
1036
+ ...body.output_config ?? {},
1037
+ format: { type: "json_schema", schema: toStructuredOutputSchema(opts.jsonSchema) }
1038
+ };
1039
+ } else if (opts.jsonSchema) {
979
1040
  body.tools = [{
980
1041
  name: ANTHROPIC_STRUCTURED_TOOL_NAME,
981
1042
  description: "Emit a single JSON object matching the requested schema.",
@@ -1188,7 +1249,10 @@ function buildGeminiRequest(opts) {
1188
1249
  const role = m.role === "user" ? "user" : "model";
1189
1250
  contents.push({
1190
1251
  role,
1191
- parts: [{ text: typeof m.content === "string" ? m.content : "" }]
1252
+ parts: [
1253
+ ...imagesOf(m).map((i) => ({ inline_data: { mime_type: i.mediaType, data: i.data } })),
1254
+ { text: typeof m.content === "string" ? m.content : "" }
1255
+ ]
1192
1256
  });
1193
1257
  }
1194
1258
  const body = {
@@ -1356,10 +1420,11 @@ function supportsNativeJsonSchema(provider, model) {
1356
1420
  return true;
1357
1421
  }
1358
1422
  function buildOpenAICompatibleRequest(opts) {
1359
- const msgs = opts.messages.map((m) => ({
1360
- role: m.role,
1361
- content: typeof m.content === "string" ? m.content : ""
1362
- }));
1423
+ const msgs = opts.messages.map((m) => {
1424
+ const text = typeof m.content === "string" ? m.content : "";
1425
+ const images = imagesOf(m);
1426
+ 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 };
1427
+ });
1363
1428
  const body = {
1364
1429
  model: opts.model,
1365
1430
  messages: msgs
@@ -1376,7 +1441,15 @@ function buildOpenAICompatibleRequest(opts) {
1376
1441
  json_schema: {
1377
1442
  name: "structured_output",
1378
1443
  schema: opts.jsonSchema,
1379
- strict: true
1444
+ // NOT strict. Strict mode requires `additionalProperties: false` on
1445
+ // every object and every property listed in `required` — the
1446
+ // engine's schemas don't meet that, so every structured OpenAI call
1447
+ // (planner, judges, synthesis) was a 400, unnoticed until the canary
1448
+ // grew a structured check (2026-10-11). Non-strict still shapes the
1449
+ // reply to the schema, and structured.ts validates it against the
1450
+ // full schema regardless, so strictness added a failure mode and no
1451
+ // safety.
1452
+ strict: false
1380
1453
  }
1381
1454
  };
1382
1455
  }
@@ -1664,7 +1737,7 @@ var import_zod = require("zod");
1664
1737
 
1665
1738
  // src/server-meta.ts
1666
1739
  var SERVER_NAME = "crosscheck-agent";
1667
- var SERVER_VERSION = true ? "0.2.28" : "0.0.0-dev";
1740
+ var SERVER_VERSION = true ? "0.2.30" : "0.0.0-dev";
1668
1741
 
1669
1742
  // src/tools/audit.ts
1670
1743
  var import_node_fs4 = require("fs");
@@ -4628,6 +4701,7 @@ async function pickAutoPanel(storage, purpose, n, providers, allowlist) {
4628
4701
  }
4629
4702
 
4630
4703
  // src/core/super-mode.ts
4704
+ var import_node_async_hooks = require("async_hooks");
4631
4705
  var SUPER_MODELS = {
4632
4706
  // Fable 5.1 verified present on the account; 5 as the fallback for accounts
4633
4707
  // that haven't been granted 5.1 yet.
@@ -4670,7 +4744,9 @@ function parseChain(v) {
4670
4744
  }
4671
4745
  return out;
4672
4746
  }
4673
- function superChainFor(provider, env = process.env) {
4747
+ var superEnvStore = new import_node_async_hooks.AsyncLocalStorage();
4748
+ var currentEnv = () => superEnvStore.getStore() ?? process.env;
4749
+ function superChainFor(provider, env = currentEnv()) {
4674
4750
  const varName = SUPER_ENV_VARS[provider];
4675
4751
  const user = varName ? parseChain(env[varName]) : [];
4676
4752
  if (user.length > 0) {
@@ -4679,14 +4755,14 @@ function superChainFor(provider, env = process.env) {
4679
4755
  }
4680
4756
  return SUPER_MODELS[provider] ?? [];
4681
4757
  }
4682
- function superPrimary(provider, env = process.env) {
4758
+ function superPrimary(provider, env = currentEnv()) {
4683
4759
  return superChainFor(provider, env)[0];
4684
4760
  }
4685
4761
  var SUPER_PROVIDER_NAMES = Object.keys(SUPER_MODELS);
4686
4762
  function isSuperRequested(args) {
4687
4763
  return args["super"] === true;
4688
4764
  }
4689
- function retargetForSuper(p, env = process.env) {
4765
+ function retargetForSuper(p, env = currentEnv()) {
4690
4766
  const chain = superChainFor(p.name, env);
4691
4767
  if (chain.length === 0) return p;
4692
4768
  const [primary, ...fallbacks] = chain;
@@ -6757,9 +6833,9 @@ var import_node_path9 = __toESM(require("path"), 1);
6757
6833
  var import_node_perf_hooks6 = require("perf_hooks");
6758
6834
 
6759
6835
  // src/core/progress.ts
6760
- var import_node_async_hooks = require("async_hooks");
6836
+ var import_node_async_hooks2 = require("async_hooks");
6761
6837
  var import_node_perf_hooks5 = require("perf_hooks");
6762
- var progressStorage = new import_node_async_hooks.AsyncLocalStorage();
6838
+ var progressStorage = new import_node_async_hooks2.AsyncLocalStorage();
6763
6839
  async function emitProgress(message, opts) {
6764
6840
  const ctx = progressStorage.getStore();
6765
6841
  const wallStart = ctx ? ctx.wallStart : import_node_perf_hooks5.performance.now();
@@ -8279,6 +8355,16 @@ function buildCallContext(args) {
8279
8355
  var CREATE_DOC_MAX_BYTES = 32 * 1024;
8280
8356
  var CREATE_AUDIT_THRESHOLD = 0.7;
8281
8357
  async function runCreate(args, opts) {
8358
+ const subEnvelopes = [];
8359
+ const tracked = (fn) => async (...a) => {
8360
+ const out = await fn(...a);
8361
+ subEnvelopes.push(out);
8362
+ return out;
8363
+ };
8364
+ const confer = tracked(runConfer);
8365
+ const orchestrate = tracked(runOrchestrate);
8366
+ const review = tracked(runReview);
8367
+ const audit = tracked(runAudit);
8282
8368
  const instruction = typeof args["instruction"] === "string" ? args["instruction"] : String(args["instruction"] ?? "");
8283
8369
  if (!instruction) {
8284
8370
  return errorEnvelope6(
@@ -8315,7 +8401,7 @@ ${constraints}
8315
8401
 
8316
8402
  ` : "") + `DOCUMENTS (${okCount} of ${descriptors.length} ingested):
8317
8403
  ${documentsPayload}`;
8318
- const scope = await runConfer({
8404
+ const scope = await confer({
8319
8405
  question: "We are about to orchestrate a full plan->build->review->audit lifecycle for this instruction. List 3-6 concrete sub-tasks (each with a one-line description and a difficulty: low|med|high), any critical assumptions, and any missing-information risks. Be concise and decisive \u2014 your output will be used to build the orchestration DAG.\n\nINSTRUCTION:\n" + instruction,
8320
8406
  context: contextBlock,
8321
8407
  ...providers !== void 0 ? { providers } : {},
@@ -8342,7 +8428,7 @@ ${scopeAnswer || "(none)"}
8342
8428
  };
8343
8429
  if (providers !== void 0) orchestrateArgs["providers"] = providers;
8344
8430
  if (planOnly) orchestrateArgs["plan_only"] = true;
8345
- let orchestration = await runOrchestrate(orchestrateArgs, {
8431
+ let orchestration = await orchestrate(orchestrateArgs, {
8346
8432
  providers: opts.providers,
8347
8433
  allowlist: opts.allowlist ?? null,
8348
8434
  moderator,
@@ -8371,7 +8457,7 @@ ${scopeAnswer || "(none)"}
8371
8457
  }
8372
8458
  let reviewEnvelope = null;
8373
8459
  if (!skipReview && typeof orchestration["final"] === "string" && orchestration["final"]) {
8374
- reviewEnvelope = await runReview({
8460
+ reviewEnvelope = await review({
8375
8461
  snippet: orchestration["final"],
8376
8462
  intent: `final deliverable for: ${instruction}`,
8377
8463
  ...providers !== void 0 ? { providers } : {},
@@ -8391,7 +8477,7 @@ ${scopeAnswer || "(none)"}
8391
8477
  const panelExhausted = producingPanel.length > 0 && producingPanel.every(
8392
8478
  (p) => p in opts.providers || Object.keys(opts.providers).includes(p.toLowerCase())
8393
8479
  ) && producingPanel.length >= Object.keys(opts.providers).length;
8394
- auditEnvelope = await runAudit({
8480
+ auditEnvelope = await audit({
8395
8481
  output_to_audit: orchestration["final"],
8396
8482
  producing_panelists: producingPanel,
8397
8483
  ...auditRubric !== void 0 ? { rubric: auditRubric } : {},
@@ -8420,7 +8506,7 @@ ${retryConstraints}
8420
8506
  DOCUMENTS:
8421
8507
  ${documentsPayload}`
8422
8508
  };
8423
- const retryOrchestration = await runOrchestrate(
8509
+ const retryOrchestration = await orchestrate(
8424
8510
  retryOrchestrateArgs,
8425
8511
  {
8426
8512
  providers: opts.providers,
@@ -8434,7 +8520,7 @@ ${documentsPayload}`
8434
8520
  if (typeof retryOrchestration["final"] === "string" && retryOrchestration["final"]) {
8435
8521
  const retryPanel = computeProducingPanel(retryOrchestration);
8436
8522
  const retryPanelExhausted = retryPanel.length > 0 && retryPanel.length >= Object.keys(opts.providers).length;
8437
- auditEnvelope = await runAudit({
8523
+ auditEnvelope = await audit({
8438
8524
  output_to_audit: retryOrchestration["final"],
8439
8525
  producing_panelists: retryPanel,
8440
8526
  ...auditRubric !== void 0 ? { rubric: auditRubric } : {},
@@ -8509,9 +8595,28 @@ ${documentsPayload}`
8509
8595
  ["auditor", "items", "overall_score", "passed", "rubric"]
8510
8596
  ),
8511
8597
  artifacts,
8512
- warnings
8598
+ warnings,
8599
+ ...combinedUsage(subEnvelopes)
8513
8600
  };
8514
8601
  }
8602
+ function combinedUsage(envelopes) {
8603
+ const usages = [];
8604
+ const byCallTiming = [];
8605
+ let wall = 0, cpu = 0;
8606
+ for (const env of envelopes) {
8607
+ const e = env ?? {};
8608
+ const calls = e["usage"]?.["by_call"] ?? [];
8609
+ const t = e["timing"] ?? {};
8610
+ const timing = Array.isArray(t["by_call"]) ? t["by_call"] : [];
8611
+ if (!Array.isArray(calls) || calls.length === 0) continue;
8612
+ usages.push(...calls);
8613
+ if (timing.length === calls.length) byCallTiming.push(...timing);
8614
+ else for (const c of calls) byCallTiming.push({ provider: c.provider, model: c.model, purpose: c.purpose ?? null, wall_ms: 0, cpu_ms: 0, cache_hit: false });
8615
+ wall += Number(t["wall_ms"] ?? 0) || 0;
8616
+ cpu += Number(t["cpu_ms"] ?? 0) || 0;
8617
+ }
8618
+ return { usage: aggregateUsage(usages), timing: { wall_ms: wall, cpu_ms: cpu, by_call: byCallTiming } };
8619
+ }
8515
8620
  async function ingestDocuments(documents, sessionId, opts) {
8516
8621
  if (documents.length === 0) return [];
8517
8622
  const out = [];
@@ -11988,7 +12093,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
11988
12093
  var DEFAULT_PACKAGE = "crosscheck-cli";
11989
12094
  var FETCH_TIMEOUT_MS = 3e3;
11990
12095
  function engineVersion() {
11991
- return true ? "0.2.28" : "0.0.0-dev";
12096
+ return true ? "0.2.30" : "0.0.0-dev";
11992
12097
  }
11993
12098
  function defaultUpdateCachePath() {
11994
12099
  const base = process.env["CROSSCHECK_DATA_DIR"] || import_node_path12.default.join(import_node_os2.default.homedir() || import_node_os2.default.tmpdir(), ".crosscheck");