crosscheck-mcp 0.2.19 → 0.2.21

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.
@@ -1168,7 +1168,13 @@ var PROVIDER_CAPS = {
1168
1168
  family: "openai_chat",
1169
1169
  system_role: "inline",
1170
1170
  supports_temperature: "model",
1171
- reasoning_prefixes: ["gpt-5", "o1", "o3", "o4"]
1171
+ // gpt-6 added 2026-09-06: GPT-6 Astra rejects `max_tokens` outright
1172
+ // ("Unsupported parameter ... use max_completion_tokens"), which is a 400,
1173
+ // NOT a model-access failure — so the fallback chain correctly declines to
1174
+ // fire and the provider silently drops out of the panel instead. Reasoning
1175
+ // -class routing is what selects max_completion_tokens and omits
1176
+ // temperature, so the prefix is the fix.
1177
+ reasoning_prefixes: ["gpt-5", "gpt-6", "o1", "o3", "o4"]
1172
1178
  },
1173
1179
  xai: { family: "openai_chat", system_role: "inline", supports_temperature: true },
1174
1180
  mistral: { family: "openai_chat", system_role: "inline", supports_temperature: true },
@@ -1224,12 +1230,20 @@ var ProviderError = class extends Error {
1224
1230
  * actually fix. Distinct from `transient`: this is never worth
1225
1231
  * retrying the SAME model, but IS worth trying a different one. */
1226
1232
  modelAccessFailure;
1233
+ /** True for "the key works, the account is out of credit / over a spend
1234
+ * limit." Rides alongside kind "auth" rather than replacing it, because
1235
+ * every retry and fallback decision downstream is already tuned to that
1236
+ * kind — but the two are completely different things to a human, and
1237
+ * reporting "API key rejected" for an unpaid invoice sends people to
1238
+ * regenerate a key that was never the problem. */
1239
+ billing;
1227
1240
  constructor(kind, message, opts) {
1228
1241
  super(message);
1229
1242
  this.kind = kind;
1230
1243
  this.status = opts?.status;
1231
1244
  this.transient = opts?.transient ?? defaultTransient(kind);
1232
1245
  this.modelAccessFailure = opts?.modelAccessFailure ?? false;
1246
+ this.billing = opts?.billing ?? false;
1233
1247
  if (opts?.retryAfterS !== void 0) {
1234
1248
  this.retryAfterS = opts.retryAfterS;
1235
1249
  }
@@ -1268,7 +1282,7 @@ function httpFailureToProviderError(provider, status, bodyText, retryAfterS, mod
1268
1282
  return new ProviderError(
1269
1283
  "auth",
1270
1284
  `${provider}: out of credits or billing isn't set up (HTTP ${status}). Your API key is reaching ${provider}, but the account has no usable balance \u2014 add credits / enable billing in your ${provider} console, then retry. Detail: ${detail}`,
1271
- { status }
1285
+ { status, billing: true }
1272
1286
  );
1273
1287
  }
1274
1288
  if (status === 401 || status === 403) {
@@ -2265,7 +2279,7 @@ import { z } from "zod";
2265
2279
  // src/server-meta.ts
2266
2280
  init_esm_shims();
2267
2281
  var SERVER_NAME = "crosscheck-agent";
2268
- var SERVER_VERSION = true ? "0.2.19" : "0.0.0-dev";
2282
+ var SERVER_VERSION = true ? "0.2.21" : "0.0.0-dev";
2269
2283
 
2270
2284
  // src/tools/audit.ts
2271
2285
  init_esm_shims();
@@ -2778,7 +2792,9 @@ async function attachUsageBlock(result, answers, opts) {
2778
2792
  purpose: a.usage?.purpose ?? null,
2779
2793
  wall_ms: a.elapsed_ms ?? 0,
2780
2794
  cpu_ms: a.cpu_ms ?? 0,
2781
- cache_hit: Boolean(a.cache_hit)
2795
+ cache_hit: Boolean(a.cache_hit),
2796
+ ...a.error_kind ? { error_kind: a.error_kind } : {},
2797
+ ...a.error_billing ? { error_billing: true } : {}
2782
2798
  }))
2783
2799
  };
2784
2800
  result["timing"] = timing;
@@ -3087,11 +3103,13 @@ async function askOne(provider, messages, opts) {
3087
3103
  const cpuMs = Math.trunc((cpu.user + cpu.system) / 1e3);
3088
3104
  const kind = e instanceof ProviderError ? e.kind : "other";
3089
3105
  const msg = e instanceof Error ? e.message : String(e);
3106
+ const billing = e instanceof ProviderError && e.billing;
3090
3107
  return {
3091
3108
  provider: provider.name,
3092
3109
  model: provider.model,
3093
3110
  error: msg,
3094
3111
  error_kind: kind,
3112
+ ...billing ? { error_billing: true } : {},
3095
3113
  attempts: 0,
3096
3114
  usage: emptyUsage(provider.name, provider.model, opts.purpose),
3097
3115
  cache_hit: false,
@@ -12770,7 +12788,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
12770
12788
  var DEFAULT_PACKAGE = "crosscheck-cli";
12771
12789
  var FETCH_TIMEOUT_MS = 3e3;
12772
12790
  function engineVersion() {
12773
- return true ? "0.2.19" : "0.0.0-dev";
12791
+ return true ? "0.2.21" : "0.0.0-dev";
12774
12792
  }
12775
12793
  function defaultUpdateCachePath() {
12776
12794
  const base = process.env["CROSSCHECK_DATA_DIR"] || path10.join(os.homedir() || os.tmpdir(), ".crosscheck");
@@ -14785,6 +14803,23 @@ function getConfig2() {
14785
14803
  config = { url, seatId, orgId, seatKey };
14786
14804
  return config;
14787
14805
  }
14806
+ function errorClassFor(kind) {
14807
+ switch (kind) {
14808
+ case "auth":
14809
+ return "auth";
14810
+ case "rate_limit":
14811
+ return "rate_limit";
14812
+ case "timeout":
14813
+ return "timeout";
14814
+ case "server":
14815
+ return "server";
14816
+ // network/parse/client/other all mean "we don't know that the key is
14817
+ // bad", and guessing wrong here is what produces a dashboard that cries
14818
+ // wolf. Say unknown.
14819
+ default:
14820
+ return "unknown";
14821
+ }
14822
+ }
14788
14823
  function intNonNeg(v) {
14789
14824
  const n = Math.trunc(Number(v));
14790
14825
  return Number.isFinite(n) && n > 0 ? n : 0;
@@ -14792,12 +14827,14 @@ function intNonNeg(v) {
14792
14827
  function usageEventsFromEnvelope(out, toolName) {
14793
14828
  if (!out || typeof out !== "object" || Array.isArray(out)) return [];
14794
14829
  const usage = out["usage"];
14795
- if (!usage || typeof usage !== "object") return [];
14796
- const byCall = usage["by_call"];
14797
- if (!Array.isArray(byCall) || byCall.length === 0) return [];
14830
+ const rawByCall = usage && typeof usage === "object" ? usage["by_call"] : void 0;
14831
+ const byCall = Array.isArray(rawByCall) ? rawByCall : [];
14798
14832
  const timing = out["timing"];
14799
14833
  const timingByCall = timing && typeof timing === "object" ? timing["by_call"] : void 0;
14800
- const latencies = Array.isArray(timingByCall) ? timingByCall : [];
14834
+ const allTiming = Array.isArray(timingByCall) ? timingByCall : [];
14835
+ const latencies = allTiming.filter(
14836
+ (t) => !t?.["error_kind"]
14837
+ );
14801
14838
  const aligned = latencies.length === byCall.length;
14802
14839
  const pattern = toolToPattern(toolName);
14803
14840
  const events = [];
@@ -14822,6 +14859,28 @@ function usageEventsFromEnvelope(out, toolName) {
14822
14859
  purpose: normalizePurpose(u.purpose)
14823
14860
  });
14824
14861
  }
14862
+ for (const t of allTiming) {
14863
+ const row = t;
14864
+ const kind = row["error_kind"];
14865
+ if (!kind) continue;
14866
+ const provider = providerToSchema(String(row["provider"] ?? ""));
14867
+ if (!provider) continue;
14868
+ events.push({
14869
+ provider,
14870
+ model: String(row["model"] ?? "").slice(0, 128) || "unknown",
14871
+ pattern,
14872
+ // A failure consumed no tokens and cost nothing. Recording it with a
14873
+ // nonzero cost would corrupt the per-feature ledger with money nobody
14874
+ // spent.
14875
+ promptTokens: 0,
14876
+ completionTokens: 0,
14877
+ costUsdEstimate: 0,
14878
+ latencyMs: intNonNeg(row["wall_ms"]),
14879
+ status: "error",
14880
+ errorClass: row["error_billing"] ? "auth" : errorClassFor(String(kind)),
14881
+ purpose: normalizePurpose(row["purpose"])
14882
+ });
14883
+ }
14825
14884
  return events;
14826
14885
  }
14827
14886
  var queue = [];
@@ -14848,6 +14907,10 @@ function enqueue(cfg2, input, runId, fingerprint) {
14848
14907
  costUsdEstimate: input.costUsdEstimate,
14849
14908
  latencyMs: input.latencyMs,
14850
14909
  status: input.status,
14910
+ // Only present on failures. The canonical body renders an absent
14911
+ // errorClass as "", so omitting and sending "" sign identically — but
14912
+ // omitting keeps the JSON honest about what we actually know.
14913
+ ...input.errorClass === void 0 ? {} : { errorClass: input.errorClass },
14851
14914
  runId,
14852
14915
  // Omit entirely (not "") when unknown, so the canonical body stays
14853
14916
  // consistent with what the server reconstructs from the JSON.