cascade-ai 0.47.0 → 0.48.1

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/index.js CHANGED
@@ -193,7 +193,7 @@ var init_audit_logger = __esm({
193
193
  });
194
194
 
195
195
  // src/constants.ts
196
- var CASCADE_VERSION = "0.47.0";
196
+ var CASCADE_VERSION = "0.48.1";
197
197
  var CASCADE_CONFIG_DIR = ".cascade";
198
198
  var CASCADE_MD_FILE = "CASCADE.md";
199
199
  var CASCADE_IGNORE_FILE = ".cascadeignore";
@@ -675,6 +675,55 @@ var BaseProvider = class {
675
675
  }
676
676
  };
677
677
 
678
+ // src/providers/model-filter.ts
679
+ var NON_CHAT_PATTERNS = [
680
+ "embed",
681
+ // text-embedding-3, nomic-embed, mxbai-embed, snowflake-arctic-embed
682
+ "(?:^|[-_/])tts(?:[-_/]|$)",
683
+ "text-to-speech",
684
+ "speech",
685
+ // text-to-speech
686
+ "whisper",
687
+ "transcrib",
688
+ "(?:^|[-_/])stt(?:[-_/]|$)",
689
+ // speech-to-text
690
+ "dall[-]?e",
691
+ "imagen",
692
+ "image-generation",
693
+ "gpt-image",
694
+ "(?:^|[-_/])image(?:[-_/]|$)",
695
+ "stable-diffusion",
696
+ "sdxl",
697
+ "flux",
698
+ // image gen
699
+ "moderation",
700
+ "(?:^|[-_/])guard(?:[-_/]|$)",
701
+ // moderation / safety
702
+ "rerank",
703
+ "(?:^|[-_/])audio(?:[-_/]|$)",
704
+ "native-audio",
705
+ "realtime",
706
+ // audio / realtime
707
+ "veo",
708
+ "sora",
709
+ "(?:^|[-_/])video(?:[-_/]|$)",
710
+ // video
711
+ "(?:^|[-_/])aqa(?:[-_/]|$)",
712
+ // attributed QA
713
+ "(?:^|[-_/])live(?:[-_/]|$)",
714
+ // live-audio variants
715
+ "(?:^|[-_/])(?:davinci|babbage)(?:[-_/]|$)"
716
+ // legacy completion-only base models
717
+ ];
718
+ var NON_CHAT = new RegExp(NON_CHAT_PATTERNS.join("|"), "i");
719
+ function isChatModel(id, methods) {
720
+ if (methods && methods.length > 0) {
721
+ const canGenerate = methods.some((m) => /generatecontent|generatemessage|chat|completion/i.test(m));
722
+ if (!canGenerate) return false;
723
+ }
724
+ return !NON_CHAT.test(id);
725
+ }
726
+
678
727
  // src/providers/anthropic.ts
679
728
  function anthropicThinkingParam(modelId, maxTokens) {
680
729
  if (!/claude-(opus|sonnet)-4/i.test(modelId)) return {};
@@ -788,7 +837,7 @@ var AnthropicProvider = class extends BaseProvider {
788
837
  if (!Array.isArray(data?.data)) {
789
838
  return Object.values(MODELS).filter((m) => m.provider === "anthropic");
790
839
  }
791
- return data.data.map((m) => {
840
+ return data.data.filter((m) => isChatModel(m.id)).map((m) => {
792
841
  const known = Object.values(MODELS).find((km) => km.id === m.id && km.provider === "anthropic");
793
842
  if (known) return known;
794
843
  return {
@@ -1003,7 +1052,7 @@ var OpenAIProvider = class extends BaseProvider {
1003
1052
  async listModels() {
1004
1053
  try {
1005
1054
  const response = await this.client.models.list();
1006
- return response.data.map((m) => {
1055
+ return response.data.filter((m) => isChatModel(m.id)).map((m) => {
1007
1056
  const known = Object.values(MODELS).find((km) => km.id === m.id && km.provider === "openai");
1008
1057
  if (known) return known;
1009
1058
  return {
@@ -1278,7 +1327,7 @@ var GeminiProvider = class extends BaseProvider {
1278
1327
  if (!Array.isArray(data?.models)) {
1279
1328
  return Object.values(MODELS).filter((m) => m.provider === "gemini");
1280
1329
  }
1281
- return data.models.map((m) => {
1330
+ return data.models.filter((m) => isChatModel(m.name.replace("models/", ""), m.supportedGenerationMethods)).map((m) => {
1282
1331
  const id = m.name.replace("models/", "");
1283
1332
  const known = Object.values(MODELS).find(
1284
1333
  (km) => km.id === id && km.provider === "gemini"
@@ -1636,10 +1685,10 @@ var OllamaProvider = class extends BaseProvider {
1636
1685
  const response = await fetch(`${this.baseUrl}/api/tags`);
1637
1686
  if (!response.ok) return [];
1638
1687
  const data = await response.json();
1639
- const supportedKeywords = ["llama3", "llama2", "gemma", "mistral", "mixtral", "qwen", "phi3", "codellama", "deepseek", "llava", "starcoder", "stable-code", "nomic-embed"];
1688
+ const supportedKeywords = ["llama3", "llama2", "gemma", "mistral", "mixtral", "qwen", "phi3", "codellama", "deepseek", "llava", "starcoder", "stable-code"];
1640
1689
  const entries = data.models.filter((m) => {
1641
1690
  const name = m.name.toLowerCase();
1642
- return supportedKeywords.some((k) => name.includes(k));
1691
+ return supportedKeywords.some((k) => name.includes(k)) && isChatModel(name);
1643
1692
  });
1644
1693
  const shows = await Promise.all(entries.map((m) => this.showModel(m.name)));
1645
1694
  return entries.map((m, i) => {
@@ -1759,7 +1808,8 @@ var OpenAICompatibleProvider = class extends OpenAIProvider {
1759
1808
  return typeof m === "string" ? m : void 0;
1760
1809
  }).filter((x) => typeof x === "string" && x.length > 0);
1761
1810
  if (ids.length === 0) return [this.model];
1762
- return ids.map((id) => ({
1811
+ const chatIds = ids.filter((id) => isChatModel(id));
1812
+ return (chatIds.length ? chatIds : ids).map((id) => ({
1763
1813
  id,
1764
1814
  name: id,
1765
1815
  provider: "openai-compatible",
@@ -15888,12 +15938,19 @@ var CloudClient = class _CloudClient {
15888
15938
  );
15889
15939
  await openUrl(authorizeUrl);
15890
15940
  const code = await listener.waitForCode;
15891
- const res = await fetch(this.url("/api/native/token"), {
15892
- method: "POST",
15893
- headers: { "Content-Type": "application/json" },
15894
- body: JSON.stringify({ code, code_verifier: verifier })
15895
- });
15896
- if (!res.ok) throw new Error("Sign-in could not be completed. Please try again.");
15941
+ let res;
15942
+ try {
15943
+ res = await fetch(this.url("/api/native/token"), {
15944
+ method: "POST",
15945
+ headers: { "Content-Type": "application/json" },
15946
+ body: JSON.stringify({ code, code_verifier: verifier })
15947
+ });
15948
+ } catch (err) {
15949
+ throw new Error(
15950
+ `Signed in, but could not reach Cascade at ${this.serverUrl} to finish. Check your connection and that the server URL is correct. (${err instanceof Error ? err.message : String(err)})`
15951
+ );
15952
+ }
15953
+ if (!res.ok) throw new Error(await loginFailureMessage(res, this.serverUrl));
15897
15954
  const tokens = await res.json();
15898
15955
  return await this.finishLogin(tokens);
15899
15956
  } finally {
@@ -16067,6 +16124,22 @@ function sleep(ms, signal) {
16067
16124
  }, { once: true });
16068
16125
  });
16069
16126
  }
16127
+ async function loginFailureMessage(res, serverUrl) {
16128
+ const body = await res.text().catch(() => "");
16129
+ let apiError = "";
16130
+ try {
16131
+ apiError = String(JSON.parse(body).error ?? "");
16132
+ } catch {
16133
+ }
16134
+ if (apiError === "invalid_grant") {
16135
+ return "Sign-in expired before it completed \u2014 the code is single-use and short-lived, and restarting the server during sign-in also invalidates it. Please try again.";
16136
+ }
16137
+ if (apiError) return `Sign-in failed: ${apiError} (HTTP ${res.status}).`;
16138
+ if (res.status === 404 || /<html/i.test(body)) {
16139
+ return `Signed in, but ${serverUrl} did not answer the Cascade API (HTTP ${res.status}). If this host was recently pointed at a new server, DNS may still be propagating.`;
16140
+ }
16141
+ return `Sign-in could not be completed (HTTP ${res.status}). Please try again.`;
16142
+ }
16070
16143
  function base64url(buf) {
16071
16144
  return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
16072
16145
  }