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.
@@ -1,5 +1,6 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
2
  import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
3
+ import { P as Provider } from './types-BcprisDl.cjs';
3
4
 
4
5
  interface PricingDoc {
5
6
  /** `<provider>` -> `<model>` -> ModelRates. */
@@ -322,81 +323,6 @@ interface Storage extends StorageRead, StorageWrite {
322
323
  close(): Promise<void>;
323
324
  }
324
325
 
325
- /** One provider call's token + cost record. Mirrors `Usage.to_dict()`
326
- * output exactly. */
327
- interface Usage {
328
- provider: string;
329
- model: string;
330
- prompt_tokens: number;
331
- completion_tokens: number;
332
- cached_tokens: number;
333
- total_tokens: number;
334
- cost_usd: number;
335
- estimated: boolean;
336
- purpose: string;
337
- }
338
-
339
- /** Inbound message shape — chat-completions style. */
340
- interface ChatMessage {
341
- role: "system" | "user" | "assistant" | string;
342
- content: string;
343
- [k: string]: unknown;
344
- }
345
- /** A successful `send()` result. */
346
- interface SendResult {
347
- /** Concatenated text from the provider response. */
348
- text: string;
349
- /** Number of HTTP attempts that produced this result (>= 1). */
350
- attempts: number;
351
- /** Parsed + cost-augmented usage record. */
352
- usage: Usage;
353
- }
354
- /** Args passed into every `Provider.send()` call. Mirrors the Python
355
- * `send(messages, max_tokens, temperature, purpose='worker')` signature
356
- * plus an optional `signal` for cancellation. */
357
- interface SendArgs {
358
- messages: readonly ChatMessage[];
359
- maxTokens: number;
360
- temperature: number;
361
- purpose?: string;
362
- /** Optional AbortSignal so callers can cancel long-running calls. */
363
- signal?: AbortSignal;
364
- /** Optional one-call model override. Used by the cheap-mode tier
365
- * picker (see core/retarget.ts) to route a node to a cheaper model
366
- * without mutating the provider's default. */
367
- modelOverride?: string;
368
- /** F4b: optional JSON Schema that the response MUST conform to.
369
- * When set, provider adapters that support native structured output
370
- * (currently: openai-compatible adapter with provider=openai) wire
371
- * it into the request as `response_format: {type: "json_schema"}`
372
- * or the provider's equivalent. Adapters that don't support native
373
- * structured mode silently ignore this field — the schema-in-prompt
374
- * + F4c tolerant parser path still works as the universal fallback.
375
- * Mirror of OpenAI's json_schema spec; downstream adapters normalise
376
- * to their own provider's native shape. */
377
- jsonSchema?: Record<string, unknown>;
378
- }
379
- /** Provider adapter. */
380
- interface Provider {
381
- /** Lowercased identifier (e.g. "anthropic", "openai"). */
382
- readonly name: string;
383
- /** Default model for this provider (env-resolved at factory time). */
384
- readonly model: string;
385
- /** Ordered fallback models — "greenlight" candidates to try, in order,
386
- * when `model` comes back with a model-access failure (the account/
387
- * project doesn't have it enabled) rather than any other kind of
388
- * error. Env-resolved at factory time from `<PROVIDER>_MODEL_
389
- * FALLBACKS`, or set from a user's/org's configured chain. Empty for
390
- * most providers — a bad key or rate limit isn't fixed by switching
391
- * models, so this is only ever consulted for that one failure mode
392
- * (see core/model-fallback.ts). */
393
- readonly fallbackModels?: readonly string[];
394
- /** Make a request and return the parsed result. Throws `ProviderError`
395
- * on classified failures (auth, rate_limit, timeout, server, parse,
396
- * network, client). */
397
- send(args: SendArgs): Promise<SendResult>;
398
- }
399
-
400
326
  interface FetchConfig {
401
327
  enabled?: boolean;
402
328
  url_allowlist?: readonly string[];
@@ -1,5 +1,6 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
2
  import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
3
+ import { P as Provider } from './types-BcprisDl.js';
3
4
 
4
5
  interface PricingDoc {
5
6
  /** `<provider>` -> `<model>` -> ModelRates. */
@@ -322,81 +323,6 @@ interface Storage extends StorageRead, StorageWrite {
322
323
  close(): Promise<void>;
323
324
  }
324
325
 
325
- /** One provider call's token + cost record. Mirrors `Usage.to_dict()`
326
- * output exactly. */
327
- interface Usage {
328
- provider: string;
329
- model: string;
330
- prompt_tokens: number;
331
- completion_tokens: number;
332
- cached_tokens: number;
333
- total_tokens: number;
334
- cost_usd: number;
335
- estimated: boolean;
336
- purpose: string;
337
- }
338
-
339
- /** Inbound message shape — chat-completions style. */
340
- interface ChatMessage {
341
- role: "system" | "user" | "assistant" | string;
342
- content: string;
343
- [k: string]: unknown;
344
- }
345
- /** A successful `send()` result. */
346
- interface SendResult {
347
- /** Concatenated text from the provider response. */
348
- text: string;
349
- /** Number of HTTP attempts that produced this result (>= 1). */
350
- attempts: number;
351
- /** Parsed + cost-augmented usage record. */
352
- usage: Usage;
353
- }
354
- /** Args passed into every `Provider.send()` call. Mirrors the Python
355
- * `send(messages, max_tokens, temperature, purpose='worker')` signature
356
- * plus an optional `signal` for cancellation. */
357
- interface SendArgs {
358
- messages: readonly ChatMessage[];
359
- maxTokens: number;
360
- temperature: number;
361
- purpose?: string;
362
- /** Optional AbortSignal so callers can cancel long-running calls. */
363
- signal?: AbortSignal;
364
- /** Optional one-call model override. Used by the cheap-mode tier
365
- * picker (see core/retarget.ts) to route a node to a cheaper model
366
- * without mutating the provider's default. */
367
- modelOverride?: string;
368
- /** F4b: optional JSON Schema that the response MUST conform to.
369
- * When set, provider adapters that support native structured output
370
- * (currently: openai-compatible adapter with provider=openai) wire
371
- * it into the request as `response_format: {type: "json_schema"}`
372
- * or the provider's equivalent. Adapters that don't support native
373
- * structured mode silently ignore this field — the schema-in-prompt
374
- * + F4c tolerant parser path still works as the universal fallback.
375
- * Mirror of OpenAI's json_schema spec; downstream adapters normalise
376
- * to their own provider's native shape. */
377
- jsonSchema?: Record<string, unknown>;
378
- }
379
- /** Provider adapter. */
380
- interface Provider {
381
- /** Lowercased identifier (e.g. "anthropic", "openai"). */
382
- readonly name: string;
383
- /** Default model for this provider (env-resolved at factory time). */
384
- readonly model: string;
385
- /** Ordered fallback models — "greenlight" candidates to try, in order,
386
- * when `model` comes back with a model-access failure (the account/
387
- * project doesn't have it enabled) rather than any other kind of
388
- * error. Env-resolved at factory time from `<PROVIDER>_MODEL_
389
- * FALLBACKS`, or set from a user's/org's configured chain. Empty for
390
- * most providers — a bad key or rate limit isn't fixed by switching
391
- * models, so this is only ever consulted for that one failure mode
392
- * (see core/model-fallback.ts). */
393
- readonly fallbackModels?: readonly string[];
394
- /** Make a request and return the parsed result. Throws `ProviderError`
395
- * on classified failures (auth, rate_limit, timeout, server, parse,
396
- * network, client). */
397
- send(args: SendArgs): Promise<SendResult>;
398
- }
399
-
400
326
  interface FetchConfig {
401
327
  enabled?: boolean;
402
328
  url_allowlist?: readonly string[];
@@ -1593,6 +1593,52 @@ function rejectsDisabledThinking(model) {
1593
1593
  const m = model.toLowerCase();
1594
1594
  return ADAPTIVE_THINKING_PREFIXES.some((p) => m.startsWith(p));
1595
1595
  }
1596
+ var NO_FORCED_TOOL_PREFIXES = [
1597
+ "claude-fable-5-1",
1598
+ "claude-mythos-5-1",
1599
+ "claude-opus-5-5",
1600
+ "claude-sonnet-5-5"
1601
+ ];
1602
+ function rejectsForcedToolUse(model) {
1603
+ const m = model.toLowerCase();
1604
+ return NO_FORCED_TOOL_PREFIXES.some((p) => m.startsWith(p));
1605
+ }
1606
+ var UNSUPPORTED_SCHEMA_KEYS = /* @__PURE__ */ new Set([
1607
+ "minimum",
1608
+ "maximum",
1609
+ "exclusiveMinimum",
1610
+ "exclusiveMaximum",
1611
+ "multipleOf",
1612
+ "minLength",
1613
+ "maxLength",
1614
+ "pattern",
1615
+ "minItems",
1616
+ "maxItems",
1617
+ "uniqueItems",
1618
+ "minProperties",
1619
+ "maxProperties",
1620
+ "$schema"
1621
+ ]);
1622
+ function toStructuredOutputSchema(schema) {
1623
+ const walk = (node) => {
1624
+ if (Array.isArray(node)) return node.map(walk);
1625
+ if (!node || typeof node !== "object") return node;
1626
+ const out = {};
1627
+ for (const [k, v] of Object.entries(node)) {
1628
+ if (UNSUPPORTED_SCHEMA_KEYS.has(k)) continue;
1629
+ if (k === "properties" && v && typeof v === "object") {
1630
+ out[k] = Object.fromEntries(Object.entries(v).map(([pk, pv]) => [pk, walk(pv)]));
1631
+ } else if (["items", "anyOf", "allOf", "oneOf", "not", "$defs", "definitions"].includes(k)) {
1632
+ out[k] = k === "$defs" || k === "definitions" ? Object.fromEntries(Object.entries(v ?? {}).map(([dk, dv]) => [dk, walk(dv)])) : walk(v);
1633
+ } else {
1634
+ out[k] = v;
1635
+ }
1636
+ }
1637
+ if (out["type"] === "object" || "properties" in out) out["additionalProperties"] = false;
1638
+ return out;
1639
+ };
1640
+ return walk(schema);
1641
+ }
1596
1642
  var ANTHROPIC_STRUCTURED_TOOL_NAME = "structured_output";
1597
1643
  function applyPromptCaching(body, env = process.env) {
1598
1644
  const flag = (env["CROSSCHECK_ANTHROPIC_PROMPT_CACHE"] ?? "").trim();
@@ -1644,7 +1690,12 @@ function buildAnthropicRequest(opts) {
1644
1690
  body.thinking = { type: "disabled" };
1645
1691
  }
1646
1692
  }
1647
- if (opts.jsonSchema) {
1693
+ if (opts.jsonSchema && rejectsForcedToolUse(opts.model)) {
1694
+ body.output_config = {
1695
+ ...body.output_config ?? {},
1696
+ format: { type: "json_schema", schema: toStructuredOutputSchema(opts.jsonSchema) }
1697
+ };
1698
+ } else if (opts.jsonSchema) {
1648
1699
  body.tools = [{
1649
1700
  name: ANTHROPIC_STRUCTURED_TOOL_NAME,
1650
1701
  description: "Emit a single JSON object matching the requested schema.",
@@ -2047,7 +2098,15 @@ function buildOpenAICompatibleRequest(opts) {
2047
2098
  json_schema: {
2048
2099
  name: "structured_output",
2049
2100
  schema: opts.jsonSchema,
2050
- strict: true
2101
+ // NOT strict. Strict mode requires `additionalProperties: false` on
2102
+ // every object and every property listed in `required` — the
2103
+ // engine's schemas don't meet that, so every structured OpenAI call
2104
+ // (planner, judges, synthesis) was a 400, unnoticed until the canary
2105
+ // grew a structured check (2026-10-11). Non-strict still shapes the
2106
+ // reply to the schema, and structured.ts validates it against the
2107
+ // full schema regardless, so strictness added a failure mode and no
2108
+ // safety.
2109
+ strict: false
2051
2110
  }
2052
2111
  };
2053
2112
  }
@@ -2435,7 +2494,7 @@ import { z } from "zod";
2435
2494
  // src/server-meta.ts
2436
2495
  init_esm_shims();
2437
2496
  var SERVER_NAME = "crosscheck-agent";
2438
- var SERVER_VERSION = true ? "0.2.28" : "0.0.0-dev";
2497
+ var SERVER_VERSION = true ? "0.2.29" : "0.0.0-dev";
2439
2498
 
2440
2499
  // src/tools/audit.ts
2441
2500
  init_esm_shims();
@@ -9139,6 +9198,16 @@ function buildCallContext(args) {
9139
9198
  var CREATE_DOC_MAX_BYTES = 32 * 1024;
9140
9199
  var CREATE_AUDIT_THRESHOLD = 0.7;
9141
9200
  async function runCreate(args, opts) {
9201
+ const subEnvelopes = [];
9202
+ const tracked = (fn) => async (...a) => {
9203
+ const out = await fn(...a);
9204
+ subEnvelopes.push(out);
9205
+ return out;
9206
+ };
9207
+ const confer = tracked(runConfer);
9208
+ const orchestrate = tracked(runOrchestrate);
9209
+ const review = tracked(runReview);
9210
+ const audit = tracked(runAudit);
9142
9211
  const instruction = typeof args["instruction"] === "string" ? args["instruction"] : String(args["instruction"] ?? "");
9143
9212
  if (!instruction) {
9144
9213
  return errorEnvelope6(
@@ -9175,7 +9244,7 @@ ${constraints}
9175
9244
 
9176
9245
  ` : "") + `DOCUMENTS (${okCount} of ${descriptors.length} ingested):
9177
9246
  ${documentsPayload}`;
9178
- const scope = await runConfer({
9247
+ const scope = await confer({
9179
9248
  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,
9180
9249
  context: contextBlock,
9181
9250
  ...providers !== void 0 ? { providers } : {},
@@ -9202,7 +9271,7 @@ ${scopeAnswer || "(none)"}
9202
9271
  };
9203
9272
  if (providers !== void 0) orchestrateArgs["providers"] = providers;
9204
9273
  if (planOnly) orchestrateArgs["plan_only"] = true;
9205
- let orchestration = await runOrchestrate(orchestrateArgs, {
9274
+ let orchestration = await orchestrate(orchestrateArgs, {
9206
9275
  providers: opts.providers,
9207
9276
  allowlist: opts.allowlist ?? null,
9208
9277
  moderator,
@@ -9231,7 +9300,7 @@ ${scopeAnswer || "(none)"}
9231
9300
  }
9232
9301
  let reviewEnvelope = null;
9233
9302
  if (!skipReview && typeof orchestration["final"] === "string" && orchestration["final"]) {
9234
- reviewEnvelope = await runReview({
9303
+ reviewEnvelope = await review({
9235
9304
  snippet: orchestration["final"],
9236
9305
  intent: `final deliverable for: ${instruction}`,
9237
9306
  ...providers !== void 0 ? { providers } : {},
@@ -9251,7 +9320,7 @@ ${scopeAnswer || "(none)"}
9251
9320
  const panelExhausted = producingPanel.length > 0 && producingPanel.every(
9252
9321
  (p) => p in opts.providers || Object.keys(opts.providers).includes(p.toLowerCase())
9253
9322
  ) && producingPanel.length >= Object.keys(opts.providers).length;
9254
- auditEnvelope = await runAudit({
9323
+ auditEnvelope = await audit({
9255
9324
  output_to_audit: orchestration["final"],
9256
9325
  producing_panelists: producingPanel,
9257
9326
  ...auditRubric !== void 0 ? { rubric: auditRubric } : {},
@@ -9280,7 +9349,7 @@ ${retryConstraints}
9280
9349
  DOCUMENTS:
9281
9350
  ${documentsPayload}`
9282
9351
  };
9283
- const retryOrchestration = await runOrchestrate(
9352
+ const retryOrchestration = await orchestrate(
9284
9353
  retryOrchestrateArgs,
9285
9354
  {
9286
9355
  providers: opts.providers,
@@ -9294,7 +9363,7 @@ ${documentsPayload}`
9294
9363
  if (typeof retryOrchestration["final"] === "string" && retryOrchestration["final"]) {
9295
9364
  const retryPanel = computeProducingPanel(retryOrchestration);
9296
9365
  const retryPanelExhausted = retryPanel.length > 0 && retryPanel.length >= Object.keys(opts.providers).length;
9297
- auditEnvelope = await runAudit({
9366
+ auditEnvelope = await audit({
9298
9367
  output_to_audit: retryOrchestration["final"],
9299
9368
  producing_panelists: retryPanel,
9300
9369
  ...auditRubric !== void 0 ? { rubric: auditRubric } : {},
@@ -9369,9 +9438,28 @@ ${documentsPayload}`
9369
9438
  ["auditor", "items", "overall_score", "passed", "rubric"]
9370
9439
  ),
9371
9440
  artifacts,
9372
- warnings
9441
+ warnings,
9442
+ ...combinedUsage(subEnvelopes)
9373
9443
  };
9374
9444
  }
9445
+ function combinedUsage(envelopes) {
9446
+ const usages = [];
9447
+ const byCallTiming = [];
9448
+ let wall = 0, cpu = 0;
9449
+ for (const env of envelopes) {
9450
+ const e = env ?? {};
9451
+ const calls = e["usage"]?.["by_call"] ?? [];
9452
+ const t = e["timing"] ?? {};
9453
+ const timing = Array.isArray(t["by_call"]) ? t["by_call"] : [];
9454
+ if (!Array.isArray(calls) || calls.length === 0) continue;
9455
+ usages.push(...calls);
9456
+ if (timing.length === calls.length) byCallTiming.push(...timing);
9457
+ 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 });
9458
+ wall += Number(t["wall_ms"] ?? 0) || 0;
9459
+ cpu += Number(t["cpu_ms"] ?? 0) || 0;
9460
+ }
9461
+ return { usage: aggregateUsage(usages), timing: { wall_ms: wall, cpu_ms: cpu, by_call: byCallTiming } };
9462
+ }
9375
9463
  async function ingestDocuments(documents, sessionId, opts) {
9376
9464
  if (documents.length === 0) return [];
9377
9465
  const out = [];
@@ -12868,7 +12956,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
12868
12956
  var DEFAULT_PACKAGE = "crosscheck-cli";
12869
12957
  var FETCH_TIMEOUT_MS = 3e3;
12870
12958
  function engineVersion() {
12871
- return true ? "0.2.28" : "0.0.0-dev";
12959
+ return true ? "0.2.29" : "0.0.0-dev";
12872
12960
  }
12873
12961
  function defaultUpdateCachePath() {
12874
12962
  const base = process.env["CROSSCHECK_DATA_DIR"] || path10.join(os.homedir() || os.tmpdir(), ".crosscheck");