crosscheck-mcp 0.2.14 → 0.2.15
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 +150 -78
- 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 +147 -75
- package/dist/node-stdio.js.map +1 -1
- package/package.json +1 -1
package/dist/node-stdio.cjs
CHANGED
|
@@ -1242,11 +1242,18 @@ var ProviderError = class extends Error {
|
|
|
1242
1242
|
status;
|
|
1243
1243
|
transient;
|
|
1244
1244
|
retryAfterS;
|
|
1245
|
+
/** True specifically for "the key is fine but this model isn't
|
|
1246
|
+
* available to this account/project" (see http-errors.ts's
|
|
1247
|
+
* isModelAccessFailure) — the one failure a model fallback chain can
|
|
1248
|
+
* actually fix. Distinct from `transient`: this is never worth
|
|
1249
|
+
* retrying the SAME model, but IS worth trying a different one. */
|
|
1250
|
+
modelAccessFailure;
|
|
1245
1251
|
constructor(kind, message, opts) {
|
|
1246
1252
|
super(message);
|
|
1247
1253
|
this.kind = kind;
|
|
1248
1254
|
this.status = opts?.status;
|
|
1249
1255
|
this.transient = opts?.transient ?? defaultTransient(kind);
|
|
1256
|
+
this.modelAccessFailure = opts?.modelAccessFailure ?? false;
|
|
1250
1257
|
if (opts?.retryAfterS !== void 0) {
|
|
1251
1258
|
this.retryAfterS = opts.retryAfterS;
|
|
1252
1259
|
}
|
|
@@ -1278,7 +1285,7 @@ function httpFailureToProviderError(provider, status, bodyText, retryAfterS, mod
|
|
|
1278
1285
|
return new ProviderError(
|
|
1279
1286
|
"client",
|
|
1280
1287
|
`${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}`,
|
|
1281
|
-
{ status }
|
|
1288
|
+
{ status, modelAccessFailure: true }
|
|
1282
1289
|
);
|
|
1283
1290
|
}
|
|
1284
1291
|
if (isCreditsFailure(status, bodyText)) {
|
|
@@ -1494,6 +1501,14 @@ function buildAnthropicRequest(opts) {
|
|
|
1494
1501
|
if (system !== void 0) {
|
|
1495
1502
|
body.system = system;
|
|
1496
1503
|
}
|
|
1504
|
+
if (isReasoningModel("anthropic", opts.model)) {
|
|
1505
|
+
if (opts.model.toLowerCase().startsWith("claude-fable-5")) {
|
|
1506
|
+
body.thinking = { type: "adaptive" };
|
|
1507
|
+
body.output_config = { effort: "low" };
|
|
1508
|
+
} else {
|
|
1509
|
+
body.thinking = { type: "disabled" };
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1497
1512
|
if (opts.jsonSchema) {
|
|
1498
1513
|
body.tools = [{
|
|
1499
1514
|
name: ANTHROPIC_STRUCTURED_TOOL_NAME,
|
|
@@ -1561,7 +1576,12 @@ function parseAnthropicResponse(opts) {
|
|
|
1561
1576
|
purpose: opts.purpose
|
|
1562
1577
|
};
|
|
1563
1578
|
usage.total_tokens = usage.prompt_tokens + usage.completion_tokens;
|
|
1564
|
-
|
|
1579
|
+
const stopReasonRaw = r["stop_reason"];
|
|
1580
|
+
const stopReason = typeof stopReasonRaw === "string" ? stopReasonRaw : void 0;
|
|
1581
|
+
return { text, usage, ...stopReason !== void 0 ? { stopReason } : {} };
|
|
1582
|
+
}
|
|
1583
|
+
function isEmptyDueToMaxTokens(text, stopReason) {
|
|
1584
|
+
return text.trim() === "" && stopReason === "max_tokens";
|
|
1565
1585
|
}
|
|
1566
1586
|
function applyPricing(usage, pricing) {
|
|
1567
1587
|
const { cost_usd, estimated } = calculateCost(
|
|
@@ -1579,49 +1599,64 @@ function applyPricing(usage, pricing) {
|
|
|
1579
1599
|
estimated: usage.estimated || estimated
|
|
1580
1600
|
};
|
|
1581
1601
|
}
|
|
1602
|
+
var MAX_TOKENS_ESCALATIONS = 2;
|
|
1603
|
+
var ESCALATION_MULTIPLIER = 2;
|
|
1582
1604
|
async function sendAnthropic(args) {
|
|
1583
|
-
const { url, headers, body } = buildAnthropicRequest({
|
|
1584
|
-
model: args.model,
|
|
1585
|
-
apiKey: args.apiKey,
|
|
1586
|
-
messages: args.messages,
|
|
1587
|
-
maxTokens: args.maxTokens,
|
|
1588
|
-
temperature: args.temperature,
|
|
1589
|
-
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
1590
|
-
});
|
|
1591
1605
|
const doFetch = args.fetchImpl ?? globalThis.fetch;
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1606
|
+
let maxTokens = args.maxTokens;
|
|
1607
|
+
let result = null;
|
|
1608
|
+
let totalAttempts = 0;
|
|
1609
|
+
for (let escalation = 0; escalation <= MAX_TOKENS_ESCALATIONS; escalation += 1) {
|
|
1610
|
+
const { url, headers, body } = buildAnthropicRequest({
|
|
1611
|
+
model: args.model,
|
|
1612
|
+
apiKey: args.apiKey,
|
|
1613
|
+
messages: args.messages,
|
|
1614
|
+
maxTokens,
|
|
1615
|
+
temperature: args.temperature,
|
|
1616
|
+
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
1617
|
+
});
|
|
1618
|
+
const init = {
|
|
1619
|
+
method: "POST",
|
|
1620
|
+
headers,
|
|
1621
|
+
body: JSON.stringify(body)
|
|
1622
|
+
};
|
|
1623
|
+
if (args.signal) init.signal = args.signal;
|
|
1624
|
+
let stopReason;
|
|
1625
|
+
const attemptOnce = async () => {
|
|
1626
|
+
let respLike;
|
|
1608
1627
|
try {
|
|
1609
|
-
|
|
1628
|
+
respLike = await doFetch(url, init);
|
|
1610
1629
|
} catch (e) {
|
|
1611
|
-
throw new ProviderError("
|
|
1630
|
+
throw new ProviderError("network", `anthropic: fetch failed: ${e.message}`);
|
|
1612
1631
|
}
|
|
1613
|
-
const
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1632
|
+
const status = respLike.status;
|
|
1633
|
+
if (status >= 200 && status < 300) {
|
|
1634
|
+
let parsed;
|
|
1635
|
+
try {
|
|
1636
|
+
parsed = await respLike.json();
|
|
1637
|
+
} catch (e) {
|
|
1638
|
+
throw new ProviderError("parse", `anthropic: response body not JSON: ${e.message}`);
|
|
1639
|
+
}
|
|
1640
|
+
const { text, usage, stopReason: sr } = parseAnthropicResponse({
|
|
1641
|
+
resp: parsed,
|
|
1642
|
+
model: args.model,
|
|
1643
|
+
purpose: args.purpose ?? "worker"
|
|
1644
|
+
});
|
|
1645
|
+
stopReason = sr;
|
|
1646
|
+
return { text, attempts: 1, usage: applyPricing(usage, args.pricing) };
|
|
1647
|
+
}
|
|
1648
|
+
const bodyText = await respLike.text().catch(() => "");
|
|
1649
|
+
throw httpFailureToProviderError("anthropic", status, bodyText, parseRetryAfter(respLike), args.model);
|
|
1650
|
+
};
|
|
1651
|
+
await acquireRateLimit("anthropic", args.signal ? { signal: args.signal } : void 0);
|
|
1652
|
+
result = await sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1653
|
+
totalAttempts += result.attempts;
|
|
1654
|
+
if (escalation >= MAX_TOKENS_ESCALATIONS || !isEmptyDueToMaxTokens(result.text, stopReason)) {
|
|
1655
|
+
break;
|
|
1619
1656
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
};
|
|
1623
|
-
await acquireRateLimit("anthropic", args.signal ? { signal: args.signal } : void 0);
|
|
1624
|
-
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1657
|
+
maxTokens *= ESCALATION_MULTIPLIER;
|
|
1658
|
+
}
|
|
1659
|
+
return { ...result, attempts: totalAttempts };
|
|
1625
1660
|
}
|
|
1626
1661
|
|
|
1627
1662
|
// src/providers/gemini.ts
|
|
@@ -2014,12 +2049,20 @@ function detectStalePins(env) {
|
|
|
2014
2049
|
}
|
|
2015
2050
|
return out;
|
|
2016
2051
|
}
|
|
2052
|
+
function parseFallbackModels(raw) {
|
|
2053
|
+
if (!raw) return [];
|
|
2054
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2055
|
+
}
|
|
2056
|
+
function fallbackModelsFor(env, provider) {
|
|
2057
|
+
const envVar = MODEL_ENV_VARS[provider];
|
|
2058
|
+
return envVar ? parseFallbackModels(env[`${envVar}_FALLBACKS`]) : [];
|
|
2059
|
+
}
|
|
2017
2060
|
function buildProviders(opts) {
|
|
2018
2061
|
const out = {};
|
|
2019
2062
|
const anthropicKey = opts.env["ANTHROPIC_API_KEY"];
|
|
2020
2063
|
if (anthropicKey) {
|
|
2021
2064
|
const model = opts.env["ANTHROPIC_MODEL"] ?? DEFAULT_MODELS.anthropic;
|
|
2022
|
-
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts);
|
|
2065
|
+
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts, fallbackModelsFor(opts.env, "anthropic"));
|
|
2023
2066
|
}
|
|
2024
2067
|
const openAiCompatSpec = [
|
|
2025
2068
|
{ name: "openai", keyEnv: "OPENAI_API_KEY", modelEnv: "OPENAI_MODEL", defaultModel: DEFAULT_MODELS["openai"] },
|
|
@@ -2034,19 +2077,20 @@ function buildProviders(opts) {
|
|
|
2034
2077
|
const apiKey = opts.env[s.keyEnv];
|
|
2035
2078
|
if (!apiKey) continue;
|
|
2036
2079
|
const model = opts.env[s.modelEnv] ?? s.defaultModel;
|
|
2037
|
-
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts);
|
|
2080
|
+
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts, fallbackModelsFor(opts.env, s.name));
|
|
2038
2081
|
}
|
|
2039
2082
|
const geminiKey = opts.env["GEMINI_API_KEY"];
|
|
2040
2083
|
if (geminiKey) {
|
|
2041
2084
|
const model = opts.env["GEMINI_MODEL"] ?? DEFAULT_MODELS.gemini;
|
|
2042
|
-
out["gemini"] = makeGeminiProvider(model, geminiKey, opts);
|
|
2085
|
+
out["gemini"] = makeGeminiProvider(model, geminiKey, opts, fallbackModelsFor(opts.env, "gemini"));
|
|
2043
2086
|
}
|
|
2044
2087
|
return out;
|
|
2045
2088
|
}
|
|
2046
|
-
function makeAnthropicProvider(model, apiKey, opts) {
|
|
2089
|
+
function makeAnthropicProvider(model, apiKey, opts, fallbackModels = []) {
|
|
2047
2090
|
return {
|
|
2048
2091
|
name: "anthropic",
|
|
2049
2092
|
model,
|
|
2093
|
+
fallbackModels,
|
|
2050
2094
|
send: async (args) => {
|
|
2051
2095
|
const sendOpts = {
|
|
2052
2096
|
...args,
|
|
@@ -2059,11 +2103,12 @@ function makeAnthropicProvider(model, apiKey, opts) {
|
|
|
2059
2103
|
}
|
|
2060
2104
|
};
|
|
2061
2105
|
}
|
|
2062
|
-
function makeOpenAICompatibleProvider(name, model, apiKey, opts) {
|
|
2106
|
+
function makeOpenAICompatibleProvider(name, model, apiKey, opts, fallbackModels = []) {
|
|
2063
2107
|
const url = OPENAI_COMPAT_DEFAULT_URLS[name];
|
|
2064
2108
|
return {
|
|
2065
2109
|
name,
|
|
2066
2110
|
model,
|
|
2111
|
+
fallbackModels,
|
|
2067
2112
|
send: async (args) => {
|
|
2068
2113
|
const sendOpts = {
|
|
2069
2114
|
...args,
|
|
@@ -2078,10 +2123,11 @@ function makeOpenAICompatibleProvider(name, model, apiKey, opts) {
|
|
|
2078
2123
|
}
|
|
2079
2124
|
};
|
|
2080
2125
|
}
|
|
2081
|
-
function makeGeminiProvider(model, apiKey, opts) {
|
|
2126
|
+
function makeGeminiProvider(model, apiKey, opts, fallbackModels = []) {
|
|
2082
2127
|
return {
|
|
2083
2128
|
name: "gemini",
|
|
2084
2129
|
model,
|
|
2130
|
+
fallbackModels,
|
|
2085
2131
|
send: async (args) => {
|
|
2086
2132
|
const sendOpts = {
|
|
2087
2133
|
...args,
|
|
@@ -2098,7 +2144,7 @@ function makeGeminiProvider(model, apiKey, opts) {
|
|
|
2098
2144
|
// src/server.ts
|
|
2099
2145
|
init_cjs_shims();
|
|
2100
2146
|
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
2101
|
-
var
|
|
2147
|
+
var import_types9 = require("@modelcontextprotocol/sdk/types.js");
|
|
2102
2148
|
|
|
2103
2149
|
// src/bridge/index.ts
|
|
2104
2150
|
init_cjs_shims();
|
|
@@ -2229,7 +2275,7 @@ var import_zod = require("zod");
|
|
|
2229
2275
|
// src/server-meta.ts
|
|
2230
2276
|
init_cjs_shims();
|
|
2231
2277
|
var SERVER_NAME = "crosscheck-agent";
|
|
2232
|
-
var SERVER_VERSION = true ? "0.2.
|
|
2278
|
+
var SERVER_VERSION = true ? "0.2.15" : "0.0.0-dev";
|
|
2233
2279
|
|
|
2234
2280
|
// src/tools/audit.ts
|
|
2235
2281
|
init_cjs_shims();
|
|
@@ -2891,6 +2937,55 @@ function operatorCeiling(purpose, provider) {
|
|
|
2891
2937
|
return null;
|
|
2892
2938
|
}
|
|
2893
2939
|
|
|
2940
|
+
// src/core/model-fallback.ts
|
|
2941
|
+
init_cjs_shims();
|
|
2942
|
+
|
|
2943
|
+
// src/core/retarget.ts
|
|
2944
|
+
init_cjs_shims();
|
|
2945
|
+
function retargetProvider(p, newModel) {
|
|
2946
|
+
if (p.model === newModel) return p;
|
|
2947
|
+
return {
|
|
2948
|
+
name: p.name,
|
|
2949
|
+
model: newModel,
|
|
2950
|
+
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
2951
|
+
};
|
|
2952
|
+
}
|
|
2953
|
+
async function loadProviderWeights(storage, names) {
|
|
2954
|
+
const out = {};
|
|
2955
|
+
for (const raw of names) {
|
|
2956
|
+
const name = raw.toLowerCase();
|
|
2957
|
+
if (name in out) continue;
|
|
2958
|
+
const row = await storage.getProviderStats(name);
|
|
2959
|
+
if (!row) {
|
|
2960
|
+
out[name] = 0.5;
|
|
2961
|
+
continue;
|
|
2962
|
+
}
|
|
2963
|
+
const total = row.wins + row.losses + row.abstains;
|
|
2964
|
+
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
2965
|
+
}
|
|
2966
|
+
return out;
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2969
|
+
// src/core/model-fallback.ts
|
|
2970
|
+
async function sendWithModelFallback(provider, args) {
|
|
2971
|
+
const chain = [provider.model, ...provider.fallbackModels ?? []];
|
|
2972
|
+
let lastErr;
|
|
2973
|
+
for (let i = 0; i < chain.length; i += 1) {
|
|
2974
|
+
const candidate = chain[i];
|
|
2975
|
+
const p = i === 0 ? provider : retargetProvider(provider, candidate);
|
|
2976
|
+
try {
|
|
2977
|
+
const result = await p.send(args);
|
|
2978
|
+
return { result, modelUsed: candidate };
|
|
2979
|
+
} catch (e) {
|
|
2980
|
+
lastErr = e;
|
|
2981
|
+
const isModelAccessFailure2 = e instanceof ProviderError && e.modelAccessFailure;
|
|
2982
|
+
const hasMoreCandidates = i < chain.length - 1;
|
|
2983
|
+
if (!isModelAccessFailure2 || !hasMoreCandidates) throw e;
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
throw lastErr;
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2894
2989
|
// src/core/structured.ts
|
|
2895
2990
|
async function requestStructured(provider, baseMessages, schema, opts) {
|
|
2896
2991
|
const maxRetries = opts.maxRetries ?? 1;
|
|
@@ -2969,7 +3064,7 @@ async function askOne(provider, messages, opts) {
|
|
|
2969
3064
|
const ceiling = operatorCeiling(opts.purpose, provider.name);
|
|
2970
3065
|
if (ceiling !== null && ceiling < maxTokens) maxTokens = ceiling;
|
|
2971
3066
|
try {
|
|
2972
|
-
const r = await provider
|
|
3067
|
+
const { result: r, modelUsed } = await sendWithModelFallback(provider, {
|
|
2973
3068
|
messages,
|
|
2974
3069
|
maxTokens,
|
|
2975
3070
|
temperature: opts.temperature,
|
|
@@ -2982,7 +3077,10 @@ async function askOne(provider, messages, opts) {
|
|
|
2982
3077
|
const cpuMs = Math.trunc((cpu.user + cpu.system) / 1e3);
|
|
2983
3078
|
return {
|
|
2984
3079
|
provider: provider.name,
|
|
2985
|
-
model
|
|
3080
|
+
// modelUsed reflects whichever model actually answered — the
|
|
3081
|
+
// primary, or a fallback if one was needed. Equivalent to the old
|
|
3082
|
+
// `provider.model` whenever no fallback occurred.
|
|
3083
|
+
model: modelUsed,
|
|
2986
3084
|
response: r.text,
|
|
2987
3085
|
attempts: r.attempts,
|
|
2988
3086
|
usage: r.usage,
|
|
@@ -3703,32 +3801,6 @@ function numberOrNull(v) {
|
|
|
3703
3801
|
// src/tools/audit.ts
|
|
3704
3802
|
var import_node_perf_hooks = require("perf_hooks");
|
|
3705
3803
|
|
|
3706
|
-
// src/core/retarget.ts
|
|
3707
|
-
init_cjs_shims();
|
|
3708
|
-
function retargetProvider(p, newModel) {
|
|
3709
|
-
if (p.model === newModel) return p;
|
|
3710
|
-
return {
|
|
3711
|
-
name: p.name,
|
|
3712
|
-
model: newModel,
|
|
3713
|
-
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
3714
|
-
};
|
|
3715
|
-
}
|
|
3716
|
-
async function loadProviderWeights(storage, names) {
|
|
3717
|
-
const out = {};
|
|
3718
|
-
for (const raw of names) {
|
|
3719
|
-
const name = raw.toLowerCase();
|
|
3720
|
-
if (name in out) continue;
|
|
3721
|
-
const row = await storage.getProviderStats(name);
|
|
3722
|
-
if (!row) {
|
|
3723
|
-
out[name] = 0.5;
|
|
3724
|
-
continue;
|
|
3725
|
-
}
|
|
3726
|
-
const total = row.wins + row.losses + row.abstains;
|
|
3727
|
-
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
3728
|
-
}
|
|
3729
|
-
return out;
|
|
3730
|
-
}
|
|
3731
|
-
|
|
3732
3804
|
// src/core/co-reason.ts
|
|
3733
3805
|
init_cjs_shims();
|
|
3734
3806
|
var CO_REASON_MODEL = "gpt-5.6";
|
|
@@ -12523,7 +12595,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
|
|
|
12523
12595
|
var DEFAULT_PACKAGE = "crosscheck-cli";
|
|
12524
12596
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
12525
12597
|
function engineVersion() {
|
|
12526
|
-
return true ? "0.2.
|
|
12598
|
+
return true ? "0.2.15" : "0.0.0-dev";
|
|
12527
12599
|
}
|
|
12528
12600
|
function defaultUpdateCachePath() {
|
|
12529
12601
|
const base = process.env["CROSSCHECK_DATA_DIR"] || import_node_path13.default.join(import_node_os2.default.homedir() || import_node_os2.default.tmpdir(), ".crosscheck");
|
|
@@ -14457,14 +14529,14 @@ function createServer(opts = {}) {
|
|
|
14457
14529
|
});
|
|
14458
14530
|
};
|
|
14459
14531
|
const tools = buildToolRegistry(opts);
|
|
14460
|
-
server.setRequestHandler(
|
|
14532
|
+
server.setRequestHandler(import_types9.ListToolsRequestSchema, async () => ({
|
|
14461
14533
|
tools: Array.from(tools.values()).map((t) => ({
|
|
14462
14534
|
name: t.name,
|
|
14463
14535
|
description: t.description,
|
|
14464
14536
|
inputSchema: t.inputSchema
|
|
14465
14537
|
}))
|
|
14466
14538
|
}));
|
|
14467
|
-
server.setRequestHandler(
|
|
14539
|
+
server.setRequestHandler(import_types9.CallToolRequestSchema, async (req) => {
|
|
14468
14540
|
const name = req.params.name;
|
|
14469
14541
|
const tool = tools.get(name);
|
|
14470
14542
|
if (!tool) {
|