claudish 7.56.0 → 7.57.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 +636 -401
  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.56.0";
732
+ var VERSION = "7.57.0";
733
733
 
734
734
  // src/logger.ts
735
735
  var exports_logger = {};
@@ -28077,6 +28077,7 @@ var init_provider_definitions = __esm(() => {
28077
28077
  { prefix: "dv/", stripPrefix: true },
28078
28078
  { prefix: "devin/", stripPrefix: true }
28079
28079
  ],
28080
+ nativeModelPatterns: [{ pattern: /^swe-/i }],
28080
28081
  modelDiscovery: { path: "", format: "devin-connect" },
28081
28082
  isDirectApi: true,
28082
28083
  description: "Devin subscription (dv@, devin@)"
@@ -28214,6 +28215,7 @@ var init_provider_definitions = __esm(() => {
28214
28215
  baseUrl: "https://api.minimax.io",
28215
28216
  baseUrlEnvVars: ["MINIMAX_CODING_BASE_URL"],
28216
28217
  apiPath: "/anthropic/v1/messages",
28218
+ modelDiscovery: { path: "/v1/models", format: "openai-models-list" },
28217
28219
  apiKeyEnvVar: "MINIMAX_CODING_API_KEY",
28218
28220
  apiKeyDescription: "MiniMax Coding Plan API Key",
28219
28221
  apiKeyUrl: "https://platform.minimax.io/user-center/basic-information/interface-key",
@@ -28250,6 +28252,7 @@ var init_provider_definitions = __esm(() => {
28250
28252
  baseUrl: "https://api.moonshot.ai",
28251
28253
  baseUrlEnvVars: ["MOONSHOT_BASE_URL", "KIMI_BASE_URL"],
28252
28254
  apiPath: "/anthropic/v1/messages",
28255
+ modelDiscovery: { path: "/v1/models", format: "openai-models-list" },
28253
28256
  apiKeyEnvVar: "MOONSHOT_API_KEY",
28254
28257
  apiKeyAliases: ["KIMI_API_KEY"],
28255
28258
  apiKeyDescription: "Kimi/Moonshot API Key",
@@ -28301,6 +28304,7 @@ var init_provider_definitions = __esm(() => {
28301
28304
  tokenStrategy: "delta-aware",
28302
28305
  baseUrl: "https://api.z.ai",
28303
28306
  apiPath: "/api/coding/paas/v4/chat/completions",
28307
+ modelDiscovery: { path: "/api/coding/paas/v4/models", format: "openai-models-list" },
28304
28308
  apiKeyEnvVar: "GLM_CODING_API_KEY",
28305
28309
  apiKeyAliases: ["ZAI_CODING_API_KEY"],
28306
28310
  apiKeyDescription: "GLM Coding Plan API Key",
@@ -28377,6 +28381,7 @@ var init_provider_definitions = __esm(() => {
28377
28381
  baseUrl: "https://opencode.ai/zen/go",
28378
28382
  baseUrlEnvVars: ["OPENCODE_GO_BASE_URL"],
28379
28383
  apiPath: "/v1/chat/completions",
28384
+ modelDiscovery: { path: "/v1/models", format: "openai-models-list" },
28380
28385
  apiKeyEnvVar: "OPENCODE_GO_API_KEY",
28381
28386
  apiKeyAliases: ["OPENCODE_API_KEY"],
28382
28387
  apiKeyDescription: "OpenCode Zen Go (Lite Plan) API Key",
@@ -28594,6 +28599,7 @@ var init_provider_definitions = __esm(() => {
28594
28599
  baseUrl: "https://api.sakana.ai",
28595
28600
  baseUrlEnvVars: ["SAKANA_BASE_URL"],
28596
28601
  apiPath: "/v1/chat/completions",
28602
+ modelDiscovery: { path: "/v1/models", format: "openai-models-list" },
28597
28603
  apiKeyEnvVar: "SAKANA_SUBSCRIPTION_API_KEY",
28598
28604
  apiKeyAliases: ["SAKANA_CODING_API_KEY"],
28599
28605
  apiKeyDescription: "Sakana Fugu Subscription API Key",
@@ -28741,6 +28747,8 @@ var init_all_models_cache = __esm(() => {
28741
28747
 
28742
28748
  // src/providers/catalog-client.ts
28743
28749
  function getCatalogEntries() {
28750
+ if (_catalogEntriesForTest !== undefined)
28751
+ return _catalogEntriesForTest;
28744
28752
  if (_memCache)
28745
28753
  return _memCache;
28746
28754
  const cache = readAllModelsCache();
@@ -28760,6 +28768,26 @@ function getCatalogEntries() {
28760
28768
  }
28761
28769
  return null;
28762
28770
  }
28771
+ function latestAnthropicTierModelId(tier) {
28772
+ const entries = getCatalogEntries();
28773
+ if (!entries)
28774
+ return null;
28775
+ const family = new RegExp(`^claude-${tier}-`, "i");
28776
+ const opus = entries.filter((e) => family.test(e.modelId));
28777
+ if (opus.length === 0)
28778
+ return null;
28779
+ opus.sort((a, b) => {
28780
+ const byDate = (b.releaseDate ?? "").localeCompare(a.releaseDate ?? "");
28781
+ if (byDate !== 0)
28782
+ return byDate;
28783
+ const aFast = /-fast$/i.test(a.modelId) ? 1 : 0;
28784
+ const bFast = /-fast$/i.test(b.modelId) ? 1 : 0;
28785
+ if (aFast !== bFast)
28786
+ return aFast - bFast;
28787
+ return b.modelId.localeCompare(a.modelId);
28788
+ });
28789
+ return opus[0].modelId;
28790
+ }
28763
28791
  function isCatalogWarm() {
28764
28792
  return _memCache !== null && _memCache.length > 0;
28765
28793
  }
@@ -28902,7 +28930,7 @@ async function ensureCatalogReady(timeoutMs = 5000) {
28902
28930
  new Promise((resolve) => setTimeout(resolve, timeoutMs))
28903
28931
  ]);
28904
28932
  }
28905
- var FIREBASE_CATALOG_URL, _memCache = null, _warmPromise = null;
28933
+ var FIREBASE_CATALOG_URL, _memCache = null, _catalogEntriesForTest, _warmPromise = null;
28906
28934
  var init_catalog_client = __esm(() => {
28907
28935
  init_all_models_cache();
28908
28936
  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";
@@ -39714,6 +39742,24 @@ var init_devin_stream_head_sniffer = __esm(() => {
39714
39742
  QUOTA_MESSAGE_RE = /quota|out of credits|credit balance|billing|plan limit|exceeded your/i;
39715
39743
  });
39716
39744
 
39745
+ // src/handlers/shared/model-unsupported.ts
39746
+ function hasModelUnsupportedWording(errorBody) {
39747
+ const lower = (errorBody || "").toLowerCase();
39748
+ return UNSUPPORTED_PHRASES.some((phrase) => lower.includes(phrase));
39749
+ }
39750
+ var UNSUPPORTED_PHRASES;
39751
+ var init_model_unsupported = __esm(() => {
39752
+ UNSUPPORTED_PHRASES = [
39753
+ "not supported",
39754
+ "unsupported model",
39755
+ "unsupported_model",
39756
+ "model not found",
39757
+ "model_not_found",
39758
+ "unknown model",
39759
+ "no such model"
39760
+ ];
39761
+ });
39762
+
39717
39763
  // src/handlers/shared/stream-head-sniffer.ts
39718
39764
  function isRetryableStreamError(code, type, message) {
39719
39765
  if (RETRYABLE_ERROR_CODES.has(code))
@@ -42515,7 +42561,7 @@ function getRecoveryHint(status, errorText, providerName, transportTerminal429)
42515
42561
  return "Rate limited. Wait, reduce concurrency, or check plan limits.";
42516
42562
  }
42517
42563
  if (status === 401 || status === 403) {
42518
- if (lower.includes("not supported") || lower.includes("unsupported model") || lower.includes("model not found")) {
42564
+ if (hasModelUnsupportedWording(errorText)) {
42519
42565
  return "Model not supported by this provider. Verify model name.";
42520
42566
  }
42521
42567
  if (isQuotaExhaustionError(status, errorText)) {
@@ -42558,6 +42604,7 @@ var init_composed_handler = __esm(() => {
42558
42604
  init_collect_sse_message();
42559
42605
  init_connection_error();
42560
42606
  init_devin_stream_head_sniffer();
42607
+ init_model_unsupported();
42561
42608
  init_openai_compat();
42562
42609
  init_quota_exhaustion();
42563
42610
  init_stream_head_sniffer();
@@ -43277,6 +43324,11 @@ async function discoverProviderModels(providerName) {
43277
43324
  return recordFailure({ kind: "no-credentials", provider: providerName, endpoint });
43278
43325
  }
43279
43326
  }
43327
+ headers = {
43328
+ "User-Agent": `claudish/${VERSION}`,
43329
+ ...def.headers ?? {},
43330
+ ...headers
43331
+ };
43280
43332
  let response;
43281
43333
  try {
43282
43334
  response = await fetch(endpoint, {
@@ -44954,6 +45006,7 @@ var init_default_routing_rules = __esm(() => {
44954
45006
  "ministral-*": ["mistralai", "openrouter"],
44955
45007
  "codestral-*": ["mistralai", "openrouter"],
44956
45008
  "labs-*": ["mistralai"],
45009
+ "swe-*": ["devin"],
44957
45010
  fugu: ["sakana-subscription", "sakana"],
44958
45011
  "fugu-*": ["sakana-subscription", "sakana"],
44959
45012
  "*-zen": ["opencode-zen"],
@@ -46369,6 +46422,15 @@ function normalizePricingDisplay(raw) {
46369
46422
  return "FREE";
46370
46423
  return pricing;
46371
46424
  }
46425
+ function formatListingPrice(entry, opts) {
46426
+ const rate = normalizePricingDisplay(entry.pricing?.average);
46427
+ if (rate !== "N/A")
46428
+ return rate;
46429
+ const plan = entry.subscription?.plan;
46430
+ if (!plan)
46431
+ return "N/A";
46432
+ return opts?.compact ? "SUB" : `SUB (${plan})`;
46433
+ }
46372
46434
  function computeQuickPicks(primaries) {
46373
46435
  if (primaries.length === 0) {
46374
46436
  return {
@@ -46603,6 +46665,412 @@ async function isPortAvailable(port) {
46603
46665
  }
46604
46666
  var init_port_manager = () => {};
46605
46667
 
46668
+ // src/providers/probe-live.ts
46669
+ function effortForProvider(provider) {
46670
+ return MINIMAL_EFFORT_UNSUPPORTED.has(provider) ? "low" : "minimal";
46671
+ }
46672
+ async function probeLink(proxyUrl, link, timeoutMs) {
46673
+ const isOAuth = OAUTH_PROVIDERS2.has(link.provider);
46674
+ if (!link.hasCredentials && !isOAuth) {
46675
+ return {
46676
+ state: "key-missing",
46677
+ latencyMs: 0,
46678
+ errorMessage: link.credentialHint
46679
+ };
46680
+ }
46681
+ const startedAt = Date.now();
46682
+ let response;
46683
+ try {
46684
+ response = await fetch(`${proxyUrl}/v1/messages`, {
46685
+ method: "POST",
46686
+ headers: {
46687
+ "Content-Type": "application/json"
46688
+ },
46689
+ body: JSON.stringify({
46690
+ model: link.modelSpec,
46691
+ system: "You are a helpful assistant.",
46692
+ messages: [{ role: "user", content: PROBE_PROMPT }],
46693
+ max_tokens: PROBE_MAX_TOKENS,
46694
+ output_config: { effort: effortForProvider(link.provider) },
46695
+ stream: true
46696
+ }),
46697
+ signal: AbortSignal.timeout(timeoutMs)
46698
+ });
46699
+ } catch (e) {
46700
+ const latencyMs = Date.now() - startedAt;
46701
+ const name = e?.name || "";
46702
+ const msg2 = String(e?.message || e);
46703
+ if (name === "TimeoutError" || name === "AbortError" || /timeout/i.test(msg2)) {
46704
+ return { state: "timeout", latencyMs, errorMessage: msg2 };
46705
+ }
46706
+ return { state: "network-error", latencyMs, errorMessage: msg2 };
46707
+ }
46708
+ const ttfbMs = Date.now() - startedAt;
46709
+ if (!response.ok) {
46710
+ const body = await safeReadBody(response);
46711
+ return annotateOAuthHint(classifyHttpError(response.status, body, ttfbMs), link.provider, isOAuth);
46712
+ }
46713
+ const streamResult = await consumeProbeStream(response, timeoutMs, startedAt);
46714
+ const totalMs = Date.now() - startedAt;
46715
+ let timing2;
46716
+ if (streamResult.state === "live" && streamResult.ttftMs !== undefined && !streamResult.truncated) {
46717
+ const ttftMs = streamResult.ttftMs;
46718
+ const tokens = streamResult.tokens ?? 0;
46719
+ const streamMs = Math.max(STREAM_MS_FLOOR, totalMs - ttftMs);
46720
+ const tokensPerSec = tokens > 0 ? tokens / streamMs * 1000 : 0;
46721
+ timing2 = { ttfbMs, ttftMs, totalMs, tokens, tokensPerSec };
46722
+ }
46723
+ const { ttftMs: _ttft, tokens: _tok, truncated: _trunc, ...rest } = streamResult;
46724
+ return annotateOAuthHint({
46725
+ ...rest,
46726
+ latencyMs: totalMs,
46727
+ timing: timing2
46728
+ }, link.provider, isOAuth);
46729
+ }
46730
+ function annotateOAuthHint(result, provider, isOAuth) {
46731
+ if (!isOAuth)
46732
+ return result;
46733
+ if (result.state === "live")
46734
+ return result;
46735
+ const loginCommand = provider === "antigravity" ? "claudish login antigravity" : provider === "vertex" ? "gcloud auth application-default login" : provider === "devin" ? "devin login" : undefined;
46736
+ if (!loginCommand)
46737
+ return result;
46738
+ if (result.httpStatus === 403)
46739
+ return result;
46740
+ const looksLikeAuthFailure = result.state === "auth-failed" || /auth|token|login|credential|unauthor/i.test(result.errorMessage || "");
46741
+ if (!looksLikeAuthFailure)
46742
+ return result;
46743
+ return {
46744
+ ...result,
46745
+ state: "auth-failed",
46746
+ actionHint: `run: ${loginCommand}`
46747
+ };
46748
+ }
46749
+ async function safeReadBody(response) {
46750
+ try {
46751
+ const text = await response.text();
46752
+ return text.slice(0, 500);
46753
+ } catch {
46754
+ return "";
46755
+ }
46756
+ }
46757
+ function extractUpstreamStatus(body) {
46758
+ if (!body)
46759
+ return;
46760
+ try {
46761
+ const parsed = JSON.parse(body);
46762
+ const status = parsed?.error?.upstream_status;
46763
+ return typeof status === "number" ? status : undefined;
46764
+ } catch {
46765
+ return;
46766
+ }
46767
+ }
46768
+ function extractErrorType(body) {
46769
+ if (!body)
46770
+ return;
46771
+ try {
46772
+ const parsed = JSON.parse(body);
46773
+ const t = parsed?.error?.type;
46774
+ return typeof t === "string" ? t : undefined;
46775
+ } catch {
46776
+ return;
46777
+ }
46778
+ }
46779
+ function classifyHttpError(status, body, latencyMs) {
46780
+ const lowered = body.toLowerCase();
46781
+ if (extractErrorType(body) === "connection_error") {
46782
+ return {
46783
+ state: "network-error",
46784
+ latencyMs,
46785
+ httpStatus: status,
46786
+ errorMessage: extractErrorMessage(body) || "Cannot reach provider"
46787
+ };
46788
+ }
46789
+ const upstream = status === 400 ? extractUpstreamStatus(body) : undefined;
46790
+ if (status === 401 || status === 403 || upstream === 401 || upstream === 403) {
46791
+ const authStatus = upstream ?? status;
46792
+ if (hasModelUnsupportedWording(body)) {
46793
+ return {
46794
+ state: "model-not-found",
46795
+ latencyMs,
46796
+ httpStatus: authStatus,
46797
+ errorMessage: extractErrorMessage(body) || `HTTP ${authStatus}`
46798
+ };
46799
+ }
46800
+ return {
46801
+ state: "auth-failed",
46802
+ latencyMs,
46803
+ httpStatus: authStatus,
46804
+ errorMessage: extractErrorMessage(body) || `HTTP ${authStatus}`
46805
+ };
46806
+ }
46807
+ if (status === 404 || /model[_ ]not[_ ]found|no such model|unknown model/.test(lowered)) {
46808
+ return {
46809
+ state: "model-not-found",
46810
+ latencyMs,
46811
+ httpStatus: status,
46812
+ errorMessage: extractErrorMessage(body) || `HTTP ${status}`
46813
+ };
46814
+ }
46815
+ if (status === 429) {
46816
+ return {
46817
+ state: "rate-limited",
46818
+ latencyMs,
46819
+ httpStatus: status,
46820
+ errorMessage: extractErrorMessage(body) || "Rate limited"
46821
+ };
46822
+ }
46823
+ if (upstream === 429 || status === 402) {
46824
+ return {
46825
+ state: "out-of-credit",
46826
+ latencyMs,
46827
+ httpStatus: upstream ?? status,
46828
+ errorMessage: extractErrorMessage(body) || "Out of credit \u2014 account balance or plan exhausted"
46829
+ };
46830
+ }
46831
+ if (status >= 500) {
46832
+ return {
46833
+ state: "server-error",
46834
+ latencyMs,
46835
+ httpStatus: status,
46836
+ errorMessage: extractErrorMessage(body) || `HTTP ${status}`
46837
+ };
46838
+ }
46839
+ return {
46840
+ state: "error",
46841
+ latencyMs,
46842
+ httpStatus: status,
46843
+ errorMessage: extractErrorMessage(body) || `HTTP ${status}`
46844
+ };
46845
+ }
46846
+ function extractErrorMessage(body) {
46847
+ if (!body)
46848
+ return;
46849
+ try {
46850
+ const parsed = JSON.parse(body);
46851
+ const msg2 = parsed?.error?.message || parsed?.error?.error?.message || parsed?.message || parsed?.detail;
46852
+ if (typeof msg2 === "string" && msg2.length > 0) {
46853
+ return msg2.length > 160 ? `${msg2.slice(0, 157)}...` : msg2;
46854
+ }
46855
+ } catch {}
46856
+ const trimmed2 = body.trim();
46857
+ if (!trimmed2)
46858
+ return;
46859
+ return trimmed2.length > 160 ? `${trimmed2.slice(0, 157)}...` : trimmed2;
46860
+ }
46861
+ async function consumeProbeStream(response, timeoutMs, startedAt) {
46862
+ const body = response.body;
46863
+ if (!body) {
46864
+ return { state: "error", errorMessage: "empty response body" };
46865
+ }
46866
+ const reader = body.getReader();
46867
+ const decoder = new TextDecoder;
46868
+ let buffered = "";
46869
+ const deadline = Date.now() + timeoutMs;
46870
+ let ttftMs;
46871
+ let sawContent = false;
46872
+ let textChars = 0;
46873
+ let reportedTokens;
46874
+ let stopReason;
46875
+ let errorVerdict = null;
46876
+ let completed = false;
46877
+ try {
46878
+ while (Date.now() < deadline) {
46879
+ const { value, done } = await reader.read();
46880
+ if (done) {
46881
+ completed = true;
46882
+ break;
46883
+ }
46884
+ buffered += decoder.decode(value, { stream: true });
46885
+ const events = buffered.split(`
46886
+
46887
+ `);
46888
+ buffered = events.pop() ?? "";
46889
+ for (const event of events) {
46890
+ const verdict = interpretSseEvent(event);
46891
+ if (verdict && typeof verdict === "object" && verdict.state !== "live") {
46892
+ errorVerdict = verdict;
46893
+ break;
46894
+ }
46895
+ const acct = accountStreamEvent(event);
46896
+ if (acct.contentDelta) {
46897
+ if (ttftMs === undefined)
46898
+ ttftMs = Date.now() - startedAt;
46899
+ sawContent = true;
46900
+ }
46901
+ if (acct.textChars)
46902
+ textChars += acct.textChars;
46903
+ if (acct.outputTokens !== undefined)
46904
+ reportedTokens = acct.outputTokens;
46905
+ if (acct.stopReason)
46906
+ stopReason = acct.stopReason;
46907
+ }
46908
+ if (errorVerdict)
46909
+ break;
46910
+ }
46911
+ } catch (e) {
46912
+ if (!sawContent) {
46913
+ return { state: "network-error", errorMessage: String(e?.message || e) };
46914
+ }
46915
+ } finally {
46916
+ try {
46917
+ await reader.cancel();
46918
+ } catch {}
46919
+ }
46920
+ if (errorVerdict)
46921
+ return errorVerdict;
46922
+ if (sawContent) {
46923
+ const tokens = reportedTokens ?? Math.max(1, Math.round(textChars / 4));
46924
+ return { state: "live", ttftMs, tokens, truncated: !completed };
46925
+ }
46926
+ const truncationReason = stopReason === "max_tokens" || stopReason === "length" ? stopReason : undefined;
46927
+ if (truncationReason || reportedTokens !== undefined && reportedTokens >= PROBE_MAX_TOKENS) {
46928
+ const cause = truncationReason ? `finish: ${truncationReason}` : `${reportedTokens} tokens consumed, none visible`;
46929
+ return {
46930
+ state: "error",
46931
+ errorMessage: `no visible output within probe budget (${cause})`
46932
+ };
46933
+ }
46934
+ return { state: "error", errorMessage: "stream ended without content" };
46935
+ }
46936
+ function accountStreamEvent(rawEvent) {
46937
+ let dataPayload = "";
46938
+ for (const line of rawEvent.split(`
46939
+ `)) {
46940
+ if (line.startsWith("data:"))
46941
+ dataPayload += line.slice(5).trim();
46942
+ }
46943
+ if (!dataPayload || dataPayload === "[DONE]") {
46944
+ return { contentDelta: false, textChars: 0 };
46945
+ }
46946
+ let parsed;
46947
+ try {
46948
+ parsed = JSON.parse(dataPayload);
46949
+ } catch {
46950
+ return { contentDelta: false, textChars: 0 };
46951
+ }
46952
+ let textChars = 0;
46953
+ let contentDelta = false;
46954
+ const text = parsed?.delta?.text ?? (Array.isArray(parsed?.choices) ? parsed.choices[0]?.delta?.content : undefined);
46955
+ if (typeof text === "string" && text.length > 0) {
46956
+ contentDelta = true;
46957
+ textChars = text.length;
46958
+ } else if (parsed?.type === "content_block_delta" || parsed?.type === "content_block_start") {
46959
+ contentDelta = true;
46960
+ }
46961
+ const outputTokens = parsed?.usage?.output_tokens ?? parsed?.message?.usage?.output_tokens ?? parsed?.usage?.completion_tokens;
46962
+ const stopReason = parsed?.delta?.stop_reason ?? (Array.isArray(parsed?.choices) ? parsed.choices[0]?.finish_reason : undefined);
46963
+ return {
46964
+ contentDelta,
46965
+ textChars,
46966
+ outputTokens: typeof outputTokens === "number" ? outputTokens : undefined,
46967
+ stopReason: typeof stopReason === "string" ? stopReason : undefined
46968
+ };
46969
+ }
46970
+ function interpretSseEvent(rawEvent) {
46971
+ const lines = rawEvent.split(`
46972
+ `);
46973
+ let eventType = "";
46974
+ let dataPayload = "";
46975
+ for (const line of lines) {
46976
+ if (line.startsWith("event:"))
46977
+ eventType = line.slice(6).trim();
46978
+ else if (line.startsWith("data:"))
46979
+ dataPayload += line.slice(5).trim();
46980
+ }
46981
+ if (!dataPayload)
46982
+ return null;
46983
+ if (dataPayload === "[DONE]")
46984
+ return null;
46985
+ let parsed;
46986
+ try {
46987
+ parsed = JSON.parse(dataPayload);
46988
+ } catch {
46989
+ return null;
46990
+ }
46991
+ if (parsed?.type === "error" || eventType === "error" || parsed?.error) {
46992
+ const message = parsed?.error?.message || parsed?.error?.error?.message || parsed?.message || "provider returned error event";
46993
+ const status = parsed?.error?.status || parsed?.status;
46994
+ if (typeof status === "number") {
46995
+ return {
46996
+ state: status === 401 || status === 403 ? "auth-failed" : "error",
46997
+ httpStatus: status,
46998
+ errorMessage: message
46999
+ };
47000
+ }
47001
+ return { state: "error", errorMessage: message };
47002
+ }
47003
+ if (isContentEvent(parsed, eventType)) {
47004
+ return "live";
47005
+ }
47006
+ return null;
47007
+ }
47008
+ function isContentEvent(parsed, eventType) {
47009
+ if (eventType === "content_block_start" || eventType === "content_block_delta")
47010
+ return true;
47011
+ if (eventType === "message_start")
47012
+ return true;
47013
+ if (parsed?.type === "content_block_start")
47014
+ return true;
47015
+ if (parsed?.type === "content_block_delta")
47016
+ return true;
47017
+ if (parsed?.type === "message_start")
47018
+ return true;
47019
+ if (parsed?.type === "message_delta")
47020
+ return true;
47021
+ if (Array.isArray(parsed?.choices) && parsed.choices.length > 0) {
47022
+ const choice = parsed.choices[0];
47023
+ if (choice?.delta || choice?.message || choice?.text || choice?.finish_reason)
47024
+ return true;
47025
+ }
47026
+ if (parsed?.candidates)
47027
+ return true;
47028
+ return false;
47029
+ }
47030
+ function withDetail(base, message) {
47031
+ return message ? `${base} \u2014 ${message}` : base;
47032
+ }
47033
+ function describeProbeState(result) {
47034
+ const status = result.httpStatus ?? "";
47035
+ const latency = result.latencyMs ? ` \xB7 ${result.latencyMs}ms` : "";
47036
+ switch (result.state) {
47037
+ case "live":
47038
+ return `live \xB7 ${result.latencyMs}ms`;
47039
+ case "key-missing":
47040
+ return result.errorMessage ? `missing (${result.errorMessage})` : "missing";
47041
+ case "auth-failed":
47042
+ return withDetail(`auth failed \xB7 ${status}${latency}`.trim(), result.errorMessage);
47043
+ case "model-not-found":
47044
+ return withDetail(`model not found \xB7 ${status}${latency}`.trim(), result.errorMessage);
47045
+ case "rate-limited":
47046
+ return withDetail(`rate limited \xB7 ${result.latencyMs}ms`, result.errorMessage);
47047
+ case "out-of-credit":
47048
+ return withDetail(`out of credit \xB7 ${status}${latency}`.trim(), result.errorMessage);
47049
+ case "server-error":
47050
+ return withDetail(`server error \xB7 ${status} \xB7 ${result.latencyMs}ms`, result.errorMessage);
47051
+ case "timeout":
47052
+ return withDetail(`timeout \xB7 ${result.latencyMs}ms`, result.errorMessage);
47053
+ case "network-error":
47054
+ return withDetail(`network error \xB7 ${result.latencyMs}ms`, result.errorMessage);
47055
+ case "error": {
47056
+ const base = `error${result.httpStatus ? ` \xB7 ${result.httpStatus}` : ""}${latency}`;
47057
+ return withDetail(base, result.errorMessage);
47058
+ }
47059
+ }
47060
+ }
47061
+ function isReadyState(state) {
47062
+ return state === "live";
47063
+ }
47064
+ function isFailureState(state) {
47065
+ return state === "auth-failed" || state === "model-not-found" || state === "rate-limited" || state === "out-of-credit" || state === "server-error" || state === "timeout" || state === "network-error" || state === "error";
47066
+ }
47067
+ var STREAM_MS_FLOOR = 50, OAUTH_PROVIDERS2, PROBE_PROMPT = "Count from one to twenty in words, one per line.", PROBE_MAX_TOKENS = 512, MINIMAL_EFFORT_UNSUPPORTED;
47068
+ var init_probe_live = __esm(() => {
47069
+ init_model_unsupported();
47070
+ OAUTH_PROVIDERS2 = new Set(["vertex", "antigravity", "devin"]);
47071
+ MINIMAL_EFFORT_UNSUPPORTED = new Set(["native-anthropic", "anthropic"]);
47072
+ });
47073
+
46606
47074
  // ../../node_modules/.bun/hono@4.10.6/node_modules/hono/dist/compose.js
46607
47075
  var compose = (middleware, onError, onNotFound) => {
46608
47076
  return (context, next) => {
@@ -53052,7 +53520,7 @@ Tokens: ${result.usage.input} input, ${result.usage.output} output`;
53052
53520
  };
53053
53521
  const renderGroup = (group) => {
53054
53522
  const m = group.primary;
53055
- const pricing = normalizePricingDisplay(m.pricing?.average);
53523
+ const pricing = formatListingPrice(m);
53056
53524
  const ctx = m.context || "N/A";
53057
53525
  const caps = [];
53058
53526
  if (m.supportsTools)
@@ -53257,6 +53725,112 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
53257
53725
  return { content: [{ type: "text", text: output }] };
53258
53726
  }
53259
53727
  });
53728
+ tools.push({
53729
+ name: "preflight",
53730
+ description: "Check a roster of models BEFORE spending a run on it. For each model: which provider " + "will actually serve it, whether that hop is covered by a SUBSCRIPTION or billed per " + "token, and whether it is reachable right now. Call this before `team` or a batch of " + "`create_session` calls \u2014 a dead or unexpectedly-metered model is then caught while " + "the roster can still be adjusted, instead of costing a slot minutes into the run.",
53731
+ inputSchema: {
53732
+ type: "object",
53733
+ properties: {
53734
+ models: {
53735
+ type: "array",
53736
+ items: { type: "string" },
53737
+ description: "Model ids to check \u2014 bare (`glm-5.2`) or explicit (`dv@swe-1.7`). Bare names go " + "through the SAME routing rules and credential filter a real run would use, so " + "the provider reported here is the provider that would serve it."
53738
+ },
53739
+ probe: {
53740
+ type: "boolean",
53741
+ description: "Send a real short request to each resolved route (default true). Set false for " + "a routing/billing answer only \u2014 far faster, but it cannot tell you the provider " + "is actually reachable, which is the failure this tool exists to catch."
53742
+ },
53743
+ timeout_ms: {
53744
+ type: "number",
53745
+ description: "Per-model probe timeout in ms (default 20000)."
53746
+ }
53747
+ },
53748
+ required: ["models"]
53749
+ },
53750
+ group: "agentic",
53751
+ heartbeat: true,
53752
+ handler: async (args, ctx) => {
53753
+ const models = Array.isArray(args.models) ? args.models : [];
53754
+ if (models.length === 0) {
53755
+ return {
53756
+ content: [{ type: "text", text: "preflight: no models given." }],
53757
+ isError: true
53758
+ };
53759
+ }
53760
+ const doProbe = args.probe !== false;
53761
+ const timeoutMs = typeof args.timeout_ms === "number" ? args.timeout_ms : 20000;
53762
+ const proxy = doProbe ? await getProxy() : null;
53763
+ const rows = [];
53764
+ const readyModels = [];
53765
+ const failedModels = [];
53766
+ let subCount = 0;
53767
+ let meteredCount = 0;
53768
+ for (const model of models) {
53769
+ ctx.reportProgress(`preflight: ${model}`);
53770
+ let plan;
53771
+ try {
53772
+ plan = await route(model);
53773
+ } catch (err) {
53774
+ failedModels.push(model);
53775
+ rows.push(`| \`${model}\` | \u2014 | \u2014 | \u274C route error | ${err instanceof Error ? err.message : String(err)} |`);
53776
+ continue;
53777
+ }
53778
+ if (plan.kind === "no-route") {
53779
+ failedModels.push(model);
53780
+ rows.push(`| \`${model}\` | \u2014 | \u2014 | \u274C no route | ${plan.reason} |`);
53781
+ continue;
53782
+ }
53783
+ const primary = plan.primary;
53784
+ const billing = isLocalProviderName(primary.provider) ? "local" : isSubscriptionProvider(primary.provider) ? "SUB" : "metered";
53785
+ if (billing === "SUB")
53786
+ subCount++;
53787
+ else if (billing === "metered")
53788
+ meteredCount++;
53789
+ let status = "not probed";
53790
+ let ok = true;
53791
+ if (proxy) {
53792
+ try {
53793
+ const result = await probeLink(proxy.url, {
53794
+ provider: primary.provider,
53795
+ modelSpec: primary.modelSpec,
53796
+ hasCredentials: true
53797
+ }, timeoutMs);
53798
+ ok = isReadyState(result.state);
53799
+ status = ok ? `\u2705 ${result.state}` : `\u274C ${result.state}${result.errorMessage ? ` \u2014 ${result.errorMessage}` : ""}`;
53800
+ } catch (err) {
53801
+ ok = false;
53802
+ status = `\u274C probe error \u2014 ${err instanceof Error ? err.message : String(err)}`;
53803
+ }
53804
+ }
53805
+ if (ok)
53806
+ readyModels.push(model);
53807
+ else
53808
+ failedModels.push(model);
53809
+ const fallbacks = plan.fallbacks.length > 0 ? ` (+${plan.fallbacks.length} fallback)` : "";
53810
+ rows.push(`| \`${model}\` | ${primary.displayName}${fallbacks} | ${billing} | ${status} | \`${primary.modelSpec}\` |`);
53811
+ }
53812
+ const lines = [
53813
+ `# Preflight \u2014 ${models.length} model${models.length === 1 ? "" : "s"}`,
53814
+ "",
53815
+ `**Ready: ${readyModels.length}** \xB7 **Failed: ${failedModels.length}** \xB7 ` + `subscription: ${subCount} \xB7 metered: ${meteredCount}`,
53816
+ "",
53817
+ "| Model | Provider | Billing | Status | Wire id |",
53818
+ "|---|---|---|---|---|",
53819
+ ...rows
53820
+ ];
53821
+ if (failedModels.length > 0) {
53822
+ lines.push("", `\u26A0\uFE0F Drop or replace before running: ${failedModels.map((m) => `\`${m}\``).join(", ")}`);
53823
+ }
53824
+ if (meteredCount > 0) {
53825
+ lines.push("", `\uD83D\uDCB8 ${meteredCount} model${meteredCount === 1 ? "" : "s"} will be billed PER TOKEN. ` + "A bare name can land on a metered provider when the subscription that covers it " + "has no credential configured \u2014 name the provider explicitly to pin it.");
53826
+ }
53827
+ if (!doProbe) {
53828
+ lines.push("", "\u2139\uFE0F `probe: false` \u2014 routing and billing only. Reachability was NOT checked.");
53829
+ }
53830
+ return { content: [{ type: "text", text: lines.join(`
53831
+ `) }] };
53832
+ }
53833
+ });
53260
53834
  tools.push({
53261
53835
  name: "team",
53262
53836
  description: "Run AI models on a task with anonymized outputs and optional blind judging. Modes: 'run' (execute models), 'judge' (blind-vote on existing outputs), 'run-and-judge' (full pipeline), 'status' (check progress).",
@@ -53851,11 +54425,15 @@ var init_mcp_server = __esm(() => {
53851
54425
  init_prehydrate();
53852
54426
  init_diagnostics();
53853
54427
  init_channel();
54428
+ init_remote_provider_types();
53854
54429
  init_progress_heartbeat();
53855
54430
  init_model_loader();
53856
54431
  init_port_manager();
54432
+ init_model_parser();
53857
54433
  init_onepassword();
54434
+ init_probe_live();
53858
54435
  init_provider_definitions();
54436
+ init_routing_rules();
53859
54437
  init_proxy_server();
53860
54438
  init_redact();
53861
54439
  init_team_orchestrator();
@@ -68684,399 +69262,6 @@ var init_model_selector = __esm(() => {
68684
69262
  };
68685
69263
  });
68686
69264
 
68687
- // src/providers/probe-live.ts
68688
- async function probeLink(proxyUrl, link, timeoutMs) {
68689
- const isOAuth = OAUTH_PROVIDERS2.has(link.provider);
68690
- if (!link.hasCredentials && !isOAuth) {
68691
- return {
68692
- state: "key-missing",
68693
- latencyMs: 0,
68694
- errorMessage: link.credentialHint
68695
- };
68696
- }
68697
- const startedAt = Date.now();
68698
- let response;
68699
- try {
68700
- response = await fetch(`${proxyUrl}/v1/messages`, {
68701
- method: "POST",
68702
- headers: {
68703
- "Content-Type": "application/json"
68704
- },
68705
- body: JSON.stringify({
68706
- model: link.modelSpec,
68707
- system: "You are a helpful assistant.",
68708
- messages: [{ role: "user", content: PROBE_PROMPT }],
68709
- max_tokens: PROBE_MAX_TOKENS,
68710
- output_config: { effort: "minimal" },
68711
- stream: true
68712
- }),
68713
- signal: AbortSignal.timeout(timeoutMs)
68714
- });
68715
- } catch (e) {
68716
- const latencyMs = Date.now() - startedAt;
68717
- const name = e?.name || "";
68718
- const msg2 = String(e?.message || e);
68719
- if (name === "TimeoutError" || name === "AbortError" || /timeout/i.test(msg2)) {
68720
- return { state: "timeout", latencyMs, errorMessage: msg2 };
68721
- }
68722
- return { state: "network-error", latencyMs, errorMessage: msg2 };
68723
- }
68724
- const ttfbMs = Date.now() - startedAt;
68725
- if (!response.ok) {
68726
- const body = await safeReadBody(response);
68727
- return annotateOAuthHint(classifyHttpError(response.status, body, ttfbMs), link.provider, isOAuth);
68728
- }
68729
- const streamResult = await consumeProbeStream(response, timeoutMs, startedAt);
68730
- const totalMs = Date.now() - startedAt;
68731
- let timing2;
68732
- if (streamResult.state === "live" && streamResult.ttftMs !== undefined && !streamResult.truncated) {
68733
- const ttftMs = streamResult.ttftMs;
68734
- const tokens = streamResult.tokens ?? 0;
68735
- const streamMs = Math.max(STREAM_MS_FLOOR, totalMs - ttftMs);
68736
- const tokensPerSec = tokens > 0 ? tokens / streamMs * 1000 : 0;
68737
- timing2 = { ttfbMs, ttftMs, totalMs, tokens, tokensPerSec };
68738
- }
68739
- const { ttftMs: _ttft, tokens: _tok, truncated: _trunc, ...rest } = streamResult;
68740
- return annotateOAuthHint({
68741
- ...rest,
68742
- latencyMs: totalMs,
68743
- timing: timing2
68744
- }, link.provider, isOAuth);
68745
- }
68746
- function annotateOAuthHint(result, provider, isOAuth) {
68747
- if (!isOAuth)
68748
- return result;
68749
- if (result.state === "live")
68750
- return result;
68751
- const loginCommand2 = provider === "antigravity" ? "claudish login antigravity" : provider === "vertex" ? "gcloud auth application-default login" : provider === "devin" ? "devin login" : undefined;
68752
- if (!loginCommand2)
68753
- return result;
68754
- if (result.httpStatus === 403)
68755
- return result;
68756
- const looksLikeAuthFailure = result.state === "auth-failed" || /auth|token|login|credential|unauthor/i.test(result.errorMessage || "");
68757
- if (!looksLikeAuthFailure)
68758
- return result;
68759
- return {
68760
- ...result,
68761
- state: "auth-failed",
68762
- actionHint: `run: ${loginCommand2}`
68763
- };
68764
- }
68765
- async function safeReadBody(response) {
68766
- try {
68767
- const text = await response.text();
68768
- return text.slice(0, 500);
68769
- } catch {
68770
- return "";
68771
- }
68772
- }
68773
- function extractUpstreamStatus(body) {
68774
- if (!body)
68775
- return;
68776
- try {
68777
- const parsed = JSON.parse(body);
68778
- const status = parsed?.error?.upstream_status;
68779
- return typeof status === "number" ? status : undefined;
68780
- } catch {
68781
- return;
68782
- }
68783
- }
68784
- function extractErrorType(body) {
68785
- if (!body)
68786
- return;
68787
- try {
68788
- const parsed = JSON.parse(body);
68789
- const t = parsed?.error?.type;
68790
- return typeof t === "string" ? t : undefined;
68791
- } catch {
68792
- return;
68793
- }
68794
- }
68795
- function classifyHttpError(status, body, latencyMs) {
68796
- const lowered = body.toLowerCase();
68797
- if (extractErrorType(body) === "connection_error") {
68798
- return {
68799
- state: "network-error",
68800
- latencyMs,
68801
- httpStatus: status,
68802
- errorMessage: extractErrorMessage(body) || "Cannot reach provider"
68803
- };
68804
- }
68805
- const upstream = status === 400 ? extractUpstreamStatus(body) : undefined;
68806
- if (status === 401 || status === 403 || upstream === 401 || upstream === 403) {
68807
- const authStatus = upstream ?? status;
68808
- return {
68809
- state: "auth-failed",
68810
- latencyMs,
68811
- httpStatus: authStatus,
68812
- errorMessage: extractErrorMessage(body) || `HTTP ${authStatus}`
68813
- };
68814
- }
68815
- if (status === 404 || /model[_ ]not[_ ]found|no such model|unknown model/.test(lowered)) {
68816
- return {
68817
- state: "model-not-found",
68818
- latencyMs,
68819
- httpStatus: status,
68820
- errorMessage: extractErrorMessage(body) || `HTTP ${status}`
68821
- };
68822
- }
68823
- if (status === 429) {
68824
- return {
68825
- state: "rate-limited",
68826
- latencyMs,
68827
- httpStatus: status,
68828
- errorMessage: extractErrorMessage(body) || "Rate limited"
68829
- };
68830
- }
68831
- if (upstream === 429 || status === 402) {
68832
- return {
68833
- state: "out-of-credit",
68834
- latencyMs,
68835
- httpStatus: upstream ?? status,
68836
- errorMessage: extractErrorMessage(body) || "Out of credit \u2014 account balance or plan exhausted"
68837
- };
68838
- }
68839
- if (status >= 500) {
68840
- return {
68841
- state: "server-error",
68842
- latencyMs,
68843
- httpStatus: status,
68844
- errorMessage: extractErrorMessage(body) || `HTTP ${status}`
68845
- };
68846
- }
68847
- return {
68848
- state: "error",
68849
- latencyMs,
68850
- httpStatus: status,
68851
- errorMessage: extractErrorMessage(body) || `HTTP ${status}`
68852
- };
68853
- }
68854
- function extractErrorMessage(body) {
68855
- if (!body)
68856
- return;
68857
- try {
68858
- const parsed = JSON.parse(body);
68859
- const msg2 = parsed?.error?.message || parsed?.error?.error?.message || parsed?.message || parsed?.detail;
68860
- if (typeof msg2 === "string" && msg2.length > 0) {
68861
- return msg2.length > 160 ? `${msg2.slice(0, 157)}...` : msg2;
68862
- }
68863
- } catch {}
68864
- const trimmed2 = body.trim();
68865
- if (!trimmed2)
68866
- return;
68867
- return trimmed2.length > 160 ? `${trimmed2.slice(0, 157)}...` : trimmed2;
68868
- }
68869
- async function consumeProbeStream(response, timeoutMs, startedAt) {
68870
- const body = response.body;
68871
- if (!body) {
68872
- return { state: "error", errorMessage: "empty response body" };
68873
- }
68874
- const reader = body.getReader();
68875
- const decoder = new TextDecoder;
68876
- let buffered = "";
68877
- const deadline = Date.now() + timeoutMs;
68878
- let ttftMs;
68879
- let sawContent = false;
68880
- let textChars = 0;
68881
- let reportedTokens;
68882
- let stopReason;
68883
- let errorVerdict = null;
68884
- let completed = false;
68885
- try {
68886
- while (Date.now() < deadline) {
68887
- const { value, done } = await reader.read();
68888
- if (done) {
68889
- completed = true;
68890
- break;
68891
- }
68892
- buffered += decoder.decode(value, { stream: true });
68893
- const events = buffered.split(`
68894
-
68895
- `);
68896
- buffered = events.pop() ?? "";
68897
- for (const event of events) {
68898
- const verdict = interpretSseEvent(event);
68899
- if (verdict && typeof verdict === "object" && verdict.state !== "live") {
68900
- errorVerdict = verdict;
68901
- break;
68902
- }
68903
- const acct = accountStreamEvent(event);
68904
- if (acct.contentDelta) {
68905
- if (ttftMs === undefined)
68906
- ttftMs = Date.now() - startedAt;
68907
- sawContent = true;
68908
- }
68909
- if (acct.textChars)
68910
- textChars += acct.textChars;
68911
- if (acct.outputTokens !== undefined)
68912
- reportedTokens = acct.outputTokens;
68913
- if (acct.stopReason)
68914
- stopReason = acct.stopReason;
68915
- }
68916
- if (errorVerdict)
68917
- break;
68918
- }
68919
- } catch (e) {
68920
- if (!sawContent) {
68921
- return { state: "network-error", errorMessage: String(e?.message || e) };
68922
- }
68923
- } finally {
68924
- try {
68925
- await reader.cancel();
68926
- } catch {}
68927
- }
68928
- if (errorVerdict)
68929
- return errorVerdict;
68930
- if (sawContent) {
68931
- const tokens = reportedTokens ?? Math.max(1, Math.round(textChars / 4));
68932
- return { state: "live", ttftMs, tokens, truncated: !completed };
68933
- }
68934
- const truncationReason = stopReason === "max_tokens" || stopReason === "length" ? stopReason : undefined;
68935
- if (truncationReason || reportedTokens !== undefined && reportedTokens >= PROBE_MAX_TOKENS) {
68936
- const cause = truncationReason ? `finish: ${truncationReason}` : `${reportedTokens} tokens consumed, none visible`;
68937
- return {
68938
- state: "error",
68939
- errorMessage: `no visible output within probe budget (${cause})`
68940
- };
68941
- }
68942
- return { state: "error", errorMessage: "stream ended without content" };
68943
- }
68944
- function accountStreamEvent(rawEvent) {
68945
- let dataPayload = "";
68946
- for (const line of rawEvent.split(`
68947
- `)) {
68948
- if (line.startsWith("data:"))
68949
- dataPayload += line.slice(5).trim();
68950
- }
68951
- if (!dataPayload || dataPayload === "[DONE]") {
68952
- return { contentDelta: false, textChars: 0 };
68953
- }
68954
- let parsed;
68955
- try {
68956
- parsed = JSON.parse(dataPayload);
68957
- } catch {
68958
- return { contentDelta: false, textChars: 0 };
68959
- }
68960
- let textChars = 0;
68961
- let contentDelta = false;
68962
- const text = parsed?.delta?.text ?? (Array.isArray(parsed?.choices) ? parsed.choices[0]?.delta?.content : undefined);
68963
- if (typeof text === "string" && text.length > 0) {
68964
- contentDelta = true;
68965
- textChars = text.length;
68966
- } else if (parsed?.type === "content_block_delta" || parsed?.type === "content_block_start") {
68967
- contentDelta = true;
68968
- }
68969
- const outputTokens = parsed?.usage?.output_tokens ?? parsed?.message?.usage?.output_tokens ?? parsed?.usage?.completion_tokens;
68970
- const stopReason = parsed?.delta?.stop_reason ?? (Array.isArray(parsed?.choices) ? parsed.choices[0]?.finish_reason : undefined);
68971
- return {
68972
- contentDelta,
68973
- textChars,
68974
- outputTokens: typeof outputTokens === "number" ? outputTokens : undefined,
68975
- stopReason: typeof stopReason === "string" ? stopReason : undefined
68976
- };
68977
- }
68978
- function interpretSseEvent(rawEvent) {
68979
- const lines = rawEvent.split(`
68980
- `);
68981
- let eventType = "";
68982
- let dataPayload = "";
68983
- for (const line of lines) {
68984
- if (line.startsWith("event:"))
68985
- eventType = line.slice(6).trim();
68986
- else if (line.startsWith("data:"))
68987
- dataPayload += line.slice(5).trim();
68988
- }
68989
- if (!dataPayload)
68990
- return null;
68991
- if (dataPayload === "[DONE]")
68992
- return null;
68993
- let parsed;
68994
- try {
68995
- parsed = JSON.parse(dataPayload);
68996
- } catch {
68997
- return null;
68998
- }
68999
- if (parsed?.type === "error" || eventType === "error" || parsed?.error) {
69000
- const message = parsed?.error?.message || parsed?.error?.error?.message || parsed?.message || "provider returned error event";
69001
- const status = parsed?.error?.status || parsed?.status;
69002
- if (typeof status === "number") {
69003
- return {
69004
- state: status === 401 || status === 403 ? "auth-failed" : "error",
69005
- httpStatus: status,
69006
- errorMessage: message
69007
- };
69008
- }
69009
- return { state: "error", errorMessage: message };
69010
- }
69011
- if (isContentEvent(parsed, eventType)) {
69012
- return "live";
69013
- }
69014
- return null;
69015
- }
69016
- function isContentEvent(parsed, eventType) {
69017
- if (eventType === "content_block_start" || eventType === "content_block_delta")
69018
- return true;
69019
- if (eventType === "message_start")
69020
- return true;
69021
- if (parsed?.type === "content_block_start")
69022
- return true;
69023
- if (parsed?.type === "content_block_delta")
69024
- return true;
69025
- if (parsed?.type === "message_start")
69026
- return true;
69027
- if (parsed?.type === "message_delta")
69028
- return true;
69029
- if (Array.isArray(parsed?.choices) && parsed.choices.length > 0) {
69030
- const choice = parsed.choices[0];
69031
- if (choice?.delta || choice?.message || choice?.text || choice?.finish_reason)
69032
- return true;
69033
- }
69034
- if (parsed?.candidates)
69035
- return true;
69036
- return false;
69037
- }
69038
- function withDetail(base, message) {
69039
- return message ? `${base} \u2014 ${message}` : base;
69040
- }
69041
- function describeProbeState(result) {
69042
- const status = result.httpStatus ?? "";
69043
- const latency = result.latencyMs ? ` \xB7 ${result.latencyMs}ms` : "";
69044
- switch (result.state) {
69045
- case "live":
69046
- return `live \xB7 ${result.latencyMs}ms`;
69047
- case "key-missing":
69048
- return result.errorMessage ? `missing (${result.errorMessage})` : "missing";
69049
- case "auth-failed":
69050
- return withDetail(`auth failed \xB7 ${status}${latency}`.trim(), result.errorMessage);
69051
- case "model-not-found":
69052
- return withDetail(`model not found \xB7 ${status}${latency}`.trim(), result.errorMessage);
69053
- case "rate-limited":
69054
- return withDetail(`rate limited \xB7 ${result.latencyMs}ms`, result.errorMessage);
69055
- case "out-of-credit":
69056
- return withDetail(`out of credit \xB7 ${status}${latency}`.trim(), result.errorMessage);
69057
- case "server-error":
69058
- return withDetail(`server error \xB7 ${status} \xB7 ${result.latencyMs}ms`, result.errorMessage);
69059
- case "timeout":
69060
- return withDetail(`timeout \xB7 ${result.latencyMs}ms`, result.errorMessage);
69061
- case "network-error":
69062
- return withDetail(`network error \xB7 ${result.latencyMs}ms`, result.errorMessage);
69063
- case "error": {
69064
- const base = `error${result.httpStatus ? ` \xB7 ${result.httpStatus}` : ""}${latency}`;
69065
- return withDetail(base, result.errorMessage);
69066
- }
69067
- }
69068
- }
69069
- function isReadyState(state) {
69070
- return state === "live";
69071
- }
69072
- function isFailureState(state) {
69073
- return state === "auth-failed" || state === "model-not-found" || state === "rate-limited" || state === "out-of-credit" || state === "server-error" || state === "timeout" || state === "network-error" || state === "error";
69074
- }
69075
- var STREAM_MS_FLOOR = 50, OAUTH_PROVIDERS2, PROBE_PROMPT = "Count from one to twenty in words, one per line.", PROBE_MAX_TOKENS = 512;
69076
- var init_probe_live = __esm(() => {
69077
- OAUTH_PROVIDERS2 = new Set(["vertex", "antigravity", "devin"]);
69078
- });
69079
-
69080
69265
  // src/tui/theme.ts
69081
69266
  import { createTextAttributes } from "@opentui/core";
69082
69267
  function latencyBucket(ms) {
@@ -71350,6 +71535,21 @@ var init_api_key_map = __esm(() => {
71350
71535
  };
71351
71536
  });
71352
71537
 
71538
+ // src/providers/claude-code-aliases.ts
71539
+ function claudeCodeTierAlias(model) {
71540
+ return TIER_ALIASES[model.trim().toLowerCase()] ?? null;
71541
+ }
71542
+ var TIER_ALIASES;
71543
+ var init_claude_code_aliases = __esm(() => {
71544
+ TIER_ALIASES = {
71545
+ opus: "opus",
71546
+ sonnet: "sonnet",
71547
+ haiku: "haiku",
71548
+ internal: "opus",
71549
+ default: "opus"
71550
+ };
71551
+ });
71552
+
71353
71553
  // src/providers/probe-runner.ts
71354
71554
  function pinProbeModelSpec(link) {
71355
71555
  if (link.provider === "native-anthropic")
@@ -72041,7 +72241,7 @@ async function printRecommendedModels(jsonOutput, forceUpdate) {
72041
72241
  const rawId = m.id;
72042
72242
  const modelId = rawId.length > 28 ? `${rawId.substring(0, 25)}...` : rawId;
72043
72243
  const modelIdPadded = modelId.padEnd(28);
72044
- const pricing = normalizePricingDisplay(m.pricing?.average);
72244
+ const pricing = formatListingPrice(m, { compact: true });
72045
72245
  const pricingPadded = pricing.padEnd(10);
72046
72246
  const context = m.context || "N/A";
72047
72247
  const contextPadded = context.padEnd(6);
@@ -72133,13 +72333,19 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
72133
72333
  };
72134
72334
  }
72135
72335
  if (parsed.provider === "native-anthropic") {
72136
- const opusModel = process.env[ENV.CLAUDISH_MODEL_OPUS] || process.env[ENV.ANTHROPIC_DEFAULT_OPUS_MODEL] || "claude-opus-4-1";
72336
+ const tier = claudeCodeTierAlias(parsed.model);
72337
+ const tierEnv = {
72338
+ opus: process.env[ENV.CLAUDISH_MODEL_OPUS] || process.env[ENV.ANTHROPIC_DEFAULT_OPUS_MODEL],
72339
+ sonnet: process.env[ENV.CLAUDISH_MODEL_SONNET] || process.env[ENV.ANTHROPIC_DEFAULT_SONNET_MODEL],
72340
+ haiku: process.env[ENV.CLAUDISH_MODEL_HAIKU] || process.env[ENV.ANTHROPIC_DEFAULT_HAIKU_MODEL]
72341
+ };
72342
+ const opusModel = tier ? tierEnv[tier] || latestAnthropicTierModelId(tier) || "claude-opus-5" : parsed.model;
72137
72343
  return {
72138
72344
  routes: [
72139
72345
  {
72140
72346
  provider: "native-anthropic",
72141
72347
  modelSpec: opusModel,
72142
- displayName: "Claude Code (Opus)"
72348
+ displayName: tier ? `Claude Code (${tier})` : "Claude Code"
72143
72349
  }
72144
72350
  ],
72145
72351
  source: "auto-chain",
@@ -72233,6 +72439,33 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
72233
72439
  }
72234
72440
  return chain.source;
72235
72441
  }
72442
+ function buildDirectChainEntry(parsed, directProbe) {
72443
+ const providerDef = getProviderByName(parsed.provider);
72444
+ const keyInfo = API_KEY_MAP[parsed.provider];
72445
+ if (!providerDef && !keyInfo)
72446
+ return [];
72447
+ let hasCredentials;
72448
+ let provenance;
72449
+ if (providerDef?.isLocal) {
72450
+ hasCredentials = isLocalProviderEnabled(parsed.provider);
72451
+ } else if (!keyInfo?.envVar) {
72452
+ hasCredentials = true;
72453
+ } else {
72454
+ provenance = resolveApiKeyProvenance(keyInfo.envVar, keyInfo.aliases);
72455
+ hasCredentials = provenance.hasValue || (keyInfo.aliases?.some((a) => !!process.env[a]) ?? false);
72456
+ }
72457
+ return [
72458
+ {
72459
+ provider: parsed.provider,
72460
+ displayName: providerDef?.displayName ?? parsed.provider,
72461
+ modelSpec: parsed.model,
72462
+ hasCredentials,
72463
+ credentialHint: !hasCredentials ? providerDef?.isLocal ? "enable local provider in global config" : keyInfo?.envVar : undefined,
72464
+ provenance,
72465
+ probe: directProbe
72466
+ }
72467
+ ];
72468
+ }
72236
72469
  function buildResultLinks(parsed, chainDetails, directProbe) {
72237
72470
  if (chainDetails.length === 0) {
72238
72471
  const directProviderDef = getProviderByName(parsed.provider);
@@ -72383,7 +72616,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
72383
72616
  isExplicit: parsed.isExplicitProvider,
72384
72617
  routingSource: chain.source,
72385
72618
  matchedPattern: chain.matchedPattern,
72386
- chain: chainDetails,
72619
+ chain: chainDetails.length > 0 ? chainDetails : buildDirectChainEntry(parsed, directProbeResult),
72387
72620
  directProbe: directProbeResult,
72388
72621
  wiring
72389
72622
  });
@@ -73010,6 +73243,8 @@ var init_cli = __esm(() => {
73010
73243
  init_profile_config();
73011
73244
  init_api_key_map();
73012
73245
  init_api_key_provenance();
73246
+ init_catalog_client();
73247
+ init_claude_code_aliases();
73013
73248
  init_endpoint_registration();
73014
73249
  init_model_parser();
73015
73250
  init_probe_live();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "7.56.0",
3
+ "version": "7.57.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.56.0",
64
- "@claudish/magmux-darwin-x64": "7.56.0",
65
- "@claudish/magmux-linux-arm64": "7.56.0",
66
- "@claudish/magmux-linux-x64": "7.56.0"
63
+ "@claudish/magmux-darwin-arm64": "7.57.0",
64
+ "@claudish/magmux-darwin-x64": "7.57.0",
65
+ "@claudish/magmux-linux-arm64": "7.57.0",
66
+ "@claudish/magmux-linux-x64": "7.57.0"
67
67
  },
68
68
  "author": "Jack Rudenko <i@madappgang.com>",
69
69
  "license": "MIT",