crosscheck-mcp 0.2.13 → 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 +173 -77
- package/dist/browser-ext.cjs.map +1 -1
- package/dist/browser-ext.js +173 -77
- package/dist/browser-ext.js.map +1 -1
- package/dist/node-stdio.cjs +169 -81
- 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 +166 -78
- package/dist/node-stdio.js.map +1 -1
- package/package.json +1 -1
package/dist/node-stdio.d.cts
CHANGED
|
@@ -436,6 +436,15 @@ interface Provider {
|
|
|
436
436
|
readonly name: string;
|
|
437
437
|
/** Default model for this provider (env-resolved at factory time). */
|
|
438
438
|
readonly model: string;
|
|
439
|
+
/** Ordered fallback models — "greenlight" candidates to try, in order,
|
|
440
|
+
* when `model` comes back with a model-access failure (the account/
|
|
441
|
+
* project doesn't have it enabled) rather than any other kind of
|
|
442
|
+
* error. Env-resolved at factory time from `<PROVIDER>_MODEL_
|
|
443
|
+
* FALLBACKS`, or set from a user's/org's configured chain. Empty for
|
|
444
|
+
* most providers — a bad key or rate limit isn't fixed by switching
|
|
445
|
+
* models, so this is only ever consulted for that one failure mode
|
|
446
|
+
* (see core/model-fallback.ts). */
|
|
447
|
+
readonly fallbackModels?: readonly string[];
|
|
439
448
|
/** Make a request and return the parsed result. Throws `ProviderError`
|
|
440
449
|
* on classified failures (auth, rate_limit, timeout, server, parse,
|
|
441
450
|
* network, client). */
|
package/dist/node-stdio.d.ts
CHANGED
|
@@ -436,6 +436,15 @@ interface Provider {
|
|
|
436
436
|
readonly name: string;
|
|
437
437
|
/** Default model for this provider (env-resolved at factory time). */
|
|
438
438
|
readonly model: string;
|
|
439
|
+
/** Ordered fallback models — "greenlight" candidates to try, in order,
|
|
440
|
+
* when `model` comes back with a model-access failure (the account/
|
|
441
|
+
* project doesn't have it enabled) rather than any other kind of
|
|
442
|
+
* error. Env-resolved at factory time from `<PROVIDER>_MODEL_
|
|
443
|
+
* FALLBACKS`, or set from a user's/org's configured chain. Empty for
|
|
444
|
+
* most providers — a bad key or rate limit isn't fixed by switching
|
|
445
|
+
* models, so this is only ever consulted for that one failure mode
|
|
446
|
+
* (see core/model-fallback.ts). */
|
|
447
|
+
readonly fallbackModels?: readonly string[];
|
|
439
448
|
/** Make a request and return the parsed result. Throws `ProviderError`
|
|
440
449
|
* on classified failures (auth, rate_limit, timeout, server, parse,
|
|
441
450
|
* network, client). */
|
package/dist/node-stdio.js
CHANGED
|
@@ -1218,11 +1218,18 @@ var ProviderError = class extends Error {
|
|
|
1218
1218
|
status;
|
|
1219
1219
|
transient;
|
|
1220
1220
|
retryAfterS;
|
|
1221
|
+
/** True specifically for "the key is fine but this model isn't
|
|
1222
|
+
* available to this account/project" (see http-errors.ts's
|
|
1223
|
+
* isModelAccessFailure) — the one failure a model fallback chain can
|
|
1224
|
+
* actually fix. Distinct from `transient`: this is never worth
|
|
1225
|
+
* retrying the SAME model, but IS worth trying a different one. */
|
|
1226
|
+
modelAccessFailure;
|
|
1221
1227
|
constructor(kind, message, opts) {
|
|
1222
1228
|
super(message);
|
|
1223
1229
|
this.kind = kind;
|
|
1224
1230
|
this.status = opts?.status;
|
|
1225
1231
|
this.transient = opts?.transient ?? defaultTransient(kind);
|
|
1232
|
+
this.modelAccessFailure = opts?.modelAccessFailure ?? false;
|
|
1226
1233
|
if (opts?.retryAfterS !== void 0) {
|
|
1227
1234
|
this.retryAfterS = opts.retryAfterS;
|
|
1228
1235
|
}
|
|
@@ -1242,8 +1249,21 @@ function isCreditsFailure(status, bodyText) {
|
|
|
1242
1249
|
}
|
|
1243
1250
|
return false;
|
|
1244
1251
|
}
|
|
1245
|
-
|
|
1252
|
+
var MODEL_ACCESS_RE = /model_not_found|does not have access to model|does not exist|model[\s_]not[\s_]found|invalid model|unknown model|unsupported model/i;
|
|
1253
|
+
function isModelAccessFailure(status, bodyText) {
|
|
1254
|
+
if (status < 400 || status >= 500) return false;
|
|
1255
|
+
return MODEL_ACCESS_RE.test(bodyText);
|
|
1256
|
+
}
|
|
1257
|
+
function httpFailureToProviderError(provider, status, bodyText, retryAfterS, model) {
|
|
1246
1258
|
const detail = bodyText.slice(0, 512);
|
|
1259
|
+
if (isModelAccessFailure(status, bodyText)) {
|
|
1260
|
+
const modelRef = model ? `"${model}"` : "the configured model";
|
|
1261
|
+
return new ProviderError(
|
|
1262
|
+
"client",
|
|
1263
|
+
`${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}`,
|
|
1264
|
+
{ status, modelAccessFailure: true }
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1247
1267
|
if (isCreditsFailure(status, bodyText)) {
|
|
1248
1268
|
return new ProviderError(
|
|
1249
1269
|
"auth",
|
|
@@ -1457,6 +1477,14 @@ function buildAnthropicRequest(opts) {
|
|
|
1457
1477
|
if (system !== void 0) {
|
|
1458
1478
|
body.system = system;
|
|
1459
1479
|
}
|
|
1480
|
+
if (isReasoningModel("anthropic", opts.model)) {
|
|
1481
|
+
if (opts.model.toLowerCase().startsWith("claude-fable-5")) {
|
|
1482
|
+
body.thinking = { type: "adaptive" };
|
|
1483
|
+
body.output_config = { effort: "low" };
|
|
1484
|
+
} else {
|
|
1485
|
+
body.thinking = { type: "disabled" };
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1460
1488
|
if (opts.jsonSchema) {
|
|
1461
1489
|
body.tools = [{
|
|
1462
1490
|
name: ANTHROPIC_STRUCTURED_TOOL_NAME,
|
|
@@ -1524,7 +1552,12 @@ function parseAnthropicResponse(opts) {
|
|
|
1524
1552
|
purpose: opts.purpose
|
|
1525
1553
|
};
|
|
1526
1554
|
usage.total_tokens = usage.prompt_tokens + usage.completion_tokens;
|
|
1527
|
-
|
|
1555
|
+
const stopReasonRaw = r["stop_reason"];
|
|
1556
|
+
const stopReason = typeof stopReasonRaw === "string" ? stopReasonRaw : void 0;
|
|
1557
|
+
return { text, usage, ...stopReason !== void 0 ? { stopReason } : {} };
|
|
1558
|
+
}
|
|
1559
|
+
function isEmptyDueToMaxTokens(text, stopReason) {
|
|
1560
|
+
return text.trim() === "" && stopReason === "max_tokens";
|
|
1528
1561
|
}
|
|
1529
1562
|
function applyPricing(usage, pricing) {
|
|
1530
1563
|
const { cost_usd, estimated } = calculateCost(
|
|
@@ -1542,49 +1575,64 @@ function applyPricing(usage, pricing) {
|
|
|
1542
1575
|
estimated: usage.estimated || estimated
|
|
1543
1576
|
};
|
|
1544
1577
|
}
|
|
1578
|
+
var MAX_TOKENS_ESCALATIONS = 2;
|
|
1579
|
+
var ESCALATION_MULTIPLIER = 2;
|
|
1545
1580
|
async function sendAnthropic(args) {
|
|
1546
|
-
const { url, headers, body } = buildAnthropicRequest({
|
|
1547
|
-
model: args.model,
|
|
1548
|
-
apiKey: args.apiKey,
|
|
1549
|
-
messages: args.messages,
|
|
1550
|
-
maxTokens: args.maxTokens,
|
|
1551
|
-
temperature: args.temperature,
|
|
1552
|
-
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
1553
|
-
});
|
|
1554
1581
|
const doFetch = args.fetchImpl ?? globalThis.fetch;
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1582
|
+
let maxTokens = args.maxTokens;
|
|
1583
|
+
let result = null;
|
|
1584
|
+
let totalAttempts = 0;
|
|
1585
|
+
for (let escalation = 0; escalation <= MAX_TOKENS_ESCALATIONS; escalation += 1) {
|
|
1586
|
+
const { url, headers, body } = buildAnthropicRequest({
|
|
1587
|
+
model: args.model,
|
|
1588
|
+
apiKey: args.apiKey,
|
|
1589
|
+
messages: args.messages,
|
|
1590
|
+
maxTokens,
|
|
1591
|
+
temperature: args.temperature,
|
|
1592
|
+
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
1593
|
+
});
|
|
1594
|
+
const init = {
|
|
1595
|
+
method: "POST",
|
|
1596
|
+
headers,
|
|
1597
|
+
body: JSON.stringify(body)
|
|
1598
|
+
};
|
|
1599
|
+
if (args.signal) init.signal = args.signal;
|
|
1600
|
+
let stopReason;
|
|
1601
|
+
const attemptOnce = async () => {
|
|
1602
|
+
let respLike;
|
|
1571
1603
|
try {
|
|
1572
|
-
|
|
1604
|
+
respLike = await doFetch(url, init);
|
|
1573
1605
|
} catch (e) {
|
|
1574
|
-
throw new ProviderError("
|
|
1606
|
+
throw new ProviderError("network", `anthropic: fetch failed: ${e.message}`);
|
|
1575
1607
|
}
|
|
1576
|
-
const
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1608
|
+
const status = respLike.status;
|
|
1609
|
+
if (status >= 200 && status < 300) {
|
|
1610
|
+
let parsed;
|
|
1611
|
+
try {
|
|
1612
|
+
parsed = await respLike.json();
|
|
1613
|
+
} catch (e) {
|
|
1614
|
+
throw new ProviderError("parse", `anthropic: response body not JSON: ${e.message}`);
|
|
1615
|
+
}
|
|
1616
|
+
const { text, usage, stopReason: sr } = parseAnthropicResponse({
|
|
1617
|
+
resp: parsed,
|
|
1618
|
+
model: args.model,
|
|
1619
|
+
purpose: args.purpose ?? "worker"
|
|
1620
|
+
});
|
|
1621
|
+
stopReason = sr;
|
|
1622
|
+
return { text, attempts: 1, usage: applyPricing(usage, args.pricing) };
|
|
1623
|
+
}
|
|
1624
|
+
const bodyText = await respLike.text().catch(() => "");
|
|
1625
|
+
throw httpFailureToProviderError("anthropic", status, bodyText, parseRetryAfter(respLike), args.model);
|
|
1626
|
+
};
|
|
1627
|
+
await acquireRateLimit("anthropic", args.signal ? { signal: args.signal } : void 0);
|
|
1628
|
+
result = await sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1629
|
+
totalAttempts += result.attempts;
|
|
1630
|
+
if (escalation >= MAX_TOKENS_ESCALATIONS || !isEmptyDueToMaxTokens(result.text, stopReason)) {
|
|
1631
|
+
break;
|
|
1582
1632
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
};
|
|
1586
|
-
await acquireRateLimit("anthropic", args.signal ? { signal: args.signal } : void 0);
|
|
1587
|
-
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1633
|
+
maxTokens *= ESCALATION_MULTIPLIER;
|
|
1634
|
+
}
|
|
1635
|
+
return { ...result, attempts: totalAttempts };
|
|
1588
1636
|
}
|
|
1589
1637
|
|
|
1590
1638
|
// src/providers/gemini.ts
|
|
@@ -1760,7 +1808,7 @@ async function sendGemini(args) {
|
|
|
1760
1808
|
return { text, attempts: 1, usage: applyPricing2(usage, args.pricing) };
|
|
1761
1809
|
}
|
|
1762
1810
|
const bodyText = await respLike.text().catch(() => "");
|
|
1763
|
-
throw httpFailureToProviderError("gemini", status, bodyText, parseRetryAfter(respLike));
|
|
1811
|
+
throw httpFailureToProviderError("gemini", status, bodyText, parseRetryAfter(respLike), args.model);
|
|
1764
1812
|
};
|
|
1765
1813
|
await acquireRateLimit("gemini", args.signal ? { signal: args.signal } : void 0);
|
|
1766
1814
|
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
@@ -1934,7 +1982,7 @@ async function sendOpenAICompatible(args) {
|
|
|
1934
1982
|
return { text, attempts: 1, usage: applyPricing3(usage, args.pricing) };
|
|
1935
1983
|
}
|
|
1936
1984
|
const bodyText = await respLike.text().catch(() => "");
|
|
1937
|
-
throw httpFailureToProviderError(args.provider, status, bodyText, parseRetryAfter(respLike));
|
|
1985
|
+
throw httpFailureToProviderError(args.provider, status, bodyText, parseRetryAfter(respLike), args.model);
|
|
1938
1986
|
};
|
|
1939
1987
|
await acquireRateLimit(args.provider, args.signal ? { signal: args.signal } : void 0);
|
|
1940
1988
|
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
@@ -1943,7 +1991,10 @@ async function sendOpenAICompatible(args) {
|
|
|
1943
1991
|
// src/providers/registry.ts
|
|
1944
1992
|
var DEFAULT_MODELS = {
|
|
1945
1993
|
anthropic: "claude-opus-5",
|
|
1946
|
-
|
|
1994
|
+
// gpt-5.5 is not enabled on every OpenAI account/project (some are still
|
|
1995
|
+
// capped at gpt-5) — gpt-5 is the safer, more broadly available default
|
|
1996
|
+
// until a user/org explicitly opts into 5.5 via `crosscheck models set`.
|
|
1997
|
+
openai: "gpt-5",
|
|
1947
1998
|
xai: "grok-4-latest",
|
|
1948
1999
|
mistral: "mistral-large-latest",
|
|
1949
2000
|
groq: "llama-3.3-70b-versatile",
|
|
@@ -1974,12 +2025,20 @@ function detectStalePins(env) {
|
|
|
1974
2025
|
}
|
|
1975
2026
|
return out;
|
|
1976
2027
|
}
|
|
2028
|
+
function parseFallbackModels(raw) {
|
|
2029
|
+
if (!raw) return [];
|
|
2030
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2031
|
+
}
|
|
2032
|
+
function fallbackModelsFor(env, provider) {
|
|
2033
|
+
const envVar = MODEL_ENV_VARS[provider];
|
|
2034
|
+
return envVar ? parseFallbackModels(env[`${envVar}_FALLBACKS`]) : [];
|
|
2035
|
+
}
|
|
1977
2036
|
function buildProviders(opts) {
|
|
1978
2037
|
const out = {};
|
|
1979
2038
|
const anthropicKey = opts.env["ANTHROPIC_API_KEY"];
|
|
1980
2039
|
if (anthropicKey) {
|
|
1981
2040
|
const model = opts.env["ANTHROPIC_MODEL"] ?? DEFAULT_MODELS.anthropic;
|
|
1982
|
-
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts);
|
|
2041
|
+
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts, fallbackModelsFor(opts.env, "anthropic"));
|
|
1983
2042
|
}
|
|
1984
2043
|
const openAiCompatSpec = [
|
|
1985
2044
|
{ name: "openai", keyEnv: "OPENAI_API_KEY", modelEnv: "OPENAI_MODEL", defaultModel: DEFAULT_MODELS["openai"] },
|
|
@@ -1994,19 +2053,20 @@ function buildProviders(opts) {
|
|
|
1994
2053
|
const apiKey = opts.env[s.keyEnv];
|
|
1995
2054
|
if (!apiKey) continue;
|
|
1996
2055
|
const model = opts.env[s.modelEnv] ?? s.defaultModel;
|
|
1997
|
-
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts);
|
|
2056
|
+
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts, fallbackModelsFor(opts.env, s.name));
|
|
1998
2057
|
}
|
|
1999
2058
|
const geminiKey = opts.env["GEMINI_API_KEY"];
|
|
2000
2059
|
if (geminiKey) {
|
|
2001
2060
|
const model = opts.env["GEMINI_MODEL"] ?? DEFAULT_MODELS.gemini;
|
|
2002
|
-
out["gemini"] = makeGeminiProvider(model, geminiKey, opts);
|
|
2061
|
+
out["gemini"] = makeGeminiProvider(model, geminiKey, opts, fallbackModelsFor(opts.env, "gemini"));
|
|
2003
2062
|
}
|
|
2004
2063
|
return out;
|
|
2005
2064
|
}
|
|
2006
|
-
function makeAnthropicProvider(model, apiKey, opts) {
|
|
2065
|
+
function makeAnthropicProvider(model, apiKey, opts, fallbackModels = []) {
|
|
2007
2066
|
return {
|
|
2008
2067
|
name: "anthropic",
|
|
2009
2068
|
model,
|
|
2069
|
+
fallbackModels,
|
|
2010
2070
|
send: async (args) => {
|
|
2011
2071
|
const sendOpts = {
|
|
2012
2072
|
...args,
|
|
@@ -2019,11 +2079,12 @@ function makeAnthropicProvider(model, apiKey, opts) {
|
|
|
2019
2079
|
}
|
|
2020
2080
|
};
|
|
2021
2081
|
}
|
|
2022
|
-
function makeOpenAICompatibleProvider(name, model, apiKey, opts) {
|
|
2082
|
+
function makeOpenAICompatibleProvider(name, model, apiKey, opts, fallbackModels = []) {
|
|
2023
2083
|
const url = OPENAI_COMPAT_DEFAULT_URLS[name];
|
|
2024
2084
|
return {
|
|
2025
2085
|
name,
|
|
2026
2086
|
model,
|
|
2087
|
+
fallbackModels,
|
|
2027
2088
|
send: async (args) => {
|
|
2028
2089
|
const sendOpts = {
|
|
2029
2090
|
...args,
|
|
@@ -2038,10 +2099,11 @@ function makeOpenAICompatibleProvider(name, model, apiKey, opts) {
|
|
|
2038
2099
|
}
|
|
2039
2100
|
};
|
|
2040
2101
|
}
|
|
2041
|
-
function makeGeminiProvider(model, apiKey, opts) {
|
|
2102
|
+
function makeGeminiProvider(model, apiKey, opts, fallbackModels = []) {
|
|
2042
2103
|
return {
|
|
2043
2104
|
name: "gemini",
|
|
2044
2105
|
model,
|
|
2106
|
+
fallbackModels,
|
|
2045
2107
|
send: async (args) => {
|
|
2046
2108
|
const sendOpts = {
|
|
2047
2109
|
...args,
|
|
@@ -2192,7 +2254,7 @@ import { z } from "zod";
|
|
|
2192
2254
|
// src/server-meta.ts
|
|
2193
2255
|
init_esm_shims();
|
|
2194
2256
|
var SERVER_NAME = "crosscheck-agent";
|
|
2195
|
-
var SERVER_VERSION = true ? "0.2.
|
|
2257
|
+
var SERVER_VERSION = true ? "0.2.15" : "0.0.0-dev";
|
|
2196
2258
|
|
|
2197
2259
|
// src/tools/audit.ts
|
|
2198
2260
|
init_esm_shims();
|
|
@@ -2854,6 +2916,55 @@ function operatorCeiling(purpose, provider) {
|
|
|
2854
2916
|
return null;
|
|
2855
2917
|
}
|
|
2856
2918
|
|
|
2919
|
+
// src/core/model-fallback.ts
|
|
2920
|
+
init_esm_shims();
|
|
2921
|
+
|
|
2922
|
+
// src/core/retarget.ts
|
|
2923
|
+
init_esm_shims();
|
|
2924
|
+
function retargetProvider(p, newModel) {
|
|
2925
|
+
if (p.model === newModel) return p;
|
|
2926
|
+
return {
|
|
2927
|
+
name: p.name,
|
|
2928
|
+
model: newModel,
|
|
2929
|
+
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2932
|
+
async function loadProviderWeights(storage, names) {
|
|
2933
|
+
const out = {};
|
|
2934
|
+
for (const raw of names) {
|
|
2935
|
+
const name = raw.toLowerCase();
|
|
2936
|
+
if (name in out) continue;
|
|
2937
|
+
const row = await storage.getProviderStats(name);
|
|
2938
|
+
if (!row) {
|
|
2939
|
+
out[name] = 0.5;
|
|
2940
|
+
continue;
|
|
2941
|
+
}
|
|
2942
|
+
const total = row.wins + row.losses + row.abstains;
|
|
2943
|
+
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
2944
|
+
}
|
|
2945
|
+
return out;
|
|
2946
|
+
}
|
|
2947
|
+
|
|
2948
|
+
// src/core/model-fallback.ts
|
|
2949
|
+
async function sendWithModelFallback(provider, args) {
|
|
2950
|
+
const chain = [provider.model, ...provider.fallbackModels ?? []];
|
|
2951
|
+
let lastErr;
|
|
2952
|
+
for (let i = 0; i < chain.length; i += 1) {
|
|
2953
|
+
const candidate = chain[i];
|
|
2954
|
+
const p = i === 0 ? provider : retargetProvider(provider, candidate);
|
|
2955
|
+
try {
|
|
2956
|
+
const result = await p.send(args);
|
|
2957
|
+
return { result, modelUsed: candidate };
|
|
2958
|
+
} catch (e) {
|
|
2959
|
+
lastErr = e;
|
|
2960
|
+
const isModelAccessFailure2 = e instanceof ProviderError && e.modelAccessFailure;
|
|
2961
|
+
const hasMoreCandidates = i < chain.length - 1;
|
|
2962
|
+
if (!isModelAccessFailure2 || !hasMoreCandidates) throw e;
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
throw lastErr;
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2857
2968
|
// src/core/structured.ts
|
|
2858
2969
|
async function requestStructured(provider, baseMessages, schema, opts) {
|
|
2859
2970
|
const maxRetries = opts.maxRetries ?? 1;
|
|
@@ -2932,7 +3043,7 @@ async function askOne(provider, messages, opts) {
|
|
|
2932
3043
|
const ceiling = operatorCeiling(opts.purpose, provider.name);
|
|
2933
3044
|
if (ceiling !== null && ceiling < maxTokens) maxTokens = ceiling;
|
|
2934
3045
|
try {
|
|
2935
|
-
const r = await provider
|
|
3046
|
+
const { result: r, modelUsed } = await sendWithModelFallback(provider, {
|
|
2936
3047
|
messages,
|
|
2937
3048
|
maxTokens,
|
|
2938
3049
|
temperature: opts.temperature,
|
|
@@ -2945,7 +3056,10 @@ async function askOne(provider, messages, opts) {
|
|
|
2945
3056
|
const cpuMs = Math.trunc((cpu.user + cpu.system) / 1e3);
|
|
2946
3057
|
return {
|
|
2947
3058
|
provider: provider.name,
|
|
2948
|
-
model
|
|
3059
|
+
// modelUsed reflects whichever model actually answered — the
|
|
3060
|
+
// primary, or a fallback if one was needed. Equivalent to the old
|
|
3061
|
+
// `provider.model` whenever no fallback occurred.
|
|
3062
|
+
model: modelUsed,
|
|
2949
3063
|
response: r.text,
|
|
2950
3064
|
attempts: r.attempts,
|
|
2951
3065
|
usage: r.usage,
|
|
@@ -3666,32 +3780,6 @@ function numberOrNull(v) {
|
|
|
3666
3780
|
// src/tools/audit.ts
|
|
3667
3781
|
import { performance as performance2 } from "perf_hooks";
|
|
3668
3782
|
|
|
3669
|
-
// src/core/retarget.ts
|
|
3670
|
-
init_esm_shims();
|
|
3671
|
-
function retargetProvider(p, newModel) {
|
|
3672
|
-
if (p.model === newModel) return p;
|
|
3673
|
-
return {
|
|
3674
|
-
name: p.name,
|
|
3675
|
-
model: newModel,
|
|
3676
|
-
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
3677
|
-
};
|
|
3678
|
-
}
|
|
3679
|
-
async function loadProviderWeights(storage, names) {
|
|
3680
|
-
const out = {};
|
|
3681
|
-
for (const raw of names) {
|
|
3682
|
-
const name = raw.toLowerCase();
|
|
3683
|
-
if (name in out) continue;
|
|
3684
|
-
const row = await storage.getProviderStats(name);
|
|
3685
|
-
if (!row) {
|
|
3686
|
-
out[name] = 0.5;
|
|
3687
|
-
continue;
|
|
3688
|
-
}
|
|
3689
|
-
const total = row.wins + row.losses + row.abstains;
|
|
3690
|
-
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
3691
|
-
}
|
|
3692
|
-
return out;
|
|
3693
|
-
}
|
|
3694
|
-
|
|
3695
3783
|
// src/core/co-reason.ts
|
|
3696
3784
|
init_esm_shims();
|
|
3697
3785
|
var CO_REASON_MODEL = "gpt-5.6";
|
|
@@ -12503,7 +12591,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
|
|
|
12503
12591
|
var DEFAULT_PACKAGE = "crosscheck-cli";
|
|
12504
12592
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
12505
12593
|
function engineVersion() {
|
|
12506
|
-
return true ? "0.2.
|
|
12594
|
+
return true ? "0.2.15" : "0.0.0-dev";
|
|
12507
12595
|
}
|
|
12508
12596
|
function defaultUpdateCachePath() {
|
|
12509
12597
|
const base = process.env["CROSSCHECK_DATA_DIR"] || path10.join(os.homedir() || os.tmpdir(), ".crosscheck");
|