crosscheck-mcp 0.2.14 → 0.2.16
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 +154 -74
- package/dist/browser-ext.cjs.map +1 -1
- package/dist/browser-ext.js +154 -74
- package/dist/browser-ext.js.map +1 -1
- package/dist/node-stdio.cjs +573 -268
- package/dist/node-stdio.cjs.map +1 -1
- package/dist/node-stdio.d.cts +9 -0
- package/dist/node-stdio.d.ts +9 -0
- package/dist/node-stdio.js +569 -264
- package/dist/node-stdio.js.map +1 -1
- package/package.json +1 -1
package/dist/browser-ext.cjs
CHANGED
|
@@ -472,11 +472,18 @@ var ProviderError = class extends Error {
|
|
|
472
472
|
status;
|
|
473
473
|
transient;
|
|
474
474
|
retryAfterS;
|
|
475
|
+
/** True specifically for "the key is fine but this model isn't
|
|
476
|
+
* available to this account/project" (see http-errors.ts's
|
|
477
|
+
* isModelAccessFailure) — the one failure a model fallback chain can
|
|
478
|
+
* actually fix. Distinct from `transient`: this is never worth
|
|
479
|
+
* retrying the SAME model, but IS worth trying a different one. */
|
|
480
|
+
modelAccessFailure;
|
|
475
481
|
constructor(kind, message, opts) {
|
|
476
482
|
super(message);
|
|
477
483
|
this.kind = kind;
|
|
478
484
|
this.status = opts?.status;
|
|
479
485
|
this.transient = opts?.transient ?? defaultTransient(kind);
|
|
486
|
+
this.modelAccessFailure = opts?.modelAccessFailure ?? false;
|
|
480
487
|
if (opts?.retryAfterS !== void 0) {
|
|
481
488
|
this.retryAfterS = opts.retryAfterS;
|
|
482
489
|
}
|
|
@@ -507,7 +514,7 @@ function httpFailureToProviderError(provider, status, bodyText, retryAfterS, mod
|
|
|
507
514
|
return new ProviderError(
|
|
508
515
|
"client",
|
|
509
516
|
`${provider}: your account/project doesn't have access to ${modelRef} (HTTP ${status}). This is a model-access problem, not a key problem \u2014 your ${provider} API key is fine, ${modelRef} just isn't enabled for this account/project. Fix: run \`crosscheck models set ${provider} <a-model-you-have-access-to>\` (or set ${provider.toUpperCase()}_MODEL in your .env until that's available). Detail: ${detail}`,
|
|
510
|
-
{ status }
|
|
517
|
+
{ status, modelAccessFailure: true }
|
|
511
518
|
);
|
|
512
519
|
}
|
|
513
520
|
if (isCreditsFailure(status, bodyText)) {
|
|
@@ -714,6 +721,14 @@ function buildAnthropicRequest(opts) {
|
|
|
714
721
|
if (system !== void 0) {
|
|
715
722
|
body.system = system;
|
|
716
723
|
}
|
|
724
|
+
if (isReasoningModel("anthropic", opts.model)) {
|
|
725
|
+
if (opts.model.toLowerCase().startsWith("claude-fable-5")) {
|
|
726
|
+
body.thinking = { type: "adaptive" };
|
|
727
|
+
body.output_config = { effort: "low" };
|
|
728
|
+
} else {
|
|
729
|
+
body.thinking = { type: "disabled" };
|
|
730
|
+
}
|
|
731
|
+
}
|
|
717
732
|
if (opts.jsonSchema) {
|
|
718
733
|
body.tools = [{
|
|
719
734
|
name: ANTHROPIC_STRUCTURED_TOOL_NAME,
|
|
@@ -781,7 +796,12 @@ function parseAnthropicResponse(opts) {
|
|
|
781
796
|
purpose: opts.purpose
|
|
782
797
|
};
|
|
783
798
|
usage.total_tokens = usage.prompt_tokens + usage.completion_tokens;
|
|
784
|
-
|
|
799
|
+
const stopReasonRaw = r["stop_reason"];
|
|
800
|
+
const stopReason = typeof stopReasonRaw === "string" ? stopReasonRaw : void 0;
|
|
801
|
+
return { text, usage, ...stopReason !== void 0 ? { stopReason } : {} };
|
|
802
|
+
}
|
|
803
|
+
function isEmptyDueToMaxTokens(text, stopReason) {
|
|
804
|
+
return text.trim() === "" && stopReason === "max_tokens";
|
|
785
805
|
}
|
|
786
806
|
function applyPricing(usage, pricing) {
|
|
787
807
|
const { cost_usd, estimated } = calculateCost(
|
|
@@ -799,49 +819,64 @@ function applyPricing(usage, pricing) {
|
|
|
799
819
|
estimated: usage.estimated || estimated
|
|
800
820
|
};
|
|
801
821
|
}
|
|
822
|
+
var MAX_TOKENS_ESCALATIONS = 2;
|
|
823
|
+
var ESCALATION_MULTIPLIER = 2;
|
|
802
824
|
async function sendAnthropic(args) {
|
|
803
|
-
const { url, headers, body } = buildAnthropicRequest({
|
|
804
|
-
model: args.model,
|
|
805
|
-
apiKey: args.apiKey,
|
|
806
|
-
messages: args.messages,
|
|
807
|
-
maxTokens: args.maxTokens,
|
|
808
|
-
temperature: args.temperature,
|
|
809
|
-
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
810
|
-
});
|
|
811
825
|
const doFetch = args.fetchImpl ?? globalThis.fetch;
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
826
|
+
let maxTokens = args.maxTokens;
|
|
827
|
+
let result = null;
|
|
828
|
+
let totalAttempts = 0;
|
|
829
|
+
for (let escalation = 0; escalation <= MAX_TOKENS_ESCALATIONS; escalation += 1) {
|
|
830
|
+
const { url, headers, body } = buildAnthropicRequest({
|
|
831
|
+
model: args.model,
|
|
832
|
+
apiKey: args.apiKey,
|
|
833
|
+
messages: args.messages,
|
|
834
|
+
maxTokens,
|
|
835
|
+
temperature: args.temperature,
|
|
836
|
+
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
837
|
+
});
|
|
838
|
+
const init = {
|
|
839
|
+
method: "POST",
|
|
840
|
+
headers,
|
|
841
|
+
body: JSON.stringify(body)
|
|
842
|
+
};
|
|
843
|
+
if (args.signal) init.signal = args.signal;
|
|
844
|
+
let stopReason;
|
|
845
|
+
const attemptOnce = async () => {
|
|
846
|
+
let respLike;
|
|
828
847
|
try {
|
|
829
|
-
|
|
848
|
+
respLike = await doFetch(url, init);
|
|
830
849
|
} catch (e) {
|
|
831
|
-
throw new ProviderError("
|
|
850
|
+
throw new ProviderError("network", `anthropic: fetch failed: ${e.message}`);
|
|
832
851
|
}
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
852
|
+
const status = respLike.status;
|
|
853
|
+
if (status >= 200 && status < 300) {
|
|
854
|
+
let parsed;
|
|
855
|
+
try {
|
|
856
|
+
parsed = await respLike.json();
|
|
857
|
+
} catch (e) {
|
|
858
|
+
throw new ProviderError("parse", `anthropic: response body not JSON: ${e.message}`);
|
|
859
|
+
}
|
|
860
|
+
const { text, usage, stopReason: sr } = parseAnthropicResponse({
|
|
861
|
+
resp: parsed,
|
|
862
|
+
model: args.model,
|
|
863
|
+
purpose: args.purpose ?? "worker"
|
|
864
|
+
});
|
|
865
|
+
stopReason = sr;
|
|
866
|
+
return { text, attempts: 1, usage: applyPricing(usage, args.pricing) };
|
|
867
|
+
}
|
|
868
|
+
const bodyText = await respLike.text().catch(() => "");
|
|
869
|
+
throw httpFailureToProviderError("anthropic", status, bodyText, parseRetryAfter(respLike), args.model);
|
|
870
|
+
};
|
|
871
|
+
await acquireRateLimit("anthropic", args.signal ? { signal: args.signal } : void 0);
|
|
872
|
+
result = await sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
873
|
+
totalAttempts += result.attempts;
|
|
874
|
+
if (escalation >= MAX_TOKENS_ESCALATIONS || !isEmptyDueToMaxTokens(result.text, stopReason)) {
|
|
875
|
+
break;
|
|
839
876
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
};
|
|
843
|
-
await acquireRateLimit("anthropic", args.signal ? { signal: args.signal } : void 0);
|
|
844
|
-
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
877
|
+
maxTokens *= ESCALATION_MULTIPLIER;
|
|
878
|
+
}
|
|
879
|
+
return { ...result, attempts: totalAttempts };
|
|
845
880
|
}
|
|
846
881
|
|
|
847
882
|
// src/providers/gemini.ts
|
|
@@ -1210,12 +1245,31 @@ var DEFAULT_MODELS = {
|
|
|
1210
1245
|
kimi: "kimi-k3",
|
|
1211
1246
|
qwen: "qwen3.8-max"
|
|
1212
1247
|
};
|
|
1248
|
+
var MODEL_ENV_VARS = {
|
|
1249
|
+
anthropic: "ANTHROPIC_MODEL",
|
|
1250
|
+
openai: "OPENAI_MODEL",
|
|
1251
|
+
xai: "XAI_MODEL",
|
|
1252
|
+
mistral: "MISTRAL_MODEL",
|
|
1253
|
+
groq: "GROQ_MODEL",
|
|
1254
|
+
deepseek: "DEEPSEEK_MODEL",
|
|
1255
|
+
gemini: "GEMINI_MODEL",
|
|
1256
|
+
kimi: "KIMI_MODEL",
|
|
1257
|
+
qwen: "QWEN_MODEL"
|
|
1258
|
+
};
|
|
1259
|
+
function parseFallbackModels(raw) {
|
|
1260
|
+
if (!raw) return [];
|
|
1261
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1262
|
+
}
|
|
1263
|
+
function fallbackModelsFor(env, provider) {
|
|
1264
|
+
const envVar = MODEL_ENV_VARS[provider];
|
|
1265
|
+
return envVar ? parseFallbackModels(env[`${envVar}_FALLBACKS`]) : [];
|
|
1266
|
+
}
|
|
1213
1267
|
function buildProviders(opts) {
|
|
1214
1268
|
const out = {};
|
|
1215
1269
|
const anthropicKey = opts.env["ANTHROPIC_API_KEY"];
|
|
1216
1270
|
if (anthropicKey) {
|
|
1217
1271
|
const model = opts.env["ANTHROPIC_MODEL"] ?? DEFAULT_MODELS.anthropic;
|
|
1218
|
-
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts);
|
|
1272
|
+
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts, fallbackModelsFor(opts.env, "anthropic"));
|
|
1219
1273
|
}
|
|
1220
1274
|
const openAiCompatSpec = [
|
|
1221
1275
|
{ name: "openai", keyEnv: "OPENAI_API_KEY", modelEnv: "OPENAI_MODEL", defaultModel: DEFAULT_MODELS["openai"] },
|
|
@@ -1230,19 +1284,20 @@ function buildProviders(opts) {
|
|
|
1230
1284
|
const apiKey = opts.env[s.keyEnv];
|
|
1231
1285
|
if (!apiKey) continue;
|
|
1232
1286
|
const model = opts.env[s.modelEnv] ?? s.defaultModel;
|
|
1233
|
-
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts);
|
|
1287
|
+
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts, fallbackModelsFor(opts.env, s.name));
|
|
1234
1288
|
}
|
|
1235
1289
|
const geminiKey = opts.env["GEMINI_API_KEY"];
|
|
1236
1290
|
if (geminiKey) {
|
|
1237
1291
|
const model = opts.env["GEMINI_MODEL"] ?? DEFAULT_MODELS.gemini;
|
|
1238
|
-
out["gemini"] = makeGeminiProvider(model, geminiKey, opts);
|
|
1292
|
+
out["gemini"] = makeGeminiProvider(model, geminiKey, opts, fallbackModelsFor(opts.env, "gemini"));
|
|
1239
1293
|
}
|
|
1240
1294
|
return out;
|
|
1241
1295
|
}
|
|
1242
|
-
function makeAnthropicProvider(model, apiKey, opts) {
|
|
1296
|
+
function makeAnthropicProvider(model, apiKey, opts, fallbackModels = []) {
|
|
1243
1297
|
return {
|
|
1244
1298
|
name: "anthropic",
|
|
1245
1299
|
model,
|
|
1300
|
+
fallbackModels,
|
|
1246
1301
|
send: async (args) => {
|
|
1247
1302
|
const sendOpts = {
|
|
1248
1303
|
...args,
|
|
@@ -1255,11 +1310,12 @@ function makeAnthropicProvider(model, apiKey, opts) {
|
|
|
1255
1310
|
}
|
|
1256
1311
|
};
|
|
1257
1312
|
}
|
|
1258
|
-
function makeOpenAICompatibleProvider(name, model, apiKey, opts) {
|
|
1313
|
+
function makeOpenAICompatibleProvider(name, model, apiKey, opts, fallbackModels = []) {
|
|
1259
1314
|
const url = OPENAI_COMPAT_DEFAULT_URLS[name];
|
|
1260
1315
|
return {
|
|
1261
1316
|
name,
|
|
1262
1317
|
model,
|
|
1318
|
+
fallbackModels,
|
|
1263
1319
|
send: async (args) => {
|
|
1264
1320
|
const sendOpts = {
|
|
1265
1321
|
...args,
|
|
@@ -1274,10 +1330,11 @@ function makeOpenAICompatibleProvider(name, model, apiKey, opts) {
|
|
|
1274
1330
|
}
|
|
1275
1331
|
};
|
|
1276
1332
|
}
|
|
1277
|
-
function makeGeminiProvider(model, apiKey, opts) {
|
|
1333
|
+
function makeGeminiProvider(model, apiKey, opts, fallbackModels = []) {
|
|
1278
1334
|
return {
|
|
1279
1335
|
name: "gemini",
|
|
1280
1336
|
model,
|
|
1337
|
+
fallbackModels,
|
|
1281
1338
|
send: async (args) => {
|
|
1282
1339
|
const sendOpts = {
|
|
1283
1340
|
...args,
|
|
@@ -1296,7 +1353,7 @@ var import_zod = require("zod");
|
|
|
1296
1353
|
|
|
1297
1354
|
// src/server-meta.ts
|
|
1298
1355
|
var SERVER_NAME = "crosscheck-agent";
|
|
1299
|
-
var SERVER_VERSION = true ? "0.2.
|
|
1356
|
+
var SERVER_VERSION = true ? "0.2.16" : "0.0.0-dev";
|
|
1300
1357
|
|
|
1301
1358
|
// src/tools/audit.ts
|
|
1302
1359
|
var import_node_fs4 = require("fs");
|
|
@@ -1947,6 +2004,51 @@ function operatorCeiling(purpose, provider) {
|
|
|
1947
2004
|
return null;
|
|
1948
2005
|
}
|
|
1949
2006
|
|
|
2007
|
+
// src/core/retarget.ts
|
|
2008
|
+
function retargetProvider(p, newModel) {
|
|
2009
|
+
if (p.model === newModel) return p;
|
|
2010
|
+
return {
|
|
2011
|
+
name: p.name,
|
|
2012
|
+
model: newModel,
|
|
2013
|
+
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
async function loadProviderWeights(storage, names) {
|
|
2017
|
+
const out = {};
|
|
2018
|
+
for (const raw of names) {
|
|
2019
|
+
const name = raw.toLowerCase();
|
|
2020
|
+
if (name in out) continue;
|
|
2021
|
+
const row = await storage.getProviderStats(name);
|
|
2022
|
+
if (!row) {
|
|
2023
|
+
out[name] = 0.5;
|
|
2024
|
+
continue;
|
|
2025
|
+
}
|
|
2026
|
+
const total = row.wins + row.losses + row.abstains;
|
|
2027
|
+
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
2028
|
+
}
|
|
2029
|
+
return out;
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
// src/core/model-fallback.ts
|
|
2033
|
+
async function sendWithModelFallback(provider, args) {
|
|
2034
|
+
const chain = [provider.model, ...provider.fallbackModels ?? []];
|
|
2035
|
+
let lastErr;
|
|
2036
|
+
for (let i = 0; i < chain.length; i += 1) {
|
|
2037
|
+
const candidate = chain[i];
|
|
2038
|
+
const p = i === 0 ? provider : retargetProvider(provider, candidate);
|
|
2039
|
+
try {
|
|
2040
|
+
const result = await p.send(args);
|
|
2041
|
+
return { result, modelUsed: candidate };
|
|
2042
|
+
} catch (e) {
|
|
2043
|
+
lastErr = e;
|
|
2044
|
+
const isModelAccessFailure2 = e instanceof ProviderError && e.modelAccessFailure;
|
|
2045
|
+
const hasMoreCandidates = i < chain.length - 1;
|
|
2046
|
+
if (!isModelAccessFailure2 || !hasMoreCandidates) throw e;
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
throw lastErr;
|
|
2050
|
+
}
|
|
2051
|
+
|
|
1950
2052
|
// src/core/structured.ts
|
|
1951
2053
|
async function requestStructured(provider, baseMessages, schema, opts) {
|
|
1952
2054
|
const maxRetries = opts.maxRetries ?? 1;
|
|
@@ -2025,7 +2127,7 @@ async function askOne(provider, messages, opts) {
|
|
|
2025
2127
|
const ceiling = operatorCeiling(opts.purpose, provider.name);
|
|
2026
2128
|
if (ceiling !== null && ceiling < maxTokens) maxTokens = ceiling;
|
|
2027
2129
|
try {
|
|
2028
|
-
const r = await provider
|
|
2130
|
+
const { result: r, modelUsed } = await sendWithModelFallback(provider, {
|
|
2029
2131
|
messages,
|
|
2030
2132
|
maxTokens,
|
|
2031
2133
|
temperature: opts.temperature,
|
|
@@ -2038,7 +2140,10 @@ async function askOne(provider, messages, opts) {
|
|
|
2038
2140
|
const cpuMs = Math.trunc((cpu.user + cpu.system) / 1e3);
|
|
2039
2141
|
return {
|
|
2040
2142
|
provider: provider.name,
|
|
2041
|
-
model
|
|
2143
|
+
// modelUsed reflects whichever model actually answered — the
|
|
2144
|
+
// primary, or a fallback if one was needed. Equivalent to the old
|
|
2145
|
+
// `provider.model` whenever no fallback occurred.
|
|
2146
|
+
model: modelUsed,
|
|
2042
2147
|
response: r.text,
|
|
2043
2148
|
attempts: r.attempts,
|
|
2044
2149
|
usage: r.usage,
|
|
@@ -2748,31 +2853,6 @@ function numberOrNull(v) {
|
|
|
2748
2853
|
// src/tools/audit.ts
|
|
2749
2854
|
var import_node_perf_hooks = require("perf_hooks");
|
|
2750
2855
|
|
|
2751
|
-
// src/core/retarget.ts
|
|
2752
|
-
function retargetProvider(p, newModel) {
|
|
2753
|
-
if (p.model === newModel) return p;
|
|
2754
|
-
return {
|
|
2755
|
-
name: p.name,
|
|
2756
|
-
model: newModel,
|
|
2757
|
-
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
2758
|
-
};
|
|
2759
|
-
}
|
|
2760
|
-
async function loadProviderWeights(storage, names) {
|
|
2761
|
-
const out = {};
|
|
2762
|
-
for (const raw of names) {
|
|
2763
|
-
const name = raw.toLowerCase();
|
|
2764
|
-
if (name in out) continue;
|
|
2765
|
-
const row = await storage.getProviderStats(name);
|
|
2766
|
-
if (!row) {
|
|
2767
|
-
out[name] = 0.5;
|
|
2768
|
-
continue;
|
|
2769
|
-
}
|
|
2770
|
-
const total = row.wins + row.losses + row.abstains;
|
|
2771
|
-
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
2772
|
-
}
|
|
2773
|
-
return out;
|
|
2774
|
-
}
|
|
2775
|
-
|
|
2776
2856
|
// src/core/co-reason.ts
|
|
2777
2857
|
var CO_REASON_MODEL = "gpt-5.6";
|
|
2778
2858
|
var CO_REASON_PROVIDER = "openai";
|
|
@@ -11506,7 +11586,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
|
|
|
11506
11586
|
var DEFAULT_PACKAGE = "crosscheck-cli";
|
|
11507
11587
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
11508
11588
|
function engineVersion() {
|
|
11509
|
-
return true ? "0.2.
|
|
11589
|
+
return true ? "0.2.16" : "0.0.0-dev";
|
|
11510
11590
|
}
|
|
11511
11591
|
function defaultUpdateCachePath() {
|
|
11512
11592
|
const base = process.env["CROSSCHECK_DATA_DIR"] || import_node_path12.default.join(import_node_os2.default.homedir() || import_node_os2.default.tmpdir(), ".crosscheck");
|