crosscheck-mcp 0.2.20 → 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.
@@ -1230,12 +1230,20 @@ var ProviderError = class extends Error {
1230
1230
  * actually fix. Distinct from `transient`: this is never worth
1231
1231
  * retrying the SAME model, but IS worth trying a different one. */
1232
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;
1233
1240
  constructor(kind, message, opts) {
1234
1241
  super(message);
1235
1242
  this.kind = kind;
1236
1243
  this.status = opts?.status;
1237
1244
  this.transient = opts?.transient ?? defaultTransient(kind);
1238
1245
  this.modelAccessFailure = opts?.modelAccessFailure ?? false;
1246
+ this.billing = opts?.billing ?? false;
1239
1247
  if (opts?.retryAfterS !== void 0) {
1240
1248
  this.retryAfterS = opts.retryAfterS;
1241
1249
  }
@@ -1274,7 +1282,7 @@ function httpFailureToProviderError(provider, status, bodyText, retryAfterS, mod
1274
1282
  return new ProviderError(
1275
1283
  "auth",
1276
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}`,
1277
- { status }
1285
+ { status, billing: true }
1278
1286
  );
1279
1287
  }
1280
1288
  if (status === 401 || status === 403) {
@@ -2271,7 +2279,7 @@ import { z } from "zod";
2271
2279
  // src/server-meta.ts
2272
2280
  init_esm_shims();
2273
2281
  var SERVER_NAME = "crosscheck-agent";
2274
- var SERVER_VERSION = true ? "0.2.20" : "0.0.0-dev";
2282
+ var SERVER_VERSION = true ? "0.2.21" : "0.0.0-dev";
2275
2283
 
2276
2284
  // src/tools/audit.ts
2277
2285
  init_esm_shims();
@@ -2784,7 +2792,9 @@ async function attachUsageBlock(result, answers, opts) {
2784
2792
  purpose: a.usage?.purpose ?? null,
2785
2793
  wall_ms: a.elapsed_ms ?? 0,
2786
2794
  cpu_ms: a.cpu_ms ?? 0,
2787
- 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 } : {}
2788
2798
  }))
2789
2799
  };
2790
2800
  result["timing"] = timing;
@@ -3093,11 +3103,13 @@ async function askOne(provider, messages, opts) {
3093
3103
  const cpuMs = Math.trunc((cpu.user + cpu.system) / 1e3);
3094
3104
  const kind = e instanceof ProviderError ? e.kind : "other";
3095
3105
  const msg = e instanceof Error ? e.message : String(e);
3106
+ const billing = e instanceof ProviderError && e.billing;
3096
3107
  return {
3097
3108
  provider: provider.name,
3098
3109
  model: provider.model,
3099
3110
  error: msg,
3100
3111
  error_kind: kind,
3112
+ ...billing ? { error_billing: true } : {},
3101
3113
  attempts: 0,
3102
3114
  usage: emptyUsage(provider.name, provider.model, opts.purpose),
3103
3115
  cache_hit: false,
@@ -12776,7 +12788,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
12776
12788
  var DEFAULT_PACKAGE = "crosscheck-cli";
12777
12789
  var FETCH_TIMEOUT_MS = 3e3;
12778
12790
  function engineVersion() {
12779
- return true ? "0.2.20" : "0.0.0-dev";
12791
+ return true ? "0.2.21" : "0.0.0-dev";
12780
12792
  }
12781
12793
  function defaultUpdateCachePath() {
12782
12794
  const base = process.env["CROSSCHECK_DATA_DIR"] || path10.join(os.homedir() || os.tmpdir(), ".crosscheck");
@@ -14791,6 +14803,23 @@ function getConfig2() {
14791
14803
  config = { url, seatId, orgId, seatKey };
14792
14804
  return config;
14793
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
+ }
14794
14823
  function intNonNeg(v) {
14795
14824
  const n = Math.trunc(Number(v));
14796
14825
  return Number.isFinite(n) && n > 0 ? n : 0;
@@ -14798,12 +14827,14 @@ function intNonNeg(v) {
14798
14827
  function usageEventsFromEnvelope(out, toolName) {
14799
14828
  if (!out || typeof out !== "object" || Array.isArray(out)) return [];
14800
14829
  const usage = out["usage"];
14801
- if (!usage || typeof usage !== "object") return [];
14802
- const byCall = usage["by_call"];
14803
- 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 : [];
14804
14832
  const timing = out["timing"];
14805
14833
  const timingByCall = timing && typeof timing === "object" ? timing["by_call"] : void 0;
14806
- const latencies = Array.isArray(timingByCall) ? timingByCall : [];
14834
+ const allTiming = Array.isArray(timingByCall) ? timingByCall : [];
14835
+ const latencies = allTiming.filter(
14836
+ (t) => !t?.["error_kind"]
14837
+ );
14807
14838
  const aligned = latencies.length === byCall.length;
14808
14839
  const pattern = toolToPattern(toolName);
14809
14840
  const events = [];
@@ -14828,6 +14859,28 @@ function usageEventsFromEnvelope(out, toolName) {
14828
14859
  purpose: normalizePurpose(u.purpose)
14829
14860
  });
14830
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
+ }
14831
14884
  return events;
14832
14885
  }
14833
14886
  var queue = [];
@@ -14854,6 +14907,10 @@ function enqueue(cfg2, input, runId, fingerprint) {
14854
14907
  costUsdEstimate: input.costUsdEstimate,
14855
14908
  latencyMs: input.latencyMs,
14856
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 },
14857
14914
  runId,
14858
14915
  // Omit entirely (not "") when unknown, so the canonical body stays
14859
14916
  // consistent with what the server reconstructs from the JSON.