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.js
CHANGED
|
@@ -439,11 +439,18 @@ var ProviderError = class extends Error {
|
|
|
439
439
|
status;
|
|
440
440
|
transient;
|
|
441
441
|
retryAfterS;
|
|
442
|
+
/** True specifically for "the key is fine but this model isn't
|
|
443
|
+
* available to this account/project" (see http-errors.ts's
|
|
444
|
+
* isModelAccessFailure) — the one failure a model fallback chain can
|
|
445
|
+
* actually fix. Distinct from `transient`: this is never worth
|
|
446
|
+
* retrying the SAME model, but IS worth trying a different one. */
|
|
447
|
+
modelAccessFailure;
|
|
442
448
|
constructor(kind, message, opts) {
|
|
443
449
|
super(message);
|
|
444
450
|
this.kind = kind;
|
|
445
451
|
this.status = opts?.status;
|
|
446
452
|
this.transient = opts?.transient ?? defaultTransient(kind);
|
|
453
|
+
this.modelAccessFailure = opts?.modelAccessFailure ?? false;
|
|
447
454
|
if (opts?.retryAfterS !== void 0) {
|
|
448
455
|
this.retryAfterS = opts.retryAfterS;
|
|
449
456
|
}
|
|
@@ -474,7 +481,7 @@ function httpFailureToProviderError(provider, status, bodyText, retryAfterS, mod
|
|
|
474
481
|
return new ProviderError(
|
|
475
482
|
"client",
|
|
476
483
|
`${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}`,
|
|
477
|
-
{ status }
|
|
484
|
+
{ status, modelAccessFailure: true }
|
|
478
485
|
);
|
|
479
486
|
}
|
|
480
487
|
if (isCreditsFailure(status, bodyText)) {
|
|
@@ -681,6 +688,14 @@ function buildAnthropicRequest(opts) {
|
|
|
681
688
|
if (system !== void 0) {
|
|
682
689
|
body.system = system;
|
|
683
690
|
}
|
|
691
|
+
if (isReasoningModel("anthropic", opts.model)) {
|
|
692
|
+
if (opts.model.toLowerCase().startsWith("claude-fable-5")) {
|
|
693
|
+
body.thinking = { type: "adaptive" };
|
|
694
|
+
body.output_config = { effort: "low" };
|
|
695
|
+
} else {
|
|
696
|
+
body.thinking = { type: "disabled" };
|
|
697
|
+
}
|
|
698
|
+
}
|
|
684
699
|
if (opts.jsonSchema) {
|
|
685
700
|
body.tools = [{
|
|
686
701
|
name: ANTHROPIC_STRUCTURED_TOOL_NAME,
|
|
@@ -748,7 +763,12 @@ function parseAnthropicResponse(opts) {
|
|
|
748
763
|
purpose: opts.purpose
|
|
749
764
|
};
|
|
750
765
|
usage.total_tokens = usage.prompt_tokens + usage.completion_tokens;
|
|
751
|
-
|
|
766
|
+
const stopReasonRaw = r["stop_reason"];
|
|
767
|
+
const stopReason = typeof stopReasonRaw === "string" ? stopReasonRaw : void 0;
|
|
768
|
+
return { text, usage, ...stopReason !== void 0 ? { stopReason } : {} };
|
|
769
|
+
}
|
|
770
|
+
function isEmptyDueToMaxTokens(text, stopReason) {
|
|
771
|
+
return text.trim() === "" && stopReason === "max_tokens";
|
|
752
772
|
}
|
|
753
773
|
function applyPricing(usage, pricing) {
|
|
754
774
|
const { cost_usd, estimated } = calculateCost(
|
|
@@ -766,49 +786,64 @@ function applyPricing(usage, pricing) {
|
|
|
766
786
|
estimated: usage.estimated || estimated
|
|
767
787
|
};
|
|
768
788
|
}
|
|
789
|
+
var MAX_TOKENS_ESCALATIONS = 2;
|
|
790
|
+
var ESCALATION_MULTIPLIER = 2;
|
|
769
791
|
async function sendAnthropic(args) {
|
|
770
|
-
const { url, headers, body } = buildAnthropicRequest({
|
|
771
|
-
model: args.model,
|
|
772
|
-
apiKey: args.apiKey,
|
|
773
|
-
messages: args.messages,
|
|
774
|
-
maxTokens: args.maxTokens,
|
|
775
|
-
temperature: args.temperature,
|
|
776
|
-
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
777
|
-
});
|
|
778
792
|
const doFetch = args.fetchImpl ?? globalThis.fetch;
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
793
|
+
let maxTokens = args.maxTokens;
|
|
794
|
+
let result = null;
|
|
795
|
+
let totalAttempts = 0;
|
|
796
|
+
for (let escalation = 0; escalation <= MAX_TOKENS_ESCALATIONS; escalation += 1) {
|
|
797
|
+
const { url, headers, body } = buildAnthropicRequest({
|
|
798
|
+
model: args.model,
|
|
799
|
+
apiKey: args.apiKey,
|
|
800
|
+
messages: args.messages,
|
|
801
|
+
maxTokens,
|
|
802
|
+
temperature: args.temperature,
|
|
803
|
+
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
804
|
+
});
|
|
805
|
+
const init = {
|
|
806
|
+
method: "POST",
|
|
807
|
+
headers,
|
|
808
|
+
body: JSON.stringify(body)
|
|
809
|
+
};
|
|
810
|
+
if (args.signal) init.signal = args.signal;
|
|
811
|
+
let stopReason;
|
|
812
|
+
const attemptOnce = async () => {
|
|
813
|
+
let respLike;
|
|
795
814
|
try {
|
|
796
|
-
|
|
815
|
+
respLike = await doFetch(url, init);
|
|
797
816
|
} catch (e) {
|
|
798
|
-
throw new ProviderError("
|
|
817
|
+
throw new ProviderError("network", `anthropic: fetch failed: ${e.message}`);
|
|
799
818
|
}
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
819
|
+
const status = respLike.status;
|
|
820
|
+
if (status >= 200 && status < 300) {
|
|
821
|
+
let parsed;
|
|
822
|
+
try {
|
|
823
|
+
parsed = await respLike.json();
|
|
824
|
+
} catch (e) {
|
|
825
|
+
throw new ProviderError("parse", `anthropic: response body not JSON: ${e.message}`);
|
|
826
|
+
}
|
|
827
|
+
const { text, usage, stopReason: sr } = parseAnthropicResponse({
|
|
828
|
+
resp: parsed,
|
|
829
|
+
model: args.model,
|
|
830
|
+
purpose: args.purpose ?? "worker"
|
|
831
|
+
});
|
|
832
|
+
stopReason = sr;
|
|
833
|
+
return { text, attempts: 1, usage: applyPricing(usage, args.pricing) };
|
|
834
|
+
}
|
|
835
|
+
const bodyText = await respLike.text().catch(() => "");
|
|
836
|
+
throw httpFailureToProviderError("anthropic", status, bodyText, parseRetryAfter(respLike), args.model);
|
|
837
|
+
};
|
|
838
|
+
await acquireRateLimit("anthropic", args.signal ? { signal: args.signal } : void 0);
|
|
839
|
+
result = await sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
840
|
+
totalAttempts += result.attempts;
|
|
841
|
+
if (escalation >= MAX_TOKENS_ESCALATIONS || !isEmptyDueToMaxTokens(result.text, stopReason)) {
|
|
842
|
+
break;
|
|
806
843
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
};
|
|
810
|
-
await acquireRateLimit("anthropic", args.signal ? { signal: args.signal } : void 0);
|
|
811
|
-
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
844
|
+
maxTokens *= ESCALATION_MULTIPLIER;
|
|
845
|
+
}
|
|
846
|
+
return { ...result, attempts: totalAttempts };
|
|
812
847
|
}
|
|
813
848
|
|
|
814
849
|
// src/providers/gemini.ts
|
|
@@ -1177,12 +1212,31 @@ var DEFAULT_MODELS = {
|
|
|
1177
1212
|
kimi: "kimi-k3",
|
|
1178
1213
|
qwen: "qwen3.8-max"
|
|
1179
1214
|
};
|
|
1215
|
+
var MODEL_ENV_VARS = {
|
|
1216
|
+
anthropic: "ANTHROPIC_MODEL",
|
|
1217
|
+
openai: "OPENAI_MODEL",
|
|
1218
|
+
xai: "XAI_MODEL",
|
|
1219
|
+
mistral: "MISTRAL_MODEL",
|
|
1220
|
+
groq: "GROQ_MODEL",
|
|
1221
|
+
deepseek: "DEEPSEEK_MODEL",
|
|
1222
|
+
gemini: "GEMINI_MODEL",
|
|
1223
|
+
kimi: "KIMI_MODEL",
|
|
1224
|
+
qwen: "QWEN_MODEL"
|
|
1225
|
+
};
|
|
1226
|
+
function parseFallbackModels(raw) {
|
|
1227
|
+
if (!raw) return [];
|
|
1228
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1229
|
+
}
|
|
1230
|
+
function fallbackModelsFor(env, provider) {
|
|
1231
|
+
const envVar = MODEL_ENV_VARS[provider];
|
|
1232
|
+
return envVar ? parseFallbackModels(env[`${envVar}_FALLBACKS`]) : [];
|
|
1233
|
+
}
|
|
1180
1234
|
function buildProviders(opts) {
|
|
1181
1235
|
const out = {};
|
|
1182
1236
|
const anthropicKey = opts.env["ANTHROPIC_API_KEY"];
|
|
1183
1237
|
if (anthropicKey) {
|
|
1184
1238
|
const model = opts.env["ANTHROPIC_MODEL"] ?? DEFAULT_MODELS.anthropic;
|
|
1185
|
-
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts);
|
|
1239
|
+
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts, fallbackModelsFor(opts.env, "anthropic"));
|
|
1186
1240
|
}
|
|
1187
1241
|
const openAiCompatSpec = [
|
|
1188
1242
|
{ name: "openai", keyEnv: "OPENAI_API_KEY", modelEnv: "OPENAI_MODEL", defaultModel: DEFAULT_MODELS["openai"] },
|
|
@@ -1197,19 +1251,20 @@ function buildProviders(opts) {
|
|
|
1197
1251
|
const apiKey = opts.env[s.keyEnv];
|
|
1198
1252
|
if (!apiKey) continue;
|
|
1199
1253
|
const model = opts.env[s.modelEnv] ?? s.defaultModel;
|
|
1200
|
-
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts);
|
|
1254
|
+
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts, fallbackModelsFor(opts.env, s.name));
|
|
1201
1255
|
}
|
|
1202
1256
|
const geminiKey = opts.env["GEMINI_API_KEY"];
|
|
1203
1257
|
if (geminiKey) {
|
|
1204
1258
|
const model = opts.env["GEMINI_MODEL"] ?? DEFAULT_MODELS.gemini;
|
|
1205
|
-
out["gemini"] = makeGeminiProvider(model, geminiKey, opts);
|
|
1259
|
+
out["gemini"] = makeGeminiProvider(model, geminiKey, opts, fallbackModelsFor(opts.env, "gemini"));
|
|
1206
1260
|
}
|
|
1207
1261
|
return out;
|
|
1208
1262
|
}
|
|
1209
|
-
function makeAnthropicProvider(model, apiKey, opts) {
|
|
1263
|
+
function makeAnthropicProvider(model, apiKey, opts, fallbackModels = []) {
|
|
1210
1264
|
return {
|
|
1211
1265
|
name: "anthropic",
|
|
1212
1266
|
model,
|
|
1267
|
+
fallbackModels,
|
|
1213
1268
|
send: async (args) => {
|
|
1214
1269
|
const sendOpts = {
|
|
1215
1270
|
...args,
|
|
@@ -1222,11 +1277,12 @@ function makeAnthropicProvider(model, apiKey, opts) {
|
|
|
1222
1277
|
}
|
|
1223
1278
|
};
|
|
1224
1279
|
}
|
|
1225
|
-
function makeOpenAICompatibleProvider(name, model, apiKey, opts) {
|
|
1280
|
+
function makeOpenAICompatibleProvider(name, model, apiKey, opts, fallbackModels = []) {
|
|
1226
1281
|
const url = OPENAI_COMPAT_DEFAULT_URLS[name];
|
|
1227
1282
|
return {
|
|
1228
1283
|
name,
|
|
1229
1284
|
model,
|
|
1285
|
+
fallbackModels,
|
|
1230
1286
|
send: async (args) => {
|
|
1231
1287
|
const sendOpts = {
|
|
1232
1288
|
...args,
|
|
@@ -1241,10 +1297,11 @@ function makeOpenAICompatibleProvider(name, model, apiKey, opts) {
|
|
|
1241
1297
|
}
|
|
1242
1298
|
};
|
|
1243
1299
|
}
|
|
1244
|
-
function makeGeminiProvider(model, apiKey, opts) {
|
|
1300
|
+
function makeGeminiProvider(model, apiKey, opts, fallbackModels = []) {
|
|
1245
1301
|
return {
|
|
1246
1302
|
name: "gemini",
|
|
1247
1303
|
model,
|
|
1304
|
+
fallbackModels,
|
|
1248
1305
|
send: async (args) => {
|
|
1249
1306
|
const sendOpts = {
|
|
1250
1307
|
...args,
|
|
@@ -1263,7 +1320,7 @@ import { z } from "zod";
|
|
|
1263
1320
|
|
|
1264
1321
|
// src/server-meta.ts
|
|
1265
1322
|
var SERVER_NAME = "crosscheck-agent";
|
|
1266
|
-
var SERVER_VERSION = true ? "0.2.
|
|
1323
|
+
var SERVER_VERSION = true ? "0.2.16" : "0.0.0-dev";
|
|
1267
1324
|
|
|
1268
1325
|
// src/tools/audit.ts
|
|
1269
1326
|
import { readdirSync, readFileSync as readFileSync3, statSync } from "fs";
|
|
@@ -1914,6 +1971,51 @@ function operatorCeiling(purpose, provider) {
|
|
|
1914
1971
|
return null;
|
|
1915
1972
|
}
|
|
1916
1973
|
|
|
1974
|
+
// src/core/retarget.ts
|
|
1975
|
+
function retargetProvider(p, newModel) {
|
|
1976
|
+
if (p.model === newModel) return p;
|
|
1977
|
+
return {
|
|
1978
|
+
name: p.name,
|
|
1979
|
+
model: newModel,
|
|
1980
|
+
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
async function loadProviderWeights(storage, names) {
|
|
1984
|
+
const out = {};
|
|
1985
|
+
for (const raw of names) {
|
|
1986
|
+
const name = raw.toLowerCase();
|
|
1987
|
+
if (name in out) continue;
|
|
1988
|
+
const row = await storage.getProviderStats(name);
|
|
1989
|
+
if (!row) {
|
|
1990
|
+
out[name] = 0.5;
|
|
1991
|
+
continue;
|
|
1992
|
+
}
|
|
1993
|
+
const total = row.wins + row.losses + row.abstains;
|
|
1994
|
+
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
1995
|
+
}
|
|
1996
|
+
return out;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
// src/core/model-fallback.ts
|
|
2000
|
+
async function sendWithModelFallback(provider, args) {
|
|
2001
|
+
const chain = [provider.model, ...provider.fallbackModels ?? []];
|
|
2002
|
+
let lastErr;
|
|
2003
|
+
for (let i = 0; i < chain.length; i += 1) {
|
|
2004
|
+
const candidate = chain[i];
|
|
2005
|
+
const p = i === 0 ? provider : retargetProvider(provider, candidate);
|
|
2006
|
+
try {
|
|
2007
|
+
const result = await p.send(args);
|
|
2008
|
+
return { result, modelUsed: candidate };
|
|
2009
|
+
} catch (e) {
|
|
2010
|
+
lastErr = e;
|
|
2011
|
+
const isModelAccessFailure2 = e instanceof ProviderError && e.modelAccessFailure;
|
|
2012
|
+
const hasMoreCandidates = i < chain.length - 1;
|
|
2013
|
+
if (!isModelAccessFailure2 || !hasMoreCandidates) throw e;
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
throw lastErr;
|
|
2017
|
+
}
|
|
2018
|
+
|
|
1917
2019
|
// src/core/structured.ts
|
|
1918
2020
|
async function requestStructured(provider, baseMessages, schema, opts) {
|
|
1919
2021
|
const maxRetries = opts.maxRetries ?? 1;
|
|
@@ -1992,7 +2094,7 @@ async function askOne(provider, messages, opts) {
|
|
|
1992
2094
|
const ceiling = operatorCeiling(opts.purpose, provider.name);
|
|
1993
2095
|
if (ceiling !== null && ceiling < maxTokens) maxTokens = ceiling;
|
|
1994
2096
|
try {
|
|
1995
|
-
const r = await provider
|
|
2097
|
+
const { result: r, modelUsed } = await sendWithModelFallback(provider, {
|
|
1996
2098
|
messages,
|
|
1997
2099
|
maxTokens,
|
|
1998
2100
|
temperature: opts.temperature,
|
|
@@ -2005,7 +2107,10 @@ async function askOne(provider, messages, opts) {
|
|
|
2005
2107
|
const cpuMs = Math.trunc((cpu.user + cpu.system) / 1e3);
|
|
2006
2108
|
return {
|
|
2007
2109
|
provider: provider.name,
|
|
2008
|
-
model
|
|
2110
|
+
// modelUsed reflects whichever model actually answered — the
|
|
2111
|
+
// primary, or a fallback if one was needed. Equivalent to the old
|
|
2112
|
+
// `provider.model` whenever no fallback occurred.
|
|
2113
|
+
model: modelUsed,
|
|
2009
2114
|
response: r.text,
|
|
2010
2115
|
attempts: r.attempts,
|
|
2011
2116
|
usage: r.usage,
|
|
@@ -2715,31 +2820,6 @@ function numberOrNull(v) {
|
|
|
2715
2820
|
// src/tools/audit.ts
|
|
2716
2821
|
import { performance as performance2 } from "perf_hooks";
|
|
2717
2822
|
|
|
2718
|
-
// src/core/retarget.ts
|
|
2719
|
-
function retargetProvider(p, newModel) {
|
|
2720
|
-
if (p.model === newModel) return p;
|
|
2721
|
-
return {
|
|
2722
|
-
name: p.name,
|
|
2723
|
-
model: newModel,
|
|
2724
|
-
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
2725
|
-
};
|
|
2726
|
-
}
|
|
2727
|
-
async function loadProviderWeights(storage, names) {
|
|
2728
|
-
const out = {};
|
|
2729
|
-
for (const raw of names) {
|
|
2730
|
-
const name = raw.toLowerCase();
|
|
2731
|
-
if (name in out) continue;
|
|
2732
|
-
const row = await storage.getProviderStats(name);
|
|
2733
|
-
if (!row) {
|
|
2734
|
-
out[name] = 0.5;
|
|
2735
|
-
continue;
|
|
2736
|
-
}
|
|
2737
|
-
const total = row.wins + row.losses + row.abstains;
|
|
2738
|
-
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
2739
|
-
}
|
|
2740
|
-
return out;
|
|
2741
|
-
}
|
|
2742
|
-
|
|
2743
2823
|
// src/core/co-reason.ts
|
|
2744
2824
|
var CO_REASON_MODEL = "gpt-5.6";
|
|
2745
2825
|
var CO_REASON_PROVIDER = "openai";
|
|
@@ -11490,7 +11570,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
|
|
|
11490
11570
|
var DEFAULT_PACKAGE = "crosscheck-cli";
|
|
11491
11571
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
11492
11572
|
function engineVersion() {
|
|
11493
|
-
return true ? "0.2.
|
|
11573
|
+
return true ? "0.2.16" : "0.0.0-dev";
|
|
11494
11574
|
}
|
|
11495
11575
|
function defaultUpdateCachePath() {
|
|
11496
11576
|
const base = process.env["CROSSCHECK_DATA_DIR"] || path9.join(os.homedir() || os.tmpdir(), ".crosscheck");
|