cascade-ai 0.12.6 → 0.12.8

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 CHANGED
@@ -101,7 +101,7 @@ var __export = (target, all) => {
101
101
  var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SECRET_FILE, GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_RUNTIME_DB_FILE, DEFAULT_DASHBOARD_PORT, DEFAULT_CONTEXT_LIMIT, DEFAULT_AUTO_SUMMARIZE_AT, MODELS, T1_MODEL_PRIORITY, T2_MODEL_PRIORITY, T3_MODEL_PRIORITY, VISION_MODEL_PRIORITY, COMPLEXITY_T2_COUNT, THEME_NAMES, DEFAULT_THEME, OLLAMA_BASE_URL, LM_STUDIO_BASE_URL, AZURE_BASE_URL_TEMPLATE, TOOL_NAMES, DEFAULT_APPROVAL_REQUIRED;
102
102
  var init_constants = __esm({
103
103
  "src/constants.ts"() {
104
- CASCADE_VERSION = "0.12.6";
104
+ CASCADE_VERSION = "0.12.8";
105
105
  CASCADE_CONFIG_FILE = ".cascade/config.json";
106
106
  CASCADE_DB_FILE = ".cascade/memory.db";
107
107
  CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
@@ -3369,6 +3369,7 @@ var ModelSelector = class {
3369
3369
  if (lower.includes("claude")) providerStr = "anthropic";
3370
3370
  else if (lower.startsWith("gpt") || lower.startsWith("o1") || lower.startsWith("o3")) providerStr = "openai";
3371
3371
  else if (lower.includes("gemini")) providerStr = "gemini";
3372
+ else if ((lower.endsWith(".gguf") || actualId.includes("/") || actualId.includes("\\")) && this.availableProviders.has("openai-compatible")) providerStr = "openai-compatible";
3372
3373
  else if (this.availableProviders.has("ollama")) providerStr = "ollama";
3373
3374
  else if (this.availableProviders.has("openai-compatible")) providerStr = "openai-compatible";
3374
3375
  else if (this.availableProviders.size === 1) providerStr = Array.from(this.availableProviders)[0];
@@ -4051,6 +4052,11 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
4051
4052
  if (availableProviders.has("ollama")) {
4052
4053
  await this.discoverOllamaModels(ollamaCfg);
4053
4054
  }
4055
+ if (availableProviders.has("openai-compatible")) {
4056
+ await Promise.all(
4057
+ config.providers.filter((p) => p.type === "openai-compatible").map((cfg) => this.discoverOpenAICompatibleModels(cfg))
4058
+ );
4059
+ }
4054
4060
  for (const tier of ["T1", "T2", "T3"]) {
4055
4061
  const override = tier === "T1" ? config.models.t1 : tier === "T2" ? config.models.t2 : config.models.t3;
4056
4062
  if (!override || override === "auto") continue;
@@ -4482,6 +4488,14 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
4482
4488
  getModelsForProvider(provider) {
4483
4489
  return this.selector.getAvailableModelsForProvider(provider);
4484
4490
  }
4491
+ /**
4492
+ * Every model available across the configured + reachable providers, after
4493
+ * discovery (Ollama tags, OpenAI-compatible/llama.cpp models, cloud catalog).
4494
+ * Used to populate the desktop model pickers with the user's real models.
4495
+ */
4496
+ getAvailableModels() {
4497
+ return this.selector?.getAllAvailableModels() ?? [];
4498
+ }
4485
4499
  // ── Private ──────────────────────────────────
4486
4500
  async detectAvailableProviders(configs) {
4487
4501
  const available = /* @__PURE__ */ new Set();
@@ -4512,6 +4526,28 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
4512
4526
  } catch {
4513
4527
  }
4514
4528
  }
4529
+ async discoverOpenAICompatibleModels(cfg) {
4530
+ try {
4531
+ const seed = {
4532
+ id: "openai-compatible",
4533
+ name: "openai-compatible",
4534
+ provider: "openai-compatible",
4535
+ contextWindow: 32e3,
4536
+ isVisionCapable: false,
4537
+ inputCostPer1kTokens: 0,
4538
+ outputCostPer1kTokens: 0,
4539
+ maxOutputTokens: 4e3,
4540
+ supportsStreaming: true,
4541
+ isLocal: false
4542
+ };
4543
+ const provider = new OpenAICompatibleProvider(cfg, seed);
4544
+ const models = await provider.listModels();
4545
+ for (const m of models) {
4546
+ this.selector.addDynamicModel(m);
4547
+ }
4548
+ } catch {
4549
+ }
4550
+ }
4515
4551
  ensureProvider(model, configs) {
4516
4552
  const key = `${model.provider}:${model.id}`;
4517
4553
  if (this.providers.has(key)) return;
@@ -4541,7 +4577,23 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
4541
4577
  }
4542
4578
  }
4543
4579
  getAnyModelForProvider(type) {
4544
- return Object.values(MODELS).find((m) => m.provider === type);
4580
+ const fromCatalog = Object.values(MODELS).find((m) => m.provider === type);
4581
+ if (fromCatalog) return fromCatalog;
4582
+ if (type === "openai-compatible" || type === "azure") {
4583
+ return {
4584
+ id: type,
4585
+ name: type,
4586
+ provider: type,
4587
+ contextWindow: 32e3,
4588
+ isVisionCapable: false,
4589
+ inputCostPer1kTokens: 0,
4590
+ outputCostPer1kTokens: 0,
4591
+ maxOutputTokens: 4e3,
4592
+ supportsStreaming: true,
4593
+ isLocal: false
4594
+ };
4595
+ }
4596
+ return void 0;
4545
4597
  }
4546
4598
  recordStats(tier, model, usage) {
4547
4599
  this.stats.totalTokens += usage.totalTokens;
@@ -10410,7 +10462,11 @@ ${last.partialOutput}` : "");
10410
10462
  looksLikeConversational(prompt) {
10411
10463
  const LOW_COMPLEXITY = [
10412
10464
  /^(?:hi|hello|hey|thanks|thank you|ok|okay|yes|no|sure|got it|sounds good)\b/i,
10413
- /^(?:what is|what are|list|show me|tell me|who is|where is|when is|how do i)\b/i,
10465
+ /^(?:what is|what are|what'?s|list|show me|tell me|who is|who are|who'?re|where is|when is|how do i)\b/i,
10466
+ // Self-identity / capability questions ("who are you", "what can you do",
10467
+ // "who made you") are pure conversation — never a multi-agent build.
10468
+ /^(?:who|what)\b.*\byou\b/i,
10469
+ /^what can you\b/i,
10414
10470
  /\b(?:simple|quick|brief|small|single|one-line|typo|rename)\b/i
10415
10471
  ];
10416
10472
  const wordCount = prompt.trim().split(/\s+/).length;
@@ -10508,10 +10564,16 @@ ${prompt}` : prompt;
10508
10564
  temperature: 0
10509
10565
  });
10510
10566
  const content = result.content.trim();
10511
- const firstWord = (content.split(/[\s—–-]+/)[0] ?? "").toLowerCase();
10567
+ const match = content.toLowerCase().match(/\b(simple|moderate|complex)\b/);
10512
10568
  const reason = content.replace(/^\S+\s*[—–-]*\s*/, "").trim();
10513
- const verdict = firstWord.includes("simple") ? "Simple" : firstWord.includes("moderate") ? "Moderate" : "Complex";
10514
- this.recordDecision("complexity", `${verdict} \u2014 classifier: ${reason || "no reason given"}`);
10569
+ let verdict;
10570
+ if (match) {
10571
+ verdict = match[1] === "simple" ? "Simple" : match[1] === "moderate" ? "Moderate" : "Complex";
10572
+ this.recordDecision("complexity", `${verdict} \u2014 classifier: ${reason || "no reason given"}`);
10573
+ } else {
10574
+ verdict = prompt.trim().split(/\s+/).length <= 12 ? "Simple" : "Moderate";
10575
+ this.recordDecision("complexity", `${verdict} \u2014 classifier output unparseable; defaulted by length`);
10576
+ }
10515
10577
  return verdict;
10516
10578
  } catch {
10517
10579
  const followUpPrompt = /^(proceed|continue|go ahead|do it|yes|yep|ok|okay|carry on)$/i.test(prompt.trim());
@@ -13920,7 +13982,8 @@ function SetupWizard({ workspacePath, onComplete }) {
13920
13982
  ) })
13921
13983
  ] });
13922
13984
  }
13923
- const prompt = isAzure && fieldStage === "deploymentName" ? `Azure deployment name (${currentEntry.label})` : isAzure && fieldStage === "baseUrl" ? `Azure endpoint URL` : isAzure && fieldStage === "apiKey" ? `${currentEntry.label} API Key` : isAzure && fieldStage === "apiVersion" ? `Azure API version (e.g. 2024-08-01-preview)` : isCompat && fieldStage === "label" ? `Name for this endpoint (e.g. Groq)` : isCompat && fieldStage === "baseUrl" ? `Base URL (e.g. https://api.groq.com/openai/v1)` : isOllama ? `Ollama URL` : `${currentEntry.label} API Key`;
13985
+ const prompt = isAzure && fieldStage === "deploymentName" ? `Azure deployment name (${currentEntry.label})` : isAzure && fieldStage === "baseUrl" ? `Azure endpoint URL` : isAzure && fieldStage === "apiKey" ? `${currentEntry.label} API Key` : isAzure && fieldStage === "apiVersion" ? `Azure API version (e.g. 2024-08-01-preview)` : isCompat && fieldStage === "label" ? `Name for this endpoint (e.g. Groq)` : isCompat && fieldStage === "baseUrl" ? `Base URL (e.g. https://api.groq.com/openai/v1)` : isCompat && fieldStage === "apiKey" ? `${currentEntry.label} API Key (optional)` : isOllama ? `Ollama URL` : `${currentEntry.label} API Key`;
13986
+ const keyOptional = isCompat && fieldStage === "apiKey";
13924
13987
  const isMasked = fieldStage === "apiKey" && !isOllama;
13925
13988
  return /* @__PURE__ */ jsxRuntime.jsxs(Frame, { theme, phase: "keys", children: [
13926
13989
  doneEntries.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { flexDirection: "column", marginBottom: 1, children: doneEntries.map((e) => /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { children: [
@@ -13938,8 +14001,8 @@ function SetupWizard({ workspacePath, onComplete }) {
13938
14001
  {
13939
14002
  theme,
13940
14003
  label: prompt,
13941
- tag: isOllama ? "optional \u2014 Enter for default" : "required",
13942
- tagColor: isOllama ? theme.colors.muted : theme.colors.error,
14004
+ tag: isOllama ? "optional \u2014 Enter for default" : keyOptional ? "optional \u2014 Enter to skip" : "required",
14005
+ tagColor: isOllama || keyOptional ? theme.colors.muted : theme.colors.error,
13943
14006
  active: true,
13944
14007
  children: /* @__PURE__ */ jsxRuntime.jsx(
13945
14008
  SafeTextInput,