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/desktop-core.cjs
CHANGED
|
@@ -291790,7 +291790,7 @@ function reconcilePrice(model, live) {
|
|
|
291790
291790
|
}
|
|
291791
291791
|
|
|
291792
291792
|
// src/constants.ts
|
|
291793
|
-
var CASCADE_VERSION = "0.
|
|
291793
|
+
var CASCADE_VERSION = "0.74.0";
|
|
291794
291794
|
var CASCADE_CONFIG_DIR = ".cascade";
|
|
291795
291795
|
var CASCADE_MD_FILE = "CASCADE.md";
|
|
291796
291796
|
var CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
@@ -292296,6 +292296,13 @@ function buildTokenUsage(inputTokens, outputTokens, model) {
|
|
|
292296
292296
|
}
|
|
292297
292297
|
|
|
292298
292298
|
// src/providers/base.ts
|
|
292299
|
+
var ProviderUnreachableError = class extends Error {
|
|
292300
|
+
transient = true;
|
|
292301
|
+
constructor(message) {
|
|
292302
|
+
super(message);
|
|
292303
|
+
this.name = "ProviderUnreachableError";
|
|
292304
|
+
}
|
|
292305
|
+
};
|
|
292299
292306
|
var BaseProvider = class {
|
|
292300
292307
|
config;
|
|
292301
292308
|
model;
|
|
@@ -318917,6 +318924,18 @@ function toGeminiFunctionCalls(toolCalls) {
|
|
|
318917
318924
|
}
|
|
318918
318925
|
}));
|
|
318919
318926
|
}
|
|
318927
|
+
function isGeminiAuthStatus(status) {
|
|
318928
|
+
return status === 400 || status === 401 || status === 403;
|
|
318929
|
+
}
|
|
318930
|
+
async function describeGeminiError(resp) {
|
|
318931
|
+
try {
|
|
318932
|
+
const body = await resp.json();
|
|
318933
|
+
const detail = body?.error?.message ?? body?.error?.status;
|
|
318934
|
+
return detail ? ` \u2014 ${detail}` : "";
|
|
318935
|
+
} catch {
|
|
318936
|
+
return "";
|
|
318937
|
+
}
|
|
318938
|
+
}
|
|
318920
318939
|
var GeminiProvider = class extends BaseProvider {
|
|
318921
318940
|
client;
|
|
318922
318941
|
constructor(config2, model) {
|
|
@@ -319000,9 +319019,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
319000
319019
|
}
|
|
319001
319020
|
async listModels() {
|
|
319002
319021
|
try {
|
|
319003
|
-
const resp = await
|
|
319004
|
-
`https://generativelanguage.googleapis.com/v1beta/models?key=${this.config.apiKey}`
|
|
319005
|
-
);
|
|
319022
|
+
const resp = await this.fetchModelList();
|
|
319006
319023
|
if (!resp.ok) {
|
|
319007
319024
|
return Object.values(MODELS).filter((m3) => m3.provider === "gemini");
|
|
319008
319025
|
}
|
|
@@ -319033,18 +319050,61 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
319033
319050
|
return Object.values(MODELS).filter((m3) => m3.provider === "gemini");
|
|
319034
319051
|
}
|
|
319035
319052
|
}
|
|
319053
|
+
/**
|
|
319054
|
+
* Is this KEY usable — not "does one particular model answer".
|
|
319055
|
+
*
|
|
319056
|
+
* This used to call countTokens() against `this.model.id`, which the router
|
|
319057
|
+
* fills from the first Gemini entry in the bundled catalogue. That made the
|
|
319058
|
+
* whole provider's fate depend on one hard-coded model id: a key that cannot
|
|
319059
|
+
* reach that one model — retired, not enabled for the key's project, served
|
|
319060
|
+
* on a different API version — failed the probe, and because every later step
|
|
319061
|
+
* is gated on the result (`validateCloudProviderModels` and
|
|
319062
|
+
* `discoverProviderModels` both return early for an unavailable provider) the
|
|
319063
|
+
* real model list was never fetched. Gemini vanished entirely, and the CLI
|
|
319064
|
+
* reported "add a provider API key first" about a key that was present and
|
|
319065
|
+
* working.
|
|
319066
|
+
*
|
|
319067
|
+
* Listing models asks the account-level question instead, the same one
|
|
319068
|
+
* OpenAI's probe asks, and it is the same request `listModels()` already
|
|
319069
|
+
* makes for discovery.
|
|
319070
|
+
*
|
|
319071
|
+
* Throws rather than returning false. The router logs the reason it catches,
|
|
319072
|
+
* and "bad key, wrong endpoint/deployment, or unreachable" — three guesses,
|
|
319073
|
+
* no answer — is what made this take a user report to find. A swallowed probe
|
|
319074
|
+
* error is the same defect that once hid a misconfigured Azure deployment.
|
|
319075
|
+
*/
|
|
319036
319076
|
async isAvailable() {
|
|
319077
|
+
if (!this.config.apiKey) throw new Error("no Gemini API key configured");
|
|
319078
|
+
let resp;
|
|
319037
319079
|
try {
|
|
319038
|
-
await this.
|
|
319039
|
-
|
|
319040
|
-
|
|
319041
|
-
|
|
319042
|
-
|
|
319043
|
-
} catch {
|
|
319044
|
-
return false;
|
|
319080
|
+
resp = await this.fetchModelList();
|
|
319081
|
+
} catch (err) {
|
|
319082
|
+
throw new ProviderUnreachableError(
|
|
319083
|
+
`could not reach the Gemini API: ${err instanceof Error ? err.message : String(err)}`
|
|
319084
|
+
);
|
|
319045
319085
|
}
|
|
319086
|
+
if (resp.ok) return true;
|
|
319087
|
+
const status = `HTTP ${resp.status} ${resp.statusText}`.trim();
|
|
319088
|
+
const detail = await describeGeminiError(resp);
|
|
319089
|
+
if (isGeminiAuthStatus(resp.status)) {
|
|
319090
|
+
throw new Error(`Gemini rejected the API key: ${status}${detail}`);
|
|
319091
|
+
}
|
|
319092
|
+
throw new ProviderUnreachableError(`Gemini model list failed: ${status}${detail}`);
|
|
319046
319093
|
}
|
|
319047
319094
|
// ── Private ──────────────────────────────────
|
|
319095
|
+
/**
|
|
319096
|
+
* The account's model list. One place so the availability probe and
|
|
319097
|
+
* discovery cannot ask different questions of the same endpoint.
|
|
319098
|
+
*
|
|
319099
|
+
* The key goes in a header, not the query string: a URL carries into proxy
|
|
319100
|
+
* logs, error reports and shell history, and this one would carry the key
|
|
319101
|
+
* with it.
|
|
319102
|
+
*/
|
|
319103
|
+
fetchModelList() {
|
|
319104
|
+
return fetch("https://generativelanguage.googleapis.com/v1beta/models", {
|
|
319105
|
+
headers: { "x-goog-api-key": this.config.apiKey ?? "" }
|
|
319106
|
+
});
|
|
319107
|
+
}
|
|
319048
319108
|
buildContents(messages, extraImages) {
|
|
319049
319109
|
const contents = [];
|
|
319050
319110
|
for (const m3 of messages) {
|
|
@@ -321771,12 +321831,28 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
321771
321831
|
return this.selector?.getAllAvailableModels() ?? [];
|
|
321772
321832
|
}
|
|
321773
321833
|
// ── Private ──────────────────────────────────
|
|
321834
|
+
/**
|
|
321835
|
+
* Why each configured provider failed its availability probe, kept so the
|
|
321836
|
+
* surfaces can say something better than "add an API key" to a user who
|
|
321837
|
+
* already has one. Cleared and refilled by each detectAvailableProviders().
|
|
321838
|
+
*/
|
|
321839
|
+
probeFailures = /* @__PURE__ */ new Map();
|
|
321840
|
+
/**
|
|
321841
|
+
* What stopped the configured providers from being usable, if anything.
|
|
321842
|
+
* Empty when every configured provider passed — or when none was configured,
|
|
321843
|
+
* which is a different problem with a different answer.
|
|
321844
|
+
*/
|
|
321845
|
+
providerProbeFailures() {
|
|
321846
|
+
return [...this.probeFailures].map(([provider, reason]) => ({ provider, reason }));
|
|
321847
|
+
}
|
|
321774
321848
|
/** Logs why a configured provider failed its availability probe. */
|
|
321775
321849
|
emitProbeFailure(type, reason) {
|
|
321850
|
+
this.probeFailures.set(type, reason);
|
|
321776
321851
|
console.warn(`[router] provider "${type}" is not available: ${reason}`);
|
|
321777
321852
|
}
|
|
321778
321853
|
async detectAvailableProviders(configs) {
|
|
321779
321854
|
const available = /* @__PURE__ */ new Set();
|
|
321855
|
+
this.probeFailures.clear();
|
|
321780
321856
|
const checks = configs.map(async (cfg) => {
|
|
321781
321857
|
try {
|
|
321782
321858
|
const testModel = this.getAnyModelForProvider(cfg.type);
|
|
@@ -321786,7 +321862,13 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
321786
321862
|
if (ok) available.add(cfg.type);
|
|
321787
321863
|
else this.emitProbeFailure(cfg.type, "availability check returned false (bad key, wrong endpoint/deployment, or unreachable)");
|
|
321788
321864
|
} catch (err) {
|
|
321789
|
-
|
|
321865
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
321866
|
+
if (err instanceof ProviderUnreachableError) {
|
|
321867
|
+
available.add(cfg.type);
|
|
321868
|
+
console.warn(`[router] provider "${cfg.type}" probe did not complete: ${reason} \u2014 continuing with it enabled`);
|
|
321869
|
+
return;
|
|
321870
|
+
}
|
|
321871
|
+
this.emitProbeFailure(cfg.type, reason);
|
|
321790
321872
|
}
|
|
321791
321873
|
});
|
|
321792
321874
|
await Promise.allSettled(checks);
|
|
@@ -374779,7 +374861,16 @@ ${finalOutput}` : ""
|
|
|
374779
374861
|
const selector = this.router.getSelector();
|
|
374780
374862
|
const model = options.fastAnswerModel ? selector.selectForTier(tier, options.fastAnswerModel) : selector.getCandidatesForTier(tier)[0] ?? selector.selectForTier(tier);
|
|
374781
374863
|
if (!model) {
|
|
374782
|
-
|
|
374864
|
+
const failures2 = this.router.providerProbeFailures();
|
|
374865
|
+
const context = failures2.length ? ` Configured providers: ${failures2.map((f4) => `${f4.provider}: ${f4.reason}`).join("; ")}` : "";
|
|
374866
|
+
if (options.fastAnswerModel) {
|
|
374867
|
+
throw new Error(
|
|
374868
|
+
`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}`
|
|
374869
|
+
);
|
|
374870
|
+
}
|
|
374871
|
+
throw new Error(
|
|
374872
|
+
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."
|
|
374873
|
+
);
|
|
374783
374874
|
}
|
|
374784
374875
|
this.recordDecision(
|
|
374785
374876
|
"model",
|
package/dist/index.cjs
CHANGED
|
@@ -1917,7 +1917,7 @@ function reconcilePrice(model, live) {
|
|
|
1917
1917
|
}
|
|
1918
1918
|
|
|
1919
1919
|
// src/constants.ts
|
|
1920
|
-
var CASCADE_VERSION = "0.
|
|
1920
|
+
var CASCADE_VERSION = "0.74.0";
|
|
1921
1921
|
var CASCADE_CONFIG_DIR = ".cascade";
|
|
1922
1922
|
var CASCADE_MD_FILE = "CASCADE.md";
|
|
1923
1923
|
var CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
@@ -2423,6 +2423,13 @@ function buildTokenUsage(inputTokens, outputTokens, model) {
|
|
|
2423
2423
|
}
|
|
2424
2424
|
|
|
2425
2425
|
// src/providers/base.ts
|
|
2426
|
+
var ProviderUnreachableError = class extends Error {
|
|
2427
|
+
transient = true;
|
|
2428
|
+
constructor(message) {
|
|
2429
|
+
super(message);
|
|
2430
|
+
this.name = "ProviderUnreachableError";
|
|
2431
|
+
}
|
|
2432
|
+
};
|
|
2426
2433
|
var BaseProvider = class {
|
|
2427
2434
|
config;
|
|
2428
2435
|
model;
|
|
@@ -3143,6 +3150,18 @@ function toGeminiFunctionCalls(toolCalls) {
|
|
|
3143
3150
|
}
|
|
3144
3151
|
}));
|
|
3145
3152
|
}
|
|
3153
|
+
function isGeminiAuthStatus(status) {
|
|
3154
|
+
return status === 400 || status === 401 || status === 403;
|
|
3155
|
+
}
|
|
3156
|
+
async function describeGeminiError(resp) {
|
|
3157
|
+
try {
|
|
3158
|
+
const body = await resp.json();
|
|
3159
|
+
const detail = body?.error?.message ?? body?.error?.status;
|
|
3160
|
+
return detail ? ` \u2014 ${detail}` : "";
|
|
3161
|
+
} catch {
|
|
3162
|
+
return "";
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3146
3165
|
var GeminiProvider = class extends BaseProvider {
|
|
3147
3166
|
client;
|
|
3148
3167
|
constructor(config, model) {
|
|
@@ -3226,9 +3245,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
3226
3245
|
}
|
|
3227
3246
|
async listModels() {
|
|
3228
3247
|
try {
|
|
3229
|
-
const resp = await
|
|
3230
|
-
`https://generativelanguage.googleapis.com/v1beta/models?key=${this.config.apiKey}`
|
|
3231
|
-
);
|
|
3248
|
+
const resp = await this.fetchModelList();
|
|
3232
3249
|
if (!resp.ok) {
|
|
3233
3250
|
return Object.values(MODELS).filter((m) => m.provider === "gemini");
|
|
3234
3251
|
}
|
|
@@ -3259,18 +3276,61 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
3259
3276
|
return Object.values(MODELS).filter((m) => m.provider === "gemini");
|
|
3260
3277
|
}
|
|
3261
3278
|
}
|
|
3279
|
+
/**
|
|
3280
|
+
* Is this KEY usable — not "does one particular model answer".
|
|
3281
|
+
*
|
|
3282
|
+
* This used to call countTokens() against `this.model.id`, which the router
|
|
3283
|
+
* fills from the first Gemini entry in the bundled catalogue. That made the
|
|
3284
|
+
* whole provider's fate depend on one hard-coded model id: a key that cannot
|
|
3285
|
+
* reach that one model — retired, not enabled for the key's project, served
|
|
3286
|
+
* on a different API version — failed the probe, and because every later step
|
|
3287
|
+
* is gated on the result (`validateCloudProviderModels` and
|
|
3288
|
+
* `discoverProviderModels` both return early for an unavailable provider) the
|
|
3289
|
+
* real model list was never fetched. Gemini vanished entirely, and the CLI
|
|
3290
|
+
* reported "add a provider API key first" about a key that was present and
|
|
3291
|
+
* working.
|
|
3292
|
+
*
|
|
3293
|
+
* Listing models asks the account-level question instead, the same one
|
|
3294
|
+
* OpenAI's probe asks, and it is the same request `listModels()` already
|
|
3295
|
+
* makes for discovery.
|
|
3296
|
+
*
|
|
3297
|
+
* Throws rather than returning false. The router logs the reason it catches,
|
|
3298
|
+
* and "bad key, wrong endpoint/deployment, or unreachable" — three guesses,
|
|
3299
|
+
* no answer — is what made this take a user report to find. A swallowed probe
|
|
3300
|
+
* error is the same defect that once hid a misconfigured Azure deployment.
|
|
3301
|
+
*/
|
|
3262
3302
|
async isAvailable() {
|
|
3303
|
+
if (!this.config.apiKey) throw new Error("no Gemini API key configured");
|
|
3304
|
+
let resp;
|
|
3263
3305
|
try {
|
|
3264
|
-
await this.
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
} catch {
|
|
3270
|
-
return false;
|
|
3306
|
+
resp = await this.fetchModelList();
|
|
3307
|
+
} catch (err) {
|
|
3308
|
+
throw new ProviderUnreachableError(
|
|
3309
|
+
`could not reach the Gemini API: ${err instanceof Error ? err.message : String(err)}`
|
|
3310
|
+
);
|
|
3271
3311
|
}
|
|
3312
|
+
if (resp.ok) return true;
|
|
3313
|
+
const status = `HTTP ${resp.status} ${resp.statusText}`.trim();
|
|
3314
|
+
const detail = await describeGeminiError(resp);
|
|
3315
|
+
if (isGeminiAuthStatus(resp.status)) {
|
|
3316
|
+
throw new Error(`Gemini rejected the API key: ${status}${detail}`);
|
|
3317
|
+
}
|
|
3318
|
+
throw new ProviderUnreachableError(`Gemini model list failed: ${status}${detail}`);
|
|
3272
3319
|
}
|
|
3273
3320
|
// ── Private ──────────────────────────────────
|
|
3321
|
+
/**
|
|
3322
|
+
* The account's model list. One place so the availability probe and
|
|
3323
|
+
* discovery cannot ask different questions of the same endpoint.
|
|
3324
|
+
*
|
|
3325
|
+
* The key goes in a header, not the query string: a URL carries into proxy
|
|
3326
|
+
* logs, error reports and shell history, and this one would carry the key
|
|
3327
|
+
* with it.
|
|
3328
|
+
*/
|
|
3329
|
+
fetchModelList() {
|
|
3330
|
+
return fetch("https://generativelanguage.googleapis.com/v1beta/models", {
|
|
3331
|
+
headers: { "x-goog-api-key": this.config.apiKey ?? "" }
|
|
3332
|
+
});
|
|
3333
|
+
}
|
|
3274
3334
|
buildContents(messages, extraImages) {
|
|
3275
3335
|
const contents = [];
|
|
3276
3336
|
for (const m of messages) {
|
|
@@ -5995,12 +6055,28 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
5995
6055
|
return this.selector?.getAllAvailableModels() ?? [];
|
|
5996
6056
|
}
|
|
5997
6057
|
// ── Private ──────────────────────────────────
|
|
6058
|
+
/**
|
|
6059
|
+
* Why each configured provider failed its availability probe, kept so the
|
|
6060
|
+
* surfaces can say something better than "add an API key" to a user who
|
|
6061
|
+
* already has one. Cleared and refilled by each detectAvailableProviders().
|
|
6062
|
+
*/
|
|
6063
|
+
probeFailures = /* @__PURE__ */ new Map();
|
|
6064
|
+
/**
|
|
6065
|
+
* What stopped the configured providers from being usable, if anything.
|
|
6066
|
+
* Empty when every configured provider passed — or when none was configured,
|
|
6067
|
+
* which is a different problem with a different answer.
|
|
6068
|
+
*/
|
|
6069
|
+
providerProbeFailures() {
|
|
6070
|
+
return [...this.probeFailures].map(([provider, reason]) => ({ provider, reason }));
|
|
6071
|
+
}
|
|
5998
6072
|
/** Logs why a configured provider failed its availability probe. */
|
|
5999
6073
|
emitProbeFailure(type, reason) {
|
|
6074
|
+
this.probeFailures.set(type, reason);
|
|
6000
6075
|
console.warn(`[router] provider "${type}" is not available: ${reason}`);
|
|
6001
6076
|
}
|
|
6002
6077
|
async detectAvailableProviders(configs) {
|
|
6003
6078
|
const available = /* @__PURE__ */ new Set();
|
|
6079
|
+
this.probeFailures.clear();
|
|
6004
6080
|
const checks = configs.map(async (cfg) => {
|
|
6005
6081
|
try {
|
|
6006
6082
|
const testModel = this.getAnyModelForProvider(cfg.type);
|
|
@@ -6010,7 +6086,13 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
6010
6086
|
if (ok) available.add(cfg.type);
|
|
6011
6087
|
else this.emitProbeFailure(cfg.type, "availability check returned false (bad key, wrong endpoint/deployment, or unreachable)");
|
|
6012
6088
|
} catch (err) {
|
|
6013
|
-
|
|
6089
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
6090
|
+
if (err instanceof ProviderUnreachableError) {
|
|
6091
|
+
available.add(cfg.type);
|
|
6092
|
+
console.warn(`[router] provider "${cfg.type}" probe did not complete: ${reason} \u2014 continuing with it enabled`);
|
|
6093
|
+
return;
|
|
6094
|
+
}
|
|
6095
|
+
this.emitProbeFailure(cfg.type, reason);
|
|
6014
6096
|
}
|
|
6015
6097
|
});
|
|
6016
6098
|
await Promise.allSettled(checks);
|
|
@@ -18699,7 +18781,16 @@ ${finalOutput}` : ""
|
|
|
18699
18781
|
const selector = this.router.getSelector();
|
|
18700
18782
|
const model = options.fastAnswerModel ? selector.selectForTier(tier, options.fastAnswerModel) : selector.getCandidatesForTier(tier)[0] ?? selector.selectForTier(tier);
|
|
18701
18783
|
if (!model) {
|
|
18702
|
-
|
|
18784
|
+
const failures2 = this.router.providerProbeFailures();
|
|
18785
|
+
const context = failures2.length ? ` Configured providers: ${failures2.map((f) => `${f.provider}: ${f.reason}`).join("; ")}` : "";
|
|
18786
|
+
if (options.fastAnswerModel) {
|
|
18787
|
+
throw new Error(
|
|
18788
|
+
`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}`
|
|
18789
|
+
);
|
|
18790
|
+
}
|
|
18791
|
+
throw new Error(
|
|
18792
|
+
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."
|
|
18793
|
+
);
|
|
18703
18794
|
}
|
|
18704
18795
|
this.recordDecision(
|
|
18705
18796
|
"model",
|