crosscheck-mcp 0.2.28 → 0.2.29

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
@@ -7,6 +7,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
  });
8
8
 
9
9
  // src/entrypoints/embed.ts
10
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
10
11
  import { existsSync as existsSync11 } from "fs";
11
12
  import path12 from "path";
12
13
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -491,6 +492,52 @@ function rejectsDisabledThinking(model) {
491
492
  const m = model.toLowerCase();
492
493
  return ADAPTIVE_THINKING_PREFIXES.some((p) => m.startsWith(p));
493
494
  }
495
+ var NO_FORCED_TOOL_PREFIXES = [
496
+ "claude-fable-5-1",
497
+ "claude-mythos-5-1",
498
+ "claude-opus-5-5",
499
+ "claude-sonnet-5-5"
500
+ ];
501
+ function rejectsForcedToolUse(model) {
502
+ const m = model.toLowerCase();
503
+ return NO_FORCED_TOOL_PREFIXES.some((p) => m.startsWith(p));
504
+ }
505
+ var UNSUPPORTED_SCHEMA_KEYS = /* @__PURE__ */ new Set([
506
+ "minimum",
507
+ "maximum",
508
+ "exclusiveMinimum",
509
+ "exclusiveMaximum",
510
+ "multipleOf",
511
+ "minLength",
512
+ "maxLength",
513
+ "pattern",
514
+ "minItems",
515
+ "maxItems",
516
+ "uniqueItems",
517
+ "minProperties",
518
+ "maxProperties",
519
+ "$schema"
520
+ ]);
521
+ function toStructuredOutputSchema(schema) {
522
+ const walk = (node) => {
523
+ if (Array.isArray(node)) return node.map(walk);
524
+ if (!node || typeof node !== "object") return node;
525
+ const out = {};
526
+ for (const [k, v] of Object.entries(node)) {
527
+ if (UNSUPPORTED_SCHEMA_KEYS.has(k)) continue;
528
+ if (k === "properties" && v && typeof v === "object") {
529
+ out[k] = Object.fromEntries(Object.entries(v).map(([pk, pv]) => [pk, walk(pv)]));
530
+ } else if (["items", "anyOf", "allOf", "oneOf", "not", "$defs", "definitions"].includes(k)) {
531
+ out[k] = k === "$defs" || k === "definitions" ? Object.fromEntries(Object.entries(v ?? {}).map(([dk, dv]) => [dk, walk(dv)])) : walk(v);
532
+ } else {
533
+ out[k] = v;
534
+ }
535
+ }
536
+ if (out["type"] === "object" || "properties" in out) out["additionalProperties"] = false;
537
+ return out;
538
+ };
539
+ return walk(schema);
540
+ }
494
541
  var ANTHROPIC_STRUCTURED_TOOL_NAME = "structured_output";
495
542
  function applyPromptCaching(body, env = process.env) {
496
543
  const flag = (env["CROSSCHECK_ANTHROPIC_PROMPT_CACHE"] ?? "").trim();
@@ -542,7 +589,12 @@ function buildAnthropicRequest(opts) {
542
589
  body.thinking = { type: "disabled" };
543
590
  }
544
591
  }
545
- if (opts.jsonSchema) {
592
+ if (opts.jsonSchema && rejectsForcedToolUse(opts.model)) {
593
+ body.output_config = {
594
+ ...body.output_config ?? {},
595
+ format: { type: "json_schema", schema: toStructuredOutputSchema(opts.jsonSchema) }
596
+ };
597
+ } else if (opts.jsonSchema) {
546
598
  body.tools = [{
547
599
  name: ANTHROPIC_STRUCTURED_TOOL_NAME,
548
600
  description: "Emit a single JSON object matching the requested schema.",
@@ -943,7 +995,15 @@ function buildOpenAICompatibleRequest(opts) {
943
995
  json_schema: {
944
996
  name: "structured_output",
945
997
  schema: opts.jsonSchema,
946
- strict: true
998
+ // NOT strict. Strict mode requires `additionalProperties: false` on
999
+ // every object and every property listed in `required` — the
1000
+ // engine's schemas don't meet that, so every structured OpenAI call
1001
+ // (planner, judges, synthesis) was a 400, unnoticed until the canary
1002
+ // grew a structured check (2026-10-11). Non-strict still shapes the
1003
+ // reply to the schema, and structured.ts validates it against the
1004
+ // full schema regardless, so strictness added a failure mode and no
1005
+ // safety.
1006
+ strict: false
947
1007
  }
948
1008
  };
949
1009
  }
@@ -1231,7 +1291,7 @@ import { z } from "zod";
1231
1291
 
1232
1292
  // src/server-meta.ts
1233
1293
  var SERVER_NAME = "crosscheck-agent";
1234
- var SERVER_VERSION = true ? "0.2.28" : "0.0.0-dev";
1294
+ var SERVER_VERSION = true ? "0.2.29" : "0.0.0-dev";
1235
1295
 
1236
1296
  // src/tools/audit.ts
1237
1297
  import { readdirSync, readFileSync as readFileSync3, statSync } from "fs";
@@ -6334,6 +6394,19 @@ import { performance as performance7 } from "perf_hooks";
6334
6394
  import { AsyncLocalStorage } from "async_hooks";
6335
6395
  import { performance as performance6 } from "perf_hooks";
6336
6396
  var progressStorage = new AsyncLocalStorage();
6397
+ async function withProgress(token, sendNotification, fn) {
6398
+ if (token === void 0 || token === null) {
6399
+ return fn();
6400
+ }
6401
+ const ctx = {
6402
+ token,
6403
+ wallStart: performance6.now(),
6404
+ cpuStart: process.cpuUsage(),
6405
+ step: { value: 0 },
6406
+ sendNotification
6407
+ };
6408
+ return progressStorage.run(ctx, fn);
6409
+ }
6337
6410
  async function emitProgress(message, opts) {
6338
6411
  const ctx = progressStorage.getStore();
6339
6412
  const wallStart = ctx ? ctx.wallStart : performance6.now();
@@ -7863,6 +7936,16 @@ function buildCallContext(args) {
7863
7936
  var CREATE_DOC_MAX_BYTES = 32 * 1024;
7864
7937
  var CREATE_AUDIT_THRESHOLD = 0.7;
7865
7938
  async function runCreate(args, opts) {
7939
+ const subEnvelopes = [];
7940
+ const tracked = (fn) => async (...a) => {
7941
+ const out = await fn(...a);
7942
+ subEnvelopes.push(out);
7943
+ return out;
7944
+ };
7945
+ const confer = tracked(runConfer);
7946
+ const orchestrate = tracked(runOrchestrate);
7947
+ const review = tracked(runReview);
7948
+ const audit = tracked(runAudit);
7866
7949
  const instruction = typeof args["instruction"] === "string" ? args["instruction"] : String(args["instruction"] ?? "");
7867
7950
  if (!instruction) {
7868
7951
  return errorEnvelope6(
@@ -7899,7 +7982,7 @@ ${constraints}
7899
7982
 
7900
7983
  ` : "") + `DOCUMENTS (${okCount} of ${descriptors.length} ingested):
7901
7984
  ${documentsPayload}`;
7902
- const scope = await runConfer({
7985
+ const scope = await confer({
7903
7986
  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,
7904
7987
  context: contextBlock,
7905
7988
  ...providers !== void 0 ? { providers } : {},
@@ -7926,7 +8009,7 @@ ${scopeAnswer || "(none)"}
7926
8009
  };
7927
8010
  if (providers !== void 0) orchestrateArgs["providers"] = providers;
7928
8011
  if (planOnly) orchestrateArgs["plan_only"] = true;
7929
- let orchestration = await runOrchestrate(orchestrateArgs, {
8012
+ let orchestration = await orchestrate(orchestrateArgs, {
7930
8013
  providers: opts.providers,
7931
8014
  allowlist: opts.allowlist ?? null,
7932
8015
  moderator,
@@ -7955,7 +8038,7 @@ ${scopeAnswer || "(none)"}
7955
8038
  }
7956
8039
  let reviewEnvelope = null;
7957
8040
  if (!skipReview && typeof orchestration["final"] === "string" && orchestration["final"]) {
7958
- reviewEnvelope = await runReview({
8041
+ reviewEnvelope = await review({
7959
8042
  snippet: orchestration["final"],
7960
8043
  intent: `final deliverable for: ${instruction}`,
7961
8044
  ...providers !== void 0 ? { providers } : {},
@@ -7975,7 +8058,7 @@ ${scopeAnswer || "(none)"}
7975
8058
  const panelExhausted = producingPanel.length > 0 && producingPanel.every(
7976
8059
  (p) => p in opts.providers || Object.keys(opts.providers).includes(p.toLowerCase())
7977
8060
  ) && producingPanel.length >= Object.keys(opts.providers).length;
7978
- auditEnvelope = await runAudit({
8061
+ auditEnvelope = await audit({
7979
8062
  output_to_audit: orchestration["final"],
7980
8063
  producing_panelists: producingPanel,
7981
8064
  ...auditRubric !== void 0 ? { rubric: auditRubric } : {},
@@ -8004,7 +8087,7 @@ ${retryConstraints}
8004
8087
  DOCUMENTS:
8005
8088
  ${documentsPayload}`
8006
8089
  };
8007
- const retryOrchestration = await runOrchestrate(
8090
+ const retryOrchestration = await orchestrate(
8008
8091
  retryOrchestrateArgs,
8009
8092
  {
8010
8093
  providers: opts.providers,
@@ -8018,7 +8101,7 @@ ${documentsPayload}`
8018
8101
  if (typeof retryOrchestration["final"] === "string" && retryOrchestration["final"]) {
8019
8102
  const retryPanel = computeProducingPanel(retryOrchestration);
8020
8103
  const retryPanelExhausted = retryPanel.length > 0 && retryPanel.length >= Object.keys(opts.providers).length;
8021
- auditEnvelope = await runAudit({
8104
+ auditEnvelope = await audit({
8022
8105
  output_to_audit: retryOrchestration["final"],
8023
8106
  producing_panelists: retryPanel,
8024
8107
  ...auditRubric !== void 0 ? { rubric: auditRubric } : {},
@@ -8093,9 +8176,28 @@ ${documentsPayload}`
8093
8176
  ["auditor", "items", "overall_score", "passed", "rubric"]
8094
8177
  ),
8095
8178
  artifacts,
8096
- warnings
8179
+ warnings,
8180
+ ...combinedUsage(subEnvelopes)
8097
8181
  };
8098
8182
  }
8183
+ function combinedUsage(envelopes) {
8184
+ const usages = [];
8185
+ const byCallTiming = [];
8186
+ let wall = 0, cpu = 0;
8187
+ for (const env of envelopes) {
8188
+ const e = env ?? {};
8189
+ const calls = e["usage"]?.["by_call"] ?? [];
8190
+ const t = e["timing"] ?? {};
8191
+ const timing = Array.isArray(t["by_call"]) ? t["by_call"] : [];
8192
+ if (!Array.isArray(calls) || calls.length === 0) continue;
8193
+ usages.push(...calls);
8194
+ if (timing.length === calls.length) byCallTiming.push(...timing);
8195
+ 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 });
8196
+ wall += Number(t["wall_ms"] ?? 0) || 0;
8197
+ cpu += Number(t["cpu_ms"] ?? 0) || 0;
8198
+ }
8199
+ return { usage: aggregateUsage(usages), timing: { wall_ms: wall, cpu_ms: cpu, by_call: byCallTiming } };
8200
+ }
8099
8201
  async function ingestDocuments(documents, sessionId, opts) {
8100
8202
  if (documents.length === 0) return [];
8101
8203
  const out = [];
@@ -11572,7 +11674,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
11572
11674
  var DEFAULT_PACKAGE = "crosscheck-cli";
11573
11675
  var FETCH_TIMEOUT_MS = 3e3;
11574
11676
  function engineVersion() {
11575
- return true ? "0.2.28" : "0.0.0-dev";
11677
+ return true ? "0.2.29" : "0.0.0-dev";
11576
11678
  }
11577
11679
  function defaultUpdateCachePath() {
11578
11680
  const base = process.env["CROSSCHECK_DATA_DIR"] || path9.join(os.homedir() || os.tmpdir(), ".crosscheck");
@@ -13363,6 +13465,107 @@ var ENGINE_TO_VAULT = { xai: "grok" };
13363
13465
  function vaultId(engineName) {
13364
13466
  return ENGINE_TO_VAULT[engineName] ?? engineName;
13365
13467
  }
13468
+ var ACTIVITY_TEXT_CAP = 8e3;
13469
+ var ASKED_CAP = 280;
13470
+ var runContext = new AsyncLocalStorage2();
13471
+ var SYNTHESIS_PURPOSES = /* @__PURE__ */ new Set(["synth", "moderator", "recombine", "judge", "unify"]);
13472
+ function exemptFromSkip(ctx, provider, purpose) {
13473
+ return provider === ctx.moderator && SYNTHESIS_PURPOSES.has(purpose);
13474
+ }
13475
+ function skippedError(provider, why, limitMs) {
13476
+ return new ProviderError(
13477
+ "client",
13478
+ why === "timeout" ? `${provider}: stopped after ${Math.round((limitMs ?? 0) / 1e3)}s (your per-model time limit)` : `${provider}: stopped waiting (you skipped it)`,
13479
+ { transient: false }
13480
+ );
13481
+ }
13482
+ function lastUserText(args) {
13483
+ const msgs = args.messages;
13484
+ for (let i = msgs.length - 1; i >= 0; i--) {
13485
+ const m = msgs[i];
13486
+ if (m && m.role === "user" && typeof m.content === "string") {
13487
+ const t = m.content.replace(/\s+/g, " ").trim();
13488
+ return t.length > ASKED_CAP ? `${t.slice(0, ASKED_CAP - 1)}\u2026` : t;
13489
+ }
13490
+ }
13491
+ return "";
13492
+ }
13493
+ function withActivity(p) {
13494
+ const send = p.send.bind(p);
13495
+ return {
13496
+ name: p.name,
13497
+ model: p.model,
13498
+ ...p.fallbackModels ? { fallbackModels: p.fallbackModels } : {},
13499
+ send: async (args) => {
13500
+ const ctx = runContext.getStore();
13501
+ if (!ctx) return send(args);
13502
+ const callId = ctx.nextCallId();
13503
+ const provider = vaultId(p.name);
13504
+ const model = args.modelOverride ?? p.model;
13505
+ const purpose = args.purpose ?? "worker";
13506
+ const started = Date.now();
13507
+ const exempt = exemptFromSkip(ctx, provider, purpose);
13508
+ if (!exempt && ctx.skipped.has(provider)) throw skippedError(provider, "user");
13509
+ ctx.emit({ type: "call.start", runId: ctx.runId, at: started, callId, provider, model, purpose, asked: lastUserText(args) });
13510
+ const controller = new AbortController();
13511
+ if (args.signal) {
13512
+ if (args.signal.aborted) controller.abort(args.signal.reason);
13513
+ else args.signal.addEventListener("abort", () => controller.abort(args.signal.reason), { once: true });
13514
+ }
13515
+ let stopped;
13516
+ if (!exempt) ctx.inflight.set(callId, { provider, purpose, controller });
13517
+ const timer = !exempt && ctx.callTimeoutMs && ctx.callTimeoutMs > 0 ? setTimeout(() => {
13518
+ stopped = "timeout";
13519
+ controller.abort("timeout");
13520
+ }, ctx.callTimeoutMs) : void 0;
13521
+ controller.signal.addEventListener("abort", () => {
13522
+ if (!stopped && controller.signal.reason === "skip") stopped = "user";
13523
+ }, { once: true });
13524
+ try {
13525
+ const res = await send({ ...args, signal: controller.signal });
13526
+ const text = typeof res.text === "string" ? res.text : "";
13527
+ ctx.emit({
13528
+ type: "call.done",
13529
+ runId: ctx.runId,
13530
+ at: Date.now(),
13531
+ callId,
13532
+ provider,
13533
+ model,
13534
+ purpose,
13535
+ ok: true,
13536
+ ms: Date.now() - started,
13537
+ text: text.slice(0, ACTIVITY_TEXT_CAP),
13538
+ truncated: text.length > ACTIVITY_TEXT_CAP,
13539
+ error: null,
13540
+ usage: usageOf(res)
13541
+ });
13542
+ return res;
13543
+ } catch (err) {
13544
+ const out = stopped ? skippedError(provider, stopped, ctx.callTimeoutMs) : err;
13545
+ ctx.emit({
13546
+ type: "call.done",
13547
+ runId: ctx.runId,
13548
+ at: Date.now(),
13549
+ callId,
13550
+ provider,
13551
+ model,
13552
+ purpose,
13553
+ ok: false,
13554
+ ms: Date.now() - started,
13555
+ text: "",
13556
+ truncated: false,
13557
+ error: out instanceof Error ? out.message : String(out),
13558
+ usage: null,
13559
+ ...stopped ? { stopped } : {}
13560
+ });
13561
+ throw out;
13562
+ } finally {
13563
+ if (timer) clearTimeout(timer);
13564
+ ctx.inflight.delete(callId);
13565
+ }
13566
+ }
13567
+ };
13568
+ }
13366
13569
  function usageOf(answer) {
13367
13570
  const u = answer["usage"];
13368
13571
  if (!u || typeof u !== "object") return null;
@@ -13398,6 +13601,8 @@ function bundledPricingPath() {
13398
13601
  }
13399
13602
  }
13400
13603
  var runCounter = 0;
13604
+ var callCounter = 0;
13605
+ var nextCallId = () => ++callCounter;
13401
13606
  function nextRunId() {
13402
13607
  runCounter += 1;
13403
13608
  return `run_${Date.now().toString(36)}_${runCounter}`;
@@ -13416,7 +13621,9 @@ function createEmbeddedEngine(opts) {
13416
13621
  (p) => Boolean(p) && existsSync11(p)
13417
13622
  );
13418
13623
  const pricing = pricingPath ? loadPricing(pricingPath) : {};
13419
- const built = buildProviders({ env, pricing });
13624
+ const built = Object.fromEntries(
13625
+ Object.entries(buildProviders({ env, pricing })).map(([name, p]) => [name, withActivity(p)])
13626
+ );
13420
13627
  const moderator = opts.moderator ?? "anthropic";
13421
13628
  const tools = registerCoreTools({ providers: built, pricing });
13422
13629
  const providerIds = Object.keys(built).map(vaultId).sort();
@@ -13433,7 +13640,8 @@ function createEmbeddedEngine(opts) {
13433
13640
  if (!tool) return Promise.reject(new Error(`unknown tool: ${name}`));
13434
13641
  return Promise.resolve(tool.handler(args));
13435
13642
  };
13436
- const run = async (name, args, onEvent) => {
13643
+ const liveRuns = /* @__PURE__ */ new Map();
13644
+ const run = async (name, args, onEvent, runOpts = {}) => {
13437
13645
  const runId = nextRunId();
13438
13646
  const started = Date.now();
13439
13647
  const emit = (e) => {
@@ -13442,6 +13650,78 @@ function createEmbeddedEngine(opts) {
13442
13650
  } catch {
13443
13651
  }
13444
13652
  };
13653
+ const ctx = {
13654
+ runId,
13655
+ emit,
13656
+ nextCallId,
13657
+ skipped: /* @__PURE__ */ new Set(),
13658
+ inflight: /* @__PURE__ */ new Map(),
13659
+ moderator: vaultId(moderator),
13660
+ ...runOpts.callTimeoutMs && runOpts.callTimeoutMs > 0 ? { callTimeoutMs: runOpts.callTimeoutMs } : {}
13661
+ };
13662
+ liveRuns.set(runId, ctx);
13663
+ try {
13664
+ return await runContext.run(ctx, () => withProgress(runId, (n) => {
13665
+ if (n.params.message) emit({ type: "progress", runId, at: Date.now(), message: n.params.message });
13666
+ }, () => runInner(name, args, runId, started, emit)));
13667
+ } finally {
13668
+ liveRuns.delete(runId);
13669
+ }
13670
+ };
13671
+ const skip = (runId, provider) => {
13672
+ const ctx = liveRuns.get(runId);
13673
+ if (!ctx) return 0;
13674
+ ctx.skipped.add(provider);
13675
+ let stopped = 0;
13676
+ for (const call2 of ctx.inflight.values()) {
13677
+ if (call2.provider === provider && !exemptFromSkip(ctx, call2.provider, call2.purpose)) {
13678
+ call2.controller.abort("skip");
13679
+ stopped += 1;
13680
+ }
13681
+ }
13682
+ return stopped;
13683
+ };
13684
+ const ask = async (o) => {
13685
+ const engineName = Object.keys(built).find((n) => vaultId(n) === o.provider);
13686
+ const base = engineName ? built[engineName] : void 0;
13687
+ if (!base) {
13688
+ return { ok: false, text: "", json: null, model: o.model ?? "", error: `No ${o.provider} key is configured, so it can't be asked.` };
13689
+ }
13690
+ const provider = o.model && o.model !== base.model ? retargetProvider(base, o.model) : base;
13691
+ const maxTokens = o.maxTokens ?? 8e3;
13692
+ const doAsk = async () => {
13693
+ if (o.schema) {
13694
+ const res = await requestStructured(provider, o.messages, o.schema, { maxTokens, purpose: o.purpose, maxRetries: 1 });
13695
+ return {
13696
+ ok: res.obj !== null,
13697
+ text: res.answer.response ?? "",
13698
+ json: res.obj,
13699
+ error: res.obj !== null ? null : res.answer.error ?? res.errors.join("; ") ?? "invalid reply",
13700
+ model: provider.model
13701
+ };
13702
+ }
13703
+ const a = await askOne(provider, o.messages, { maxTokens, temperature: 0.3, purpose: o.purpose });
13704
+ return { ok: !a.error, text: a.response ?? "", json: null, error: a.error ?? null, model: provider.model };
13705
+ };
13706
+ if (!o.runId || !o.onEvent) return doAsk();
13707
+ const sink = o.onEvent;
13708
+ const emit = (e) => {
13709
+ try {
13710
+ sink(e);
13711
+ } catch {
13712
+ }
13713
+ };
13714
+ const ctx = {
13715
+ runId: o.runId,
13716
+ emit,
13717
+ nextCallId,
13718
+ skipped: /* @__PURE__ */ new Set(),
13719
+ inflight: /* @__PURE__ */ new Map(),
13720
+ moderator: o.provider
13721
+ };
13722
+ return runContext.run(ctx, doAsk);
13723
+ };
13724
+ const runInner = async (name, args, runId, started, emit) => {
13445
13725
  emit({
13446
13726
  type: "run.start",
13447
13727
  runId,
@@ -13519,8 +13799,16 @@ function createEmbeddedEngine(opts) {
13519
13799
  providers: providerIds,
13520
13800
  models,
13521
13801
  listTools: () => [...tools.keys()],
13802
+ requiredArgs: (name) => {
13803
+ const tool = tools.get(name);
13804
+ if (!tool) return null;
13805
+ const req = tool.inputSchema?.required;
13806
+ return Array.isArray(req) ? req.filter((x) => typeof x === "string") : [];
13807
+ },
13522
13808
  call,
13523
13809
  run,
13810
+ skip,
13811
+ ask,
13524
13812
  close: async () => {
13525
13813
  await flush().catch(() => {
13526
13814
  });
@@ -13529,6 +13817,7 @@ function createEmbeddedEngine(opts) {
13529
13817
  }
13530
13818
  var __test_internals = { usageOf, textOf, errorOf, vaultId };
13531
13819
  export {
13820
+ ACTIVITY_TEXT_CAP,
13532
13821
  DEFAULT_EXCLUDED_PROVIDERS,
13533
13822
  DEFAULT_FALLBACKS,
13534
13823
  DEFAULT_MODELS,