crosscheck-mcp 0.2.20 → 0.2.22
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/browser-ext.cjs +74 -10
- package/dist/browser-ext.cjs.map +1 -1
- package/dist/browser-ext.js +74 -10
- package/dist/browser-ext.js.map +1 -1
- package/dist/node-stdio.cjs +129 -17
- package/dist/node-stdio.cjs.map +1 -1
- package/dist/node-stdio.js +129 -17
- package/dist/node-stdio.js.map +1 -1
- package/package.json +1 -1
package/dist/node-stdio.cjs
CHANGED
|
@@ -1200,7 +1200,27 @@ var PROVIDER_CAPS = {
|
|
|
1200
1200
|
// temperature, so the prefix is the fix.
|
|
1201
1201
|
reasoning_prefixes: ["gpt-5", "gpt-6", "o1", "o3", "o4"]
|
|
1202
1202
|
},
|
|
1203
|
-
xai: {
|
|
1203
|
+
xai: {
|
|
1204
|
+
family: "openai_chat",
|
|
1205
|
+
system_role: "inline",
|
|
1206
|
+
// Kept `true`, unlike other reasoning models: grok-4 accepts temperature
|
|
1207
|
+
// (verified 200 at 0.4 and 1.0) and a panel wants the variance. Dropping
|
|
1208
|
+
// it would buy nothing and cost diversity.
|
|
1209
|
+
supports_temperature: true,
|
|
1210
|
+
// grok-4 IS reasoning-class — it emitted 1,432 reasoning tokens against 64
|
|
1211
|
+
// visible on one call. Saying so strips the "think step by step" preambles
|
|
1212
|
+
// it does not need, and lifts it off the non-reasoning ceiling (1500) onto
|
|
1213
|
+
// the reasoning-safe one (2048).
|
|
1214
|
+
//
|
|
1215
|
+
// NOT the reason grok answers short, which was the initial guess and was
|
|
1216
|
+
// wrong: at caps of 1500, 2048 and 6144 it returned 445, 404 and 461
|
|
1217
|
+
// visible tokens, finish_reason=stop every time. The cap was never
|
|
1218
|
+
// binding; grok is simply terse. The ceiling change removes a latent
|
|
1219
|
+
// constraint, it does not make grok say more.
|
|
1220
|
+
reasoning_prefixes: ["grok-4"],
|
|
1221
|
+
// Measured: xAI's cap bounds visible output only. See the field docs.
|
|
1222
|
+
reasoning_shares_output_budget: false
|
|
1223
|
+
},
|
|
1204
1224
|
mistral: { family: "openai_chat", system_role: "inline", supports_temperature: true },
|
|
1205
1225
|
groq: { family: "openai_chat", system_role: "inline", supports_temperature: true },
|
|
1206
1226
|
deepseek: { family: "openai_chat", system_role: "inline", supports_temperature: true },
|
|
@@ -1239,6 +1259,10 @@ function supportsTemperature(provider, model) {
|
|
|
1239
1259
|
if (caps.supports_temperature === "model") return !isReasoningModel(provider, model);
|
|
1240
1260
|
return Boolean(caps.supports_temperature);
|
|
1241
1261
|
}
|
|
1262
|
+
function reasoningSharesOutputBudget(provider) {
|
|
1263
|
+
const caps = PROVIDER_CAPS[provider.toLowerCase()];
|
|
1264
|
+
return caps?.reasoning_shares_output_budget ?? true;
|
|
1265
|
+
}
|
|
1242
1266
|
|
|
1243
1267
|
// src/providers/types.ts
|
|
1244
1268
|
init_cjs_shims();
|
|
@@ -1254,12 +1278,20 @@ var ProviderError = class extends Error {
|
|
|
1254
1278
|
* actually fix. Distinct from `transient`: this is never worth
|
|
1255
1279
|
* retrying the SAME model, but IS worth trying a different one. */
|
|
1256
1280
|
modelAccessFailure;
|
|
1281
|
+
/** True for "the key works, the account is out of credit / over a spend
|
|
1282
|
+
* limit." Rides alongside kind "auth" rather than replacing it, because
|
|
1283
|
+
* every retry and fallback decision downstream is already tuned to that
|
|
1284
|
+
* kind — but the two are completely different things to a human, and
|
|
1285
|
+
* reporting "API key rejected" for an unpaid invoice sends people to
|
|
1286
|
+
* regenerate a key that was never the problem. */
|
|
1287
|
+
billing;
|
|
1257
1288
|
constructor(kind, message, opts) {
|
|
1258
1289
|
super(message);
|
|
1259
1290
|
this.kind = kind;
|
|
1260
1291
|
this.status = opts?.status;
|
|
1261
1292
|
this.transient = opts?.transient ?? defaultTransient(kind);
|
|
1262
1293
|
this.modelAccessFailure = opts?.modelAccessFailure ?? false;
|
|
1294
|
+
this.billing = opts?.billing ?? false;
|
|
1263
1295
|
if (opts?.retryAfterS !== void 0) {
|
|
1264
1296
|
this.retryAfterS = opts.retryAfterS;
|
|
1265
1297
|
}
|
|
@@ -1298,7 +1330,7 @@ function httpFailureToProviderError(provider, status, bodyText, retryAfterS, mod
|
|
|
1298
1330
|
return new ProviderError(
|
|
1299
1331
|
"auth",
|
|
1300
1332
|
`${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 }
|
|
1333
|
+
{ status, billing: true }
|
|
1302
1334
|
);
|
|
1303
1335
|
}
|
|
1304
1336
|
if (status === 401 || status === 403) {
|
|
@@ -1483,6 +1515,23 @@ async function acquireRateLimit(provider, deps) {
|
|
|
1483
1515
|
var ANTHROPIC_API_URL = "https://api.anthropic.com/v1/messages";
|
|
1484
1516
|
var ANTHROPIC_VERSION_HEADER = "2023-06-01";
|
|
1485
1517
|
var ANTHROPIC_STRUCTURED_TOOL_NAME = "structured_output";
|
|
1518
|
+
function applyPromptCaching(body, env = process.env) {
|
|
1519
|
+
const flag = (env["CROSSCHECK_ANTHROPIC_PROMPT_CACHE"] ?? "").trim();
|
|
1520
|
+
if (flag === "0" || flag.toLowerCase() === "false") return;
|
|
1521
|
+
if (typeof body.system === "string" && body.system !== "") {
|
|
1522
|
+
body.system = [{ type: "text", text: body.system, cache_control: { type: "ephemeral" } }];
|
|
1523
|
+
}
|
|
1524
|
+
if (body.messages.length >= 2) {
|
|
1525
|
+
const idx = body.messages.length - 2;
|
|
1526
|
+
const m = body.messages[idx];
|
|
1527
|
+
if (typeof m.content === "string" && m.content !== "") {
|
|
1528
|
+
body.messages[idx] = {
|
|
1529
|
+
role: m.role,
|
|
1530
|
+
content: [{ type: "text", text: m.content, cache_control: { type: "ephemeral" } }]
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1486
1535
|
function buildAnthropicRequest(opts) {
|
|
1487
1536
|
let system;
|
|
1488
1537
|
const convo = [];
|
|
@@ -1507,6 +1556,7 @@ function buildAnthropicRequest(opts) {
|
|
|
1507
1556
|
if (system !== void 0) {
|
|
1508
1557
|
body.system = system;
|
|
1509
1558
|
}
|
|
1559
|
+
applyPromptCaching(body);
|
|
1510
1560
|
if (isReasoningModel("anthropic", opts.model)) {
|
|
1511
1561
|
if (opts.model.toLowerCase().startsWith("claude-fable-5")) {
|
|
1512
1562
|
body.thinking = { type: "adaptive" };
|
|
@@ -1566,6 +1616,7 @@ function parseAnthropicResponse(opts) {
|
|
|
1566
1616
|
const prompt = Math.trunc(Number(u["input_tokens"] ?? 0)) || 0;
|
|
1567
1617
|
const cached = Math.trunc(Number(u["cache_read_input_tokens"] ?? 0)) || 0;
|
|
1568
1618
|
const completion = Math.trunc(Number(u["output_tokens"] ?? 0)) || 0;
|
|
1619
|
+
const cacheWrite = Math.trunc(Number(u["cache_creation_input_tokens"] ?? 0)) || 0;
|
|
1569
1620
|
const usage = {
|
|
1570
1621
|
provider: "anthropic",
|
|
1571
1622
|
model: opts.model,
|
|
@@ -1573,7 +1624,7 @@ function parseAnthropicResponse(opts) {
|
|
|
1573
1624
|
// production helper folds them in so prompt_tokens is the FULL
|
|
1574
1625
|
// input volume; calculateCost then bills the cached subset at the
|
|
1575
1626
|
// cached rate (cached <= prompt_tokens, since prompt = input+cached).
|
|
1576
|
-
prompt_tokens: prompt + cached,
|
|
1627
|
+
prompt_tokens: prompt + cached + cacheWrite,
|
|
1577
1628
|
completion_tokens: completion,
|
|
1578
1629
|
cached_tokens: cached,
|
|
1579
1630
|
total_tokens: 0,
|
|
@@ -1925,16 +1976,21 @@ function parseOpenAICompatibleResponse(opts) {
|
|
|
1925
1976
|
const u = r["usage"] ?? {};
|
|
1926
1977
|
const details = u["prompt_tokens_details"] ?? {};
|
|
1927
1978
|
const cached = Math.trunc(Number(details["cached_tokens"] ?? 0)) || 0;
|
|
1979
|
+
const promptTokens = Math.trunc(Number(u["prompt_tokens"] ?? 0)) || 0;
|
|
1980
|
+
const reportedCompletion = Math.trunc(Number(u["completion_tokens"] ?? 0)) || 0;
|
|
1981
|
+
const reportedTotal = Math.trunc(Number(u["total_tokens"] ?? 0)) || 0;
|
|
1982
|
+
const derivedCompletion = reportedTotal > promptTokens ? reportedTotal - promptTokens : reportedCompletion;
|
|
1983
|
+
const completionTokens = Math.max(reportedCompletion, derivedCompletion);
|
|
1928
1984
|
const usage = {
|
|
1929
1985
|
provider: opts.provider,
|
|
1930
1986
|
model: opts.model,
|
|
1931
|
-
prompt_tokens:
|
|
1932
|
-
completion_tokens:
|
|
1987
|
+
prompt_tokens: promptTokens,
|
|
1988
|
+
completion_tokens: completionTokens,
|
|
1933
1989
|
cached_tokens: cached,
|
|
1934
1990
|
// Use the response's reported total_tokens directly. Python's
|
|
1935
1991
|
// `Usage.to_dict()` falls back to prompt+completion only when
|
|
1936
1992
|
// total_tokens is 0/missing; mirror that.
|
|
1937
|
-
total_tokens:
|
|
1993
|
+
total_tokens: reportedTotal,
|
|
1938
1994
|
cost_usd: 0,
|
|
1939
1995
|
estimated: Object.keys(u).length === 0,
|
|
1940
1996
|
purpose: opts.purpose
|
|
@@ -1976,7 +2032,11 @@ async function sendOpenAICompatible(args) {
|
|
|
1976
2032
|
body.reasoning_effort = effort;
|
|
1977
2033
|
}
|
|
1978
2034
|
}
|
|
1979
|
-
if (isReasoningModel(args.provider, args.model) &&
|
|
2035
|
+
if (isReasoningModel(args.provider, args.model) && // Only where the cap is a SHARED budget. On a provider that bounds visible
|
|
2036
|
+
// output only (xAI), headroom cannot protect the answer from being crowded
|
|
2037
|
+
// out — nothing is crowding it — so it would merely authorise a
|
|
2038
|
+
// 25,000-token reply and the bill that comes with it.
|
|
2039
|
+
reasoningSharesOutputBudget(args.provider) && typeof body.max_completion_tokens === "number") {
|
|
1980
2040
|
const raw = Number(process.env["CROSSCHECK_OPENAI_REASONING_HEADROOM_TOKENS"]);
|
|
1981
2041
|
const headroom = Number.isFinite(raw) && raw >= 0 ? Math.trunc(raw) : 25e3;
|
|
1982
2042
|
body.max_completion_tokens += headroom;
|
|
@@ -2292,7 +2352,7 @@ var import_zod = require("zod");
|
|
|
2292
2352
|
// src/server-meta.ts
|
|
2293
2353
|
init_cjs_shims();
|
|
2294
2354
|
var SERVER_NAME = "crosscheck-agent";
|
|
2295
|
-
var SERVER_VERSION = true ? "0.2.
|
|
2355
|
+
var SERVER_VERSION = true ? "0.2.22" : "0.0.0-dev";
|
|
2296
2356
|
|
|
2297
2357
|
// src/tools/audit.ts
|
|
2298
2358
|
init_cjs_shims();
|
|
@@ -2805,7 +2865,9 @@ async function attachUsageBlock(result, answers, opts) {
|
|
|
2805
2865
|
purpose: a.usage?.purpose ?? null,
|
|
2806
2866
|
wall_ms: a.elapsed_ms ?? 0,
|
|
2807
2867
|
cpu_ms: a.cpu_ms ?? 0,
|
|
2808
|
-
cache_hit: Boolean(a.cache_hit)
|
|
2868
|
+
cache_hit: Boolean(a.cache_hit),
|
|
2869
|
+
...a.error_kind ? { error_kind: a.error_kind } : {},
|
|
2870
|
+
...a.error_billing ? { error_billing: true } : {}
|
|
2809
2871
|
}))
|
|
2810
2872
|
};
|
|
2811
2873
|
result["timing"] = timing;
|
|
@@ -3114,11 +3176,13 @@ async function askOne(provider, messages, opts) {
|
|
|
3114
3176
|
const cpuMs = Math.trunc((cpu.user + cpu.system) / 1e3);
|
|
3115
3177
|
const kind = e instanceof ProviderError ? e.kind : "other";
|
|
3116
3178
|
const msg = e instanceof Error ? e.message : String(e);
|
|
3179
|
+
const billing = e instanceof ProviderError && e.billing;
|
|
3117
3180
|
return {
|
|
3118
3181
|
provider: provider.name,
|
|
3119
3182
|
model: provider.model,
|
|
3120
3183
|
error: msg,
|
|
3121
3184
|
error_kind: kind,
|
|
3185
|
+
...billing ? { error_billing: true } : {},
|
|
3122
3186
|
attempts: 0,
|
|
3123
3187
|
usage: emptyUsage(provider.name, provider.model, opts.purpose),
|
|
3124
3188
|
cache_hit: false,
|
|
@@ -12780,7 +12844,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
|
|
|
12780
12844
|
var DEFAULT_PACKAGE = "crosscheck-cli";
|
|
12781
12845
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
12782
12846
|
function engineVersion() {
|
|
12783
|
-
return true ? "0.2.
|
|
12847
|
+
return true ? "0.2.22" : "0.0.0-dev";
|
|
12784
12848
|
}
|
|
12785
12849
|
function defaultUpdateCachePath() {
|
|
12786
12850
|
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 +14859,23 @@ function getConfig2() {
|
|
|
14795
14859
|
config = { url, seatId, orgId, seatKey };
|
|
14796
14860
|
return config;
|
|
14797
14861
|
}
|
|
14862
|
+
function errorClassFor(kind) {
|
|
14863
|
+
switch (kind) {
|
|
14864
|
+
case "auth":
|
|
14865
|
+
return "auth";
|
|
14866
|
+
case "rate_limit":
|
|
14867
|
+
return "rate_limit";
|
|
14868
|
+
case "timeout":
|
|
14869
|
+
return "timeout";
|
|
14870
|
+
case "server":
|
|
14871
|
+
return "server";
|
|
14872
|
+
// network/parse/client/other all mean "we don't know that the key is
|
|
14873
|
+
// bad", and guessing wrong here is what produces a dashboard that cries
|
|
14874
|
+
// wolf. Say unknown.
|
|
14875
|
+
default:
|
|
14876
|
+
return "unknown";
|
|
14877
|
+
}
|
|
14878
|
+
}
|
|
14798
14879
|
function intNonNeg(v) {
|
|
14799
14880
|
const n = Math.trunc(Number(v));
|
|
14800
14881
|
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
@@ -14802,12 +14883,12 @@ function intNonNeg(v) {
|
|
|
14802
14883
|
function usageEventsFromEnvelope(out, toolName) {
|
|
14803
14884
|
if (!out || typeof out !== "object" || Array.isArray(out)) return [];
|
|
14804
14885
|
const usage = out["usage"];
|
|
14805
|
-
|
|
14806
|
-
const byCall =
|
|
14807
|
-
if (!Array.isArray(byCall) || byCall.length === 0) return [];
|
|
14886
|
+
const rawByCall = usage && typeof usage === "object" ? usage["by_call"] : void 0;
|
|
14887
|
+
const byCall = Array.isArray(rawByCall) ? rawByCall : [];
|
|
14808
14888
|
const timing = out["timing"];
|
|
14809
14889
|
const timingByCall = timing && typeof timing === "object" ? timing["by_call"] : void 0;
|
|
14810
|
-
const
|
|
14890
|
+
const allTiming = Array.isArray(timingByCall) ? timingByCall : [];
|
|
14891
|
+
const latencies = allTiming;
|
|
14811
14892
|
const aligned = latencies.length === byCall.length;
|
|
14812
14893
|
const pattern = toolToPattern(toolName);
|
|
14813
14894
|
const events = [];
|
|
@@ -14817,13 +14898,18 @@ function usageEventsFromEnvelope(out, toolName) {
|
|
|
14817
14898
|
if (!provider) continue;
|
|
14818
14899
|
const model = String(u.model ?? "").slice(0, 128) || "unknown";
|
|
14819
14900
|
const cost = Math.max(0, Number(u.cost_usd) || 0);
|
|
14820
|
-
const
|
|
14901
|
+
const timingRow = aligned ? latencies[i] : void 0;
|
|
14902
|
+
if (timingRow?.["error_kind"]) continue;
|
|
14903
|
+
const promptTokens = intNonNeg(u.prompt_tokens);
|
|
14904
|
+
const completionTokens = intNonNeg(u.completion_tokens);
|
|
14905
|
+
if (promptTokens === 0 && completionTokens === 0) continue;
|
|
14906
|
+
const latencyMs = intNonNeg(timingRow?.["wall_ms"]);
|
|
14821
14907
|
events.push({
|
|
14822
14908
|
provider,
|
|
14823
14909
|
model,
|
|
14824
14910
|
pattern,
|
|
14825
|
-
promptTokens
|
|
14826
|
-
completionTokens
|
|
14911
|
+
promptTokens,
|
|
14912
|
+
completionTokens,
|
|
14827
14913
|
costUsdEstimate: cost,
|
|
14828
14914
|
latencyMs,
|
|
14829
14915
|
status: "ok",
|
|
@@ -14832,6 +14918,28 @@ function usageEventsFromEnvelope(out, toolName) {
|
|
|
14832
14918
|
purpose: normalizePurpose(u.purpose)
|
|
14833
14919
|
});
|
|
14834
14920
|
}
|
|
14921
|
+
for (const t of allTiming) {
|
|
14922
|
+
const row = t;
|
|
14923
|
+
const kind = row["error_kind"];
|
|
14924
|
+
if (!kind) continue;
|
|
14925
|
+
const provider = providerToSchema(String(row["provider"] ?? ""));
|
|
14926
|
+
if (!provider) continue;
|
|
14927
|
+
events.push({
|
|
14928
|
+
provider,
|
|
14929
|
+
model: String(row["model"] ?? "").slice(0, 128) || "unknown",
|
|
14930
|
+
pattern,
|
|
14931
|
+
// A failure consumed no tokens and cost nothing. Recording it with a
|
|
14932
|
+
// nonzero cost would corrupt the per-feature ledger with money nobody
|
|
14933
|
+
// spent.
|
|
14934
|
+
promptTokens: 0,
|
|
14935
|
+
completionTokens: 0,
|
|
14936
|
+
costUsdEstimate: 0,
|
|
14937
|
+
latencyMs: intNonNeg(row["wall_ms"]),
|
|
14938
|
+
status: "error",
|
|
14939
|
+
errorClass: row["error_billing"] ? "auth" : errorClassFor(String(kind)),
|
|
14940
|
+
purpose: normalizePurpose(row["purpose"])
|
|
14941
|
+
});
|
|
14942
|
+
}
|
|
14835
14943
|
return events;
|
|
14836
14944
|
}
|
|
14837
14945
|
var queue = [];
|
|
@@ -14858,6 +14966,10 @@ function enqueue(cfg2, input, runId, fingerprint) {
|
|
|
14858
14966
|
costUsdEstimate: input.costUsdEstimate,
|
|
14859
14967
|
latencyMs: input.latencyMs,
|
|
14860
14968
|
status: input.status,
|
|
14969
|
+
// Only present on failures. The canonical body renders an absent
|
|
14970
|
+
// errorClass as "", so omitting and sending "" sign identically — but
|
|
14971
|
+
// omitting keeps the JSON honest about what we actually know.
|
|
14972
|
+
...input.errorClass === void 0 ? {} : { errorClass: input.errorClass },
|
|
14861
14973
|
runId,
|
|
14862
14974
|
// Omit entirely (not "") when unknown, so the canonical body stays
|
|
14863
14975
|
// consistent with what the server reconstructs from the JSON.
|