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.cjs
CHANGED
|
@@ -1819,7 +1819,7 @@ var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SEC
|
|
|
1819
1819
|
var init_constants = __esm({
|
|
1820
1820
|
"src/constants.ts"() {
|
|
1821
1821
|
init_pricing();
|
|
1822
|
-
CASCADE_VERSION = "0.
|
|
1822
|
+
CASCADE_VERSION = "0.74.0";
|
|
1823
1823
|
CASCADE_CONFIG_FILE = ".cascade/config.json";
|
|
1824
1824
|
CASCADE_DB_FILE = ".cascade/memory.db";
|
|
1825
1825
|
CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
|
|
@@ -2317,10 +2317,17 @@ var init_cost = __esm({
|
|
|
2317
2317
|
});
|
|
2318
2318
|
|
|
2319
2319
|
// src/providers/base.ts
|
|
2320
|
-
var BaseProvider;
|
|
2320
|
+
var ProviderUnreachableError, BaseProvider;
|
|
2321
2321
|
var init_base = __esm({
|
|
2322
2322
|
"src/providers/base.ts"() {
|
|
2323
2323
|
init_cost();
|
|
2324
|
+
ProviderUnreachableError = class extends Error {
|
|
2325
|
+
transient = true;
|
|
2326
|
+
constructor(message) {
|
|
2327
|
+
super(message);
|
|
2328
|
+
this.name = "ProviderUnreachableError";
|
|
2329
|
+
}
|
|
2330
|
+
};
|
|
2324
2331
|
BaseProvider = class {
|
|
2325
2332
|
config;
|
|
2326
2333
|
model;
|
|
@@ -3108,6 +3115,18 @@ function toGeminiFunctionCalls(toolCalls) {
|
|
|
3108
3115
|
}
|
|
3109
3116
|
}));
|
|
3110
3117
|
}
|
|
3118
|
+
function isGeminiAuthStatus(status) {
|
|
3119
|
+
return status === 400 || status === 401 || status === 403;
|
|
3120
|
+
}
|
|
3121
|
+
async function describeGeminiError(resp) {
|
|
3122
|
+
try {
|
|
3123
|
+
const body = await resp.json();
|
|
3124
|
+
const detail = body?.error?.message ?? body?.error?.status;
|
|
3125
|
+
return detail ? ` \u2014 ${detail}` : "";
|
|
3126
|
+
} catch {
|
|
3127
|
+
return "";
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3111
3130
|
var GeminiProvider;
|
|
3112
3131
|
var init_gemini = __esm({
|
|
3113
3132
|
"src/providers/gemini.ts"() {
|
|
@@ -3199,9 +3218,7 @@ var init_gemini = __esm({
|
|
|
3199
3218
|
}
|
|
3200
3219
|
async listModels() {
|
|
3201
3220
|
try {
|
|
3202
|
-
const resp = await
|
|
3203
|
-
`https://generativelanguage.googleapis.com/v1beta/models?key=${this.config.apiKey}`
|
|
3204
|
-
);
|
|
3221
|
+
const resp = await this.fetchModelList();
|
|
3205
3222
|
if (!resp.ok) {
|
|
3206
3223
|
return Object.values(MODELS).filter((m) => m.provider === "gemini");
|
|
3207
3224
|
}
|
|
@@ -3232,18 +3249,61 @@ var init_gemini = __esm({
|
|
|
3232
3249
|
return Object.values(MODELS).filter((m) => m.provider === "gemini");
|
|
3233
3250
|
}
|
|
3234
3251
|
}
|
|
3252
|
+
/**
|
|
3253
|
+
* Is this KEY usable — not "does one particular model answer".
|
|
3254
|
+
*
|
|
3255
|
+
* This used to call countTokens() against `this.model.id`, which the router
|
|
3256
|
+
* fills from the first Gemini entry in the bundled catalogue. That made the
|
|
3257
|
+
* whole provider's fate depend on one hard-coded model id: a key that cannot
|
|
3258
|
+
* reach that one model — retired, not enabled for the key's project, served
|
|
3259
|
+
* on a different API version — failed the probe, and because every later step
|
|
3260
|
+
* is gated on the result (`validateCloudProviderModels` and
|
|
3261
|
+
* `discoverProviderModels` both return early for an unavailable provider) the
|
|
3262
|
+
* real model list was never fetched. Gemini vanished entirely, and the CLI
|
|
3263
|
+
* reported "add a provider API key first" about a key that was present and
|
|
3264
|
+
* working.
|
|
3265
|
+
*
|
|
3266
|
+
* Listing models asks the account-level question instead, the same one
|
|
3267
|
+
* OpenAI's probe asks, and it is the same request `listModels()` already
|
|
3268
|
+
* makes for discovery.
|
|
3269
|
+
*
|
|
3270
|
+
* Throws rather than returning false. The router logs the reason it catches,
|
|
3271
|
+
* and "bad key, wrong endpoint/deployment, or unreachable" — three guesses,
|
|
3272
|
+
* no answer — is what made this take a user report to find. A swallowed probe
|
|
3273
|
+
* error is the same defect that once hid a misconfigured Azure deployment.
|
|
3274
|
+
*/
|
|
3235
3275
|
async isAvailable() {
|
|
3276
|
+
if (!this.config.apiKey) throw new Error("no Gemini API key configured");
|
|
3277
|
+
let resp;
|
|
3236
3278
|
try {
|
|
3237
|
-
await this.
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
} catch {
|
|
3243
|
-
return false;
|
|
3279
|
+
resp = await this.fetchModelList();
|
|
3280
|
+
} catch (err) {
|
|
3281
|
+
throw new ProviderUnreachableError(
|
|
3282
|
+
`could not reach the Gemini API: ${err instanceof Error ? err.message : String(err)}`
|
|
3283
|
+
);
|
|
3244
3284
|
}
|
|
3285
|
+
if (resp.ok) return true;
|
|
3286
|
+
const status = `HTTP ${resp.status} ${resp.statusText}`.trim();
|
|
3287
|
+
const detail = await describeGeminiError(resp);
|
|
3288
|
+
if (isGeminiAuthStatus(resp.status)) {
|
|
3289
|
+
throw new Error(`Gemini rejected the API key: ${status}${detail}`);
|
|
3290
|
+
}
|
|
3291
|
+
throw new ProviderUnreachableError(`Gemini model list failed: ${status}${detail}`);
|
|
3245
3292
|
}
|
|
3246
3293
|
// ── Private ──────────────────────────────────
|
|
3294
|
+
/**
|
|
3295
|
+
* The account's model list. One place so the availability probe and
|
|
3296
|
+
* discovery cannot ask different questions of the same endpoint.
|
|
3297
|
+
*
|
|
3298
|
+
* The key goes in a header, not the query string: a URL carries into proxy
|
|
3299
|
+
* logs, error reports and shell history, and this one would carry the key
|
|
3300
|
+
* with it.
|
|
3301
|
+
*/
|
|
3302
|
+
fetchModelList() {
|
|
3303
|
+
return fetch("https://generativelanguage.googleapis.com/v1beta/models", {
|
|
3304
|
+
headers: { "x-goog-api-key": this.config.apiKey ?? "" }
|
|
3305
|
+
});
|
|
3306
|
+
}
|
|
3247
3307
|
buildContents(messages, extraImages) {
|
|
3248
3308
|
const contents = [];
|
|
3249
3309
|
for (const m of messages) {
|
|
@@ -6323,6 +6383,7 @@ init_gemini();
|
|
|
6323
6383
|
init_ollama();
|
|
6324
6384
|
init_openai_compatible();
|
|
6325
6385
|
init_openai();
|
|
6386
|
+
init_base();
|
|
6326
6387
|
|
|
6327
6388
|
// src/core/router/selector.ts
|
|
6328
6389
|
init_constants();
|
|
@@ -8582,12 +8643,28 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
8582
8643
|
return this.selector?.getAllAvailableModels() ?? [];
|
|
8583
8644
|
}
|
|
8584
8645
|
// ── Private ──────────────────────────────────
|
|
8646
|
+
/**
|
|
8647
|
+
* Why each configured provider failed its availability probe, kept so the
|
|
8648
|
+
* surfaces can say something better than "add an API key" to a user who
|
|
8649
|
+
* already has one. Cleared and refilled by each detectAvailableProviders().
|
|
8650
|
+
*/
|
|
8651
|
+
probeFailures = /* @__PURE__ */ new Map();
|
|
8652
|
+
/**
|
|
8653
|
+
* What stopped the configured providers from being usable, if anything.
|
|
8654
|
+
* Empty when every configured provider passed — or when none was configured,
|
|
8655
|
+
* which is a different problem with a different answer.
|
|
8656
|
+
*/
|
|
8657
|
+
providerProbeFailures() {
|
|
8658
|
+
return [...this.probeFailures].map(([provider, reason]) => ({ provider, reason }));
|
|
8659
|
+
}
|
|
8585
8660
|
/** Logs why a configured provider failed its availability probe. */
|
|
8586
8661
|
emitProbeFailure(type, reason) {
|
|
8662
|
+
this.probeFailures.set(type, reason);
|
|
8587
8663
|
console.warn(`[router] provider "${type}" is not available: ${reason}`);
|
|
8588
8664
|
}
|
|
8589
8665
|
async detectAvailableProviders(configs) {
|
|
8590
8666
|
const available = /* @__PURE__ */ new Set();
|
|
8667
|
+
this.probeFailures.clear();
|
|
8591
8668
|
const checks = configs.map(async (cfg) => {
|
|
8592
8669
|
try {
|
|
8593
8670
|
const testModel = this.getAnyModelForProvider(cfg.type);
|
|
@@ -8597,7 +8674,13 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
8597
8674
|
if (ok) available.add(cfg.type);
|
|
8598
8675
|
else this.emitProbeFailure(cfg.type, "availability check returned false (bad key, wrong endpoint/deployment, or unreachable)");
|
|
8599
8676
|
} catch (err) {
|
|
8600
|
-
|
|
8677
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
8678
|
+
if (err instanceof ProviderUnreachableError) {
|
|
8679
|
+
available.add(cfg.type);
|
|
8680
|
+
console.warn(`[router] provider "${cfg.type}" probe did not complete: ${reason} \u2014 continuing with it enabled`);
|
|
8681
|
+
return;
|
|
8682
|
+
}
|
|
8683
|
+
this.emitProbeFailure(cfg.type, reason);
|
|
8601
8684
|
}
|
|
8602
8685
|
});
|
|
8603
8686
|
await Promise.allSettled(checks);
|
|
@@ -20645,7 +20728,16 @@ ${finalOutput}` : ""
|
|
|
20645
20728
|
const selector = this.router.getSelector();
|
|
20646
20729
|
const model = options.fastAnswerModel ? selector.selectForTier(tier, options.fastAnswerModel) : selector.getCandidatesForTier(tier)[0] ?? selector.selectForTier(tier);
|
|
20647
20730
|
if (!model) {
|
|
20648
|
-
|
|
20731
|
+
const failures2 = this.router.providerProbeFailures();
|
|
20732
|
+
const context = failures2.length ? ` Configured providers: ${failures2.map((f) => `${f.provider}: ${f.reason}`).join("; ")}` : "";
|
|
20733
|
+
if (options.fastAnswerModel) {
|
|
20734
|
+
throw new Error(
|
|
20735
|
+
`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}`
|
|
20736
|
+
);
|
|
20737
|
+
}
|
|
20738
|
+
throw new Error(
|
|
20739
|
+
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."
|
|
20740
|
+
);
|
|
20649
20741
|
}
|
|
20650
20742
|
this.recordDecision(
|
|
20651
20743
|
"model",
|