cascade-ai 0.73.0 → 0.74.0
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/cli.cjs +106 -14
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +106 -14
- package/dist/cli.js.map +1 -1
- package/dist/desktop-core.cjs +104 -13
- package/dist/index.cjs +104 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +104 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1766,7 +1766,7 @@ var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SEC
|
|
|
1766
1766
|
var init_constants = __esm({
|
|
1767
1767
|
"src/constants.ts"() {
|
|
1768
1768
|
init_pricing();
|
|
1769
|
-
CASCADE_VERSION = "0.
|
|
1769
|
+
CASCADE_VERSION = "0.74.0";
|
|
1770
1770
|
CASCADE_CONFIG_FILE = ".cascade/config.json";
|
|
1771
1771
|
CASCADE_DB_FILE = ".cascade/memory.db";
|
|
1772
1772
|
CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
|
|
@@ -2264,10 +2264,17 @@ var init_cost = __esm({
|
|
|
2264
2264
|
});
|
|
2265
2265
|
|
|
2266
2266
|
// src/providers/base.ts
|
|
2267
|
-
var BaseProvider;
|
|
2267
|
+
var ProviderUnreachableError, BaseProvider;
|
|
2268
2268
|
var init_base = __esm({
|
|
2269
2269
|
"src/providers/base.ts"() {
|
|
2270
2270
|
init_cost();
|
|
2271
|
+
ProviderUnreachableError = class extends Error {
|
|
2272
|
+
transient = true;
|
|
2273
|
+
constructor(message) {
|
|
2274
|
+
super(message);
|
|
2275
|
+
this.name = "ProviderUnreachableError";
|
|
2276
|
+
}
|
|
2277
|
+
};
|
|
2271
2278
|
BaseProvider = class {
|
|
2272
2279
|
config;
|
|
2273
2280
|
model;
|
|
@@ -3055,6 +3062,18 @@ function toGeminiFunctionCalls(toolCalls) {
|
|
|
3055
3062
|
}
|
|
3056
3063
|
}));
|
|
3057
3064
|
}
|
|
3065
|
+
function isGeminiAuthStatus(status) {
|
|
3066
|
+
return status === 400 || status === 401 || status === 403;
|
|
3067
|
+
}
|
|
3068
|
+
async function describeGeminiError(resp) {
|
|
3069
|
+
try {
|
|
3070
|
+
const body = await resp.json();
|
|
3071
|
+
const detail = body?.error?.message ?? body?.error?.status;
|
|
3072
|
+
return detail ? ` \u2014 ${detail}` : "";
|
|
3073
|
+
} catch {
|
|
3074
|
+
return "";
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3058
3077
|
var GeminiProvider;
|
|
3059
3078
|
var init_gemini = __esm({
|
|
3060
3079
|
"src/providers/gemini.ts"() {
|
|
@@ -3146,9 +3165,7 @@ var init_gemini = __esm({
|
|
|
3146
3165
|
}
|
|
3147
3166
|
async listModels() {
|
|
3148
3167
|
try {
|
|
3149
|
-
const resp = await
|
|
3150
|
-
`https://generativelanguage.googleapis.com/v1beta/models?key=${this.config.apiKey}`
|
|
3151
|
-
);
|
|
3168
|
+
const resp = await this.fetchModelList();
|
|
3152
3169
|
if (!resp.ok) {
|
|
3153
3170
|
return Object.values(MODELS).filter((m) => m.provider === "gemini");
|
|
3154
3171
|
}
|
|
@@ -3179,18 +3196,61 @@ var init_gemini = __esm({
|
|
|
3179
3196
|
return Object.values(MODELS).filter((m) => m.provider === "gemini");
|
|
3180
3197
|
}
|
|
3181
3198
|
}
|
|
3199
|
+
/**
|
|
3200
|
+
* Is this KEY usable — not "does one particular model answer".
|
|
3201
|
+
*
|
|
3202
|
+
* This used to call countTokens() against `this.model.id`, which the router
|
|
3203
|
+
* fills from the first Gemini entry in the bundled catalogue. That made the
|
|
3204
|
+
* whole provider's fate depend on one hard-coded model id: a key that cannot
|
|
3205
|
+
* reach that one model — retired, not enabled for the key's project, served
|
|
3206
|
+
* on a different API version — failed the probe, and because every later step
|
|
3207
|
+
* is gated on the result (`validateCloudProviderModels` and
|
|
3208
|
+
* `discoverProviderModels` both return early for an unavailable provider) the
|
|
3209
|
+
* real model list was never fetched. Gemini vanished entirely, and the CLI
|
|
3210
|
+
* reported "add a provider API key first" about a key that was present and
|
|
3211
|
+
* working.
|
|
3212
|
+
*
|
|
3213
|
+
* Listing models asks the account-level question instead, the same one
|
|
3214
|
+
* OpenAI's probe asks, and it is the same request `listModels()` already
|
|
3215
|
+
* makes for discovery.
|
|
3216
|
+
*
|
|
3217
|
+
* Throws rather than returning false. The router logs the reason it catches,
|
|
3218
|
+
* and "bad key, wrong endpoint/deployment, or unreachable" — three guesses,
|
|
3219
|
+
* no answer — is what made this take a user report to find. A swallowed probe
|
|
3220
|
+
* error is the same defect that once hid a misconfigured Azure deployment.
|
|
3221
|
+
*/
|
|
3182
3222
|
async isAvailable() {
|
|
3223
|
+
if (!this.config.apiKey) throw new Error("no Gemini API key configured");
|
|
3224
|
+
let resp;
|
|
3183
3225
|
try {
|
|
3184
|
-
await this.
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
} catch {
|
|
3190
|
-
return false;
|
|
3226
|
+
resp = await this.fetchModelList();
|
|
3227
|
+
} catch (err) {
|
|
3228
|
+
throw new ProviderUnreachableError(
|
|
3229
|
+
`could not reach the Gemini API: ${err instanceof Error ? err.message : String(err)}`
|
|
3230
|
+
);
|
|
3191
3231
|
}
|
|
3232
|
+
if (resp.ok) return true;
|
|
3233
|
+
const status = `HTTP ${resp.status} ${resp.statusText}`.trim();
|
|
3234
|
+
const detail = await describeGeminiError(resp);
|
|
3235
|
+
if (isGeminiAuthStatus(resp.status)) {
|
|
3236
|
+
throw new Error(`Gemini rejected the API key: ${status}${detail}`);
|
|
3237
|
+
}
|
|
3238
|
+
throw new ProviderUnreachableError(`Gemini model list failed: ${status}${detail}`);
|
|
3192
3239
|
}
|
|
3193
3240
|
// ── Private ──────────────────────────────────
|
|
3241
|
+
/**
|
|
3242
|
+
* The account's model list. One place so the availability probe and
|
|
3243
|
+
* discovery cannot ask different questions of the same endpoint.
|
|
3244
|
+
*
|
|
3245
|
+
* The key goes in a header, not the query string: a URL carries into proxy
|
|
3246
|
+
* logs, error reports and shell history, and this one would carry the key
|
|
3247
|
+
* with it.
|
|
3248
|
+
*/
|
|
3249
|
+
fetchModelList() {
|
|
3250
|
+
return fetch("https://generativelanguage.googleapis.com/v1beta/models", {
|
|
3251
|
+
headers: { "x-goog-api-key": this.config.apiKey ?? "" }
|
|
3252
|
+
});
|
|
3253
|
+
}
|
|
3194
3254
|
buildContents(messages, extraImages) {
|
|
3195
3255
|
const contents = [];
|
|
3196
3256
|
for (const m of messages) {
|
|
@@ -6270,6 +6330,7 @@ init_gemini();
|
|
|
6270
6330
|
init_ollama();
|
|
6271
6331
|
init_openai_compatible();
|
|
6272
6332
|
init_openai();
|
|
6333
|
+
init_base();
|
|
6273
6334
|
|
|
6274
6335
|
// src/core/router/selector.ts
|
|
6275
6336
|
init_constants();
|
|
@@ -8529,12 +8590,28 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
8529
8590
|
return this.selector?.getAllAvailableModels() ?? [];
|
|
8530
8591
|
}
|
|
8531
8592
|
// ── Private ──────────────────────────────────
|
|
8593
|
+
/**
|
|
8594
|
+
* Why each configured provider failed its availability probe, kept so the
|
|
8595
|
+
* surfaces can say something better than "add an API key" to a user who
|
|
8596
|
+
* already has one. Cleared and refilled by each detectAvailableProviders().
|
|
8597
|
+
*/
|
|
8598
|
+
probeFailures = /* @__PURE__ */ new Map();
|
|
8599
|
+
/**
|
|
8600
|
+
* What stopped the configured providers from being usable, if anything.
|
|
8601
|
+
* Empty when every configured provider passed — or when none was configured,
|
|
8602
|
+
* which is a different problem with a different answer.
|
|
8603
|
+
*/
|
|
8604
|
+
providerProbeFailures() {
|
|
8605
|
+
return [...this.probeFailures].map(([provider, reason]) => ({ provider, reason }));
|
|
8606
|
+
}
|
|
8532
8607
|
/** Logs why a configured provider failed its availability probe. */
|
|
8533
8608
|
emitProbeFailure(type, reason) {
|
|
8609
|
+
this.probeFailures.set(type, reason);
|
|
8534
8610
|
console.warn(`[router] provider "${type}" is not available: ${reason}`);
|
|
8535
8611
|
}
|
|
8536
8612
|
async detectAvailableProviders(configs) {
|
|
8537
8613
|
const available = /* @__PURE__ */ new Set();
|
|
8614
|
+
this.probeFailures.clear();
|
|
8538
8615
|
const checks = configs.map(async (cfg) => {
|
|
8539
8616
|
try {
|
|
8540
8617
|
const testModel = this.getAnyModelForProvider(cfg.type);
|
|
@@ -8544,7 +8621,13 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
8544
8621
|
if (ok) available.add(cfg.type);
|
|
8545
8622
|
else this.emitProbeFailure(cfg.type, "availability check returned false (bad key, wrong endpoint/deployment, or unreachable)");
|
|
8546
8623
|
} catch (err) {
|
|
8547
|
-
|
|
8624
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
8625
|
+
if (err instanceof ProviderUnreachableError) {
|
|
8626
|
+
available.add(cfg.type);
|
|
8627
|
+
console.warn(`[router] provider "${cfg.type}" probe did not complete: ${reason} \u2014 continuing with it enabled`);
|
|
8628
|
+
return;
|
|
8629
|
+
}
|
|
8630
|
+
this.emitProbeFailure(cfg.type, reason);
|
|
8548
8631
|
}
|
|
8549
8632
|
});
|
|
8550
8633
|
await Promise.allSettled(checks);
|
|
@@ -20592,7 +20675,16 @@ ${finalOutput}` : ""
|
|
|
20592
20675
|
const selector = this.router.getSelector();
|
|
20593
20676
|
const model = options.fastAnswerModel ? selector.selectForTier(tier, options.fastAnswerModel) : selector.getCandidatesForTier(tier)[0] ?? selector.selectForTier(tier);
|
|
20594
20677
|
if (!model) {
|
|
20595
|
-
|
|
20678
|
+
const failures2 = this.router.providerProbeFailures();
|
|
20679
|
+
const context = failures2.length ? ` Configured providers: ${failures2.map((f) => `${f.provider}: ${f.reason}`).join("; ")}` : "";
|
|
20680
|
+
if (options.fastAnswerModel) {
|
|
20681
|
+
throw new Error(
|
|
20682
|
+
`No model is available for a fast answer: the pinned model "${options.fastAnswerModel}" could not be resolved \u2014 check it is spelled correctly and served by a configured provider.${context}`
|
|
20683
|
+
);
|
|
20684
|
+
}
|
|
20685
|
+
throw new Error(
|
|
20686
|
+
failures2.length ? `No model is available for a fast answer.${context}` : "No model is available for a fast answer \u2014 add a provider API key first."
|
|
20687
|
+
);
|
|
20596
20688
|
}
|
|
20597
20689
|
this.recordDecision(
|
|
20598
20690
|
"model",
|