claudish 7.43.0 → 7.45.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.
Files changed (2) hide show
  1. package/dist/index.js +729 -319
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -729,7 +729,7 @@ var init_onepassword_config = __esm(() => {
729
729
  });
730
730
 
731
731
  // src/version.ts
732
- var VERSION = "7.43.0";
732
+ var VERSION = "7.45.0";
733
733
 
734
734
  // src/logger.ts
735
735
  var exports_logger = {};
@@ -28338,6 +28338,7 @@ var init_provider_definitions = __esm(() => {
28338
28338
  { prefix: "ollama:", stripPrefix: true }
28339
28339
  ],
28340
28340
  isLocal: true,
28341
+ modelDiscovery: { path: "/api/tags", format: "ollama-tags" },
28341
28342
  description: "Local Ollama (ollama@)"
28342
28343
  },
28343
28344
  {
@@ -28359,6 +28360,7 @@ var init_provider_definitions = __esm(() => {
28359
28360
  { prefix: "mlstudio:", stripPrefix: true }
28360
28361
  ],
28361
28362
  isLocal: true,
28363
+ modelDiscovery: { path: "/v1/models", format: "openai-models-list" },
28362
28364
  description: "Local LM Studio (lms@)"
28363
28365
  },
28364
28366
  {
@@ -28553,6 +28555,217 @@ var init_auto_route = __esm(() => {
28553
28555
  })();
28554
28556
  });
28555
28557
 
28558
+ // src/providers/all-models-cache.ts
28559
+ import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
28560
+ import { homedir as homedir8 } from "os";
28561
+ import { dirname as dirname5, join as join8 } from "path";
28562
+ function readAllModelsCache(path = ALL_MODELS_CACHE_PATH) {
28563
+ if (!existsSync6(path))
28564
+ return null;
28565
+ let raw;
28566
+ try {
28567
+ raw = JSON.parse(readFileSync6(path, "utf-8"));
28568
+ } catch {
28569
+ return null;
28570
+ }
28571
+ if (!raw || typeof raw !== "object")
28572
+ return null;
28573
+ const data = raw;
28574
+ const lastUpdated = typeof data.lastUpdated === "string" ? data.lastUpdated : new Date(0).toISOString();
28575
+ const models = Array.isArray(data.models) ? data.models : [];
28576
+ const entries = Array.isArray(data.entries) ? data.entries : [];
28577
+ return {
28578
+ version: 2,
28579
+ lastUpdated,
28580
+ entries,
28581
+ models
28582
+ };
28583
+ }
28584
+ function writeAllModelsCache(data, path = ALL_MODELS_CACHE_PATH) {
28585
+ const existing = readAllModelsCache(path);
28586
+ const merged = {
28587
+ version: 2,
28588
+ lastUpdated: data.lastUpdated ?? new Date().toISOString(),
28589
+ entries: data.entries ?? existing?.entries ?? [],
28590
+ models: data.models ?? existing?.models ?? []
28591
+ };
28592
+ mkdirSync6(dirname5(path), { recursive: true });
28593
+ writeFileSync6(path, JSON.stringify(merged), "utf-8");
28594
+ }
28595
+ var ALL_MODELS_CACHE_PATH;
28596
+ var init_all_models_cache = __esm(() => {
28597
+ ALL_MODELS_CACHE_PATH = join8(homedir8(), ".claudish", "all-models.json");
28598
+ });
28599
+
28600
+ // src/providers/catalog-client.ts
28601
+ function getCatalogEntries() {
28602
+ if (_memCache)
28603
+ return _memCache;
28604
+ const cache = readAllModelsCache();
28605
+ if (!cache)
28606
+ return null;
28607
+ if (cache.entries.length > 0) {
28608
+ _memCache = cache.entries;
28609
+ return _memCache;
28610
+ }
28611
+ if (cache.models.length > 0) {
28612
+ _memCache = cache.models.map((m) => ({
28613
+ modelId: m.id.includes("/") ? m.id.split("/").slice(1).join("/") : m.id,
28614
+ aliases: [],
28615
+ sources: { "openrouter-api": { externalId: m.id } }
28616
+ }));
28617
+ return _memCache;
28618
+ }
28619
+ return null;
28620
+ }
28621
+ function isCatalogWarm() {
28622
+ return _memCache !== null && _memCache.length > 0;
28623
+ }
28624
+ function externalIdFor(entry, provider) {
28625
+ const agg = entry.aggregators?.find((a) => a.provider === provider);
28626
+ if (agg?.externalId)
28627
+ return agg.externalId;
28628
+ if (provider !== "openrouter")
28629
+ return null;
28630
+ const orSource = entry.sources["openrouter-api"];
28631
+ if (orSource?.externalId)
28632
+ return orSource.externalId;
28633
+ for (const src of Object.values(entry.sources)) {
28634
+ if (src.externalId.includes("/"))
28635
+ return src.externalId;
28636
+ }
28637
+ return null;
28638
+ }
28639
+ function resolveExternalId(userInput, provider) {
28640
+ const entries = getCatalogEntries();
28641
+ if (userInput.includes("/")) {
28642
+ if (entries) {
28643
+ for (const entry of entries) {
28644
+ for (const src of Object.values(entry.sources)) {
28645
+ if (src.externalId === userInput)
28646
+ return userInput;
28647
+ }
28648
+ }
28649
+ }
28650
+ return userInput;
28651
+ }
28652
+ if (!entries)
28653
+ return null;
28654
+ const byModelId = entries.find((e) => e.modelId === userInput);
28655
+ if (byModelId)
28656
+ return externalIdFor(byModelId, provider);
28657
+ const byAlias = entries.find((e) => e.aliases.includes(userInput));
28658
+ if (byAlias) {
28659
+ const id = externalIdFor(byAlias, provider);
28660
+ if (id)
28661
+ return id;
28662
+ }
28663
+ for (const entry of entries) {
28664
+ for (const src of Object.values(entry.sources)) {
28665
+ if (src.externalId === userInput) {
28666
+ const id = externalIdFor(entry, provider);
28667
+ if (id)
28668
+ return id;
28669
+ }
28670
+ }
28671
+ }
28672
+ const suffix = `/${userInput}`;
28673
+ for (const entry of entries) {
28674
+ const id = externalIdFor(entry, provider);
28675
+ if (id?.endsWith(suffix))
28676
+ return id;
28677
+ }
28678
+ const lowerSuffix = `/${userInput.toLowerCase()}`;
28679
+ for (const entry of entries) {
28680
+ const id = externalIdFor(entry, provider);
28681
+ if (id?.toLowerCase().endsWith(lowerSuffix))
28682
+ return id;
28683
+ }
28684
+ return null;
28685
+ }
28686
+ function resolveModelNameSync(userInput, targetProvider) {
28687
+ if (targetProvider !== "openrouter" && userInput.includes("/")) {
28688
+ return { resolvedId: userInput, wasResolved: false, sourceLabel: "passthrough" };
28689
+ }
28690
+ const resolved = resolveExternalId(userInput, targetProvider);
28691
+ if (!resolved || resolved === userInput) {
28692
+ return { resolvedId: userInput, wasResolved: false, sourceLabel: "passthrough" };
28693
+ }
28694
+ return { resolvedId: resolved, wasResolved: true, sourceLabel: `${targetProvider} catalog` };
28695
+ }
28696
+ function resolveTargetForCatalog(target, isExplicitProvider, model, provider, resolve = resolveModelNameSync) {
28697
+ if (!isExplicitProvider)
28698
+ return { target, resolution: null };
28699
+ const resolution = resolve(model, provider);
28700
+ return {
28701
+ target: resolution.wasResolved ? `${provider}@${resolution.resolvedId}` : target,
28702
+ resolution
28703
+ };
28704
+ }
28705
+ function logResolution(userInput, result, quiet = false) {
28706
+ if (result.wasResolved && !quiet) {
28707
+ process.stderr.write(`[Model] Resolved "${userInput}" \u2192 "${result.resolvedId}" (${result.sourceLabel})
28708
+ `);
28709
+ }
28710
+ }
28711
+ async function refreshCatalog(timeoutMs) {
28712
+ let response;
28713
+ try {
28714
+ response = await fetch(FIREBASE_CATALOG_URL, { signal: AbortSignal.timeout(timeoutMs) });
28715
+ } catch (err) {
28716
+ const name = err?.name;
28717
+ const reason = name === "TimeoutError" || name === "AbortError" ? "timeout" : "network";
28718
+ return { kind: "fetch_failed", reason };
28719
+ }
28720
+ if (!response.ok)
28721
+ return { kind: "fetch_failed", reason: "http_error" };
28722
+ let data;
28723
+ try {
28724
+ data = await response.json();
28725
+ } catch {
28726
+ return { kind: "fetch_failed", reason: "network" };
28727
+ }
28728
+ if (!Array.isArray(data.models) || data.models.length === 0) {
28729
+ return { kind: "fetch_failed", reason: "empty" };
28730
+ }
28731
+ const backwardCompatModels = [];
28732
+ for (const entry of data.models) {
28733
+ const id = externalIdFor(entry, "openrouter");
28734
+ if (id)
28735
+ backwardCompatModels.push({ id });
28736
+ }
28737
+ _memCache = data.models;
28738
+ writeAllModelsCache({ entries: data.models, models: backwardCompatModels });
28739
+ _warmPromise = Promise.resolve();
28740
+ return { kind: "refreshed", modelCount: data.models.length };
28741
+ }
28742
+ async function warmCatalog() {
28743
+ if (!_warmPromise) {
28744
+ _warmPromise = refreshCatalog(8000).then(() => {
28745
+ return;
28746
+ });
28747
+ }
28748
+ await _warmPromise;
28749
+ }
28750
+ async function ensureCatalogReady(timeoutMs = 5000) {
28751
+ if (isCatalogWarm())
28752
+ return;
28753
+ if (!_warmPromise) {
28754
+ _warmPromise = refreshCatalog(8000).then(() => {
28755
+ return;
28756
+ });
28757
+ }
28758
+ await Promise.race([
28759
+ _warmPromise,
28760
+ new Promise((resolve) => setTimeout(resolve, timeoutMs))
28761
+ ]);
28762
+ }
28763
+ var FIREBASE_CATALOG_URL, _memCache = null, _warmPromise = null;
28764
+ var init_catalog_client = __esm(() => {
28765
+ init_all_models_cache();
28766
+ FIREBASE_CATALOG_URL = process.env.CLAUDISH_CATALOG_URL ?? process.env.FIREBASE_CATALOG_URL ?? "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels?status=active&catalog=slim&limit=1000";
28767
+ });
28768
+
28556
28769
  // ../../node_modules/.bun/zod@4.1.13/node_modules/zod/index.js
28557
28770
  var init_zod = __esm(() => {
28558
28771
  init_external2();
@@ -28612,48 +28825,6 @@ var init_remote_provider_types = __esm(() => {
28612
28825
  };
28613
28826
  });
28614
28827
 
28615
- // src/providers/all-models-cache.ts
28616
- import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
28617
- import { homedir as homedir8 } from "os";
28618
- import { dirname as dirname5, join as join8 } from "path";
28619
- function readAllModelsCache(path = ALL_MODELS_CACHE_PATH) {
28620
- if (!existsSync6(path))
28621
- return null;
28622
- let raw;
28623
- try {
28624
- raw = JSON.parse(readFileSync6(path, "utf-8"));
28625
- } catch {
28626
- return null;
28627
- }
28628
- if (!raw || typeof raw !== "object")
28629
- return null;
28630
- const data = raw;
28631
- const lastUpdated = typeof data.lastUpdated === "string" ? data.lastUpdated : new Date(0).toISOString();
28632
- const models = Array.isArray(data.models) ? data.models : [];
28633
- const entries = Array.isArray(data.entries) ? data.entries : [];
28634
- return {
28635
- version: 2,
28636
- lastUpdated,
28637
- entries,
28638
- models
28639
- };
28640
- }
28641
- function writeAllModelsCache(data, path = ALL_MODELS_CACHE_PATH) {
28642
- const existing = readAllModelsCache(path);
28643
- const merged = {
28644
- version: 2,
28645
- lastUpdated: data.lastUpdated ?? new Date().toISOString(),
28646
- entries: data.entries ?? existing?.entries ?? [],
28647
- models: data.models ?? existing?.models ?? []
28648
- };
28649
- mkdirSync6(dirname5(path), { recursive: true });
28650
- writeFileSync6(path, JSON.stringify(merged), "utf-8");
28651
- }
28652
- var ALL_MODELS_CACHE_PATH;
28653
- var init_all_models_cache = __esm(() => {
28654
- ALL_MODELS_CACHE_PATH = join8(homedir8(), ".claudish", "all-models.json");
28655
- });
28656
-
28657
28828
  // src/adapters/model-catalog.ts
28658
28829
  function lookupModel(modelId, cachePath) {
28659
28830
  const entry = findCacheEntry(modelId, cachePath);
@@ -30447,10 +30618,10 @@ ${lines.join(`
30447
30618
  }
30448
30619
  var ANTIGRAVITY_BASE = "https://cloudcode-pa.googleapis.com", ANTIGRAVITY_ENDPOINT, MAX_RETRY_ATTEMPTS = 3, DEFAULT_RATE_LIMIT_DELAY_MS = 1e4, REASONING_TIER_RANK;
30449
30620
  var init_antigravity = __esm(() => {
30621
+ init_model_catalog();
30450
30622
  init_antigravity_token();
30451
- init_authority();
30452
30623
  init_antigravity_user();
30453
- init_model_catalog();
30624
+ init_authority();
30454
30625
  init_gemini_queue();
30455
30626
  init_logger();
30456
30627
  ANTIGRAVITY_ENDPOINT = `${ANTIGRAVITY_BASE}/v1internal:streamGenerateContent?alt=sse`;
@@ -34927,6 +35098,11 @@ var init_digest = __esm(() => {
34927
35098
  });
34928
35099
 
34929
35100
  // src/providers/ollama-discovery.ts
35101
+ var exports_ollama_discovery = {};
35102
+ __export(exports_ollama_discovery, {
35103
+ ollamaBaseUrl: () => ollamaBaseUrl,
35104
+ fetchOllamaModels: () => fetchOllamaModels
35105
+ });
34930
35106
  function ollamaBaseUrl() {
34931
35107
  return process.env.OLLAMA_HOST || process.env.OLLAMA_BASE_URL || "http://localhost:11434";
34932
35108
  }
@@ -36495,6 +36671,10 @@ function warnGoAliasDeprecatedOnce() {
36495
36671
  process.stderr.write(`[claudish] go@ is deprecated \u2014 use ag@<model> (Antigravity). Routing there.
36496
36672
  `);
36497
36673
  }
36674
+ function parseModelChain(modelSpec) {
36675
+ const parts = modelSpec.split(MODEL_CHAIN_SEPARATOR).map((s) => s.trim()).filter(Boolean);
36676
+ return parts.length > 0 ? parts : [modelSpec];
36677
+ }
36498
36678
  function parseModelSpec(modelSpec) {
36499
36679
  const original = modelSpec;
36500
36680
  if (modelSpec.startsWith("http://") || modelSpec.startsWith("https://")) {
@@ -36594,7 +36774,7 @@ function getLegacySyntaxWarning(parsed) {
36594
36774
  return `Deprecation warning: "${parsed.original}" uses legacy prefix syntax.
36595
36775
  ` + ` Consider using: ${newSyntax}`;
36596
36776
  }
36597
- var PROVIDER_SHORTCUTS, _goDeprecationWarned = false, LOCAL_PROVIDERS, NATIVE_MODEL_PATTERNS, LEGACY_PREFIX_PATTERNS;
36777
+ var PROVIDER_SHORTCUTS, _goDeprecationWarned = false, LOCAL_PROVIDERS, NATIVE_MODEL_PATTERNS, LEGACY_PREFIX_PATTERNS, MODEL_CHAIN_SEPARATOR = "+";
36598
36778
  var init_model_parser = __esm(() => {
36599
36779
  init_provider_definitions();
36600
36780
  PROVIDER_SHORTCUTS = getShortcuts();
@@ -40501,6 +40681,13 @@ class ComposedHandler {
40501
40681
  response = next;
40502
40682
  }
40503
40683
  }
40684
+ describeComposition() {
40685
+ return {
40686
+ transport: this.provider.name,
40687
+ streamFormat: this.resolveStreamFormat(),
40688
+ endpoint: this.provider.getEndpoint(this.bareModelName)
40689
+ };
40690
+ }
40504
40691
  resolveStreamFormat() {
40505
40692
  return this.provider.overrideStreamFormat?.() ?? this.explicitAdapter?.getStreamFormat() ?? this.modelAdapter?.getStreamFormat() ?? this.getAdapter().getStreamFormat();
40506
40693
  }
@@ -40796,6 +40983,7 @@ __export(exports_devin_models, {
40796
40983
  getServedDevinModels: () => getServedDevinModels,
40797
40984
  fetchDevinModelConfigs: () => fetchDevinModelConfigs,
40798
40985
  fetchDevinAllowedUids: () => fetchDevinAllowedUids,
40986
+ decodeModelConfigs: () => decodeModelConfigs,
40799
40987
  _resetDevinModelCache: () => _resetDevinModelCache
40800
40988
  });
40801
40989
  function unaryMetadata(apiKey) {
@@ -40835,11 +41023,59 @@ function decodeModelDetails(payload) {
40835
41023
  }
40836
41024
  return { maxOutput, family };
40837
41025
  }
41026
+ function decodeFamilyMetadata(payload) {
41027
+ let groupLabel;
41028
+ const axes = [];
41029
+ for (const sub of parseTLV(payload)) {
41030
+ if (sub.no === 1 && sub.wire === 2) {
41031
+ groupLabel = readString(sub) || undefined;
41032
+ continue;
41033
+ }
41034
+ if (sub.no !== 2 || sub.wire !== 2)
41035
+ continue;
41036
+ const axis = { key: "", enabled: false };
41037
+ for (const entry of parseTLV(sub.payload)) {
41038
+ if (entry.no === 1 && entry.wire === 2)
41039
+ axis.key = readString(entry);
41040
+ else if (entry.no === 2 && entry.wire === 2) {
41041
+ for (const value of parseTLV(entry.payload)) {
41042
+ if (value.no === 1 && value.wire === 0)
41043
+ axis.enabled = readVarintValue(value) === 1;
41044
+ else if (value.no === 2 && value.wire === 2)
41045
+ axis.label = readString(value) || undefined;
41046
+ }
41047
+ }
41048
+ }
41049
+ if (axis.key)
41050
+ axes.push(axis);
41051
+ }
41052
+ return { groupLabel, axes };
41053
+ }
41054
+ function decodePromo(payload) {
41055
+ for (const sub of parseTLV(payload)) {
41056
+ if (sub.wire !== 2)
41057
+ continue;
41058
+ for (const inner of parseTLV(sub.payload)) {
41059
+ if (inner.no === 1 && inner.wire === 0) {
41060
+ const expiresAt = readVarintValue(inner);
41061
+ if (expiresAt > 0)
41062
+ return { expiresAt };
41063
+ }
41064
+ }
41065
+ }
41066
+ return;
41067
+ }
40838
41068
  function decodeModelConfig(payload) {
40839
41069
  let uid = "";
40840
41070
  let displayName = "";
40841
41071
  let contextWindow = 0;
40842
41072
  let details = { maxOutput: 0, family: "" };
41073
+ let family = { axes: [] };
41074
+ let creditMultiplier;
41075
+ let costTier;
41076
+ let isFamilyDefault = false;
41077
+ let isRecommended = false;
41078
+ let promo;
40843
41079
  for (const field of parseTLV(payload)) {
40844
41080
  if (field.no === 22 && field.wire === 2)
40845
41081
  uid = readString(field);
@@ -40849,10 +41085,34 @@ function decodeModelConfig(payload) {
40849
41085
  contextWindow = readVarintValue(field);
40850
41086
  else if (field.no === 23 && field.wire === 2)
40851
41087
  details = decodeModelDetails(field.payload);
41088
+ else if (field.no === 30 && field.wire === 2)
41089
+ family = decodeFamilyMetadata(field.payload);
41090
+ else if (field.no === 3 && field.wire === 5)
41091
+ creditMultiplier = readFloat32LE(field);
41092
+ else if (field.no === 24 && field.wire === 0)
41093
+ costTier = readVarintValue(field);
41094
+ else if (field.no === 31 && field.wire === 0)
41095
+ isFamilyDefault = readVarintValue(field) === 1;
41096
+ else if (field.no === 11 && field.wire === 0)
41097
+ isRecommended = readVarintValue(field) === 1;
41098
+ else if (field.no === 19 && field.wire === 2)
41099
+ promo = decodePromo(field.payload);
40852
41100
  }
40853
41101
  if (!uid)
40854
41102
  return null;
40855
- return { uid, displayName: displayName || uid, contextWindow, ...details };
41103
+ return {
41104
+ uid,
41105
+ displayName: displayName || uid,
41106
+ contextWindow,
41107
+ ...details,
41108
+ groupLabel: family.groupLabel,
41109
+ axes: family.axes,
41110
+ creditMultiplier,
41111
+ costTier,
41112
+ isFamilyDefault,
41113
+ isRecommended,
41114
+ promo
41115
+ };
40856
41116
  }
40857
41117
  function topLevelDelimited(body, fieldNumber) {
40858
41118
  return parseTLV(body).filter((field) => field.no === fieldNumber && field.wire === 2);
@@ -40861,13 +41121,17 @@ async function fetchDevinModelConfigs(apiKey) {
40861
41121
  const body = await postUnary(MODEL_CONFIGS_PATH, apiKey);
40862
41122
  if (!body)
40863
41123
  return [];
41124
+ const configs = decodeModelConfigs(body);
41125
+ log(`[Devin] GetCliModelConfigs: ${configs.length} configs`);
41126
+ return configs;
41127
+ }
41128
+ function decodeModelConfigs(body) {
40864
41129
  const configs = [];
40865
41130
  for (const field of topLevelDelimited(body, 1)) {
40866
41131
  const config2 = decodeModelConfig(field.payload);
40867
41132
  if (config2)
40868
41133
  configs.push(config2);
40869
41134
  }
40870
- log(`[Devin] GetCliModelConfigs: ${configs.length} configs`);
40871
41135
  return configs;
40872
41136
  }
40873
41137
  async function fetchDevinAllowedUids(apiKey) {
@@ -40923,7 +41187,246 @@ var init_devin_models = __esm(() => {
40923
41187
  ROSTER_TTL_MS = 5 * 60 * 1000;
40924
41188
  });
40925
41189
 
41190
+ // src/providers/model-resolvers/types.ts
41191
+ function nonEmpty(value) {
41192
+ const trimmed2 = value?.trim();
41193
+ return trimmed2 ? trimmed2 : undefined;
41194
+ }
41195
+ function groupKeyOf(entry) {
41196
+ return nonEmpty(entry.groupLabel) ?? nonEmpty(entry.family) ?? entry.wireId;
41197
+ }
41198
+ function offerIsLive(offer, now = Date.now()) {
41199
+ if (!offer)
41200
+ return false;
41201
+ if (offer.expiresAt === undefined)
41202
+ return true;
41203
+ return offer.expiresAt * 1000 > now;
41204
+ }
41205
+
41206
+ // src/providers/model-resolvers/devin.ts
41207
+ var exports_devin = {};
41208
+ __export(exports_devin, {
41209
+ devinRosterEntry: () => devinRosterEntry,
41210
+ devinHasSpeedPremium: () => devinHasSpeedPremium,
41211
+ devinEffortOf: () => devinEffortOf,
41212
+ DevinModelResolver: () => DevinModelResolver
41213
+ });
41214
+ function toEffortLevel(label) {
41215
+ const normalised = label?.toLowerCase().replace(/[^a-z]/g, "");
41216
+ if (!normalised)
41217
+ return;
41218
+ if (normalised === "nothinking")
41219
+ return "none";
41220
+ return isEffortLevel(normalised) ? normalised : undefined;
41221
+ }
41222
+ function effortFromUid(uid) {
41223
+ for (const part of uid.toLowerCase().split("-").reverse()) {
41224
+ if (isEffortLevel(part))
41225
+ return part;
41226
+ if (!SPEED_SUFFIXES.includes(part) && part !== "1m")
41227
+ break;
41228
+ }
41229
+ return;
41230
+ }
41231
+ function devinEffortOf(config2) {
41232
+ const axis = config2.axes.find((a) => EFFORT_AXIS_KEYS.includes(a.key.toLowerCase()));
41233
+ return toEffortLevel(axis?.label) ?? effortFromUid(config2.uid);
41234
+ }
41235
+ function devinHasSpeedPremium(config2) {
41236
+ const axis = config2.axes.find((a) => a.key.toLowerCase() === SPEED_AXIS_KEY);
41237
+ if (axis)
41238
+ return axis.enabled;
41239
+ return SPEED_SUFFIXES.some((suffix) => config2.uid.toLowerCase().endsWith(`-${suffix}`));
41240
+ }
41241
+ function namesEffortTier(id) {
41242
+ return id.toLowerCase().split("-").some((part) => isEffortLevel(part));
41243
+ }
41244
+ function modifiersOf(config2) {
41245
+ const parts = config2.uid.toLowerCase().split("-");
41246
+ const named = SPEED_SUFFIXES.filter((suffix) => parts.includes(suffix));
41247
+ if (named.length > 0)
41248
+ return named;
41249
+ return devinHasSpeedPremium(config2) ? ["premium"] : [];
41250
+ }
41251
+ function devinRosterEntry(config2) {
41252
+ return {
41253
+ wireId: config2.uid,
41254
+ displayName: config2.displayName,
41255
+ contextWindow: config2.contextWindow,
41256
+ groupLabel: config2.groupLabel,
41257
+ family: config2.family,
41258
+ costFactor: config2.creditMultiplier,
41259
+ costTier: config2.costTier,
41260
+ isFamilyDefault: config2.isFamilyDefault,
41261
+ isRecommended: config2.isRecommended,
41262
+ axes: config2.axes,
41263
+ offer: config2.promo ? { kind: "promo", expiresAt: config2.promo.expiresAt } : undefined
41264
+ };
41265
+ }
41266
+ function asConfig(entry) {
41267
+ return {
41268
+ uid: entry.wireId,
41269
+ displayName: entry.displayName ?? entry.wireId,
41270
+ contextWindow: entry.contextWindow ?? 0,
41271
+ maxOutput: 0,
41272
+ family: entry.family ?? "",
41273
+ groupLabel: entry.groupLabel,
41274
+ axes: entry.axes ?? [],
41275
+ creditMultiplier: entry.costFactor,
41276
+ costTier: entry.costTier,
41277
+ isFamilyDefault: entry.isFamilyDefault ?? false,
41278
+ isRecommended: entry.isRecommended ?? false
41279
+ };
41280
+ }
41281
+ function byCost(a, b) {
41282
+ const tier = (a.costTier ?? Number.MAX_SAFE_INTEGER) - (b.costTier ?? Number.MAX_SAFE_INTEGER);
41283
+ if (tier !== 0)
41284
+ return tier;
41285
+ const cost = (a.costFactor ?? Number.MAX_SAFE_INTEGER) - (b.costFactor ?? Number.MAX_SAFE_INTEGER);
41286
+ if (cost !== 0)
41287
+ return cost;
41288
+ const length = a.wireId.length - b.wireId.length;
41289
+ return length !== 0 ? length : a.wireId.localeCompare(b.wireId);
41290
+ }
41291
+ function defaultOf(group) {
41292
+ const declared = group.find((entry) => entry.isFamilyDefault);
41293
+ if (declared)
41294
+ return declared;
41295
+ const plain = group.filter((entry) => !devinHasSpeedPremium(asConfig(entry)));
41296
+ const pool = plain.length > 0 ? plain : group;
41297
+ const tierless = pool.filter((entry) => !namesEffortTier(entry.wireId));
41298
+ return [...tierless.length > 0 ? tierless : pool].sort(byCost)[0];
41299
+ }
41300
+ function keyOf(entry) {
41301
+ return `${groupKeyOf(entry)}\x00${entry.contextWindow ?? 0}`;
41302
+ }
41303
+ function formatWindow(tokens) {
41304
+ return tokens >= 1e6 ? `${Math.round(tokens / 1e5) / 10}M`.replace(".0M", "M") : `${Math.round(tokens / 1000)}K`;
41305
+ }
41306
+
41307
+ class DevinModelResolver {
41308
+ provider = "devin";
41309
+ collapse(roster) {
41310
+ const groups = new Map;
41311
+ for (const entry of roster) {
41312
+ const key = keyOf(entry);
41313
+ const bucket = groups.get(key);
41314
+ if (bucket)
41315
+ bucket.push(entry);
41316
+ else
41317
+ groups.set(key, [entry]);
41318
+ }
41319
+ const windowsPerLabel = new Map;
41320
+ for (const entry of roster) {
41321
+ const label = groupKeyOf(entry);
41322
+ const seen = windowsPerLabel.get(label) ?? new Set;
41323
+ seen.add(entry.contextWindow ?? 0);
41324
+ windowsPerLabel.set(label, seen);
41325
+ }
41326
+ const choices = [];
41327
+ for (const group of groups.values()) {
41328
+ const chosen = defaultOf(group);
41329
+ const label = groupKeyOf(chosen);
41330
+ const ambiguous = (windowsPerLabel.get(label)?.size ?? 1) > 1;
41331
+ const window2 = chosen.contextWindow ?? 0;
41332
+ const variants = group.map((entry) => {
41333
+ const config2 = asConfig(entry);
41334
+ return {
41335
+ wireId: entry.wireId,
41336
+ effort: devinEffortOf(config2),
41337
+ modifiers: modifiersOf(config2),
41338
+ costFactor: entry.costFactor
41339
+ };
41340
+ });
41341
+ choices.push({
41342
+ id: chosen.wireId,
41343
+ displayName: ambiguous && window2 > 0 ? `${label} (${formatWindow(window2)})` : label,
41344
+ contextWindow: chosen.contextWindow,
41345
+ variants,
41346
+ costFactor: chosen.costFactor,
41347
+ offer: chosen.offer,
41348
+ isRecommended: chosen.isRecommended
41349
+ });
41350
+ }
41351
+ return choices;
41352
+ }
41353
+ expand(selection, roster, ctx) {
41354
+ const requested = selection.trim();
41355
+ if (!requested || roster.length === 0)
41356
+ return requested || selection;
41357
+ const groups = new Map;
41358
+ for (const entry of roster) {
41359
+ const key = keyOf(entry);
41360
+ const bucket = groups.get(key);
41361
+ if (bucket)
41362
+ bucket.push(entry);
41363
+ else
41364
+ groups.set(key, [entry]);
41365
+ }
41366
+ const lower = requested.toLowerCase();
41367
+ const exact = roster.find((entry) => entry.wireId.toLowerCase() === lower);
41368
+ if (exact) {
41369
+ if (!ctx.effort)
41370
+ return exact.wireId;
41371
+ const group = groups.get(keyOf(exact));
41372
+ if (exact.wireId === defaultOf(group).wireId)
41373
+ return pickForEffort(group, ctx.effort).wireId;
41374
+ if (namesEffortTier(exact.wireId))
41375
+ return exact.wireId;
41376
+ return pickForEffort(group, ctx.effort).wireId;
41377
+ }
41378
+ const named = roster.filter((entry) => {
41379
+ const label = nonEmpty(entry.groupLabel)?.toLowerCase();
41380
+ const family = nonEmpty(entry.family)?.toLowerCase();
41381
+ return label === lower || family === lower;
41382
+ });
41383
+ const pool = named.length > 0 ? named : roster.filter((entry) => entry.wireId.toLowerCase().startsWith(`${lower}-`));
41384
+ if (pool.length === 0)
41385
+ return requested;
41386
+ if (named.length === 0 && new Set(pool.map(keyOf)).size > 1)
41387
+ return requested;
41388
+ const chosen = pool.filter((entry) => entry.isFamilyDefault).sort(byCost)[0] ?? pool.slice().sort(byCost)[0];
41389
+ return pickForEffort(groups.get(keyOf(chosen)), ctx.effort).wireId;
41390
+ }
41391
+ }
41392
+ function pickForEffort(group, effort) {
41393
+ if (!effort)
41394
+ return defaultOf(group);
41395
+ const plain = group.filter((entry) => !devinHasSpeedPremium(asConfig(entry)));
41396
+ const pool = plain.length > 0 ? plain : group;
41397
+ const tiered = pool.map((entry) => ({ entry, effort: devinEffortOf(asConfig(entry)) })).filter((row) => row.effort !== undefined);
41398
+ if (tiered.length === 0)
41399
+ return defaultOf(group);
41400
+ const target = EFFORT_LEVELS.indexOf(effort);
41401
+ const ranked = tiered.slice().sort((a, b) => {
41402
+ const distanceA = Math.abs(EFFORT_LEVELS.indexOf(a.effort) - target);
41403
+ const distanceB = Math.abs(EFFORT_LEVELS.indexOf(b.effort) - target);
41404
+ if (distanceA !== distanceB)
41405
+ return distanceA - distanceB;
41406
+ const levelA = EFFORT_LEVELS.indexOf(a.effort);
41407
+ const levelB = EFFORT_LEVELS.indexOf(b.effort);
41408
+ if (levelA !== levelB)
41409
+ return levelB - levelA;
41410
+ const defaultA = a.entry.isFamilyDefault ? 0 : 1;
41411
+ const defaultB = b.entry.isFamilyDefault ? 0 : 1;
41412
+ if (defaultA !== defaultB)
41413
+ return defaultA - defaultB;
41414
+ return byCost(a.entry, b.entry);
41415
+ });
41416
+ return ranked[0].entry;
41417
+ }
41418
+ var EFFORT_AXIS_KEYS, SPEED_AXIS_KEY = "fast mode", SPEED_SUFFIXES;
41419
+ var init_devin = __esm(() => {
41420
+ init_base_api_format();
41421
+ EFFORT_AXIS_KEYS = ["effort", "reasoning effort"];
41422
+ SPEED_SUFFIXES = ["fast", "priority"];
41423
+ });
41424
+
40926
41425
  // src/providers/model-discovery.ts
41426
+ function toRosterEntry(model) {
41427
+ const { id, ...rest } = model;
41428
+ return { wireId: id, ...rest };
41429
+ }
40927
41430
  function resolveBaseUrl(catalogName) {
40928
41431
  const def = getProviderByName(catalogName);
40929
41432
  if (!def)
@@ -40990,26 +41493,46 @@ async function discoverProviderModels(providerName) {
40990
41493
  log(`[model-discovery:${providerName}] no models for this subscription`);
40991
41494
  return [];
40992
41495
  }
40993
- const models2 = served.map((model) => ({
40994
- id: model.uid,
40995
- displayName: model.displayName,
40996
- contextWindow: model.contextWindow
40997
- }));
41496
+ const { devinRosterEntry: devinRosterEntry2 } = await Promise.resolve().then(() => (init_devin(), exports_devin));
41497
+ const models2 = served.map((model) => {
41498
+ const { wireId, ...rest } = devinRosterEntry2(model);
41499
+ return { id: wireId, ...rest };
41500
+ });
40998
41501
  log(`[model-discovery:${providerName}] discovered ${models2.length} models`);
40999
41502
  _cache.set(providerName, { models: models2, expiresAt: Date.now() + CACHE_TTL_MS });
41000
41503
  return models2;
41001
41504
  }
41505
+ if (descriptor.format === "ollama-tags") {
41506
+ const { fetchOllamaModels: fetchOllamaModels2 } = await Promise.resolve().then(() => exports_ollama_discovery);
41507
+ const installed = await fetchOllamaModels2({ enrichCapabilities: false });
41508
+ if (installed.length === 0)
41509
+ return [];
41510
+ const models2 = installed.map((model) => ({
41511
+ id: model.name,
41512
+ displayName: model.name,
41513
+ supportsTools: model.supportsTools
41514
+ }));
41515
+ log(`[model-discovery:${providerName}] discovered ${models2.length} local models`);
41516
+ _cache.set(providerName, { models: models2, expiresAt: Date.now() + CACHE_TTL_MS });
41517
+ return models2;
41518
+ }
41002
41519
  const baseUrl = resolveBaseUrl(providerName);
41003
41520
  if (!baseUrl)
41004
41521
  return [];
41005
41522
  const endpoint = `${baseUrl}${descriptor.path}`;
41006
41523
  let headers = {};
41007
- try {
41008
- const auth = await credentials.getRequestAuth(providerName, { model: "" });
41009
- headers = { ...auth.headers };
41010
- } catch (e) {
41011
- log(`[model-discovery:${providerName}] no credentials, skipping: ${e?.message}`);
41012
- return [];
41524
+ if (def.isLocal) {
41525
+ const key = def.apiKeyEnvVar ? process.env[def.apiKeyEnvVar] : undefined;
41526
+ if (key)
41527
+ headers = { Authorization: `Bearer ${key}` };
41528
+ } else {
41529
+ try {
41530
+ const auth = await credentials.getRequestAuth(providerName, { model: "" });
41531
+ headers = { ...auth.headers };
41532
+ } catch (e) {
41533
+ log(`[model-discovery:${providerName}] no credentials, skipping: ${e?.message}`);
41534
+ return [];
41535
+ }
41013
41536
  }
41014
41537
  let response;
41015
41538
  try {
@@ -41789,166 +42312,6 @@ var init_custom_endpoints_loader = __esm(() => {
41789
42312
  init_openai();
41790
42313
  });
41791
42314
 
41792
- // src/providers/catalog-client.ts
41793
- function getCatalogEntries() {
41794
- if (_memCache)
41795
- return _memCache;
41796
- const cache2 = readAllModelsCache();
41797
- if (!cache2)
41798
- return null;
41799
- if (cache2.entries.length > 0) {
41800
- _memCache = cache2.entries;
41801
- return _memCache;
41802
- }
41803
- if (cache2.models.length > 0) {
41804
- _memCache = cache2.models.map((m) => ({
41805
- modelId: m.id.includes("/") ? m.id.split("/").slice(1).join("/") : m.id,
41806
- aliases: [],
41807
- sources: { "openrouter-api": { externalId: m.id } }
41808
- }));
41809
- return _memCache;
41810
- }
41811
- return null;
41812
- }
41813
- function isCatalogWarm() {
41814
- return _memCache !== null && _memCache.length > 0;
41815
- }
41816
- function externalIdFor(entry, provider) {
41817
- const agg = entry.aggregators?.find((a) => a.provider === provider);
41818
- if (agg?.externalId)
41819
- return agg.externalId;
41820
- if (provider !== "openrouter")
41821
- return null;
41822
- const orSource = entry.sources["openrouter-api"];
41823
- if (orSource?.externalId)
41824
- return orSource.externalId;
41825
- for (const src of Object.values(entry.sources)) {
41826
- if (src.externalId.includes("/"))
41827
- return src.externalId;
41828
- }
41829
- return null;
41830
- }
41831
- function resolveExternalId(userInput, provider) {
41832
- const entries = getCatalogEntries();
41833
- if (userInput.includes("/")) {
41834
- if (entries) {
41835
- for (const entry of entries) {
41836
- for (const src of Object.values(entry.sources)) {
41837
- if (src.externalId === userInput)
41838
- return userInput;
41839
- }
41840
- }
41841
- }
41842
- return userInput;
41843
- }
41844
- if (!entries)
41845
- return null;
41846
- const byModelId = entries.find((e) => e.modelId === userInput);
41847
- if (byModelId)
41848
- return externalIdFor(byModelId, provider);
41849
- const byAlias = entries.find((e) => e.aliases.includes(userInput));
41850
- if (byAlias) {
41851
- const id = externalIdFor(byAlias, provider);
41852
- if (id)
41853
- return id;
41854
- }
41855
- for (const entry of entries) {
41856
- for (const src of Object.values(entry.sources)) {
41857
- if (src.externalId === userInput) {
41858
- const id = externalIdFor(entry, provider);
41859
- if (id)
41860
- return id;
41861
- }
41862
- }
41863
- }
41864
- const suffix = `/${userInput}`;
41865
- for (const entry of entries) {
41866
- const id = externalIdFor(entry, provider);
41867
- if (id?.endsWith(suffix))
41868
- return id;
41869
- }
41870
- const lowerSuffix = `/${userInput.toLowerCase()}`;
41871
- for (const entry of entries) {
41872
- const id = externalIdFor(entry, provider);
41873
- if (id?.toLowerCase().endsWith(lowerSuffix))
41874
- return id;
41875
- }
41876
- return null;
41877
- }
41878
- function resolveModelNameSync(userInput, targetProvider) {
41879
- if (targetProvider !== "openrouter" && userInput.includes("/")) {
41880
- return { resolvedId: userInput, wasResolved: false, sourceLabel: "passthrough" };
41881
- }
41882
- const resolved = resolveExternalId(userInput, targetProvider);
41883
- if (!resolved || resolved === userInput) {
41884
- return { resolvedId: userInput, wasResolved: false, sourceLabel: "passthrough" };
41885
- }
41886
- return { resolvedId: resolved, wasResolved: true, sourceLabel: `${targetProvider} catalog` };
41887
- }
41888
- function logResolution(userInput, result, quiet = false) {
41889
- if (result.wasResolved && !quiet) {
41890
- process.stderr.write(`[Model] Resolved "${userInput}" \u2192 "${result.resolvedId}" (${result.sourceLabel})
41891
- `);
41892
- }
41893
- }
41894
- async function refreshCatalog(timeoutMs) {
41895
- let response;
41896
- try {
41897
- response = await fetch(FIREBASE_CATALOG_URL, { signal: AbortSignal.timeout(timeoutMs) });
41898
- } catch (err) {
41899
- const name = err?.name;
41900
- const reason = name === "TimeoutError" || name === "AbortError" ? "timeout" : "network";
41901
- return { kind: "fetch_failed", reason };
41902
- }
41903
- if (!response.ok)
41904
- return { kind: "fetch_failed", reason: "http_error" };
41905
- let data;
41906
- try {
41907
- data = await response.json();
41908
- } catch {
41909
- return { kind: "fetch_failed", reason: "network" };
41910
- }
41911
- if (!Array.isArray(data.models) || data.models.length === 0) {
41912
- return { kind: "fetch_failed", reason: "empty" };
41913
- }
41914
- const backwardCompatModels = [];
41915
- for (const entry of data.models) {
41916
- const id = externalIdFor(entry, "openrouter");
41917
- if (id)
41918
- backwardCompatModels.push({ id });
41919
- }
41920
- _memCache = data.models;
41921
- writeAllModelsCache({ entries: data.models, models: backwardCompatModels });
41922
- _warmPromise = Promise.resolve();
41923
- return { kind: "refreshed", modelCount: data.models.length };
41924
- }
41925
- async function warmCatalog() {
41926
- if (!_warmPromise) {
41927
- _warmPromise = refreshCatalog(8000).then(() => {
41928
- return;
41929
- });
41930
- }
41931
- await _warmPromise;
41932
- }
41933
- async function ensureCatalogReady(timeoutMs = 5000) {
41934
- if (isCatalogWarm())
41935
- return;
41936
- if (!_warmPromise) {
41937
- _warmPromise = refreshCatalog(8000).then(() => {
41938
- return;
41939
- });
41940
- }
41941
- await Promise.race([
41942
- _warmPromise,
41943
- new Promise((resolve2) => setTimeout(resolve2, timeoutMs))
41944
- ]);
41945
- }
41946
- var FIREBASE_CATALOG_URL, _memCache = null, _warmPromise = null;
41947
- var init_catalog_client = __esm(() => {
41948
- init_all_models_cache();
41949
- FIREBASE_CATALOG_URL = process.env.CLAUDISH_CATALOG_URL ?? process.env.FIREBASE_CATALOG_URL ?? "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels?status=active&catalog=slim&limit=1000";
41950
- });
41951
-
41952
42315
  // src/providers/provider-registry.ts
41953
42316
  function resolveBaseUrl2(envVar, fallbackEnvVars, staticDefault) {
41954
42317
  for (const v of [envVar, ...fallbackEnvVars]) {
@@ -42447,8 +42810,8 @@ var init_routing_rules = __esm(() => {
42447
42810
  init_authority();
42448
42811
  init_profile_config();
42449
42812
  init_auto_route();
42450
- init_default_routing_rules();
42451
42813
  init_catalog_client();
42814
+ init_default_routing_rules();
42452
42815
  init_model_parser();
42453
42816
  init_model_parser();
42454
42817
  init_routing_hints();
@@ -42827,11 +43190,15 @@ async function pinSpecFor(model, router = route) {
42827
43190
  const plan = await router(model);
42828
43191
  if (plan.kind !== "ok")
42829
43192
  return null;
42830
- return normalizePinnedSpec(plan.primary);
43193
+ return joinPinnedChain([plan.primary, ...plan.fallbacks]);
42831
43194
  } catch {
42832
43195
  return null;
42833
43196
  }
42834
43197
  }
43198
+ function joinPinnedChain(routes) {
43199
+ const specs = routes.map(normalizePinnedSpec).filter((s) => !!s);
43200
+ return specs.length > 0 ? specs.join(MODEL_CHAIN_SEPARATOR) : null;
43201
+ }
42835
43202
  function normalizePinnedSpec(r) {
42836
43203
  const spec = r.modelSpec?.trim();
42837
43204
  if (!spec)
@@ -42857,8 +43224,8 @@ var CATALOG_WARM_TIMEOUT_MS = 5000, parentRoutingContextReady = false;
42857
43224
  var init_prehydrate = __esm(() => {
42858
43225
  init_profile_config();
42859
43226
  init_auto_route();
42860
- init_custom_endpoints_loader();
42861
43227
  init_catalog_client();
43228
+ init_custom_endpoints_loader();
42862
43229
  init_model_parser();
42863
43230
  init_onepassword();
42864
43231
  init_provider_resolver();
@@ -43115,6 +43482,18 @@ var init_signal_watcher = __esm(() => {
43115
43482
  QUESTION_PATTERNS = [/\?\s*$/m, /\bchoose\b.*:/im, /\bselect\b.*:/im, /\benter\b.*:/im];
43116
43483
  });
43117
43484
 
43485
+ // src/spawn-claudish.ts
43486
+ function resolveClaudishSpawn(env = process.env) {
43487
+ const bin = env[CLAUDISH_BIN_ENV]?.trim();
43488
+ if (!bin)
43489
+ return { command: "claudish", prefixArgs: [] };
43490
+ if (/\.(ts|tsx|js|mjs|cjs)$/.test(bin)) {
43491
+ return { command: process.execPath, prefixArgs: ["run", bin] };
43492
+ }
43493
+ return { command: bin, prefixArgs: [] };
43494
+ }
43495
+ var CLAUDISH_BIN_ENV = "CLAUDISH_BIN";
43496
+
43118
43497
  // src/channel/session-manager.ts
43119
43498
  import { spawn } from "child_process";
43120
43499
  import { randomUUID as randomUUID4 } from "crypto";
@@ -43155,7 +43534,8 @@ class SessionManager {
43155
43534
  "--quiet",
43156
43535
  ...opts.claudishFlags ?? []
43157
43536
  ];
43158
- const proc = spawn("claudish", args, {
43537
+ const spawnTarget = resolveClaudishSpawn();
43538
+ const proc = spawn(spawnTarget.command, [...spawnTarget.prefixArgs, ...args], {
43159
43539
  cwd: opts.cwd ?? process.cwd(),
43160
43540
  stdio: ["pipe", "pipe", "pipe"],
43161
43541
  shell: false
@@ -45806,7 +46186,7 @@ ${summary}`);
45806
46186
  error: truncate(parseErrorMessage(e.message), 200)
45807
46187
  }))
45808
46188
  }
45809
- }, 502);
46189
+ }, exhaustedChainStatus(errors3));
45810
46190
  }
45811
46191
  async shutdown() {
45812
46192
  for (const { handler } of this.candidates) {
@@ -45840,6 +46220,9 @@ function isRetryableError(status, errorBody, provider) {
45840
46220
  if (provider?.toLowerCase().includes("antigravity") && lower.includes("invalid argument")) {
45841
46221
  return true;
45842
46222
  }
46223
+ if (isProvider(provider, "opencodezen") && (lower.includes("upstream request failed") || lower.includes("error from provider ("))) {
46224
+ return true;
46225
+ }
45843
46226
  }
45844
46227
  if (status === 500) {
45845
46228
  if (lower.includes("insufficient balance") || lower.includes("insufficient credit") || lower.includes("quota exceeded") || lower.includes("billing")) {
@@ -45848,6 +46231,17 @@ function isRetryableError(status, errorBody, provider) {
45848
46231
  }
45849
46232
  return false;
45850
46233
  }
46234
+ function exhaustedChainStatus(errors3) {
46235
+ if (errors3.length === 0)
46236
+ return 400;
46237
+ const allTransient = errors3.every((e) => e.status === 429 || e.status === 503 || hasQuotaExhaustionWording(e.message));
46238
+ return allTransient ? 503 : 400;
46239
+ }
46240
+ function isProvider(provider, needle) {
46241
+ if (!provider)
46242
+ return false;
46243
+ return provider.toLowerCase().replace(/[^a-z0-9]/g, "").includes(needle);
46244
+ }
45851
46245
  function parseErrorMessage(body) {
45852
46246
  try {
45853
46247
  const parsed = JSON.parse(body);
@@ -46237,8 +46631,8 @@ Be direct. Limit your response to 300-500 words.`, COLLECTOR_SYSTEM_PROMPT = `Yo
46237
46631
  Be concise. Do not attribute advice to specific models.`;
46238
46632
  var init_native_handler_advisor = __esm(() => {
46239
46633
  init_logger();
46240
- init_catalog_query();
46241
46634
  init_catalog_client();
46635
+ init_catalog_query();
46242
46636
  init_model_parser();
46243
46637
  advisorToolUseIds = new Set;
46244
46638
  });
@@ -46989,56 +47383,44 @@ var init_api_key_provenance = __esm(() => {
46989
47383
  import_dotenv = __toESM(require_main(), 1);
46990
47384
  });
46991
47385
 
46992
- // src/providers/devin/model-id-resolver.ts
46993
- function parseDevinUidTier(uid) {
46994
- let base = uid.trim();
46995
- let fast = false;
46996
- if (base.toLowerCase().endsWith(FAST_SUFFIX)) {
46997
- fast = true;
46998
- base = base.slice(0, -FAST_SUFFIX.length);
46999
- }
47000
- const match2 = base.match(TIER_SUFFIX_RE);
47001
- const candidate = match2?.[1]?.toLowerCase();
47002
- return { tier: isEffortLevel(candidate) ? candidate : null, fast };
47386
+ // src/providers/model-resolvers/registry.ts
47387
+ function getModelResolver(provider) {
47388
+ return BY_PROVIDER.get(provider);
47003
47389
  }
47004
- function effortIndex(level) {
47005
- return EFFORT_LEVELS.indexOf(level);
47390
+ function collapseRoster(provider, roster) {
47391
+ const resolver = getModelResolver(provider);
47392
+ if (resolver)
47393
+ return resolver.collapse(roster);
47394
+ return roster.map((entry) => ({
47395
+ id: entry.wireId,
47396
+ displayName: entry.displayName ?? entry.wireId,
47397
+ contextWindow: entry.contextWindow,
47398
+ variants: [{ wireId: entry.wireId, modifiers: [] }],
47399
+ costFactor: entry.costFactor,
47400
+ offer: entry.offer,
47401
+ isRecommended: entry.isRecommended
47402
+ }));
47006
47403
  }
47404
+ function expandSelection(provider, selection, roster, ctx = {}) {
47405
+ return getModelResolver(provider)?.expand(selection, roster, ctx) ?? selection;
47406
+ }
47407
+ var RESOLVERS, BY_PROVIDER;
47408
+ var init_registry2 = __esm(() => {
47409
+ init_devin();
47410
+ RESOLVERS = [new DevinModelResolver];
47411
+ BY_PROVIDER = new Map(RESOLVERS.map((resolver) => [resolver.provider, resolver]));
47412
+ });
47413
+
47414
+ // src/providers/devin/model-id-resolver.ts
47007
47415
  function resolveDevinModelUid(requested, effort, served) {
47008
- const req = requested.trim();
47009
- if (!req || served.length === 0)
47010
- return req || requested;
47011
- const lower = req.toLowerCase();
47012
- const exact = served.find((model) => model.uid.toLowerCase() === lower);
47013
- if (exact)
47014
- return exact.uid;
47015
- const prefix = `${lower}-`;
47016
- const candidates = served.filter((model) => model.family.toLowerCase() === lower || model.uid.toLowerCase().startsWith(prefix));
47017
- if (candidates.length === 0)
47018
- return req;
47019
- const nonFast = candidates.filter((model) => !parseDevinUidTier(model.uid).fast);
47020
- const pool = nonFast.length > 0 ? nonFast : candidates;
47021
- if (pool.length === 1)
47022
- return pool[0].uid;
47023
- const tiered = pool.map((model) => ({ model, tier: parseDevinUidTier(model.uid).tier })).filter((entry) => entry.tier !== null);
47024
- if (tiered.length === 0)
47025
- return pool[0].uid;
47026
- const target = effort ? effortIndex(effort) : EFFORT_LEVELS.length - 1;
47027
- let best = tiered[0];
47028
- let bestDistance = Number.POSITIVE_INFINITY;
47029
- for (const entry of tiered) {
47030
- const distance = Math.abs(effortIndex(entry.tier) - target);
47031
- if (distance < bestDistance || distance === bestDistance && effortIndex(entry.tier) > effortIndex(best.tier)) {
47032
- best = entry;
47033
- bestDistance = distance;
47034
- }
47035
- }
47036
- return best.model.uid;
47416
+ const trimmed2 = requested.trim();
47417
+ if (!trimmed2 || served.length === 0)
47418
+ return trimmed2 || requested;
47419
+ return expandSelection("devin", trimmed2, served.map(devinRosterEntry), { effort });
47037
47420
  }
47038
- var FAST_SUFFIX = "-fast", TIER_SUFFIX_RE;
47039
47421
  var init_model_id_resolver = __esm(() => {
47040
- init_base_api_format();
47041
- TIER_SUFFIX_RE = new RegExp(`-(${[...EFFORT_LEVELS].sort((a, b) => b.length - a.length).join("|")})$`, "i");
47422
+ init_devin();
47423
+ init_registry2();
47042
47424
  });
47043
47425
 
47044
47426
  // src/providers/transport/devin.ts
@@ -47134,7 +47516,7 @@ class DevinProviderTransport {
47134
47516
  }
47135
47517
  }
47136
47518
  var CHAT_PATH = "/exa.api_server_pb.ApiServerService/GetChatMessage", CHAT_CONTENT_TYPE = "application/connect+proto";
47137
- var init_devin = __esm(() => {
47519
+ var init_devin2 = __esm(() => {
47138
47520
  init_authority();
47139
47521
  init_devin_credential();
47140
47522
  init_logger();
@@ -47352,7 +47734,7 @@ var init_provider_profiles = __esm(() => {
47352
47734
  init_runtime_providers();
47353
47735
  init_anthropic_compat();
47354
47736
  init_antigravity();
47355
- init_devin();
47737
+ init_devin2();
47356
47738
  init_gemini_apikey();
47357
47739
  init_litellm();
47358
47740
  init_ollamacloud();
@@ -47464,17 +47846,6 @@ var init_provider_profiles = __esm(() => {
47464
47846
  createHandler(ctx) {
47465
47847
  const zenApiKey = ctx.apiKey;
47466
47848
  const isGoProvider = ctx.provider.name === "opencode-zen-go";
47467
- if (ctx.modelName.toLowerCase().includes("minimax")) {
47468
- const bearerProvider = { ...ctx.provider, authScheme: "bearer" };
47469
- const transport2 = new AnthropicProviderTransport(bearerProvider, zenApiKey);
47470
- const adapter2 = new AnthropicAPIFormat(ctx.modelName, ctx.provider.name);
47471
- const handler2 = new ComposedHandler(transport2, ctx.targetModel, ctx.modelName, ctx.port, {
47472
- adapter: adapter2,
47473
- ...ctx.sharedOpts
47474
- });
47475
- log(`[Proxy] Created OpenCode Zen${isGoProvider ? " Go" : ""} (Anthropic composed): ${ctx.modelName}`);
47476
- return handler2;
47477
- }
47478
47849
  if (ctx.modelName.toLowerCase().startsWith("gpt-")) {
47479
47850
  const responsesProvider = { ...ctx.provider, apiPath: "/v1/responses" };
47480
47851
  const transport2 = new OpenAIProviderTransport(responsesProvider, ctx.modelName, zenApiKey);
@@ -48589,13 +48960,38 @@ async function createProxyServer(port, _openrouterApiKey, model, monitorMode = f
48589
48960
  target = model;
48590
48961
  }
48591
48962
  const invocationMode = detectInvocationMode(target, wasFromModelMap);
48963
+ if (options.modelChain && options.modelChain.length > 1 && target === options.modelChain[0]) {
48964
+ const cacheKey = `chain:${options.modelChain.join("+")}`;
48965
+ const cached2 = fallbackHandlerCache.get(cacheKey);
48966
+ if (cached2)
48967
+ return cached2;
48968
+ await ensureCatalogReady(5000);
48969
+ const candidates = [];
48970
+ for (const spec of options.modelChain) {
48971
+ const parsed = parseModelSpec(spec);
48972
+ const resolvedSpec = resolveTargetForCatalog(spec, parsed.isExplicitProvider, parsed.model, parsed.provider).target;
48973
+ const handler = parsed.provider === "openrouter" ? getOpenRouterHandler(resolvedSpec, invocationMode) : await getRemoteProviderHandler(resolvedSpec, invocationMode) ?? getLocalProviderHandler(resolvedSpec, invocationMode);
48974
+ if (handler) {
48975
+ candidates.push({ name: DISPLAY_NAMES[parsed.provider] ?? parsed.provider, handler });
48976
+ }
48977
+ }
48978
+ if (candidates.length > 0) {
48979
+ const resultHandler = candidates.length > 1 ? new FallbackHandler(candidates) : candidates[0].handler;
48980
+ fallbackHandlerCache.set(cacheKey, resultHandler);
48981
+ if (!options.quiet && candidates.length > 1) {
48982
+ logStderr(`[Route] ${candidates.length} pinned providers for ${target}: ${candidates.map((c) => c.name).join(" \u2192 ")}`);
48983
+ }
48984
+ return resultHandler;
48985
+ }
48986
+ }
48592
48987
  {
48593
48988
  const parsedTarget = parseModelSpec(target);
48594
- await ensureCatalogReady(5000);
48595
- const resolution = resolveModelNameSync(parsedTarget.model, parsedTarget.provider);
48596
- logResolution(parsedTarget.model, resolution, options.quiet);
48597
- if (resolution.wasResolved) {
48598
- target = `${parsedTarget.provider}@${resolution.resolvedId}`;
48989
+ if (parsedTarget.isExplicitProvider) {
48990
+ await ensureCatalogReady(5000);
48991
+ const outcome = resolveTargetForCatalog(target, parsedTarget.isExplicitProvider, parsedTarget.model, parsedTarget.provider);
48992
+ if (outcome.resolution)
48993
+ logResolution(parsedTarget.model, outcome.resolution, options.quiet);
48994
+ target = outcome.target;
48599
48995
  }
48600
48996
  }
48601
48997
  {
@@ -48812,8 +49208,9 @@ var init_proxy_server = __esm(() => {
48812
49208
  init_model_loader();
48813
49209
  init_profile_config();
48814
49210
  init_api_key_map();
48815
- init_custom_endpoints_loader();
49211
+ init_auto_route();
48816
49212
  init_catalog_client();
49213
+ init_custom_endpoints_loader();
48817
49214
  init_model_parser();
48818
49215
  init_provider_profiles();
48819
49216
  init_provider_registry();
@@ -49206,7 +49603,8 @@ async function runModels(sessionPath, opts = {}) {
49206
49603
  state: "RUNNING",
49207
49604
  startedAt: new Date().toISOString()
49208
49605
  });
49209
- const proc = spawn2("claudish", args, {
49606
+ const teamSpawnTarget = resolveClaudishSpawn();
49607
+ const proc = spawn2(teamSpawnTarget.command, [...teamSpawnTarget.prefixArgs, ...args], {
49210
49608
  stdio: ["pipe", "pipe", "pipe"],
49211
49609
  shell: false,
49212
49610
  env: {
@@ -64574,28 +64972,52 @@ async function pickModelFromList(provider, displayName, tierName, models) {
64574
64972
  });
64575
64973
  return selected === CUSTOM_VALUE ? null : selected;
64576
64974
  }
64975
+ function describeOffer(offer) {
64976
+ if (!offerIsLive(offer) || offer?.kind !== "promo")
64977
+ return;
64978
+ if (offer.expiresAt === undefined)
64979
+ return "FREE";
64980
+ const until = new Date(offer.expiresAt * 1000).toLocaleDateString("en-US", {
64981
+ month: "short",
64982
+ day: "numeric"
64983
+ });
64984
+ return `FREE until ${until}`;
64985
+ }
64577
64986
  async function buildDiscoveredModelRows(provider, displayName, catalog) {
64578
64987
  const discovered = rankDiscoveredModels(await discoverProviderModels(provider)).filter((m) => isChatCapable(m.id));
64579
64988
  if (discovered.length === 0)
64580
64989
  return [];
64581
64990
  const subscription = isSubscriptionProvider(provider);
64582
- const pricingById = subscription ? new Map : new Map((await loadModelsForPickerProvider(provider, catalog)).map((m) => [
64991
+ const local = getProviderByName(provider)?.isLocal === true;
64992
+ const flatRate = subscription || local;
64993
+ const pricingById = flatRate ? new Map : new Map((await loadModelsForPickerProvider(provider, catalog)).map((m) => [
64583
64994
  m.id.toLowerCase(),
64584
64995
  m.pricing
64585
64996
  ]));
64586
- const rows = discovered.map((m) => {
64587
- const contextLength = resolveDiscoveredContextLength(m);
64997
+ const choices = collapseRoster(provider, discovered.map(toRosterEntry));
64998
+ const discoveredById = new Map(discovered.map((m) => [m.id, m]));
64999
+ const rows = choices.map((c) => {
65000
+ const source = discoveredById.get(c.id);
65001
+ const contextLength = source ? resolveDiscoveredContextLength(source) : c.contextWindow ?? 0;
65002
+ const parts = [c.displayName];
65003
+ if (contextLength)
65004
+ parts.push(`${Math.round(contextLength / 1024)}K context`);
65005
+ if (subscription && c.costFactor !== undefined)
65006
+ parts.push(`\xD7${c.costFactor}`);
65007
+ const promo = describeOffer(c.offer);
65008
+ if (promo)
65009
+ parts.push(promo);
64588
65010
  return {
64589
- id: m.id,
64590
- name: m.displayName || m.id,
64591
- description: contextLength ? `${m.displayName ?? m.id} \xB7 ${Math.round(contextLength / 1024)}K context` : m.displayName ?? m.id,
65011
+ id: c.id,
65012
+ name: c.displayName,
65013
+ description: parts.join(" \xB7 "),
64592
65014
  provider: displayName,
64593
- releaseDate: resolveDiscoveredReleaseDate(m),
64594
- pricing: subscription ? SUBSCRIPTION_PRICING : pricingById.get(m.id.toLowerCase()),
65015
+ releaseDate: source ? resolveDiscoveredReleaseDate(source) : undefined,
65016
+ pricing: subscription ? SUBSCRIPTION_PRICING : pricingById.get(c.id.toLowerCase()),
64595
65017
  context: formatContextLength(contextLength),
64596
65018
  contextLength,
64597
- supportsTools: lookupModelCapabilities(m.id)?.supportsTools ?? true,
64598
- isFree: subscription,
65019
+ supportsTools: lookupModelCapabilities(c.id)?.supportsTools ?? source?.supportsTools ?? true,
65020
+ isFree: flatRate,
64599
65021
  source: displayName
64600
65022
  };
64601
65023
  });
@@ -64613,23 +65035,6 @@ async function selectModelFromProvider(provider, tierName, recommendedModels, _f
64613
65035
  return picked;
64614
65036
  }
64615
65037
  }
64616
- if (provider === "ollama") {
64617
- const ollamaModels = await fetchOllamaModels({ enrichCapabilities: false });
64618
- const chatModels = sortModelsNewestFirst(ollamaModels.map((m) => ({
64619
- id: m.name,
64620
- name: m.name,
64621
- description: m.description,
64622
- provider: displayName,
64623
- supportsTools: m.supportsTools,
64624
- isFree: true,
64625
- source: displayName
64626
- })));
64627
- if (chatModels.length > 0) {
64628
- const picked = await pickModelFromList(provider, displayName, tierName, chatModels);
64629
- if (picked)
64630
- return picked;
64631
- }
64632
- }
64633
65038
  if (isUserDeployedProvider(provider)) {
64634
65039
  const modelName = await dist_default5({
64635
65040
  message: tierName === "interactive session" ? `Enter ${displayName} model name:` : `Enter ${displayName} model name for ${tierName}:`,
@@ -64793,6 +65198,7 @@ var init_model_selector = __esm(() => {
64793
65198
  init_model_loader();
64794
65199
  init_model_catalog2();
64795
65200
  init_model_discovery();
65201
+ init_registry2();
64796
65202
  init_provider_definitions();
64797
65203
  init_probe_discovery();
64798
65204
  pickerProviderToFirebaseSlug = {
@@ -67773,7 +68179,10 @@ async function parseArgs(args) {
67773
68179
  printAvailableModels();
67774
68180
  process.exit(1);
67775
68181
  }
67776
- config3.model = modelArg;
68182
+ const chain = parseModelChain(modelArg);
68183
+ config3.model = chain[0];
68184
+ if (chain.length > 1)
68185
+ config3.modelChain = chain;
67777
68186
  } else if (arg === "--model-opus") {
67778
68187
  const val = args[++i];
67779
68188
  if (val)
@@ -78362,7 +78771,8 @@ Team Status`);
78362
78771
  quiet: cliConfig.quiet,
78363
78772
  isInteractive: cliConfig.interactive,
78364
78773
  advisorModels: cliConfig.advisorModels,
78365
- advisorCollector: cliConfig.advisorCollector
78774
+ advisorCollector: cliConfig.advisorCollector,
78775
+ modelChain: cliConfig.monitor ? undefined : cliConfig.modelChain
78366
78776
  }));
78367
78777
  const diag = createDiagOutput2({
78368
78778
  interactive: cliConfig.interactive,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "7.43.0",
3
+ "version": "7.45.0",
4
4
  "description": "Run Claude Code with any model - OpenRouter, Ollama, LM Studio & local models",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -60,10 +60,10 @@
60
60
  "ai"
61
61
  ],
62
62
  "optionalDependencies": {
63
- "@claudish/magmux-darwin-arm64": "7.43.0",
64
- "@claudish/magmux-darwin-x64": "7.43.0",
65
- "@claudish/magmux-linux-arm64": "7.43.0",
66
- "@claudish/magmux-linux-x64": "7.43.0"
63
+ "@claudish/magmux-darwin-arm64": "7.45.0",
64
+ "@claudish/magmux-darwin-x64": "7.45.0",
65
+ "@claudish/magmux-linux-arm64": "7.45.0",
66
+ "@claudish/magmux-linux-x64": "7.45.0"
67
67
  },
68
68
  "author": "Jack Rudenko <i@madappgang.com>",
69
69
  "license": "MIT",