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.
@@ -1254,12 +1254,20 @@ var ProviderError = class extends Error {
1254
1254
  * actually fix. Distinct from `transient`: this is never worth
1255
1255
  * retrying the SAME model, but IS worth trying a different one. */
1256
1256
  modelAccessFailure;
1257
+ /** True for "the key works, the account is out of credit / over a spend
1258
+ * limit." Rides alongside kind "auth" rather than replacing it, because
1259
+ * every retry and fallback decision downstream is already tuned to that
1260
+ * kind — but the two are completely different things to a human, and
1261
+ * reporting "API key rejected" for an unpaid invoice sends people to
1262
+ * regenerate a key that was never the problem. */
1263
+ billing;
1257
1264
  constructor(kind, message, opts) {
1258
1265
  super(message);
1259
1266
  this.kind = kind;
1260
1267
  this.status = opts?.status;
1261
1268
  this.transient = opts?.transient ?? defaultTransient(kind);
1262
1269
  this.modelAccessFailure = opts?.modelAccessFailure ?? false;
1270
+ this.billing = opts?.billing ?? false;
1263
1271
  if (opts?.retryAfterS !== void 0) {
1264
1272
  this.retryAfterS = opts.retryAfterS;
1265
1273
  }
@@ -1298,7 +1306,7 @@ function httpFailureToProviderError(provider, status, bodyText, retryAfterS, mod
1298
1306
  return new ProviderError(
1299
1307
  "auth",
1300
1308
  `${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}`,
1301
- { status }
1309
+ { status, billing: true }
1302
1310
  );
1303
1311
  }
1304
1312
  if (status === 401 || status === 403) {
@@ -2292,7 +2300,7 @@ var import_zod = require("zod");
2292
2300
  // src/server-meta.ts
2293
2301
  init_cjs_shims();
2294
2302
  var SERVER_NAME = "crosscheck-agent";
2295
- var SERVER_VERSION = true ? "0.2.20" : "0.0.0-dev";
2303
+ var SERVER_VERSION = true ? "0.2.21" : "0.0.0-dev";
2296
2304
 
2297
2305
  // src/tools/audit.ts
2298
2306
  init_cjs_shims();
@@ -2805,7 +2813,9 @@ async function attachUsageBlock(result, answers, opts) {
2805
2813
  purpose: a.usage?.purpose ?? null,
2806
2814
  wall_ms: a.elapsed_ms ?? 0,
2807
2815
  cpu_ms: a.cpu_ms ?? 0,
2808
- cache_hit: Boolean(a.cache_hit)
2816
+ cache_hit: Boolean(a.cache_hit),
2817
+ ...a.error_kind ? { error_kind: a.error_kind } : {},
2818
+ ...a.error_billing ? { error_billing: true } : {}
2809
2819
  }))
2810
2820
  };
2811
2821
  result["timing"] = timing;
@@ -3114,11 +3124,13 @@ async function askOne(provider, messages, opts) {
3114
3124
  const cpuMs = Math.trunc((cpu.user + cpu.system) / 1e3);
3115
3125
  const kind = e instanceof ProviderError ? e.kind : "other";
3116
3126
  const msg = e instanceof Error ? e.message : String(e);
3127
+ const billing = e instanceof ProviderError && e.billing;
3117
3128
  return {
3118
3129
  provider: provider.name,
3119
3130
  model: provider.model,
3120
3131
  error: msg,
3121
3132
  error_kind: kind,
3133
+ ...billing ? { error_billing: true } : {},
3122
3134
  attempts: 0,
3123
3135
  usage: emptyUsage(provider.name, provider.model, opts.purpose),
3124
3136
  cache_hit: false,
@@ -12780,7 +12792,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
12780
12792
  var DEFAULT_PACKAGE = "crosscheck-cli";
12781
12793
  var FETCH_TIMEOUT_MS = 3e3;
12782
12794
  function engineVersion() {
12783
- return true ? "0.2.20" : "0.0.0-dev";
12795
+ return true ? "0.2.21" : "0.0.0-dev";
12784
12796
  }
12785
12797
  function defaultUpdateCachePath() {
12786
12798
  const base = process.env["CROSSCHECK_DATA_DIR"] || import_node_path13.default.join(import_node_os2.default.homedir() || import_node_os2.default.tmpdir(), ".crosscheck");
@@ -14795,6 +14807,23 @@ function getConfig2() {
14795
14807
  config = { url, seatId, orgId, seatKey };
14796
14808
  return config;
14797
14809
  }
14810
+ function errorClassFor(kind) {
14811
+ switch (kind) {
14812
+ case "auth":
14813
+ return "auth";
14814
+ case "rate_limit":
14815
+ return "rate_limit";
14816
+ case "timeout":
14817
+ return "timeout";
14818
+ case "server":
14819
+ return "server";
14820
+ // network/parse/client/other all mean "we don't know that the key is
14821
+ // bad", and guessing wrong here is what produces a dashboard that cries
14822
+ // wolf. Say unknown.
14823
+ default:
14824
+ return "unknown";
14825
+ }
14826
+ }
14798
14827
  function intNonNeg(v) {
14799
14828
  const n = Math.trunc(Number(v));
14800
14829
  return Number.isFinite(n) && n > 0 ? n : 0;
@@ -14802,12 +14831,14 @@ function intNonNeg(v) {
14802
14831
  function usageEventsFromEnvelope(out, toolName) {
14803
14832
  if (!out || typeof out !== "object" || Array.isArray(out)) return [];
14804
14833
  const usage = out["usage"];
14805
- if (!usage || typeof usage !== "object") return [];
14806
- const byCall = usage["by_call"];
14807
- if (!Array.isArray(byCall) || byCall.length === 0) return [];
14834
+ const rawByCall = usage && typeof usage === "object" ? usage["by_call"] : void 0;
14835
+ const byCall = Array.isArray(rawByCall) ? rawByCall : [];
14808
14836
  const timing = out["timing"];
14809
14837
  const timingByCall = timing && typeof timing === "object" ? timing["by_call"] : void 0;
14810
- const latencies = Array.isArray(timingByCall) ? timingByCall : [];
14838
+ const allTiming = Array.isArray(timingByCall) ? timingByCall : [];
14839
+ const latencies = allTiming.filter(
14840
+ (t) => !t?.["error_kind"]
14841
+ );
14811
14842
  const aligned = latencies.length === byCall.length;
14812
14843
  const pattern = toolToPattern(toolName);
14813
14844
  const events = [];
@@ -14832,6 +14863,28 @@ function usageEventsFromEnvelope(out, toolName) {
14832
14863
  purpose: normalizePurpose(u.purpose)
14833
14864
  });
14834
14865
  }
14866
+ for (const t of allTiming) {
14867
+ const row = t;
14868
+ const kind = row["error_kind"];
14869
+ if (!kind) continue;
14870
+ const provider = providerToSchema(String(row["provider"] ?? ""));
14871
+ if (!provider) continue;
14872
+ events.push({
14873
+ provider,
14874
+ model: String(row["model"] ?? "").slice(0, 128) || "unknown",
14875
+ pattern,
14876
+ // A failure consumed no tokens and cost nothing. Recording it with a
14877
+ // nonzero cost would corrupt the per-feature ledger with money nobody
14878
+ // spent.
14879
+ promptTokens: 0,
14880
+ completionTokens: 0,
14881
+ costUsdEstimate: 0,
14882
+ latencyMs: intNonNeg(row["wall_ms"]),
14883
+ status: "error",
14884
+ errorClass: row["error_billing"] ? "auth" : errorClassFor(String(kind)),
14885
+ purpose: normalizePurpose(row["purpose"])
14886
+ });
14887
+ }
14835
14888
  return events;
14836
14889
  }
14837
14890
  var queue = [];
@@ -14858,6 +14911,10 @@ function enqueue(cfg2, input, runId, fingerprint) {
14858
14911
  costUsdEstimate: input.costUsdEstimate,
14859
14912
  latencyMs: input.latencyMs,
14860
14913
  status: input.status,
14914
+ // Only present on failures. The canonical body renders an absent
14915
+ // errorClass as "", so omitting and sending "" sign identically — but
14916
+ // omitting keeps the JSON honest about what we actually know.
14917
+ ...input.errorClass === void 0 ? {} : { errorClass: input.errorClass },
14861
14918
  runId,
14862
14919
  // Omit entirely (not "") when unknown, so the canonical body stays
14863
14920
  // consistent with what the server reconstructs from the JSON.