claudish 7.30.0 → 7.32.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 +1300 -492
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -651,7 +651,7 @@ var init_onepassword_config = __esm(() => {
651
651
  });
652
652
 
653
653
  // src/version.ts
654
- var VERSION = "7.30.0";
654
+ var VERSION = "7.32.0";
655
655
 
656
656
  // src/logger.ts
657
657
  var exports_logger = {};
@@ -28218,6 +28218,25 @@ var init_provider_definitions = __esm(() => {
28218
28218
  isDirectApi: true,
28219
28219
  description: "Sakana Fugu Subscription (sc@)"
28220
28220
  },
28221
+ {
28222
+ name: "qwen-cloud",
28223
+ displayName: "Qwen Plan",
28224
+ transport: "anthropic",
28225
+ baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com",
28226
+ baseUrlEnvVars: ["QWEN_CLOUD_PLAN_BASE_URL"],
28227
+ apiPath: "/apps/anthropic/v1/messages",
28228
+ apiKeyEnvVar: "QWEN_CLOUD_PLAN_API_KEY",
28229
+ apiKeyDescription: "Qwen Plan API Key",
28230
+ apiKeyUrl: "https://www.alibabacloud.com/help/en/model-studio/claude-code",
28231
+ authScheme: "bearer",
28232
+ shortcuts: ["qc"],
28233
+ shortestPrefix: "qc",
28234
+ legacyPrefixes: [{ prefix: "qc/", stripPrefix: true }],
28235
+ nativeModelPatterns: [{ pattern: /^qwen3\.\d/i }],
28236
+ modelDiscovery: { path: "/compatible-mode/v1/models", format: "openai-models-list" },
28237
+ isDirectApi: true,
28238
+ description: "Qwen Plan (qc@)"
28239
+ },
28221
28240
  {
28222
28241
  name: "qwen",
28223
28242
  displayName: "Qwen",
@@ -31500,6 +31519,7 @@ var init_routing_hints = __esm(() => {
31500
31519
  deepseek: { apiKeyEnvVar: "DEEPSEEK_API_KEY" },
31501
31520
  sakana: { apiKeyEnvVar: "SAKANA_API_KEY" },
31502
31521
  "sakana-subscription": { apiKeyEnvVar: "SAKANA_SUBSCRIPTION_API_KEY" },
31522
+ "qwen-cloud": { apiKeyEnvVar: "QWEN_CLOUD_PLAN_API_KEY" },
31503
31523
  ollamacloud: { apiKeyEnvVar: "OLLAMA_API_KEY" },
31504
31524
  "native-anthropic": { apiKeyEnvVar: "ANTHROPIC_API_KEY" },
31505
31525
  openrouter: { apiKeyEnvVar: "OPENROUTER_API_KEY" },
@@ -31559,9 +31579,13 @@ function lookupModel(modelId, cachePath) {
31559
31579
  return {
31560
31580
  modelId: entry.modelId,
31561
31581
  contextWindow: entry.contextWindow,
31562
- supportsVision: entry.supportsVision
31582
+ supportsVision: entry.supportsVision,
31583
+ releaseDate: entry.releaseDate
31563
31584
  };
31564
31585
  }
31586
+ function lookupModelReasoning(modelId, cachePath) {
31587
+ return findCacheEntry(modelId, cachePath)?.reasoning;
31588
+ }
31565
31589
  function lookupModelForProvider(modelId, provider, cachePath) {
31566
31590
  const entry = findCacheEntry(modelId, cachePath);
31567
31591
  if (!entry)
@@ -31668,6 +31692,7 @@ var init_default_routing_rules = __esm(() => {
31668
31692
  "k3*": ["kimi-coding", "kimi", "openrouter"],
31669
31693
  "minimax-*": ["minimax-coding", "minimax", "openrouter"],
31670
31694
  "glm-*": ["glm-coding", "glm", "openrouter"],
31695
+ "qwen3.*": ["qwen-cloud", "openrouter"],
31671
31696
  "z-ai-*": ["z-ai", "openrouter"],
31672
31697
  "deepseek-*": ["deepseek", "openrouter"],
31673
31698
  fugu: ["sakana-subscription", "sakana"],
@@ -32917,18 +32942,80 @@ var init_cache_ttl = __esm(() => {
32917
32942
  FIREBASE_CACHE_TTL_MS = FIREBASE_CACHE_TTL_HOURS * 60 * 60 * 1000;
32918
32943
  });
32919
32944
 
32945
+ // src/providers/model-ordering.ts
32946
+ function extractVersionParts(modelId) {
32947
+ const tokens = modelId.toLowerCase().split(/[\/_-]+/);
32948
+ let started = false;
32949
+ const parts = [];
32950
+ for (const token of tokens) {
32951
+ const match = token.match(/\d+(?:\.\d+)*/);
32952
+ if (!match) {
32953
+ if (started)
32954
+ break;
32955
+ continue;
32956
+ }
32957
+ if (!started) {
32958
+ started = true;
32959
+ for (const part of match[0].split(".")) {
32960
+ parts.push(Number.parseInt(part, 10));
32961
+ }
32962
+ if (!/^\d+(?:\.\d+)*$/.test(token)) {
32963
+ break;
32964
+ }
32965
+ continue;
32966
+ }
32967
+ if (!/^\d{1,2}(?:\.\d+)?$/.test(token)) {
32968
+ break;
32969
+ }
32970
+ for (const part of token.split(".")) {
32971
+ parts.push(Number.parseInt(part, 10));
32972
+ }
32973
+ }
32974
+ return parts;
32975
+ }
32976
+ function compareVersionPartsDesc(a, b) {
32977
+ const maxLength = Math.max(a.length, b.length);
32978
+ for (let i = 0;i < maxLength; i++) {
32979
+ const aPart = a[i] ?? -1;
32980
+ const bPart = b[i] ?? -1;
32981
+ if (aPart !== bPart) {
32982
+ return bPart - aPart;
32983
+ }
32984
+ }
32985
+ return 0;
32986
+ }
32987
+ function compareByReleaseDateDesc(a, b) {
32988
+ const aReleaseRaw = a.releaseDate ? Date.parse(a.releaseDate) : 0;
32989
+ const bReleaseRaw = b.releaseDate ? Date.parse(b.releaseDate) : 0;
32990
+ const aRelease = Number.isNaN(aReleaseRaw) ? 0 : aReleaseRaw;
32991
+ const bRelease = Number.isNaN(bReleaseRaw) ? 0 : bReleaseRaw;
32992
+ if (aRelease !== bRelease) {
32993
+ return bRelease - aRelease;
32994
+ }
32995
+ const aId = a.id ?? a.modelId ?? "";
32996
+ const bId = b.id ?? b.modelId ?? "";
32997
+ const versionCompare = compareVersionPartsDesc(extractVersionParts(aId), extractVersionParts(bId));
32998
+ if (versionCompare !== 0) {
32999
+ return versionCompare;
33000
+ }
33001
+ return aId.localeCompare(bId);
33002
+ }
33003
+
32920
33004
  // src/model-loader.ts
32921
33005
  import { existsSync as existsSync14, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
32922
33006
  import { homedir as homedir17 } from "os";
32923
33007
  import { join as join17 } from "path";
32924
33008
  function groupRecommendedModels(entries) {
32925
33009
  const byId = new Map;
33010
+ const categoryOrder = new Map;
32926
33011
  for (const entry of entries) {
32927
33012
  const list = byId.get(entry.id);
32928
33013
  if (list)
32929
33014
  list.push(entry);
32930
33015
  else
32931
33016
  byId.set(entry.id, [entry]);
33017
+ if (!categoryOrder.has(entry.category))
33018
+ categoryOrder.set(entry.category, categoryOrder.size);
32932
33019
  }
32933
33020
  const flagship = [];
32934
33021
  const fast = [];
@@ -32942,6 +33029,17 @@ function groupRecommendedModels(entries) {
32942
33029
  else
32943
33030
  fast.push(group);
32944
33031
  }
33032
+ const byCuratedPriorityThenFreshness = (a, b) => {
33033
+ const aCat = categoryOrder.get(a.primary.category) ?? Number.MAX_SAFE_INTEGER;
33034
+ const bCat = categoryOrder.get(b.primary.category) ?? Number.MAX_SAFE_INTEGER;
33035
+ if (aCat !== bCat)
33036
+ return aCat - bCat;
33037
+ if (a.primary.priority !== b.primary.priority)
33038
+ return a.primary.priority - b.primary.priority;
33039
+ return compareByReleaseDateDesc(a.primary, b.primary);
33040
+ };
33041
+ flagship.sort(byCuratedPriorityThenFreshness);
33042
+ fast.sort(byCuratedPriorityThenFreshness);
32945
33043
  return { flagship, fast };
32946
33044
  }
32947
33045
  function collectRoutingPrefixes(group, getNativePrefix) {
@@ -34932,6 +35030,9 @@ var cors = (options) => {
34932
35030
  var init_cors = () => {};
34933
35031
 
34934
35032
  // src/handlers/shared/remote-provider-types.ts
35033
+ function isSubscriptionProvider(provider) {
35034
+ return SUBSCRIPTION_PROVIDERS.has(provider.toLowerCase());
35035
+ }
34935
35036
  function registerDynamicPricingLookup(fn) {
34936
35037
  _dynamicLookup = fn;
34937
35038
  }
@@ -34940,7 +35041,7 @@ function getModelPricing(provider, modelName) {
34940
35041
  if (FREE_PROVIDERS.has(p)) {
34941
35042
  return { inputCostPer1M: 0, outputCostPer1M: 0, isFree: true };
34942
35043
  }
34943
- if (SUBSCRIPTION_PROVIDERS.has(p)) {
35044
+ if (isSubscriptionProvider(p)) {
34944
35045
  return { inputCostPer1M: 0, outputCostPer1M: 0, isSubscription: true };
34945
35046
  }
34946
35047
  if (_dynamicLookup) {
@@ -34962,7 +35063,12 @@ var init_remote_provider_types = __esm(() => {
34962
35063
  ollamacloud: { inputCostPer1M: 1, outputCostPer1M: 4, isEstimate: true }
34963
35064
  };
34964
35065
  FREE_PROVIDERS = new Set(["opencode-zen", "zen"]);
34965
- SUBSCRIPTION_PROVIDERS = new Set(["minimax-coding", "kimi-coding", "glm-coding"]);
35066
+ SUBSCRIPTION_PROVIDERS = new Set([
35067
+ "minimax-coding",
35068
+ "kimi-coding",
35069
+ "glm-coding",
35070
+ "qwen-cloud"
35071
+ ]);
34966
35072
  PROVIDER_ALIAS = {
34967
35073
  google: "gemini",
34968
35074
  oai: "openai",
@@ -35326,16 +35432,24 @@ function matchesModelFamily(modelId, family) {
35326
35432
  const fam = family.toLowerCase();
35327
35433
  return lower.startsWith(fam) || lower.includes(`/${fam}`);
35328
35434
  }
35435
+ function isEffortLevel(value) {
35436
+ return typeof value === "string" && EFFORT_ORDER.includes(value);
35437
+ }
35329
35438
 
35330
35439
  class BaseAPIFormat {
35331
35440
  modelId;
35441
+ wireFormat;
35332
35442
  toolNameMap = new Map;
35333
- constructor(modelId) {
35443
+ constructor(modelId, wireFormat) {
35334
35444
  this.modelId = modelId;
35445
+ this.wireFormat = wireFormat;
35335
35446
  }
35336
35447
  getModelId() {
35337
35448
  return this.modelId;
35338
35449
  }
35450
+ getWireFormat() {
35451
+ return this.wireFormat;
35452
+ }
35339
35453
  getToolNameLimit() {
35340
35454
  return null;
35341
35455
  }
@@ -35348,9 +35462,114 @@ class BaseAPIFormat {
35348
35462
  restoreToolName(name) {
35349
35463
  return this.toolNameMap.get(name) || name;
35350
35464
  }
35351
- prepareRequest(request, _originalRequest) {
35465
+ prepareRequest(request, originalRequest) {
35466
+ const prepared = this.prepareRequestCommon(request, originalRequest) ?? request;
35467
+ if (!this.isAnthropicWire()) {
35468
+ return this.applyNativeReasoning(prepared, originalRequest) ?? prepared;
35469
+ }
35470
+ this.stripNonAnthropicReasoningFields(prepared);
35471
+ return this.applyAnthropicWireReasoning(prepared, originalRequest) ?? prepared;
35472
+ }
35473
+ prepareRequestCommon(request, _originalRequest) {
35474
+ return request;
35475
+ }
35476
+ applyNativeReasoning(request, _originalRequest) {
35477
+ return request;
35478
+ }
35479
+ isAnthropicWire() {
35480
+ return this.wireFormat === "anthropic-sse";
35481
+ }
35482
+ stripNonAnthropicReasoningFields(request) {
35483
+ if (!request)
35484
+ return;
35485
+ for (const field of NON_ANTHROPIC_REASONING_FIELDS) {
35486
+ if (request[field] !== undefined)
35487
+ delete request[field];
35488
+ }
35489
+ }
35490
+ applyAnthropicWireReasoning(request, originalRequest) {
35491
+ const reasoning = this.lookupReasoningCapability();
35492
+ if (reasoning?.supported === false) {
35493
+ request.thinking = { type: "disabled" };
35494
+ log(`[${this.getName()}] ${this.modelId} reports no reasoning support -> thinking: disabled`);
35495
+ return request;
35496
+ }
35497
+ const effort = this.resolveEffortLevel(originalRequest);
35498
+ if (!effort)
35499
+ return request;
35500
+ if (effort === "none" || effort === "minimal") {
35501
+ request.thinking = { type: "disabled" };
35502
+ log(`[${this.getName()}] effort ${effort} -> thinking.type: disabled for ${this.modelId}`);
35503
+ return request;
35504
+ }
35505
+ if (reasoning && (reasoning.control === "budget" || reasoning.supportsBudgetTokens)) {
35506
+ return this.enableAnthropicThinkingWithBudget(request, effort, "catalog: budget-controlled");
35507
+ }
35508
+ const advertised = reasoning?.efforts?.length ? reasoning : undefined;
35509
+ if (advertised) {
35510
+ const level = this.clampToAdvertisedEffort(effort, advertised);
35511
+ request.thinking = { type: "enabled" };
35512
+ if (level) {
35513
+ request.output_config = { ...request.output_config ?? {}, effort: level };
35514
+ }
35515
+ log(`[${this.getName()}] effort ${effort} -> thinking: enabled, output_config.effort: ${level ?? "(none advertised)"} for ${this.modelId} (advertised: ${advertised.efforts?.join("/")})`);
35516
+ return request;
35517
+ }
35518
+ if (reasoning) {
35519
+ request.thinking = { type: "enabled" };
35520
+ log(`[${this.getName()}] effort ${effort} -> thinking: enabled (no depth knob; catalog control=${reasoning.control ?? "unknown"}) for ${this.modelId}`);
35521
+ return request;
35522
+ }
35523
+ return this.enableAnthropicThinkingWithBudget(request, effort, "no catalog entry");
35524
+ }
35525
+ enableAnthropicThinkingWithBudget(request, effort, why) {
35526
+ const budget = this.effortToThinkingTokenBudget(effort);
35527
+ request.thinking = budget === undefined ? { type: "enabled" } : { type: "enabled", budget_tokens: budget };
35528
+ log(`[${this.getName()}] effort ${effort} -> thinking: enabled, budget_tokens: ${budget ?? "(model max)"} for ${this.modelId} (${why})`);
35352
35529
  return request;
35353
35530
  }
35531
+ clampToAdvertisedEffort(requested, reasoning) {
35532
+ const advertised = (reasoning.efforts ?? []).filter(isEffortLevel);
35533
+ if (advertised.length === 0) {
35534
+ return isEffortLevel(reasoning.defaultEffort) ? reasoning.defaultEffort : undefined;
35535
+ }
35536
+ if (advertised.includes(requested))
35537
+ return requested;
35538
+ const target = EFFORT_ORDER.indexOf(requested);
35539
+ let best = advertised[0];
35540
+ let bestDistance = Number.POSITIVE_INFINITY;
35541
+ for (const candidate of advertised) {
35542
+ const distance = Math.abs(EFFORT_ORDER.indexOf(candidate) - target);
35543
+ if (distance < bestDistance || distance === bestDistance && EFFORT_ORDER.indexOf(candidate) > EFFORT_ORDER.indexOf(best)) {
35544
+ best = candidate;
35545
+ bestDistance = distance;
35546
+ }
35547
+ }
35548
+ return best;
35549
+ }
35550
+ lookupReasoningCapability() {
35551
+ try {
35552
+ return lookupModelReasoning(this.modelId);
35553
+ } catch {
35554
+ return;
35555
+ }
35556
+ }
35557
+ effortToThinkingTokenBudget(effort) {
35558
+ switch (effort) {
35559
+ case "low":
35560
+ return 2048;
35561
+ case "medium":
35562
+ return 8192;
35563
+ case "high":
35564
+ return 24576;
35565
+ case "xhigh":
35566
+ return 38912;
35567
+ case "max":
35568
+ return;
35569
+ default:
35570
+ return 8192;
35571
+ }
35572
+ }
35354
35573
  resolveEffortLevel(originalRequest) {
35355
35574
  const lvl = originalRequest?.output_config?.effort;
35356
35575
  if (typeof lvl === "string") {
@@ -35412,7 +35631,7 @@ class BaseAPIFormat {
35412
35631
  return true;
35413
35632
  }
35414
35633
  shouldFilterThinking() {
35415
- return false;
35634
+ return this.isAnthropicWire();
35416
35635
  }
35417
35636
  truncateToolNames(request) {
35418
35637
  const limit = this.getToolNameLimit();
@@ -35451,13 +35670,19 @@ class BaseAPIFormat {
35451
35670
  }
35452
35671
  }
35453
35672
  }
35454
- var EFFORT_ORDER, DefaultAPIFormat;
35673
+ var EFFORT_ORDER, NON_ANTHROPIC_REASONING_FIELDS, DefaultAPIFormat;
35455
35674
  var init_base_api_format = __esm(() => {
35456
35675
  init_remote_provider_types();
35676
+ init_logger();
35457
35677
  init_model_catalog();
35458
35678
  init_tool_name_utils();
35459
35679
  init_openai_tools();
35460
35680
  EFFORT_ORDER = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
35681
+ NON_ANTHROPIC_REASONING_FIELDS = [
35682
+ "reasoning_effort",
35683
+ "enable_thinking",
35684
+ "thinking_budget"
35685
+ ];
35461
35686
  DefaultAPIFormat = class DefaultAPIFormat extends BaseAPIFormat {
35462
35687
  processTextContent(textContent, _accumulatedText) {
35463
35688
  return {
@@ -35707,7 +35932,7 @@ var init_deepseek_model_dialect = __esm(() => {
35707
35932
  wasTransformed: false
35708
35933
  };
35709
35934
  }
35710
- prepareRequest(request, originalRequest) {
35935
+ applyNativeReasoning(request, originalRequest) {
35711
35936
  const effort = this.resolveEffortLevel(originalRequest);
35712
35937
  if (effort && this.isV4Model()) {
35713
35938
  if (effort === "none" || effort === "minimal") {
@@ -36426,6 +36651,7 @@ data: ${JSON.stringify(d)}
36426
36651
  output_tokens: state.usage?.completion_tokens || 0
36427
36652
  }
36428
36653
  });
36654
+ behavior?.onTurnEnd?.();
36429
36655
  send("message_stop", { type: "message_stop" });
36430
36656
  }
36431
36657
  if (onTokenUpdate) {
@@ -36492,6 +36718,7 @@ data: ${JSON.stringify(d)}
36492
36718
  });
36493
36719
  }
36494
36720
  if (delta.reasoning_content) {
36721
+ behavior?.onAssistantText?.(delta.reasoning_content, "reasoning");
36495
36722
  state.lastActivity = Date.now();
36496
36723
  if (!state.reasoningStarted) {
36497
36724
  state.reasoningIdx = state.curIdx++;
@@ -36509,6 +36736,8 @@ data: ${JSON.stringify(d)}
36509
36736
  });
36510
36737
  }
36511
36738
  const txt = delta.content || "";
36739
+ if (txt)
36740
+ behavior?.onAssistantText?.(txt, "text");
36512
36741
  log(`[Streaming] Text chunk: "${txt.substring(0, 30).replace(/\n/g, "\\n")}" (${txt.length} chars)`);
36513
36742
  if (txt) {
36514
36743
  state.lastActivity = Date.now();
@@ -37136,7 +37365,7 @@ var init_glm_model_dialect = __esm(() => {
37136
37365
  wasTransformed: false
37137
37366
  };
37138
37367
  }
37139
- prepareRequest(request, originalRequest) {
37368
+ applyNativeReasoning(request, originalRequest) {
37140
37369
  const effort = this.resolveEffortLevel(originalRequest);
37141
37370
  if (effort && this.isHybridThinkingModel()) {
37142
37371
  const type = effort === "none" || effort === "minimal" ? "disabled" : "enabled";
@@ -37218,7 +37447,7 @@ var init_grok_model_dialect = __esm(() => {
37218
37447
  wasTransformed: true
37219
37448
  };
37220
37449
  }
37221
- prepareRequest(request, originalRequest) {
37450
+ applyNativeReasoning(request, originalRequest) {
37222
37451
  const effort = this.resolveEffortLevel(originalRequest);
37223
37452
  if (effort) {
37224
37453
  const value = this.effortToReasoningEffort(effort);
@@ -37310,7 +37539,7 @@ var init_minimax_model_dialect = __esm(() => {
37310
37539
  wasTransformed: false
37311
37540
  };
37312
37541
  }
37313
- prepareRequest(request, originalRequest) {
37542
+ prepareRequestCommon(request, _originalRequest) {
37314
37543
  if (request.temperature !== undefined) {
37315
37544
  if (request.temperature < TEMPERATURE_RANGE.min) {
37316
37545
  log(`[MiniMaxModelDialect] Clamping temperature ${request.temperature} \u2192 ${TEMPERATURE_RANGE.min} (MiniMax requires >= ${TEMPERATURE_RANGE.min})`);
@@ -37320,6 +37549,9 @@ var init_minimax_model_dialect = __esm(() => {
37320
37549
  request.temperature = TEMPERATURE_RANGE.max;
37321
37550
  }
37322
37551
  }
37552
+ return request;
37553
+ }
37554
+ applyMiniMaxThinking(request, originalRequest) {
37323
37555
  const effort = this.resolveEffortLevel(originalRequest);
37324
37556
  if (effort) {
37325
37557
  const type = effort === "none" ? "disabled" : "adaptive";
@@ -37328,6 +37560,12 @@ var init_minimax_model_dialect = __esm(() => {
37328
37560
  }
37329
37561
  return request;
37330
37562
  }
37563
+ applyNativeReasoning(request, originalRequest) {
37564
+ return this.applyMiniMaxThinking(request, originalRequest);
37565
+ }
37566
+ applyAnthropicWireReasoning(request, originalRequest) {
37567
+ return this.applyMiniMaxThinking(request, originalRequest);
37568
+ }
37331
37569
  getContextWindow() {
37332
37570
  return lookupModel(this.modelId)?.contextWindow ?? 0;
37333
37571
  }
@@ -37365,7 +37603,14 @@ var init_openai_api_format = __esm(() => {
37365
37603
  getMaxToolCount() {
37366
37604
  return 128;
37367
37605
  }
37368
- prepareRequest(request, originalRequest) {
37606
+ prepareRequestCommon(request, _originalRequest) {
37607
+ this.truncateToolNames(request);
37608
+ if (request.messages) {
37609
+ this.truncateToolNamesInMessages(request.messages);
37610
+ }
37611
+ return request;
37612
+ }
37613
+ applyNativeReasoning(request, originalRequest) {
37369
37614
  if (this.supportsReasoningEffort() && request.reasoning_effort === undefined) {
37370
37615
  const effort = this.resolveReasoningEffort(originalRequest);
37371
37616
  if (effort) {
@@ -37375,10 +37620,6 @@ var init_openai_api_format = __esm(() => {
37375
37620
  }
37376
37621
  if (request.thinking)
37377
37622
  delete request.thinking;
37378
- this.truncateToolNames(request);
37379
- if (request.messages) {
37380
- this.truncateToolNamesInMessages(request.messages);
37381
- }
37382
37623
  return request;
37383
37624
  }
37384
37625
  shouldHandle(modelId) {
@@ -37536,41 +37777,25 @@ var init_qwen_model_dialect = __esm(() => {
37536
37777
  wasTransformed
37537
37778
  };
37538
37779
  }
37539
- prepareRequest(request, originalRequest) {
37780
+ applyNativeReasoning(request, originalRequest) {
37540
37781
  const effort = this.resolveEffortLevel(originalRequest);
37541
- if (effort) {
37542
- if (effort === "none" || effort === "minimal") {
37543
- request.enable_thinking = false;
37544
- log(`[QwenModelDialect] effort ${effort} -> enable_thinking: false for ${this.modelId}`);
37545
- } else {
37546
- request.enable_thinking = true;
37547
- const budget = this.effortToThinkingBudget(effort);
37548
- if (budget !== undefined) {
37549
- request.thinking_budget = budget;
37550
- }
37551
- log(`[QwenModelDialect] effort ${effort} -> enable_thinking: true, thinking_budget: ${budget ?? "(model max)"} for ${this.modelId}`);
37782
+ if (!effort)
37783
+ return request;
37784
+ if (effort === "none" || effort === "minimal") {
37785
+ request.enable_thinking = false;
37786
+ log(`[QwenModelDialect] effort ${effort} -> enable_thinking: false for ${this.modelId}`);
37787
+ } else {
37788
+ request.enable_thinking = true;
37789
+ const budget = this.effortToThinkingTokenBudget(effort);
37790
+ if (budget !== undefined) {
37791
+ request.thinking_budget = budget;
37552
37792
  }
37553
- if (originalRequest.thinking)
37554
- delete request.thinking;
37793
+ log(`[QwenModelDialect] effort ${effort} -> enable_thinking: true, thinking_budget: ${budget ?? "(model max)"} for ${this.modelId}`);
37555
37794
  }
37795
+ if (originalRequest.thinking)
37796
+ delete request.thinking;
37556
37797
  return request;
37557
37798
  }
37558
- effortToThinkingBudget(effort) {
37559
- switch (effort) {
37560
- case "low":
37561
- return 2048;
37562
- case "medium":
37563
- return 8192;
37564
- case "high":
37565
- return 24576;
37566
- case "xhigh":
37567
- return 38912;
37568
- case "max":
37569
- return;
37570
- default:
37571
- return 8192;
37572
- }
37573
- }
37574
37799
  shouldHandle(modelId) {
37575
37800
  return matchesModelFamily(modelId, "qwen") || matchesModelFamily(modelId, "alibaba");
37576
37801
  }
@@ -37596,17 +37821,20 @@ var init_xiaomi_model_dialect = __esm(() => {
37596
37821
  getToolNameLimit() {
37597
37822
  return 64;
37598
37823
  }
37599
- prepareRequest(request, originalRequest) {
37600
- if (originalRequest.thinking) {
37601
- log("[XiaomiModelDialect] Stripping thinking object (not supported by Xiaomi API)");
37602
- delete request.thinking;
37603
- }
37824
+ prepareRequestCommon(request, _originalRequest) {
37604
37825
  this.truncateToolNames(request);
37605
37826
  if (request.messages) {
37606
37827
  this.truncateToolNamesInMessages(request.messages);
37607
37828
  }
37608
37829
  return request;
37609
37830
  }
37831
+ applyNativeReasoning(request, originalRequest) {
37832
+ if (originalRequest.thinking) {
37833
+ log("[XiaomiModelDialect] Stripping thinking object (not supported by Xiaomi API)");
37834
+ delete request.thinking;
37835
+ }
37836
+ return request;
37837
+ }
37610
37838
  shouldHandle(modelId) {
37611
37839
  return matchesModelFamily(modelId, "xiaomi") || matchesModelFamily(modelId, "mimo");
37612
37840
  }
@@ -37626,19 +37854,19 @@ __export(exports_dialect_manager, {
37626
37854
  class DialectManager {
37627
37855
  adapters;
37628
37856
  defaultAdapter;
37629
- constructor(modelId) {
37857
+ constructor(modelId, wireFormat) {
37630
37858
  this.adapters = [
37631
- new GrokModelDialect(modelId),
37632
- new GeminiAPIFormat(modelId),
37633
- new CodexAPIFormat(modelId),
37634
- new OpenAIAPIFormat(modelId),
37635
- new QwenModelDialect(modelId),
37636
- new MiniMaxModelDialect(modelId),
37637
- new DeepSeekModelDialect(modelId),
37638
- new GLMModelDialect(modelId),
37639
- new XiaomiModelDialect(modelId)
37859
+ new GrokModelDialect(modelId, wireFormat),
37860
+ new GeminiAPIFormat(modelId, wireFormat),
37861
+ new CodexAPIFormat(modelId, wireFormat),
37862
+ new OpenAIAPIFormat(modelId, wireFormat),
37863
+ new QwenModelDialect(modelId, wireFormat),
37864
+ new MiniMaxModelDialect(modelId, wireFormat),
37865
+ new DeepSeekModelDialect(modelId, wireFormat),
37866
+ new GLMModelDialect(modelId, wireFormat),
37867
+ new XiaomiModelDialect(modelId, wireFormat)
37640
37868
  ];
37641
- this.defaultAdapter = new DefaultAPIFormat(modelId);
37869
+ this.defaultAdapter = new DefaultAPIFormat(modelId, wireFormat);
37642
37870
  }
37643
37871
  getAdapter() {
37644
37872
  for (const adapter of this.adapters) {
@@ -37745,7 +37973,7 @@ ${messages[0].content}`;
37745
37973
  }
37746
37974
  return payload;
37747
37975
  }
37748
- prepareRequest(request, originalRequest) {
37976
+ prepareRequestCommon(request, originalRequest) {
37749
37977
  this.innerAdapter.prepareRequest(request, originalRequest);
37750
37978
  for (const [k, v] of this.innerAdapter.getToolNameMap()) {
37751
37979
  this.toolNameMap.set(k, v);
@@ -37931,7 +38159,7 @@ ${text}`;
37931
38159
  }
37932
38160
  return payload;
37933
38161
  }
37934
- prepareRequest(request, originalRequest) {
38162
+ prepareRequestCommon(request, originalRequest) {
37935
38163
  return this.innerAdapter.prepareRequest(request, originalRequest);
37936
38164
  }
37937
38165
  getToolNameMap() {
@@ -37967,24 +38195,44 @@ function parseBehaviorConfig(raw2) {
37967
38195
  }
37968
38196
  return result.data;
37969
38197
  }
37970
- function resolveSeverity(ruleId, defaultSeverity, config2) {
38198
+ function resolveSeverity(ruleId, defaultSeverity, config2, modelId) {
37971
38199
  const rules = config2.rules;
37972
38200
  if (!rules)
37973
38201
  return defaultSeverity;
37974
- const exact = rules[ruleId];
37975
- if (exact)
37976
- return exact;
38202
+ if (modelId) {
38203
+ const scoped = bestMatch(rules, ruleId, modelId);
38204
+ if (scoped)
38205
+ return scoped;
38206
+ }
38207
+ return bestMatch(rules, ruleId, undefined) ?? defaultSeverity;
38208
+ }
38209
+ function bestMatch(rules, ruleId, modelId) {
37977
38210
  let best = null;
37978
- for (const [pattern, severity] of Object.entries(rules)) {
37979
- if (!pattern.includes("*"))
38211
+ for (const [key, severity] of Object.entries(rules)) {
38212
+ const { model: keyModel, rule: keyRule } = parseRuleKey(key);
38213
+ if (!scopeMatches(keyModel, modelId))
37980
38214
  continue;
37981
- if (!globMatches(pattern, ruleId))
38215
+ if (!globMatches(keyRule, ruleId))
37982
38216
  continue;
37983
- const len = pattern.replace(/\*/g, "").length;
37984
- if (!best || len > best.len)
37985
- best = { len, severity };
38217
+ const score = specificity(keyRule);
38218
+ if (!best || score > best.score)
38219
+ best = { score, severity };
37986
38220
  }
37987
- return best ? best.severity : defaultSeverity;
38221
+ return best ? best.severity : null;
38222
+ }
38223
+ function parseRuleKey(key) {
38224
+ const sep = key.indexOf(":");
38225
+ if (sep <= 0 || key.startsWith("hook:"))
38226
+ return { rule: key };
38227
+ return { model: key.slice(0, sep), rule: key.slice(sep + 1) };
38228
+ }
38229
+ function scopeMatches(keyModel, modelId) {
38230
+ if (modelId === undefined)
38231
+ return keyModel === undefined;
38232
+ return keyModel !== undefined && globMatches(keyModel, modelId);
38233
+ }
38234
+ function specificity(pattern) {
38235
+ return pattern.includes("*") ? pattern.replace(/\*/g, "").length : Number.MAX_SAFE_INTEGER;
37988
38236
  }
37989
38237
  function globMatches(pattern, value) {
37990
38238
  const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
@@ -38011,6 +38259,41 @@ var init_config = __esm(() => {
38011
38259
  });
38012
38260
 
38013
38261
  // src/behavior/harness.ts
38262
+ function extractAvailableSkills(systemText) {
38263
+ if (!systemText)
38264
+ return [];
38265
+ const start = SKILL_SECTION.exec(systemText);
38266
+ if (!start)
38267
+ return [];
38268
+ const out = [];
38269
+ const body = systemText.slice(start.index + start[0].length);
38270
+ for (const line of body.split(`
38271
+ `)) {
38272
+ const trimmed = line.trim();
38273
+ if (!trimmed)
38274
+ continue;
38275
+ const m = SKILL_LINE.exec(trimmed);
38276
+ if (!m) {
38277
+ if (out.length > 0)
38278
+ break;
38279
+ continue;
38280
+ }
38281
+ out.push({ name: m[1], description: m[2].trim() });
38282
+ }
38283
+ return out;
38284
+ }
38285
+ function extractSessionId(claudeRequest) {
38286
+ const raw2 = claudeRequest?.metadata?.user_id;
38287
+ if (typeof raw2 !== "string")
38288
+ return;
38289
+ try {
38290
+ const parsed = JSON.parse(raw2);
38291
+ const id = parsed?.session_id;
38292
+ return typeof id === "string" && id.length > 0 ? id : undefined;
38293
+ } catch {
38294
+ return;
38295
+ }
38296
+ }
38014
38297
  function textOf(value) {
38015
38298
  if (!value)
38016
38299
  return "";
@@ -38062,7 +38345,7 @@ function detectHarnessFacts(claudeRequest) {
38062
38345
  }
38063
38346
  return facts;
38064
38347
  }
38065
- var PLAN_PATH_PATTERNS, PLAN_MODE_HINT;
38348
+ var PLAN_PATH_PATTERNS, PLAN_MODE_HINT, SKILL_SECTION, SKILL_LINE;
38066
38349
  var init_harness = __esm(() => {
38067
38350
  PLAN_PATH_PATTERNS = [
38068
38351
  /You should create your plan at\s+(\S+?\.md)/,
@@ -38070,10 +38353,12 @@ var init_harness = __esm(() => {
38070
38353
  /Read-only except plan file\s*\(([^)]+\.md)\)/
38071
38354
  ];
38072
38355
  PLAN_MODE_HINT = /plan file|create your plan at|Plan mode is active|Plan mode still active/i;
38356
+ SKILL_SECTION = /The following skills are available[^\n]*\n/;
38357
+ SKILL_LINE = /^-\s+([a-z0-9][a-z0-9:_-]*):\s*(.+)$/i;
38073
38358
  });
38074
38359
 
38075
38360
  // src/behavior/journal.ts
38076
- import { appendFile as appendFile2, mkdir, stat } from "fs/promises";
38361
+ import { appendFile as appendFile2, mkdir, readFile, rename, stat, writeFile } from "fs/promises";
38077
38362
  import { homedir as homedir18 } from "os";
38078
38363
  import { dirname as dirname6, join as join18 } from "path";
38079
38364
  function classifyPath(observed, expected) {
@@ -38089,28 +38374,46 @@ function classifyPath(observed, expected) {
38089
38374
  function journalPath() {
38090
38375
  return join18(homedir18(), ".claudish", "behavior-journal.jsonl");
38091
38376
  }
38377
+ async function prune(path) {
38378
+ const content = await readFile(path, "utf8");
38379
+ const lines = content.split(`
38380
+ `).filter(Boolean);
38381
+ let kept = 0;
38382
+ let firstKept = lines.length;
38383
+ for (let i = lines.length - 1;i >= 0; i--) {
38384
+ const cost = Buffer.byteLength(lines[i]) + 1;
38385
+ if (kept + cost > PRUNE_TO_BYTES)
38386
+ break;
38387
+ kept += cost;
38388
+ firstKept = i;
38389
+ }
38390
+ const survivors = lines.slice(firstKept);
38391
+ const tmp = `${path}.pruning`;
38392
+ await writeFile(tmp, survivors.length ? `${survivors.join(`
38393
+ `)}
38394
+ ` : "");
38395
+ await rename(tmp, path);
38396
+ log(`[behavior:journal] pruned ${lines.length - survivors.length} of ${lines.length} entries ` + `to stay under ${Math.round(MAX_JOURNAL_BYTES / 1e6)}MB`);
38397
+ }
38092
38398
  async function recordDecision(entry, path = journalPath()) {
38093
38399
  try {
38094
38400
  const size = await stat(path).then((s) => s.size, () => 0);
38095
- if (size > MAX_JOURNAL_BYTES) {
38096
- if (!capWarned) {
38097
- capWarned = true;
38098
- log(`[behavior:journal] ${path} exceeded ${Math.round(MAX_JOURNAL_BYTES / 1e6)}MB \u2014 ` + "no longer recording. Archive or delete it to resume.");
38099
- }
38100
- return;
38101
- }
38102
38401
  if (size === 0)
38103
38402
  await mkdir(dirname6(path), { recursive: true }).catch(() => {});
38403
+ if (size > MAX_JOURNAL_BYTES) {
38404
+ await prune(path).catch((err) => log(`[behavior:journal] prune failed: ${err}`));
38405
+ }
38104
38406
  await appendFile2(path, `${JSON.stringify(entry)}
38105
38407
  `);
38106
38408
  } catch (err) {
38107
38409
  log(`[behavior:journal] could not record: ${err}`);
38108
38410
  }
38109
38411
  }
38110
- var MAX_JOURNAL_BYTES, capWarned = false;
38412
+ var MAX_JOURNAL_BYTES, PRUNE_TO_BYTES;
38111
38413
  var init_journal = __esm(() => {
38112
38414
  init_logger();
38113
38415
  MAX_JOURNAL_BYTES = 32 * 1024 * 1024;
38416
+ PRUNE_TO_BYTES = Math.floor(MAX_JOURNAL_BYTES * 0.6);
38114
38417
  });
38115
38418
 
38116
38419
  // src/behavior/observer/digest.ts
@@ -38327,13 +38630,23 @@ class BehaviorSession {
38327
38630
  modelId;
38328
38631
  providerName;
38329
38632
  config;
38633
+ engine;
38330
38634
  facts = { planModeActive: false };
38331
38635
  bufferedTools = new Set;
38332
- constructor(active, modelId, providerName, config2 = {}) {
38636
+ textBuf = "";
38637
+ reasoningBuf = "";
38638
+ toolsCalled = [];
38639
+ sessionId;
38640
+ systemText = "";
38641
+ get watchesOutput() {
38642
+ return this.active.some((a) => typeof a.rule.onModelOutput === "function");
38643
+ }
38644
+ constructor(active, modelId, providerName, config2 = {}, engine = { queueCorrection() {}, drainCorrections: () => [] }) {
38333
38645
  this.active = active;
38334
38646
  this.modelId = modelId;
38335
38647
  this.providerName = providerName;
38336
38648
  this.config = config2;
38649
+ this.engine = engine;
38337
38650
  }
38338
38651
  get observerOn() {
38339
38652
  return this.config.observer?.enabled === true && (this.config.observer.mode ?? "suggest") !== "off";
@@ -38367,7 +38680,26 @@ class BehaviorSession {
38367
38680
  if (this.active.length === 0)
38368
38681
  return;
38369
38682
  this.facts = detectHarnessFacts(claudeRequest);
38683
+ this.sessionId = extractSessionId(claudeRequest);
38684
+ this.systemText = typeof claudeRequest?.system === "string" ? claudeRequest.system : String(claudeRequest?.system ?? "");
38685
+ this.facts.sessionId = this.sessionId;
38686
+ this.facts.skills = extractAvailableSkills(this.systemText);
38370
38687
  this.armBuffering();
38688
+ if (this.sessionId) {
38689
+ for (const text of this.engine.drainCorrections(this.sessionId)) {
38690
+ this.applyAction("behavior/pending-correction", "fix", { type: "injectSystemNote", text }, {
38691
+ modelId: this.modelId,
38692
+ providerName: this.providerName,
38693
+ isNativeAnthropic: false,
38694
+ claudeRequest,
38695
+ claudeTools,
38696
+ tools,
38697
+ messages,
38698
+ systemText: this.systemText,
38699
+ harness: this.facts
38700
+ });
38701
+ }
38702
+ }
38371
38703
  const ctx = {
38372
38704
  modelId: this.modelId,
38373
38705
  providerName: this.providerName,
@@ -38376,6 +38708,7 @@ class BehaviorSession {
38376
38708
  claudeTools,
38377
38709
  tools,
38378
38710
  messages,
38711
+ systemText: this.systemText,
38379
38712
  harness: this.facts
38380
38713
  };
38381
38714
  for (const { rule, severity } of this.active) {
@@ -38395,6 +38728,66 @@ class BehaviorSession {
38395
38728
  interceptsTool(toolName) {
38396
38729
  return this.bufferedTools.has(toolName);
38397
38730
  }
38731
+ observeText(text, kind = "text") {
38732
+ if (!this.watchesOutput || !text)
38733
+ return;
38734
+ const buf = kind === "reasoning" ? this.reasoningBuf : this.textBuf;
38735
+ if (buf.length >= MAX_OBSERVED_CHARS)
38736
+ return;
38737
+ if (kind === "reasoning")
38738
+ this.reasoningBuf += text;
38739
+ else
38740
+ this.textBuf += text;
38741
+ }
38742
+ observeToolCall(toolName) {
38743
+ if (!this.watchesOutput)
38744
+ return;
38745
+ if (this.toolsCalled.length < MAX_OBSERVED_TOOLS)
38746
+ this.toolsCalled.push(toolName);
38747
+ }
38748
+ finishTurn() {
38749
+ if (!this.watchesOutput)
38750
+ return;
38751
+ const ctx = {
38752
+ modelId: this.modelId,
38753
+ providerName: this.providerName,
38754
+ text: this.textBuf,
38755
+ reasoning: this.reasoningBuf,
38756
+ toolsCalled: this.toolsCalled,
38757
+ harness: this.facts
38758
+ };
38759
+ for (const { rule, severity } of this.active) {
38760
+ if (!rule.onModelOutput)
38761
+ continue;
38762
+ let actions = [];
38763
+ try {
38764
+ actions = rule.onModelOutput(ctx) ?? [];
38765
+ } catch (err) {
38766
+ log(`[behavior] rule ${rule.id} onModelOutput threw: ${err}`);
38767
+ continue;
38768
+ }
38769
+ for (const action of actions) {
38770
+ if (action.type === "warn") {
38771
+ log(`[behavior] ${rule.id} (output): ${action.message}`);
38772
+ this.journal("model_output", "warned", { ruleId: rule.id, note: action.message });
38773
+ continue;
38774
+ }
38775
+ if (action.type !== "injectSystemNote")
38776
+ continue;
38777
+ if (severity !== "fix") {
38778
+ this.journal("model_output", "warned", { ruleId: rule.id, note: "correction withheld" });
38779
+ continue;
38780
+ }
38781
+ if (this.sessionId)
38782
+ this.engine.queueCorrection(this.sessionId, action.text);
38783
+ this.journal("model_output", "matched", { ruleId: rule.id, note: "correction queued" });
38784
+ log(`[behavior] ${rule.id} queued a correction for the next request`);
38785
+ }
38786
+ }
38787
+ this.textBuf = "";
38788
+ this.reasoningBuf = "";
38789
+ this.toolsCalled = [];
38790
+ }
38398
38791
  repairToolCall(toolName, rawArgs) {
38399
38792
  if (!this.bufferedTools.has(toolName))
38400
38793
  return null;
@@ -38562,15 +38955,32 @@ ${action.text}`;
38562
38955
  class BehaviorEngine {
38563
38956
  config;
38564
38957
  rules;
38958
+ corrections = new Map;
38565
38959
  constructor(config2, rules) {
38566
38960
  this.config = config2;
38567
38961
  this.rules = rules;
38568
38962
  }
38963
+ queueCorrection(key, text) {
38964
+ const list = this.corrections.get(key) ?? [];
38965
+ list.push(text);
38966
+ this.corrections.set(key, list);
38967
+ while (this.corrections.size > MAX_TRACKED_CONVERSATIONS) {
38968
+ const oldest = this.corrections.keys().next().value;
38969
+ if (oldest === undefined)
38970
+ break;
38971
+ this.corrections.delete(oldest);
38972
+ }
38973
+ }
38974
+ drainCorrections(key) {
38975
+ const list = this.corrections.get(key) ?? [];
38976
+ this.corrections.delete(key);
38977
+ return list;
38978
+ }
38569
38979
  startSession(params) {
38570
38980
  const active = [];
38571
38981
  if (!params.isNativeAnthropic) {
38572
38982
  for (const rule of this.rules) {
38573
- const severity = resolveSeverity(rule.id, rule.defaultSeverity, this.config);
38983
+ const severity = resolveSeverity(rule.id, rule.defaultSeverity, this.config, params.modelId);
38574
38984
  if (severity === "off")
38575
38985
  continue;
38576
38986
  let applies = false;
@@ -38587,14 +38997,16 @@ class BehaviorEngine {
38587
38997
  if (active.length > 0) {
38588
38998
  log(`[behavior] ${active.length} rule(s) active for ${params.modelId}: ` + active.map((a) => `${a.rule.id}=${a.severity}`).join(", "));
38589
38999
  }
38590
- return new BehaviorSession(active, params.modelId, params.providerName, this.config);
39000
+ return new BehaviorSession(active, params.modelId, params.providerName, this.config, this);
38591
39001
  }
38592
39002
  }
39003
+ var MAX_OBSERVED_CHARS, MAX_OBSERVED_TOOLS = 200, MAX_TRACKED_CONVERSATIONS = 64;
38593
39004
  var init_engine = __esm(() => {
38594
39005
  init_logger();
38595
39006
  init_config();
38596
39007
  init_harness();
38597
39008
  init_journal();
39009
+ MAX_OBSERVED_CHARS = 64 * 1024;
38598
39010
  });
38599
39011
 
38600
39012
  // src/behavior/rules/plan-mode.ts
@@ -40104,7 +40516,8 @@ var init_telemetry = __esm(() => {
40104
40516
  "x-ai",
40105
40517
  "minimax-coding",
40106
40518
  "kimi-coding",
40107
- "glm-coding"
40519
+ "glm-coding",
40520
+ "qwen-cloud"
40108
40521
  ]);
40109
40522
  });
40110
40523
 
@@ -40602,6 +41015,60 @@ var init_connection_error = __esm(() => {
40602
41015
  BUN_CONNECT_MESSAGE = /unable to connect\. is the computer able to access the url\?/i;
40603
41016
  });
40604
41017
 
41018
+ // src/handlers/shared/context-window-fallback.ts
41019
+ function isDisabled() {
41020
+ if (process.env.CLAUDISH_NO_CATALOG_FALLBACK)
41021
+ return true;
41022
+ return false;
41023
+ }
41024
+ function resolveCatalogContextWindow(modelId) {
41025
+ if (!modelId || isDisabled())
41026
+ return Promise.resolve(null);
41027
+ const cached2 = results.get(modelId);
41028
+ if (cached2 !== undefined)
41029
+ return Promise.resolve(cached2);
41030
+ const pending = inFlight2.get(modelId);
41031
+ if (pending)
41032
+ return pending;
41033
+ const promise3 = fetcher(modelId).then((cw) => {
41034
+ const value = typeof cw === "number" && cw > 0 ? cw : null;
41035
+ results.set(modelId, value);
41036
+ return value;
41037
+ }).catch((err) => {
41038
+ results.set(modelId, null);
41039
+ log(`[ContextWindow] Catalog lookup failed for ${modelId}: ${err}`);
41040
+ return null;
41041
+ }).finally(() => {
41042
+ inFlight2.delete(modelId);
41043
+ });
41044
+ inFlight2.set(modelId, promise3);
41045
+ return promise3;
41046
+ }
41047
+ function requestCatalogContextWindow(modelId, apply) {
41048
+ resolveCatalogContextWindow(modelId).then((cw) => {
41049
+ if (cw === null)
41050
+ return;
41051
+ try {
41052
+ apply(cw);
41053
+ log(`[ContextWindow] Resolved ${modelId} = ${cw} tokens from the cloud catalog`);
41054
+ } catch (err) {
41055
+ log(`[ContextWindow] Failed to apply catalog window for ${modelId}: ${err}`);
41056
+ }
41057
+ });
41058
+ }
41059
+ var defaultFetcher = async (modelId) => {
41060
+ const doc2 = await getModelByIdFromFirebase(modelId);
41061
+ const cw = doc2?.contextWindow;
41062
+ return typeof cw === "number" && cw > 0 ? cw : null;
41063
+ }, fetcher, results, inFlight2;
41064
+ var init_context_window_fallback = __esm(() => {
41065
+ init_logger();
41066
+ init_model_loader();
41067
+ fetcher = defaultFetcher;
41068
+ results = new Map;
41069
+ inFlight2 = new Map;
41070
+ });
41071
+
40605
41072
  // src/handlers/shared/stream-head-sniffer.ts
40606
41073
  function isRetryableStreamError(code, type, message) {
40607
41074
  if (RETRYABLE_ERROR_CODES.has(code))
@@ -40737,6 +41204,12 @@ var init_stream_head_sniffer = __esm(() => {
40737
41204
  });
40738
41205
 
40739
41206
  // src/handlers/shared/stream-parsers/anthropic-sse.ts
41207
+ function sseDataPayload(line) {
41208
+ if (!line.startsWith("data:"))
41209
+ return null;
41210
+ const rest = line.slice(5);
41211
+ return rest.startsWith(" ") ? rest.slice(1) : rest;
41212
+ }
40740
41213
  function createToolRepairInterceptor(opts) {
40741
41214
  const heldTools = new Map;
40742
41215
  const flush = (index, stopFrame) => {
@@ -40809,12 +41282,24 @@ function createAnthropicPassthroughStream(c, response, opts) {
40809
41282
  let pingInterval = null;
40810
41283
  const filterThinking = opts.adapter?.shouldFilterThinking() ?? false;
40811
41284
  const interceptToolFrame = createToolRepairInterceptor(opts);
41285
+ let pendingEventLine = null;
41286
+ const flushPendingEvent = (controller) => {
41287
+ if (pendingEventLine !== null && !isClosed) {
41288
+ controller.enqueue(encoder.encode(`${pendingEventLine}
41289
+ `));
41290
+ }
41291
+ pendingEventLine = null;
41292
+ };
40812
41293
  const enqueueData = (controller, data, line) => {
40813
41294
  if (isClosed)
40814
41295
  return;
40815
41296
  const out = interceptToolFrame(data, line);
40816
- if (out !== null)
40817
- controller.enqueue(encoder.encode(out));
41297
+ if (out === null) {
41298
+ pendingEventLine = null;
41299
+ return;
41300
+ }
41301
+ flushPendingEvent(controller);
41302
+ controller.enqueue(encoder.encode(out));
40818
41303
  };
40819
41304
  return c.body(new ReadableStream({
40820
41305
  async start(controller) {
@@ -40843,6 +41328,7 @@ data: {"type":"ping"}
40843
41328
  let stopReason = null;
40844
41329
  let insideThinkingBlock = false;
40845
41330
  let thinkingBlocksSuppressed = 0;
41331
+ let suppressedFrame = false;
40846
41332
  while (true) {
40847
41333
  const { done, value } = await reader.read();
40848
41334
  if (done)
@@ -40854,9 +41340,10 @@ data: {"type":"ping"}
40854
41340
  buffer = lines.pop() || "";
40855
41341
  for (const line of lines) {
40856
41342
  totalLines++;
40857
- if (filterThinking && line.startsWith("data: ")) {
41343
+ const payload = sseDataPayload(line);
41344
+ if (filterThinking && payload !== null) {
40858
41345
  try {
40859
- const data = JSON.parse(line.slice(6));
41346
+ const data = JSON.parse(payload);
40860
41347
  if (data.error) {
40861
41348
  const errMsg = data.error.message || JSON.stringify(data.error);
40862
41349
  log(`[AnthropicSSE] In-stream error detected: ${errMsg}`);
@@ -40881,19 +41368,26 @@ data: ${JSON.stringify({
40881
41368
  insideThinkingBlock = true;
40882
41369
  thinkingBlocksSuppressed++;
40883
41370
  log(`[AnthropicSSE] Filtering thinking block at index ${data.index}`);
41371
+ pendingEventLine = null;
41372
+ suppressedFrame = true;
40884
41373
  continue;
40885
41374
  }
40886
41375
  if (insideThinkingBlock && data.type === "content_block_stop") {
40887
41376
  insideThinkingBlock = false;
41377
+ pendingEventLine = null;
41378
+ suppressedFrame = true;
40888
41379
  continue;
40889
41380
  }
40890
41381
  if (insideThinkingBlock) {
41382
+ pendingEventLine = null;
41383
+ suppressedFrame = true;
40891
41384
  continue;
40892
41385
  }
40893
41386
  if (typeof data.index === "number" && thinkingBlocksSuppressed > 0) {
40894
41387
  const reindexed = data.index - thinkingBlocksSuppressed;
40895
41388
  const modifiedLine = `data: ${JSON.stringify({ ...data, index: reindexed })}`;
40896
41389
  if (!isClosed) {
41390
+ flushPendingEvent(controller);
40897
41391
  controller.enqueue(encoder.encode(`${modifiedLine}
40898
41392
  `));
40899
41393
  }
@@ -40902,14 +41396,15 @@ data: ${JSON.stringify({
40902
41396
  }
40903
41397
  } catch {
40904
41398
  if (!isClosed) {
41399
+ flushPendingEvent(controller);
40905
41400
  controller.enqueue(encoder.encode(`${line}
40906
41401
  `));
40907
41402
  }
40908
41403
  }
40909
41404
  } else {
40910
- if (!filterThinking && line.startsWith("data: ")) {
41405
+ if (!filterThinking && payload !== null) {
40911
41406
  try {
40912
- const data = JSON.parse(line.slice(6));
41407
+ const data = JSON.parse(payload);
40913
41408
  if (data.error) {
40914
41409
  const errMsg = data.error.message || JSON.stringify(data.error);
40915
41410
  log(`[AnthropicSSE] In-stream error detected: ${errMsg}`);
@@ -40941,11 +41436,13 @@ data: ${JSON.stringify({
40941
41436
  }
40942
41437
  if (data.type === "content_block_delta" && data.delta?.type === "text_delta") {
40943
41438
  const txt = data.delta.text || "";
41439
+ opts.onAssistantText?.(txt, "text");
40944
41440
  textChunks++;
40945
41441
  log(`[AnthropicSSE] Text chunk: "${txt.substring(0, 30).replace(/\n/g, "\\n")}" (${txt.length} chars)`);
40946
41442
  }
40947
41443
  if (data.type === "content_block_start" && data.content_block?.type === "tool_use") {
40948
41444
  toolUseBlocks++;
41445
+ opts.onToolCallObserved?.(data.content_block.name);
40949
41446
  log(`[AnthropicSSE] Tool use: ${data.content_block.name}`);
40950
41447
  }
40951
41448
  if (data.type === "message_delta" && data.delta?.stop_reason) {
@@ -40957,6 +41454,15 @@ data: ${JSON.stringify({
40957
41454
  `));
40958
41455
  }
40959
41456
  }
41457
+ } else if (filterThinking) {
41458
+ if (line.startsWith("event:")) {
41459
+ pendingEventLine = line;
41460
+ } else if (line.trim() === "" && suppressedFrame) {
41461
+ suppressedFrame = false;
41462
+ } else if (!isClosed) {
41463
+ controller.enqueue(encoder.encode(`${line}
41464
+ `));
41465
+ }
40960
41466
  } else {
40961
41467
  if (!isClosed) {
40962
41468
  controller.enqueue(encoder.encode(`${line}
@@ -40964,9 +41470,9 @@ data: ${JSON.stringify({
40964
41470
  }
40965
41471
  }
40966
41472
  }
40967
- if (filterThinking && line.startsWith("data: ")) {
41473
+ if (filterThinking && payload !== null) {
40968
41474
  try {
40969
- const data = JSON.parse(line.slice(6));
41475
+ const data = JSON.parse(payload);
40970
41476
  if (data.message?.usage) {
40971
41477
  inputTokens = data.message.usage.input_tokens || inputTokens;
40972
41478
  outputTokens = data.message.usage.output_tokens || outputTokens;
@@ -40976,10 +41482,12 @@ data: ${JSON.stringify({
40976
41482
  outputTokens = data.usage.output_tokens || outputTokens;
40977
41483
  }
40978
41484
  if (data.type === "content_block_delta" && data.delta?.type === "text_delta") {
41485
+ opts.onAssistantText?.(data.delta.text || "", "text");
40979
41486
  textChunks++;
40980
41487
  }
40981
41488
  if (data.type === "content_block_start" && data.content_block?.type === "tool_use") {
40982
41489
  toolUseBlocks++;
41490
+ opts.onToolCallObserved?.(data.content_block.name);
40983
41491
  log(`[AnthropicSSE] Tool use: ${data.content_block.name}`);
40984
41492
  }
40985
41493
  if (data.type === "message_delta" && data.delta?.stop_reason) {
@@ -40990,6 +41498,7 @@ data: ${JSON.stringify({
40990
41498
  }
40991
41499
  }
40992
41500
  log(`[AnthropicSSE] Stream complete for ${opts.modelName}: ${totalLines} lines, ${textChunks} text chunks, ${toolUseBlocks} tool_use blocks, stop_reason=${stopReason}${filterThinking ? `, filtered ${thinkingBlocksSuppressed} thinking blocks` : ""}`);
41501
+ opts.onTurnEnd?.();
40993
41502
  if (opts.onTokenUpdate) {
40994
41503
  opts.onTokenUpdate(inputTokens, outputTokens);
40995
41504
  }
@@ -41118,6 +41627,7 @@ data: ${JSON.stringify(data)}
41118
41627
  output_tokens: outputTokens
41119
41628
  }
41120
41629
  });
41630
+ opts.onTurnEnd?.();
41121
41631
  send("message_stop", { type: "message_stop" });
41122
41632
  }
41123
41633
  if (!isClosed) {
@@ -41225,6 +41735,7 @@ data: ${JSON.stringify(data)}
41225
41735
  const toolIdx = toolCalls.size;
41226
41736
  const toolId = `toolu_${Date.now()}_${toolIdx}`;
41227
41737
  const blockIndex = curIdx++;
41738
+ opts.onToolCallObserved?.(part.functionCall.name);
41228
41739
  let args = JSON.stringify(part.functionCall.args || {});
41229
41740
  if (opts.repairToolArgs) {
41230
41741
  try {
@@ -41563,6 +42074,7 @@ data: ${JSON.stringify(data)}
41563
42074
  log(`[ResponsesSSE] Event: ${event.type}`);
41564
42075
  }
41565
42076
  if (event.type === "response.output_text.delta") {
42077
+ opts.onAssistantText?.(event.delta ?? "", "text");
41566
42078
  closeReasoning();
41567
42079
  if (textIdx < 0) {
41568
42080
  textIdx = curIdx++;
@@ -41598,6 +42110,7 @@ data: ${JSON.stringify(data)}
41598
42110
  functionCalls.set(itemId, fnCallData);
41599
42111
  }
41600
42112
  openToolBlocks.add(fnCallData);
42113
+ opts.onToolCallObserved?.(fnName);
41601
42114
  if (pendingReasoning.length > 0) {
41602
42115
  rememberReasoningForCall(callId, pendingReasoning);
41603
42116
  pendingReasoning = [];
@@ -41610,6 +42123,7 @@ data: ${JSON.stringify(data)}
41610
42123
  hasToolUse = true;
41611
42124
  }
41612
42125
  } else if (event.type === "response.reasoning_summary_text.delta") {
42126
+ opts.onAssistantText?.(event.delta ?? "", "reasoning");
41613
42127
  const summaryIndex = typeof event.summary_index === "number" ? event.summary_index : 0;
41614
42128
  if (reasoningIdx < 0) {
41615
42129
  closeText();
@@ -41772,6 +42286,7 @@ data: ${JSON.stringify(data)}
41772
42286
  if (opts.middlewareManager) {
41773
42287
  await opts.middlewareManager.afterStreamComplete(opts.modelName, streamMetadata);
41774
42288
  }
42289
+ opts.onTurnEnd?.();
41775
42290
  safeClose();
41776
42291
  } catch (error46) {
41777
42292
  if (pingInterval) {
@@ -41838,6 +42353,10 @@ var init_openai_responses_sse = __esm(() => {
41838
42353
  import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
41839
42354
  import { homedir as homedir22 } from "os";
41840
42355
  import { dirname as dirname7, join as join22 } from "path";
42356
+ function stripProviderPrefix(name) {
42357
+ const at = name.indexOf("@");
42358
+ return at === -1 ? name : name.slice(at + 1);
42359
+ }
41841
42360
 
41842
42361
  class TokenTracker {
41843
42362
  port;
@@ -41928,6 +42447,9 @@ class TokenTracker {
41928
42447
  setContextWindow(contextWindow) {
41929
42448
  this.config.contextWindow = contextWindow;
41930
42449
  }
42450
+ getContextWindow() {
42451
+ return this.config.contextWindow;
42452
+ }
41931
42453
  getTotalCost() {
41932
42454
  return this.sessionTotalCost;
41933
42455
  }
@@ -41974,8 +42496,9 @@ class TokenTracker {
41974
42496
  is_free: isFreeModel,
41975
42497
  is_estimated: isEstimate || false
41976
42498
  };
41977
- if (this.modelNameOverride) {
41978
- data.model_name = this.modelNameOverride;
42499
+ const displayModel = stripProviderPrefix(this.modelNameOverride || this.config.modelName || "");
42500
+ if (displayModel) {
42501
+ data.model_name = displayModel;
41979
42502
  }
41980
42503
  if (this.quotaRemaining !== undefined) {
41981
42504
  data.quota_remaining = this.quotaRemaining;
@@ -42026,7 +42549,7 @@ class ComposedHandler {
42026
42549
  this.options = options;
42027
42550
  this.explicitAdapter = options.adapter;
42028
42551
  this.isInteractive = options.isInteractive ?? false;
42029
- this.adapterManager = new DialectManager(this.bareModelName);
42552
+ this.adapterManager = new DialectManager(this.bareModelName, this.explicitAdapter?.getStreamFormat());
42030
42553
  const resolvedModelAdapter = this.adapterManager.getAdapter();
42031
42554
  if (resolvedModelAdapter.getName() !== "DefaultAPIFormat") {
42032
42555
  this.modelAdapter = resolvedModelAdapter;
@@ -42209,7 +42732,13 @@ class ComposedHandler {
42209
42732
  }
42210
42733
  }
42211
42734
  if (this.provider.getContextWindow) {
42212
- this.tokenTracker.setContextWindow(this.provider.getContextWindow());
42735
+ const providerWindow = this.provider.getContextWindow();
42736
+ if (providerWindow > 0) {
42737
+ this.tokenTracker.setContextWindow(providerWindow);
42738
+ }
42739
+ }
42740
+ if (this.tokenTracker.getContextWindow() <= 0) {
42741
+ requestCatalogContextWindow(this.bareModelName, (cw) => this.tokenTracker.setContextWindow(cw));
42213
42742
  }
42214
42743
  if (this.provider.transformPayload) {
42215
42744
  requestPayload = this.provider.transformPayload(requestPayload);
@@ -42574,7 +43103,10 @@ class ComposedHandler {
42574
43103
  case "openai-sse":
42575
43104
  return createStreamingResponseHandler(c, response, adapter, this.bareModelName, this.middlewareManager, onTokenUpdate, claudeRequest.tools, toolNameMap, priorInputTokens, behaviorSession && {
42576
43105
  shouldBufferTool: (name) => behaviorSession.interceptsTool(name),
42577
- onToolCall: (name, argsJson) => behaviorSession.repairToolCall(name, argsJson)
43106
+ onToolCall: (name, argsJson) => behaviorSession.repairToolCall(name, argsJson),
43107
+ onAssistantText: (text, kind) => behaviorSession.observeText(text, kind),
43108
+ onToolCallObserved: (name) => behaviorSession.observeToolCall(name),
43109
+ onTurnEnd: () => behaviorSession.finishTurn()
42578
43110
  });
42579
43111
  case "openai-responses-sse":
42580
43112
  return createResponsesStreamHandler(c, response, {
@@ -42586,15 +43118,21 @@ class ComposedHandler {
42586
43118
  priorInputTokens,
42587
43119
  middlewareManager: this.middlewareManager,
42588
43120
  shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
42589
- onToolCall: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null
43121
+ onToolCall: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
43122
+ onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
43123
+ onToolCallObserved: (name) => behaviorSession?.observeToolCall(name),
43124
+ onTurnEnd: () => behaviorSession?.finishTurn()
42590
43125
  });
42591
43126
  case "anthropic-sse":
42592
43127
  return createAnthropicPassthroughStream(c, response, {
42593
43128
  modelName: this.bareModelName,
42594
43129
  onTokenUpdate,
42595
- adapter,
43130
+ adapter: this.modelAdapter ?? adapter,
42596
43131
  shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
42597
- repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null
43132
+ repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
43133
+ onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
43134
+ onToolCallObserved: (name) => behaviorSession?.observeToolCall(name),
43135
+ onTurnEnd: () => behaviorSession?.finishTurn()
42598
43136
  });
42599
43137
  case "gemini-sse": {
42600
43138
  const onToolCall = (toolId, name, thoughtSignature) => {
@@ -42609,6 +43147,9 @@ class ComposedHandler {
42609
43147
  onTokenUpdate,
42610
43148
  onToolCall,
42611
43149
  repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
43150
+ onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
43151
+ onToolCallObserved: (name) => behaviorSession?.observeToolCall(name),
43152
+ onTurnEnd: () => behaviorSession?.finishTurn(),
42612
43153
  unwrapResponse: this.options.unwrapGeminiResponse,
42613
43154
  priorInputTokens
42614
43155
  });
@@ -42694,6 +43235,7 @@ var init_composed_handler = __esm(() => {
42694
43235
  init_transform();
42695
43236
  init_anthropic_error();
42696
43237
  init_connection_error();
43238
+ init_context_window_fallback();
42697
43239
  init_openai_compat();
42698
43240
  init_stream_head_sniffer();
42699
43241
  init_anthropic_sse();
@@ -43151,11 +43693,11 @@ ${a.text}`).join(`
43151
43693
  }
43152
43694
  }
43153
43695
  async function fetchMultiModelAdvice(_toolUseId, messages, models, collector, apiKeys) {
43154
- const results = await Promise.allSettled(models.map((model) => callAdvisorModel(model, messages, apiKeys)));
43696
+ const results2 = await Promise.allSettled(models.map((model) => callAdvisorModel(model, messages, apiKeys)));
43155
43697
  const sections = [];
43156
43698
  const successfulAdvice = [];
43157
43699
  for (let i = 0;i < models.length; i++) {
43158
- const result = results[i];
43700
+ const result = results2[i];
43159
43701
  if (result.status === "fulfilled") {
43160
43702
  sections.push(`## ${models[i]}
43161
43703
  ${result.value}`);
@@ -43474,6 +44016,7 @@ var init_api_key_map = __esm(() => {
43474
44016
  envVar: "SAKANA_SUBSCRIPTION_API_KEY",
43475
44017
  aliases: ["SAKANA_CODING_API_KEY"]
43476
44018
  },
44019
+ "qwen-cloud": { envVar: "QWEN_CLOUD_PLAN_API_KEY" },
43477
44020
  ollamacloud: { envVar: "OLLAMA_API_KEY" },
43478
44021
  "opencode-zen": { envVar: "OPENCODE_API_KEY" },
43479
44022
  "opencode-zen-go": { envVar: "OPENCODE_API_KEY" },
@@ -43699,90 +44242,127 @@ var init_config_schema = __esm(() => {
43699
44242
  DefaultProviderSchema = exports_external.union([BuiltinDefaultProviderSchema, exports_external.string().min(1)]);
43700
44243
  });
43701
44244
 
43702
- // src/providers/transport/anthropic-compat.ts
43703
- class AnthropicProviderTransport {
43704
- name;
43705
- displayName;
43706
- streamFormat = "anthropic-sse";
43707
- provider;
43708
- apiKey;
43709
- constructor(provider, apiKey) {
43710
- this.provider = provider;
43711
- this.apiKey = apiKey;
43712
- this.name = provider.name;
43713
- this.displayName = AnthropicProviderTransport.formatDisplayName(provider.name);
44245
+ // src/providers/model-discovery.ts
44246
+ function resolveBaseUrl2(catalogName) {
44247
+ const def = getProviderByName(catalogName);
44248
+ if (!def)
44249
+ return null;
44250
+ for (const envVar of def.baseUrlEnvVars ?? []) {
44251
+ const v = process.env[envVar];
44252
+ if (v)
44253
+ return v.replace(/\/+$/, "");
43714
44254
  }
43715
- getEndpoint() {
43716
- return `${this.provider.baseUrl}${this.provider.apiPath}`;
44255
+ return (def.baseUrl || "").replace(/\/+$/, "") || null;
44256
+ }
44257
+ function readContextWindow(row) {
44258
+ for (const field of ["context_length", "context_window", "max_context_length"]) {
44259
+ const v = row[field];
44260
+ if (typeof v === "number" && Number.isFinite(v) && v > 0)
44261
+ return v;
43717
44262
  }
43718
- async getHeaders() {
43719
- const headers = {
43720
- "anthropic-version": "2023-06-01"
43721
- };
43722
- if (this.provider.authScheme === "bearer") {
43723
- headers.Authorization = `Bearer ${this.apiKey}`;
43724
- } else {
43725
- headers["x-api-key"] = this.apiKey;
43726
- }
43727
- if (this.provider.headers) {
43728
- Object.assign(headers, this.provider.headers);
43729
- }
43730
- if (this.provider.name === "kimi-coding") {
43731
- try {
43732
- const auth = await credentials.getRequestAuth("kimi-coding", { model: "" });
43733
- if (auth.headers.Authorization) {
43734
- delete headers["x-api-key"];
43735
- }
43736
- Object.assign(headers, auth.headers);
43737
- } catch (e) {
43738
- log(`[${this.displayName}] OAuth path failed, falling back to API key: ${e.message}`);
43739
- }
43740
- }
43741
- return headers;
44263
+ return;
44264
+ }
44265
+ function readCreatedDate(row) {
44266
+ const raw2 = row.created;
44267
+ const seconds = typeof raw2 === "number" ? raw2 : typeof raw2 === "string" ? Number(raw2) : NaN;
44268
+ if (!Number.isFinite(seconds))
44269
+ return;
44270
+ if (seconds < MIN_CREATED_SECONDS || seconds > MAX_CREATED_SECONDS)
44271
+ return;
44272
+ const iso = new Date(seconds * 1000).toISOString();
44273
+ return iso.slice(0, 10);
44274
+ }
44275
+ function parseOpenAIModelsList(body) {
44276
+ const data = body?.data;
44277
+ if (!Array.isArray(data))
44278
+ return [];
44279
+ const models = [];
44280
+ for (const raw2 of data) {
44281
+ if (!raw2 || typeof raw2 !== "object")
44282
+ continue;
44283
+ const row = raw2;
44284
+ const id = row.id;
44285
+ if (typeof id !== "string" || id.trim().length === 0)
44286
+ continue;
44287
+ const displayName = typeof row.display_name === "string" ? row.display_name : undefined;
44288
+ models.push({
44289
+ id,
44290
+ displayName,
44291
+ contextWindow: readContextWindow(row),
44292
+ releaseDate: readCreatedDate(row)
44293
+ });
43742
44294
  }
43743
- async enqueueRequest(fetchFn) {
43744
- const maxRetries = 2;
43745
- let lastResponse = null;
43746
- for (let attempt = 0;attempt <= maxRetries; attempt++) {
43747
- const response = await fetchFn();
43748
- if (response.status === 429 && attempt < maxRetries) {
43749
- const bodyText = await response.clone().text().catch(() => "");
43750
- if (isTerminal429(bodyText)) {
43751
- log(`[${this.displayName}] 429 is terminal (billing/quota), not retrying`);
43752
- return response;
43753
- }
43754
- lastResponse = response;
43755
- const retryAfter = response.headers.get("Retry-After");
43756
- let delayMs;
43757
- if (retryAfter && !Number.isNaN(Number(retryAfter))) {
43758
- delayMs = Math.min(Number(retryAfter) * 1000, 2000);
43759
- } else {
43760
- delayMs = 500 * (attempt + 1);
43761
- }
43762
- log(`[${this.displayName}] 429 rate limited, retry ${attempt + 1}/${maxRetries} in ${(delayMs / 1000).toFixed(1)}s`);
43763
- await new Promise((resolve2) => setTimeout(resolve2, delayMs));
43764
- continue;
43765
- }
43766
- return response;
43767
- }
43768
- return lastResponse;
44295
+ return models;
44296
+ }
44297
+ async function discoverProviderModels(providerName) {
44298
+ const cached2 = _cache.get(providerName);
44299
+ if (cached2 && cached2.expiresAt > Date.now())
44300
+ return cached2.models;
44301
+ const def = getProviderByName(providerName);
44302
+ const descriptor = def?.modelDiscovery;
44303
+ if (!def || !descriptor)
44304
+ return [];
44305
+ const baseUrl = resolveBaseUrl2(providerName);
44306
+ if (!baseUrl)
44307
+ return [];
44308
+ const endpoint = `${baseUrl}${descriptor.path}`;
44309
+ let headers = {};
44310
+ try {
44311
+ const auth = await credentials.getRequestAuth(providerName, { model: "" });
44312
+ headers = { ...auth.headers };
44313
+ } catch (e) {
44314
+ log(`[model-discovery:${providerName}] no credentials, skipping: ${e?.message}`);
44315
+ return [];
43769
44316
  }
43770
- static formatDisplayName(name) {
43771
- const map3 = {
43772
- minimax: "MiniMax",
43773
- "minimax-coding": "MiniMax Coding",
43774
- kimi: "Kimi",
43775
- "kimi-coding": "Kimi Coding",
43776
- moonshot: "Kimi",
43777
- "z-ai": "Z.AI"
43778
- };
43779
- return map3[name.toLowerCase()] || name.charAt(0).toUpperCase() + name.slice(1);
44317
+ let response;
44318
+ try {
44319
+ response = await fetch(endpoint, {
44320
+ method: "GET",
44321
+ headers,
44322
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
44323
+ });
44324
+ } catch (e) {
44325
+ log(`[model-discovery:${providerName}] fetch failed: ${e?.message}`);
44326
+ return [];
44327
+ }
44328
+ if (!response.ok) {
44329
+ log(`[model-discovery:${providerName}] HTTP ${response.status} from ${endpoint}`);
44330
+ return [];
43780
44331
  }
44332
+ let body;
44333
+ try {
44334
+ body = await response.json();
44335
+ } catch {
44336
+ log(`[model-discovery:${providerName}] response was not JSON`);
44337
+ return [];
44338
+ }
44339
+ const models = parseOpenAIModelsList(body);
44340
+ if (models.length === 0) {
44341
+ log(`[model-discovery:${providerName}] endpoint reachable but listed no models`);
44342
+ return [];
44343
+ }
44344
+ log(`[model-discovery:${providerName}] discovered ${models.length} models: ` + models.map((m) => `${m.id}(${m.contextWindow ?? "?"})`).join(", "));
44345
+ _cache.set(providerName, { models, expiresAt: Date.now() + CACHE_TTL_MS });
44346
+ return models;
43781
44347
  }
43782
- var init_anthropic_compat = __esm(() => {
44348
+ async function discoverContextWindow(providerName, modelId) {
44349
+ const models = await discoverProviderModels(providerName);
44350
+ const match2 = models.find((m) => m.id.toLowerCase() === modelId.toLowerCase());
44351
+ return match2?.contextWindow;
44352
+ }
44353
+ function rankDiscoveredModels(models) {
44354
+ return [...models].sort((a, b) => {
44355
+ const diff = (b.contextWindow ?? 0) - (a.contextWindow ?? 0);
44356
+ return diff !== 0 ? diff : a.id.localeCompare(b.id);
44357
+ });
44358
+ }
44359
+ var CACHE_TTL_MS, FETCH_TIMEOUT_MS = 5000, _cache, MIN_CREATED_SECONDS = 946684800, MAX_CREATED_SECONDS = 4102444800;
44360
+ var init_model_discovery = __esm(() => {
43783
44361
  init_authority();
43784
44362
  init_logger();
43785
- init_openai();
44363
+ init_provider_definitions();
44364
+ CACHE_TTL_MS = 5 * 60 * 1000;
44365
+ _cache = new Map;
43786
44366
  });
43787
44367
 
43788
44368
  // src/providers/transport/probe-discovery.ts
@@ -43813,11 +44393,11 @@ function rankProbeCandidates(names) {
43813
44393
  });
43814
44394
  }
43815
44395
  function cacheGet(key, exclude = new Set) {
43816
- const hit = _cache.get(key);
44396
+ const hit = _cache2.get(key);
43817
44397
  if (!hit)
43818
44398
  return;
43819
44399
  if (Date.now() > hit.expiresAt) {
43820
- _cache.delete(key);
44400
+ _cache2.delete(key);
43821
44401
  return;
43822
44402
  }
43823
44403
  if (hit.ranked.length === 0) {
@@ -43833,10 +44413,10 @@ function cacheGet(key, exclude = new Set) {
43833
44413
  return { model: pick2 };
43834
44414
  }
43835
44415
  function cacheSetFailure(key, reason) {
43836
- _cache.set(key, { ranked: [], reason, expiresAt: Date.now() + CACHE_TTL_MS });
44416
+ _cache2.set(key, { ranked: [], reason, expiresAt: Date.now() + CACHE_TTL_MS2 });
43837
44417
  }
43838
44418
  function cacheSetRanked(key, ranked) {
43839
- _cache.set(key, { ranked, expiresAt: Date.now() + CACHE_TTL_MS });
44419
+ _cache2.set(key, { ranked, expiresAt: Date.now() + CACHE_TTL_MS2 });
43840
44420
  }
43841
44421
  async function discoverViaOpenAIModels(endpoint, headers, cacheKey) {
43842
44422
  const cached2 = cacheGet(cacheKey.key, cacheKey.exclude);
@@ -43847,7 +44427,7 @@ async function discoverViaOpenAIModels(endpoint, headers, cacheKey) {
43847
44427
  response = await fetch(endpoint, {
43848
44428
  method: "GET",
43849
44429
  headers,
43850
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
44430
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2)
43851
44431
  });
43852
44432
  } catch (e) {
43853
44433
  const reason = classifyFetchError(e, endpoint);
@@ -43901,7 +44481,7 @@ function classifyFetchError(e, endpoint) {
43901
44481
  const host = url2?.host ?? endpoint;
43902
44482
  const isLocal = !!url2 && /^(localhost|127\.0\.0\.1|0\.0\.0\.0|::1)$/i.test(url2.hostname);
43903
44483
  if (name === "TimeoutError" || name === "AbortError" || /timeout/i.test(msg)) {
43904
- return `${host} unresponsive (>${FETCH_TIMEOUT_MS / 1000}s) \u2014 check if the server is overloaded`;
44484
+ return `${host} unresponsive (>${FETCH_TIMEOUT_MS2 / 1000}s) \u2014 check if the server is overloaded`;
43905
44485
  }
43906
44486
  if (code === "ENOTFOUND" || code === "EAI_AGAIN") {
43907
44487
  return `cannot resolve host ${url2?.hostname ?? endpoint} \u2014 check the URL`;
@@ -43984,7 +44564,7 @@ async function discoverViaLMStudio(baseUrl, headers, cacheKey) {
43984
44564
  response = await fetch(`${baseUrl}/api/v0/models`, {
43985
44565
  method: "GET",
43986
44566
  headers,
43987
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
44567
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2)
43988
44568
  });
43989
44569
  } catch (e) {
43990
44570
  return discoverViaOpenAIModels(`${baseUrl}/v1/models`, headers, cacheKey);
@@ -44056,7 +44636,7 @@ function extractLMStudioModels(body) {
44056
44636
  async function fetchOllamaModels2(url2) {
44057
44637
  const response = await fetch(url2, {
44058
44638
  method: "GET",
44059
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
44639
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2)
44060
44640
  });
44061
44641
  if (!response.ok)
44062
44642
  return [];
@@ -44069,17 +44649,17 @@ async function fetchOllamaModels2(url2) {
44069
44649
  })).filter((m) => m.name.length > 0);
44070
44650
  }
44071
44651
  function invalidateProbeDiscovery(providerSlug) {
44072
- for (const key of _cache.keys()) {
44652
+ for (const key of _cache2.keys()) {
44073
44653
  if (key.startsWith(`${providerSlug}:`)) {
44074
- _cache.delete(key);
44654
+ _cache2.delete(key);
44075
44655
  }
44076
44656
  }
44077
44657
  }
44078
- var _cache, CACHE_TTL_MS, FETCH_TIMEOUT_MS = 5000, SMALL_MODEL_PATTERNS, NON_CHAT_PATTERNS, STANDARD_VENDOR_PREFIXES;
44658
+ var _cache2, CACHE_TTL_MS2, FETCH_TIMEOUT_MS2 = 5000, SMALL_MODEL_PATTERNS, NON_CHAT_PATTERNS, STANDARD_VENDOR_PREFIXES;
44079
44659
  var init_probe_discovery = __esm(() => {
44080
44660
  init_logger();
44081
- _cache = new Map;
44082
- CACHE_TTL_MS = 5 * 60 * 1000;
44661
+ _cache2 = new Map;
44662
+ CACHE_TTL_MS2 = 5 * 60 * 1000;
44083
44663
  SMALL_MODEL_PATTERNS = [
44084
44664
  /\bmini\b/i,
44085
44665
  /\bnano\b/i,
@@ -44127,6 +44707,127 @@ var init_probe_discovery = __esm(() => {
44127
44707
  ];
44128
44708
  });
44129
44709
 
44710
+ // src/providers/transport/anthropic-compat.ts
44711
+ class AnthropicProviderTransport {
44712
+ name;
44713
+ displayName;
44714
+ streamFormat = "anthropic-sse";
44715
+ provider;
44716
+ apiKey;
44717
+ constructor(provider, apiKey) {
44718
+ this.provider = provider;
44719
+ this.apiKey = apiKey;
44720
+ this.name = provider.name;
44721
+ this.displayName = AnthropicProviderTransport.formatDisplayName(provider.name);
44722
+ }
44723
+ getEndpoint() {
44724
+ return `${this.provider.baseUrl}${this.provider.apiPath}`;
44725
+ }
44726
+ async getHeaders() {
44727
+ const headers = {
44728
+ "anthropic-version": "2023-06-01"
44729
+ };
44730
+ if (this.provider.authScheme === "bearer") {
44731
+ headers.Authorization = `Bearer ${this.apiKey}`;
44732
+ } else {
44733
+ headers["x-api-key"] = this.apiKey;
44734
+ }
44735
+ if (this.provider.headers) {
44736
+ Object.assign(headers, this.provider.headers);
44737
+ }
44738
+ if (this.provider.name === "kimi-coding") {
44739
+ try {
44740
+ const auth = await credentials.getRequestAuth("kimi-coding", { model: "" });
44741
+ if (auth.headers.Authorization) {
44742
+ delete headers["x-api-key"];
44743
+ }
44744
+ Object.assign(headers, auth.headers);
44745
+ } catch (e) {
44746
+ log(`[${this.displayName}] OAuth path failed, falling back to API key: ${e.message}`);
44747
+ }
44748
+ }
44749
+ return headers;
44750
+ }
44751
+ async discoverProbeModel(exclude) {
44752
+ const def = getProviderByName(this.provider.name);
44753
+ if (!def?.modelDiscovery) {
44754
+ return {
44755
+ model: null,
44756
+ reason: `${this.displayName} publishes no live model list (no modelDiscovery endpoint) \u2014 its probe model must come from the cloud catalog`
44757
+ };
44758
+ }
44759
+ const discovered = await discoverProviderModels(this.provider.name);
44760
+ if (discovered.length === 0) {
44761
+ return {
44762
+ model: null,
44763
+ reason: `${this.displayName} listed no models at ${def.modelDiscovery.path} \u2014 check the API key and that the subscription is active`
44764
+ };
44765
+ }
44766
+ const ranked = rankDiscoveredModels(discovered).map((m) => m.id).filter(isChatCapable);
44767
+ if (ranked.length === 0) {
44768
+ return {
44769
+ model: null,
44770
+ reason: `no chat-capable model among the ${discovered.length} listed by ${this.displayName}`
44771
+ };
44772
+ }
44773
+ const pick2 = ranked.find((m) => !exclude?.has(m));
44774
+ if (!pick2) {
44775
+ return {
44776
+ model: null,
44777
+ reason: `all ${ranked.length} candidate model(s) already tried`
44778
+ };
44779
+ }
44780
+ return { model: pick2 };
44781
+ }
44782
+ async enqueueRequest(fetchFn) {
44783
+ const maxRetries = 2;
44784
+ let lastResponse = null;
44785
+ for (let attempt = 0;attempt <= maxRetries; attempt++) {
44786
+ const response = await fetchFn();
44787
+ if (response.status === 429 && attempt < maxRetries) {
44788
+ const bodyText = await response.clone().text().catch(() => "");
44789
+ if (isTerminal429(bodyText)) {
44790
+ log(`[${this.displayName}] 429 is terminal (billing/quota), not retrying`);
44791
+ return response;
44792
+ }
44793
+ lastResponse = response;
44794
+ const retryAfter = response.headers.get("Retry-After");
44795
+ let delayMs;
44796
+ if (retryAfter && !Number.isNaN(Number(retryAfter))) {
44797
+ delayMs = Math.min(Number(retryAfter) * 1000, 2000);
44798
+ } else {
44799
+ delayMs = 500 * (attempt + 1);
44800
+ }
44801
+ log(`[${this.displayName}] 429 rate limited, retry ${attempt + 1}/${maxRetries} in ${(delayMs / 1000).toFixed(1)}s`);
44802
+ await new Promise((resolve2) => setTimeout(resolve2, delayMs));
44803
+ continue;
44804
+ }
44805
+ return response;
44806
+ }
44807
+ return lastResponse;
44808
+ }
44809
+ static formatDisplayName(name) {
44810
+ const map3 = {
44811
+ minimax: "MiniMax",
44812
+ "minimax-coding": "MiniMax Coding",
44813
+ kimi: "Kimi",
44814
+ "kimi-coding": "Kimi Coding",
44815
+ "qwen-cloud": "Qwen Plan",
44816
+ moonshot: "Kimi",
44817
+ "z-ai": "Z.AI"
44818
+ };
44819
+ return map3[name.toLowerCase()] || name.charAt(0).toUpperCase() + name.slice(1);
44820
+ }
44821
+ }
44822
+ var init_anthropic_compat = __esm(() => {
44823
+ init_authority();
44824
+ init_logger();
44825
+ init_model_discovery();
44826
+ init_provider_definitions();
44827
+ init_openai();
44828
+ init_probe_discovery();
44829
+ });
44830
+
44130
44831
  // src/providers/transport/litellm.ts
44131
44832
  class LiteLLMProviderTransport {
44132
44833
  name = "litellm";
@@ -44549,14 +45250,14 @@ function resolveApiKeyProvenance(envVar, aliases) {
44549
45250
  }
44550
45251
  return {
44551
45252
  envVar: runtimeVar,
44552
- effectiveValue: runtimeValue,
45253
+ hasValue: !!runtimeValue,
44553
45254
  effectiveMasked: maskKey(runtimeValue),
44554
45255
  effectiveSource,
44555
45256
  layers
44556
45257
  };
44557
45258
  }
44558
45259
  function formatProvenanceLog(p) {
44559
- if (!p.effectiveValue) {
45260
+ if (!p.hasValue) {
44560
45261
  return `${p.envVar}=(not set)`;
44561
45262
  }
44562
45263
  return `${p.envVar}=${p.effectiveMasked} [from: ${p.effectiveSource}]`;
@@ -45324,6 +46025,7 @@ var init_provider_profiles = __esm(() => {
45324
46025
  "minimax-coding": anthropicCompatProfile,
45325
46026
  kimi: anthropicCompatProfile,
45326
46027
  "kimi-coding": anthropicCompatProfile,
46028
+ "qwen-cloud": anthropicCompatProfile,
45327
46029
  "z-ai": anthropicCompatProfile,
45328
46030
  glm: glmProfile,
45329
46031
  "glm-coding": glmProfile,
@@ -46097,7 +46799,7 @@ function loadDiskCache() {
46097
46799
  return false;
46098
46800
  const stat2 = statSync4(CACHE_FILE);
46099
46801
  const age = Date.now() - stat2.mtimeMs;
46100
- const isFresh = age < CACHE_TTL_MS2;
46802
+ const isFresh = age < CACHE_TTL_MS3;
46101
46803
  const raw2 = readFileSync15(CACHE_FILE, "utf-8");
46102
46804
  const data = JSON.parse(raw2);
46103
46805
  for (const [key, pricing] of Object.entries(data)) {
@@ -46108,7 +46810,7 @@ function loadDiskCache() {
46108
46810
  return false;
46109
46811
  }
46110
46812
  }
46111
- var pricingMap, CACHE_DIR, CACHE_FILE, CACHE_TTL_MS2, cacheWarmed = false;
46813
+ var pricingMap, CACHE_DIR, CACHE_FILE, CACHE_TTL_MS3, cacheWarmed = false;
46112
46814
  var init_pricing_cache = __esm(() => {
46113
46815
  init_remote_provider_types();
46114
46816
  init_logger();
@@ -46116,7 +46818,7 @@ var init_pricing_cache = __esm(() => {
46116
46818
  pricingMap = new Map;
46117
46819
  CACHE_DIR = join24(homedir24(), ".claudish");
46118
46820
  CACHE_FILE = join24(CACHE_DIR, "pricing-cache.json");
46119
- CACHE_TTL_MS2 = 24 * 60 * 60 * 1000;
46821
+ CACHE_TTL_MS3 = 24 * 60 * 60 * 1000;
46120
46822
  });
46121
46823
 
46122
46824
  // src/proxy-server.ts
@@ -47407,6 +48109,10 @@ function fuzzyScore(text, query) {
47407
48109
  }
47408
48110
  return queryIndex === lowerQuery.length ? score / lowerText.length : 0;
47409
48111
  }
48112
+ function orderingKey(model) {
48113
+ const releaseDate = typeof model?.releaseDate === "string" ? model.releaseDate : typeof model?.created === "number" && Number.isFinite(model.created) ? new Date(model.created * 1000).toISOString() : undefined;
48114
+ return { releaseDate, id: typeof model?.id === "string" ? model.id : "" };
48115
+ }
47410
48116
  function fmtSize(n) {
47411
48117
  if (n <= 0)
47412
48118
  return "0B";
@@ -47634,13 +48340,17 @@ Tokens: ${result.usage.input} input, ${result.usage.output} output`;
47634
48340
  isError: true
47635
48341
  };
47636
48342
  }
47637
- const results = allModels.map((model) => {
48343
+ const results2 = allModels.map((model) => {
47638
48344
  const nameScore = fuzzyScore(model.name || "", query);
47639
48345
  const idScore = fuzzyScore(model.id || "", query);
47640
48346
  const descScore = fuzzyScore(model.description || "", query) * 0.5;
47641
48347
  return { model, score: Math.max(nameScore, idScore, descScore) };
47642
- }).filter((item) => item.score > 0.2).sort((a, b) => b.score - a.score).slice(0, maxResults);
47643
- if (results.length === 0) {
48348
+ }).filter((item) => item.score > 0.2).sort((a, b) => {
48349
+ if (Math.abs(a.score - b.score) > SCORE_TIE_EPSILON)
48350
+ return b.score - a.score;
48351
+ return compareByReleaseDateDesc(orderingKey(a.model), orderingKey(b.model));
48352
+ }).slice(0, maxResults);
48353
+ if (results2.length === 0) {
47644
48354
  return {
47645
48355
  content: [{ type: "text", text: `No models found matching "${query}"` }]
47646
48356
  };
@@ -47652,7 +48362,7 @@ Tokens: ${result.usage.input} input, ${result.usage.output} output`;
47652
48362
  `;
47653
48363
  output += `|-------|----------|---------|----------|
47654
48364
  `;
47655
- for (const { model } of results) {
48365
+ for (const { model } of results2) {
47656
48366
  const provider = model.id.split("/")[0];
47657
48367
  const promptPrice = Number.parseFloat(model.pricing?.prompt || "0") * 1e6;
47658
48368
  const completionPrice = Number.parseFloat(model.pricing?.completion || "0") * 1e6;
@@ -47663,7 +48373,7 @@ Tokens: ${result.usage.input} input, ${result.usage.output} output`;
47663
48373
  `;
47664
48374
  }
47665
48375
  output += `
47666
- Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
48376
+ Use with: run_prompt(model="${results2[0].model.id}", prompt="your prompt")`;
47667
48377
  return { content: [{ type: "text", text: output }] };
47668
48378
  }
47669
48379
  });
@@ -47693,13 +48403,13 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
47693
48403
  const prompt = args.prompt;
47694
48404
  const systemPrompt = args.system_prompt;
47695
48405
  const maxTokens = args.max_tokens;
47696
- const results = [];
48406
+ const results2 = [];
47697
48407
  for (const model of modelIds) {
47698
48408
  try {
47699
48409
  const result = await runPromptViaProxy(model, prompt, systemPrompt, maxTokens);
47700
- results.push({ model, response: result.content, tokens: result.usage });
48410
+ results2.push({ model, response: result.content, tokens: result.usage });
47701
48411
  } catch (error46) {
47702
- results.push({
48412
+ results2.push({
47703
48413
  model,
47704
48414
  response: "",
47705
48415
  error: error46 instanceof Error ? error46.message : String(error46)
@@ -47712,7 +48422,7 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
47712
48422
  output += `**Prompt:** ${prompt.slice(0, 100)}${prompt.length > 100 ? "..." : ""}
47713
48423
 
47714
48424
  `;
47715
- for (const result of results) {
48425
+ for (const result of results2) {
47716
48426
  output += `## ${result.model}
47717
48427
 
47718
48428
  `;
@@ -47734,7 +48444,7 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
47734
48444
 
47735
48445
  `;
47736
48446
  }
47737
- const failed = results.filter((r) => r.error);
48447
+ const failed = results2.filter((r) => r.error);
47738
48448
  if (failed.length > 0) {
47739
48449
  output += '---\n**To report failed model(s)**, use the `report_error` tool with `error_type: "provider_failure"` and the model ID(s) above.\n';
47740
48450
  }
@@ -48296,7 +49006,7 @@ When channel mode is active, you receive <channel source="claudish" ...> notific
48296
49006
  5. Use list_sessions to see all active/completed sessions.
48297
49007
  6. Use cancel_session to stop a running session.
48298
49008
 
48299
- The session_id in the channel tag's meta attributes is the key for all tool calls.`, proxyInstance = null, proxyStarting = null, NEXT_STEP, sanitize, EVENT_TO_TASK_STATUS;
49009
+ The session_id in the channel tag's meta attributes is the key for all tool calls.`, proxyInstance = null, proxyStarting = null, SCORE_TIE_EPSILON = 0.000001, NEXT_STEP, sanitize, EVENT_TO_TASK_STATUS;
48300
49010
  var init_mcp_server = __esm(() => {
48301
49011
  init_server2();
48302
49012
  init_stdio2();
@@ -48482,6 +49192,28 @@ Behavior rules
48482
49192
  console.log(` ${dim2(`observer model: ${obs.model}`)}`);
48483
49193
  console.log();
48484
49194
  }
49195
+ function printByModel(records) {
49196
+ const byModel = new Map;
49197
+ for (const r of records) {
49198
+ const m = r.model ?? "unknown";
49199
+ const e = byModel.get(m) ?? { ok: 0, degraded: 0 };
49200
+ if (r.outcome === "degraded")
49201
+ e.degraded++;
49202
+ else
49203
+ e.ok++;
49204
+ byModel.set(m, e);
49205
+ }
49206
+ if (byModel.size === 0)
49207
+ return;
49208
+ console.log(bold2(` by model (degraded / ok)
49209
+ `));
49210
+ const ranked = [...byModel.entries()].sort((a, b) => b[1].degraded - a[1].degraded || b[1].ok - a[1].ok);
49211
+ for (const [model, v] of ranked) {
49212
+ const flag = v.degraded > 0 ? yellow(String(v.degraded)) : dim2(String(v.degraded));
49213
+ console.log(` ${model.padEnd(26)} ${flag} / ${v.ok}`);
49214
+ }
49215
+ console.log();
49216
+ }
48485
49217
  function showCorpus(write, json2) {
48486
49218
  const result = buildCorpus({ write });
48487
49219
  if (json2) {
@@ -48500,25 +49232,7 @@ Behavior divergence corpus
48500
49232
  console.log(` ${yellow("degraded (no plan)")} : ${degraded.length}`);
48501
49233
  console.log(` of those, a rule would have fired on ${catchable.length}
48502
49234
  `);
48503
- const byModel = new Map;
48504
- for (const r of result.records) {
48505
- const m = r.model ?? "unknown";
48506
- const e = byModel.get(m) ?? { ok: 0, degraded: 0 };
48507
- if (r.outcome === "degraded")
48508
- e.degraded++;
48509
- else
48510
- e.ok++;
48511
- byModel.set(m, e);
48512
- }
48513
- if (byModel.size > 0) {
48514
- console.log(bold2(` by model (degraded / ok)
48515
- `));
48516
- for (const [model, v] of [...byModel.entries()].sort((a, b) => b[1].degraded - a[1].degraded || b[1].ok - a[1].ok)) {
48517
- const flag = v.degraded > 0 ? yellow(String(v.degraded)) : dim2("0");
48518
- console.log(` ${model.padEnd(26)} ${flag} / ${v.ok}`);
48519
- }
48520
- console.log();
48521
- }
49235
+ printByModel(result.records);
48522
49236
  if (result.outputPath) {
48523
49237
  console.log(dim2(` appended to ${result.outputPath}
48524
49238
  `));
@@ -48544,9 +49258,9 @@ async function behaviorCommand(argv) {
48544
49258
  default:
48545
49259
  console.error(`Unknown action "${action}".
48546
49260
 
48547
- ` + `Usage:
48548
- ` + ` claudish behavior rules [--json]
48549
- ` + ` claudish behavior corpus [--write] [--json]
49261
+ Usage:
49262
+ claudish behavior rules [--json]
49263
+ claudish behavior corpus [--write] [--json]
48550
49264
  `);
48551
49265
  process.exit(1);
48552
49266
  }
@@ -60761,13 +61475,13 @@ var init_dist14 = __esm(() => {
60761
61475
  setSearchError(undefined);
60762
61476
  const fetchResults = async () => {
60763
61477
  try {
60764
- const results = await config3.source(searchTerm || undefined, {
61478
+ const results2 = await config3.source(searchTerm || undefined, {
60765
61479
  signal: controller.signal
60766
61480
  });
60767
61481
  if (!controller.signal.aborted) {
60768
61482
  setActive(undefined);
60769
61483
  setSearchError(undefined);
60770
- setSearchResults(normalizeChoices4(results));
61484
+ setSearchResults(normalizeChoices4(results2));
60771
61485
  setStatus("idle");
60772
61486
  }
60773
61487
  } catch (error47) {
@@ -61475,6 +62189,8 @@ var init_config2 = __esm(() => {
61475
62189
  CLAUDISH_MODEL: "CLAUDISH_MODEL",
61476
62190
  CLAUDISH_PORT: "CLAUDISH_PORT",
61477
62191
  CLAUDISH_ACTIVE_MODEL_NAME: "CLAUDISH_ACTIVE_MODEL_NAME",
62192
+ CLAUDISH_TOKEN_FILE: "CLAUDISH_TOKEN_FILE",
62193
+ CLAUDISH_PROVIDER_NAME: "CLAUDISH_PROVIDER_NAME",
61478
62194
  ANTHROPIC_MODEL: "ANTHROPIC_MODEL",
61479
62195
  ANTHROPIC_SMALL_FAST_MODEL: "ANTHROPIC_SMALL_FAST_MODEL",
61480
62196
  CLAUDISH_MODEL_OPUS: "CLAUDISH_MODEL_OPUS",
@@ -61655,114 +62371,6 @@ var init_model_catalog2 = __esm(() => {
61655
62371
  NO_CATALOG_VENDOR_SLUGS = new Set(["litellm", "ollama", "lmstudio", "lm-studio"]);
61656
62372
  });
61657
62373
 
61658
- // src/providers/model-discovery.ts
61659
- function resolveBaseUrl2(catalogName) {
61660
- const def = getProviderByName(catalogName);
61661
- if (!def)
61662
- return null;
61663
- for (const envVar of def.baseUrlEnvVars ?? []) {
61664
- const v = process.env[envVar];
61665
- if (v)
61666
- return v.replace(/\/+$/, "");
61667
- }
61668
- return (def.baseUrl || "").replace(/\/+$/, "") || null;
61669
- }
61670
- function readContextWindow(row) {
61671
- for (const field of ["context_length", "context_window", "max_context_length"]) {
61672
- const v = row[field];
61673
- if (typeof v === "number" && Number.isFinite(v) && v > 0)
61674
- return v;
61675
- }
61676
- return;
61677
- }
61678
- function parseOpenAIModelsList(body) {
61679
- const data = body?.data;
61680
- if (!Array.isArray(data))
61681
- return [];
61682
- const models = [];
61683
- for (const raw2 of data) {
61684
- if (!raw2 || typeof raw2 !== "object")
61685
- continue;
61686
- const row = raw2;
61687
- const id = row.id;
61688
- if (typeof id !== "string" || id.trim().length === 0)
61689
- continue;
61690
- const displayName = typeof row.display_name === "string" ? row.display_name : undefined;
61691
- models.push({ id, displayName, contextWindow: readContextWindow(row) });
61692
- }
61693
- return models;
61694
- }
61695
- async function discoverProviderModels(providerName) {
61696
- const cached2 = _cache2.get(providerName);
61697
- if (cached2 && cached2.expiresAt > Date.now())
61698
- return cached2.models;
61699
- const def = getProviderByName(providerName);
61700
- const descriptor = def?.modelDiscovery;
61701
- if (!def || !descriptor)
61702
- return [];
61703
- const baseUrl = resolveBaseUrl2(providerName);
61704
- if (!baseUrl)
61705
- return [];
61706
- const endpoint = `${baseUrl}${descriptor.path}`;
61707
- let headers = {};
61708
- try {
61709
- const auth = await credentials.getRequestAuth(providerName, { model: "" });
61710
- headers = { ...auth.headers };
61711
- } catch (e) {
61712
- log(`[model-discovery:${providerName}] no credentials, skipping: ${e?.message}`);
61713
- return [];
61714
- }
61715
- let response;
61716
- try {
61717
- response = await fetch(endpoint, {
61718
- method: "GET",
61719
- headers,
61720
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2)
61721
- });
61722
- } catch (e) {
61723
- log(`[model-discovery:${providerName}] fetch failed: ${e?.message}`);
61724
- return [];
61725
- }
61726
- if (!response.ok) {
61727
- log(`[model-discovery:${providerName}] HTTP ${response.status} from ${endpoint}`);
61728
- return [];
61729
- }
61730
- let body;
61731
- try {
61732
- body = await response.json();
61733
- } catch {
61734
- log(`[model-discovery:${providerName}] response was not JSON`);
61735
- return [];
61736
- }
61737
- const models = parseOpenAIModelsList(body);
61738
- if (models.length === 0) {
61739
- log(`[model-discovery:${providerName}] endpoint reachable but listed no models`);
61740
- return [];
61741
- }
61742
- log(`[model-discovery:${providerName}] discovered ${models.length} models: ` + models.map((m) => `${m.id}(${m.contextWindow ?? "?"})`).join(", "));
61743
- _cache2.set(providerName, { models, expiresAt: Date.now() + CACHE_TTL_MS3 });
61744
- return models;
61745
- }
61746
- async function discoverContextWindow(providerName, modelId) {
61747
- const models = await discoverProviderModels(providerName);
61748
- const match2 = models.find((m) => m.id.toLowerCase() === modelId.toLowerCase());
61749
- return match2?.contextWindow;
61750
- }
61751
- function rankDiscoveredModels(models) {
61752
- return [...models].sort((a, b) => {
61753
- const diff = (b.contextWindow ?? 0) - (a.contextWindow ?? 0);
61754
- return diff !== 0 ? diff : a.id.localeCompare(b.id);
61755
- });
61756
- }
61757
- var CACHE_TTL_MS3, FETCH_TIMEOUT_MS2 = 5000, _cache2;
61758
- var init_model_discovery = __esm(() => {
61759
- init_authority();
61760
- init_logger();
61761
- init_provider_definitions();
61762
- CACHE_TTL_MS3 = 5 * 60 * 1000;
61763
- _cache2 = new Map;
61764
- });
61765
-
61766
62374
  // src/model-selector.ts
61767
62375
  var exports_model_selector = {};
61768
62376
  __export(exports_model_selector, {
@@ -61778,7 +62386,8 @@ __export(exports_model_selector, {
61778
62386
  isUserDeployedProvider: () => isUserDeployedProvider,
61779
62387
  confirmAction: () => confirmAction,
61780
62388
  compareByReleaseDateDesc: () => compareByReleaseDateDesc,
61781
- buildExplicitModelSpec: () => buildExplicitModelSpec
62389
+ buildExplicitModelSpec: () => buildExplicitModelSpec,
62390
+ buildDiscoveredModelRows: () => buildDiscoveredModelRows
61782
62391
  });
61783
62392
  function isUserDeployedProvider(value) {
61784
62393
  return LOCAL_OR_USER_DEPLOYED.has(value);
@@ -61810,7 +62419,7 @@ function formatFirebaseProviderLabel(slug) {
61810
62419
  async function loadRecommendedModels(forceRefresh = false) {
61811
62420
  try {
61812
62421
  const doc2 = await getRecommendedModels({ forceRefresh });
61813
- return doc2.models.map((model) => ({
62422
+ return sortModelsNewestFirst(doc2.models.map((model) => ({
61814
62423
  id: model.id,
61815
62424
  name: model.name,
61816
62425
  description: model.description,
@@ -61823,7 +62432,7 @@ async function loadRecommendedModels(forceRefresh = false) {
61823
62432
  supportsReasoning: model.supportsReasoning,
61824
62433
  supportsVision: model.supportsVision,
61825
62434
  source: formatFirebaseProviderLabel(model.provider)
61826
- }));
62435
+ })));
61827
62436
  } catch {
61828
62437
  return [];
61829
62438
  }
@@ -61867,6 +62476,48 @@ function formatAveragePricing(pricing) {
61867
62476
  average: avg === 0 ? "FREE" : `$${avg.toFixed(2)}/1M`
61868
62477
  };
61869
62478
  }
62479
+ function resolveDiscoveredContextLength(m) {
62480
+ if (typeof m.contextWindow === "number" && m.contextWindow > 0)
62481
+ return m.contextWindow;
62482
+ try {
62483
+ return lookupModel(m.id)?.contextWindow ?? 0;
62484
+ } catch {
62485
+ return 0;
62486
+ }
62487
+ }
62488
+ function resolveDiscoveredReleaseDate(m) {
62489
+ try {
62490
+ const catalogDate = lookupModel(m.id)?.releaseDate;
62491
+ if (catalogDate)
62492
+ return catalogDate;
62493
+ } catch {}
62494
+ return m.releaseDate;
62495
+ }
62496
+ async function resolveMissingContextWindows(ids, timeoutMs = DISCOVERY_CONTEXT_LOOKUP_TIMEOUT_MS) {
62497
+ const resolved = new Map;
62498
+ if (ids.length === 0)
62499
+ return resolved;
62500
+ let timer;
62501
+ const budget = new Promise((resolve4) => {
62502
+ timer = setTimeout(() => resolve4(null), timeoutMs);
62503
+ timer.unref?.();
62504
+ });
62505
+ try {
62506
+ const lookups = Promise.all(ids.map(async (id) => [id, await resolveCatalogContextWindow(id)]));
62507
+ const settled = await Promise.race([lookups, budget]);
62508
+ if (settled) {
62509
+ for (const [id, contextWindow] of settled) {
62510
+ if (typeof contextWindow === "number" && contextWindow > 0) {
62511
+ resolved.set(id, contextWindow);
62512
+ }
62513
+ }
62514
+ }
62515
+ } catch {} finally {
62516
+ if (timer)
62517
+ clearTimeout(timer);
62518
+ }
62519
+ return resolved;
62520
+ }
61870
62521
  function modelDocToModelInfo(model) {
61871
62522
  const providerLabel = formatFirebaseProviderLabel(model.provider || "unknown");
61872
62523
  const contextLength = model.contextWindow || 0;
@@ -61918,63 +62569,6 @@ function dedupeModels(models) {
61918
62569
  }
61919
62570
  return deduped;
61920
62571
  }
61921
- function extractVersionParts(modelId) {
61922
- const tokens = modelId.toLowerCase().split(/[\/_-]+/);
61923
- let started = false;
61924
- const parts = [];
61925
- for (const token of tokens) {
61926
- const match2 = token.match(/\d+(?:\.\d+)*/);
61927
- if (!match2) {
61928
- if (started)
61929
- break;
61930
- continue;
61931
- }
61932
- if (!started) {
61933
- started = true;
61934
- for (const part of match2[0].split(".")) {
61935
- parts.push(Number.parseInt(part, 10));
61936
- }
61937
- if (!/^\d+(?:\.\d+)*$/.test(token)) {
61938
- break;
61939
- }
61940
- continue;
61941
- }
61942
- if (!/^\d{1,2}(?:\.\d+)?$/.test(token)) {
61943
- break;
61944
- }
61945
- for (const part of token.split(".")) {
61946
- parts.push(Number.parseInt(part, 10));
61947
- }
61948
- }
61949
- return parts;
61950
- }
61951
- function compareVersionPartsDesc(a, b) {
61952
- const maxLength = Math.max(a.length, b.length);
61953
- for (let i = 0;i < maxLength; i++) {
61954
- const aPart = a[i] ?? -1;
61955
- const bPart = b[i] ?? -1;
61956
- if (aPart !== bPart) {
61957
- return bPart - aPart;
61958
- }
61959
- }
61960
- return 0;
61961
- }
61962
- function compareByReleaseDateDesc(a, b) {
61963
- const aReleaseRaw = a.releaseDate ? Date.parse(a.releaseDate) : 0;
61964
- const bReleaseRaw = b.releaseDate ? Date.parse(b.releaseDate) : 0;
61965
- const aRelease = Number.isNaN(aReleaseRaw) ? 0 : aReleaseRaw;
61966
- const bRelease = Number.isNaN(bReleaseRaw) ? 0 : bReleaseRaw;
61967
- if (aRelease !== bRelease) {
61968
- return bRelease - aRelease;
61969
- }
61970
- const aId = a.id ?? a.modelId ?? "";
61971
- const bId = b.id ?? b.modelId ?? "";
61972
- const versionCompare = compareVersionPartsDesc(extractVersionParts(aId), extractVersionParts(bId));
61973
- if (versionCompare !== 0) {
61974
- return versionCompare;
61975
- }
61976
- return aId.localeCompare(bId);
61977
- }
61978
62572
  function sortModelsNewestFirst(models) {
61979
62573
  return [...models].sort(compareByReleaseDateDesc);
61980
62574
  }
@@ -62272,22 +62866,42 @@ async function pickModelFromList(provider, displayName, tierName, models) {
62272
62866
  });
62273
62867
  return selected === CUSTOM_VALUE ? null : selected;
62274
62868
  }
62869
+ async function buildDiscoveredModelRows(provider, displayName, catalog) {
62870
+ const discovered = rankDiscoveredModels(await discoverProviderModels(provider)).filter((m) => isChatCapable(m.id));
62871
+ if (discovered.length === 0)
62872
+ return [];
62873
+ const subscription = isSubscriptionProvider(provider);
62874
+ const pricingById = subscription ? new Map : new Map((await loadModelsForPickerProvider(provider, catalog)).map((m) => [
62875
+ m.id.toLowerCase(),
62876
+ m.pricing
62877
+ ]));
62878
+ const offlineContext = new Map(discovered.map((m) => [m.id, resolveDiscoveredContextLength(m)]));
62879
+ const cloudContext = await resolveMissingContextWindows(discovered.filter((m) => !offlineContext.get(m.id)).map((m) => m.id));
62880
+ const rows = discovered.map((m) => {
62881
+ const contextLength = offlineContext.get(m.id) || cloudContext.get(m.id) || 0;
62882
+ return {
62883
+ id: m.id,
62884
+ name: m.displayName || m.id,
62885
+ description: contextLength ? `${m.displayName ?? m.id} \xB7 ${Math.round(contextLength / 1024)}K context` : m.displayName ?? m.id,
62886
+ provider: displayName,
62887
+ releaseDate: resolveDiscoveredReleaseDate(m),
62888
+ pricing: subscription ? SUBSCRIPTION_PRICING : pricingById.get(m.id.toLowerCase()),
62889
+ context: formatContextLength(contextLength),
62890
+ contextLength,
62891
+ supportsTools: true,
62892
+ isFree: subscription,
62893
+ source: displayName
62894
+ };
62895
+ });
62896
+ return sortModelsNewestFirst(rows);
62897
+ }
62275
62898
  async function selectModelFromProvider(provider, tierName, recommendedModels, _forceUpdate, catalog) {
62276
62899
  const prefix = PROVIDER_MODEL_PREFIX[provider] || `${provider}@`;
62277
62900
  const displayName = getPickerDisplayName(provider);
62278
62901
  const def = getProviderByName(provider);
62279
62902
  if (def?.modelDiscovery) {
62280
- const discovered = rankDiscoveredModels(await discoverProviderModels(provider));
62281
- if (discovered.length > 0) {
62282
- const discoveredModels = discovered.map((m) => ({
62283
- id: m.id,
62284
- name: m.displayName || m.id,
62285
- description: m.contextWindow ? `${m.displayName ?? m.id} \xB7 ${Math.round(m.contextWindow / 1024)}K context` : m.displayName ?? m.id,
62286
- provider: displayName,
62287
- supportsTools: true,
62288
- isFree: true,
62289
- source: displayName
62290
- }));
62903
+ const discoveredModels = await buildDiscoveredModelRows(provider, displayName, catalog);
62904
+ if (discoveredModels.length > 0) {
62291
62905
  const picked = await pickModelFromList(provider, displayName, tierName, discoveredModels);
62292
62906
  if (picked)
62293
62907
  return picked;
@@ -62295,7 +62909,7 @@ async function selectModelFromProvider(provider, tierName, recommendedModels, _f
62295
62909
  }
62296
62910
  if (provider === "ollama") {
62297
62911
  const ollamaModels = await fetchOllamaModels({ enrichCapabilities: false });
62298
- const chatModels = ollamaModels.map((m) => ({
62912
+ const chatModels = sortModelsNewestFirst(ollamaModels.map((m) => ({
62299
62913
  id: m.name,
62300
62914
  name: m.name,
62301
62915
  description: m.description,
@@ -62303,7 +62917,7 @@ async function selectModelFromProvider(provider, tierName, recommendedModels, _f
62303
62917
  supportsTools: m.supportsTools,
62304
62918
  isFree: true,
62305
62919
  source: displayName
62306
- }));
62920
+ })));
62307
62921
  if (chatModels.length > 0) {
62308
62922
  const picked = await pickModelFromList(provider, displayName, tierName, chatModels);
62309
62923
  if (picked)
@@ -62464,14 +63078,18 @@ async function selectProfile(profiles) {
62464
63078
  async function confirmAction(message) {
62465
63079
  return dist_default4({ message, default: false });
62466
63080
  }
62467
- var pickerProviderToFirebaseSlug, LOCAL_OR_USER_DEPLOYED, PROVIDER_FILTER_ALIASES, ALL_PROVIDER_CHOICES, PROVIDER_MODEL_PREFIX;
63081
+ var pickerProviderToFirebaseSlug, LOCAL_OR_USER_DEPLOYED, SUBSCRIPTION_PRICING, DISCOVERY_CONTEXT_LOOKUP_TIMEOUT_MS = 1500, PROVIDER_FILTER_ALIASES, ALL_PROVIDER_CHOICES, PROVIDER_MODEL_PREFIX;
62468
63082
  var init_model_selector = __esm(() => {
62469
63083
  init_dist16();
63084
+ init_model_catalog();
62470
63085
  init_authority();
63086
+ init_context_window_fallback();
63087
+ init_remote_provider_types();
62471
63088
  init_model_loader();
62472
63089
  init_model_catalog2();
62473
63090
  init_model_discovery();
62474
63091
  init_provider_definitions();
63092
+ init_probe_discovery();
62475
63093
  pickerProviderToFirebaseSlug = {
62476
63094
  openrouter: "openrouter",
62477
63095
  google: "google",
@@ -62495,6 +63113,11 @@ var init_model_selector = __esm(() => {
62495
63113
  ollamacloud: "ollamacloud"
62496
63114
  };
62497
63115
  LOCAL_OR_USER_DEPLOYED = new Set(["litellm", "ollama", "lmstudio"]);
63116
+ SUBSCRIPTION_PRICING = {
63117
+ input: "SUB",
63118
+ output: "SUB",
63119
+ average: "SUB"
63120
+ };
62498
63121
  PROVIDER_FILTER_ALIASES = {
62499
63122
  openrouter: "openrouter",
62500
63123
  or: "openrouter",
@@ -62531,7 +63154,9 @@ var init_model_selector = __esm(() => {
62531
63154
  sakana: "sakana",
62532
63155
  fugu: "sakana",
62533
63156
  "sakana-subscription": "sakana-subscription",
62534
- sc: "sakana-subscription"
63157
+ sc: "sakana-subscription",
63158
+ "qwen-cloud": "qwen-cloud",
63159
+ qc: "qwen-cloud"
62535
63160
  };
62536
63161
  ALL_PROVIDER_CHOICES = [
62537
63162
  {
@@ -62582,6 +63207,12 @@ var init_model_selector = __esm(() => {
62582
63207
  description: "Coding subscription",
62583
63208
  provider: "kimi-coding"
62584
63209
  },
63210
+ {
63211
+ name: "Qwen Plan",
63212
+ value: "qwen-cloud",
63213
+ description: "Alibaba Model Studio subscription",
63214
+ provider: "qwen-cloud"
63215
+ },
62585
63216
  { name: "GLM / Zhipu", value: "glm", description: "Direct API", provider: "glm" },
62586
63217
  {
62587
63218
  name: "GLM Coding Plan",
@@ -62628,6 +63259,7 @@ var init_model_selector = __esm(() => {
62628
63259
  kimi: "kimi@",
62629
63260
  "minimax-coding": "mmc@",
62630
63261
  "kimi-coding": "kc@",
63262
+ "qwen-cloud": "qc@",
62631
63263
  glm: "glm@",
62632
63264
  "glm-coding": "gc@",
62633
63265
  "z-ai": "z-ai@",
@@ -63251,7 +63883,7 @@ function wordWrap(text, maxWidth) {
63251
63883
  lines.push(current);
63252
63884
  return lines;
63253
63885
  }
63254
- function computeBarScales(results) {
63886
+ function computeBarScales(results2) {
63255
63887
  let maxTokPerSec = 1;
63256
63888
  const liveTotals = [];
63257
63889
  const consider = (probe) => {
@@ -63264,7 +63896,7 @@ function computeBarScales(results) {
63264
63896
  if (scaledTps > maxTokPerSec)
63265
63897
  maxTokPerSec = scaledTps;
63266
63898
  };
63267
- for (const r of results) {
63899
+ for (const r of results2) {
63268
63900
  consider(r.directProbe);
63269
63901
  for (const c of r.chain ?? [])
63270
63902
  consider(c.probe);
@@ -63564,7 +64196,7 @@ function formatContextWindow(ctx) {
63564
64196
  function buildKeyLine(activeEntry, directKeyVar) {
63565
64197
  if (activeEntry?.provenance) {
63566
64198
  const p = activeEntry.provenance;
63567
- if (p.effectiveValue) {
64199
+ if (p.hasValue) {
63568
64200
  return `${pc.bold}Key${pc.reset} $${p.envVar} ${pc.dim}(${p.effectiveSource})${pc.reset}`;
63569
64201
  }
63570
64202
  return `${pc.bold}Key${pc.reset} $${p.envVar} ${pc.dim}(not set)${pc.reset}`;
@@ -63720,8 +64352,8 @@ function pickRepresentative(result) {
63720
64352
  }
63721
64353
  return { model: result.model, provider: result.nativeProvider };
63722
64354
  }
63723
- function renderLeaderboard(results, scales, maxWidth, w) {
63724
- const reps = results.map(pickRepresentative);
64355
+ function renderLeaderboard(results2, scales, maxWidth, w) {
64356
+ const reps = results2.map(pickRepresentative);
63725
64357
  const live = reps.filter((r) => r.timing).sort((a, b) => a.timing.totalMs - b.timing.totalMs);
63726
64358
  const unavailable = reps.filter((r) => !r.timing);
63727
64359
  if (live.length === 0)
@@ -63822,16 +64454,16 @@ function renderLeaderboard(results, scales, maxWidth, w) {
63822
64454
  w(`
63823
64455
  `);
63824
64456
  }
63825
- function printProbeResults(results, isLiveProbe) {
64457
+ function printProbeResults(results2, isLiveProbe) {
63826
64458
  const w = process.stderr.write.bind(process.stderr);
63827
64459
  w(`
63828
64460
  `);
63829
- const scales = computeBarScales(results);
63830
- const anyTimedLive = results.some((r) => r.directProbe?.state === "live" && r.directProbe.timing !== undefined) || results.some((r) => (r.chain ?? []).some((c) => c.probe?.state === "live" && c.probe.timing !== undefined));
64461
+ const scales = computeBarScales(results2);
64462
+ const anyTimedLive = results2.some((r) => r.directProbe?.state === "live" && r.directProbe.timing !== undefined) || results2.some((r) => (r.chain ?? []).some((c) => c.probe?.state === "live" && c.probe.timing !== undefined));
63831
64463
  if (isLiveProbe && anyTimedLive) {
63832
64464
  renderLegend(w);
63833
64465
  }
63834
- const requiredWidths = results.map((r) => computeRequiredWidth(r, isLiveProbe));
64466
+ const requiredWidths = results2.map((r) => computeRequiredWidth(r, isLiveProbe));
63835
64467
  const termCols = process.stderr.columns ?? process.stdout.columns ?? 100;
63836
64468
  const maxAllowed = Math.max(MIN_CARD_WIDTH, termCols - 4);
63837
64469
  let globalWidth = requiredWidths.reduce((a, b) => Math.max(a, b), MIN_CARD_WIDTH);
@@ -63839,14 +64471,14 @@ function printProbeResults(results, isLiveProbe) {
63839
64471
  globalWidth = maxAllowed;
63840
64472
  const showedLeaderboard = isLiveProbe && anyTimedLive;
63841
64473
  if (showedLeaderboard) {
63842
- renderLeaderboard(results, scales, maxAllowed, w);
64474
+ renderLeaderboard(results2, scales, maxAllowed, w);
63843
64475
  }
63844
64476
  if (showedLeaderboard) {
63845
64477
  w(` ${pc.bold}${pc.cyan}Details${pc.reset}${pc.dim} \u2014 per-model routing chains${pc.reset}
63846
64478
 
63847
64479
  `);
63848
64480
  }
63849
- for (const result of results) {
64481
+ for (const result of results2) {
63850
64482
  renderCard(result, isLiveProbe, w, globalWidth, scales);
63851
64483
  w(`
63852
64484
  `);
@@ -63898,8 +64530,8 @@ class ProbeStore {
63898
64530
  for (const fn of this.listeners)
63899
64531
  fn();
63900
64532
  }
63901
- setResults(results) {
63902
- this.setState((prev) => ({ ...prev, results, phase: "done" }));
64533
+ setResults(results2) {
64534
+ this.setState((prev) => ({ ...prev, results: results2, phase: "done" }));
63903
64535
  }
63904
64536
  setActiveTab(tab) {
63905
64537
  this.setState((prev) => ({ ...prev, activeTab: tab }));
@@ -64740,24 +65372,24 @@ function DetailModel({
64740
65372
  }, undefined, true, undefined, this);
64741
65373
  }
64742
65374
  function DetailsView({
64743
- results,
65375
+ results: results2,
64744
65376
  layout,
64745
65377
  termWidth,
64746
65378
  maxTotalMs,
64747
65379
  maxTokPerSec
64748
65380
  }) {
64749
- const provW = Math.min(22, Math.max(8, ...results.flatMap((r) => r.links.map((l) => l.displayName.length))));
65381
+ const provW = Math.min(22, Math.max(8, ...results2.flatMap((r) => r.links.map((l) => l.displayName.length))));
64750
65382
  const headerW = Math.max(24, Math.min(detailRowWidth(provW, layout), (termWidth || 100) - 3));
64751
65383
  return /* @__PURE__ */ jsxDEV("box", {
64752
65384
  flexDirection: "column",
64753
- children: results.map((r, idx) => /* @__PURE__ */ jsxDEV(DetailModel, {
65385
+ children: results2.map((r, idx) => /* @__PURE__ */ jsxDEV(DetailModel, {
64754
65386
  result: r,
64755
65387
  provW,
64756
65388
  headerW,
64757
65389
  layout,
64758
65390
  maxTotalMs,
64759
65391
  maxTokPerSec,
64760
- isLast: idx === results.length - 1
65392
+ isLast: idx === results2.length - 1
64761
65393
  }, r.model, false, undefined, this))
64762
65394
  }, undefined, false, undefined, this);
64763
65395
  }
@@ -64931,12 +65563,12 @@ function LeaderLiveRow({
64931
65563
  }, undefined, true, undefined, this);
64932
65564
  }
64933
65565
  function LeaderboardView({
64934
- results,
65566
+ results: results2,
64935
65567
  layout,
64936
65568
  maxTotalMs,
64937
65569
  maxTokPerSec
64938
65570
  }) {
64939
- const reps = results.map(pickRepresentativeLink);
65571
+ const reps = results2.map(pickRepresentativeLink);
64940
65572
  const live = reps.filter((r) => r.timing).sort((a, b) => a.timing.totalMs - b.timing.totalMs);
64941
65573
  const unavailable = reps.filter((r) => !r.timing);
64942
65574
  const nameW = Math.min(28, Math.max(5, ...reps.map((r) => r.model.length)));
@@ -65785,16 +66417,16 @@ function formatModelDocCaps(caps) {
65785
66417
  return parts.length > 0 ? parts.join("") : "\xB7";
65786
66418
  }
65787
66419
  async function searchAndPrintModels(query, jsonOutput) {
65788
- let results;
66420
+ let results2;
65789
66421
  try {
65790
66422
  console.error(`\uD83D\uDD04 Searching Firebase catalog for "${query}"...`);
65791
- results = await searchModels(query, 50);
66423
+ results2 = await searchModels(query, 50);
65792
66424
  } catch (error46) {
65793
66425
  console.error(`\u274C Failed to reach Firebase model catalog: ${error46 instanceof Error ? error46.message : String(error46)}`);
65794
66426
  console.error(" Check your network connection.");
65795
66427
  process.exit(1);
65796
66428
  }
65797
- if (results.length === 0) {
66429
+ if (results2.length === 0) {
65798
66430
  if (jsonOutput) {
65799
66431
  console.log(JSON.stringify({ query, count: 0, models: [] }, null, 2));
65800
66432
  } else {
@@ -65805,8 +66437,8 @@ async function searchAndPrintModels(query, jsonOutput) {
65805
66437
  if (jsonOutput) {
65806
66438
  console.log(JSON.stringify({
65807
66439
  query,
65808
- count: results.length,
65809
- models: results.map((m) => ({
66440
+ count: results2.length,
66441
+ models: results2.map((m) => ({
65810
66442
  id: m.modelId,
65811
66443
  provider: m.provider,
65812
66444
  contextWindow: m.contextWindow,
@@ -65819,9 +66451,9 @@ async function searchAndPrintModels(query, jsonOutput) {
65819
66451
  return;
65820
66452
  }
65821
66453
  console.log(`
65822
- Found ${results.length} matching models:
66454
+ Found ${results2.length} matching models:
65823
66455
  `);
65824
- const sorted = [...results].sort(compareByReleaseDateDesc);
66456
+ const sorted = [...results2].sort(compareByReleaseDateDesc);
65825
66457
  renderModelDocTable(sorted, false);
65826
66458
  console.log("");
65827
66459
  console.log("Caps: T = tools R = reasoning V = vision");
@@ -66112,7 +66744,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
66112
66744
  hasCredentials = true;
66113
66745
  } else {
66114
66746
  provenance = resolveApiKeyProvenance(keyInfo.envVar, keyInfo.aliases);
66115
- hasCredentials = !!provenance.effectiveValue;
66747
+ hasCredentials = provenance.hasValue;
66116
66748
  if (!hasCredentials && keyInfo.aliases) {
66117
66749
  hasCredentials = keyInfo.aliases.some((a) => !!process.env[a]);
66118
66750
  }
@@ -66185,7 +66817,14 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
66185
66817
  const modelName = resolvedSpec?.modelName || parsedModel;
66186
66818
  let formatAdapterName = "OpenAIAPIFormat";
66187
66819
  let declaredStreamFormat = "openai-sse";
66188
- const anthropicCompatProviders = ["minimax", "minimax-coding", "kimi", "kimi-coding", "z-ai"];
66820
+ const anthropicCompatProviders = [
66821
+ "minimax",
66822
+ "minimax-coding",
66823
+ "kimi",
66824
+ "kimi-coding",
66825
+ "qwen-cloud",
66826
+ "z-ai"
66827
+ ];
66189
66828
  const isMinimaxModel = modelName.toLowerCase().includes("minimax");
66190
66829
  if (anthropicCompatProviders.includes(providerName)) {
66191
66830
  formatAdapterName = "AnthropicAPIFormat";
@@ -66246,7 +66885,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
66246
66885
  }
66247
66886
  }
66248
66887
  try {
66249
- const results = [];
66888
+ const results2 = [];
66250
66889
  for (const modelInput of models) {
66251
66890
  const { parsed, chain, chainDetails } = buildModelChain(modelInput);
66252
66891
  let directProbeResult;
@@ -66284,7 +66923,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
66284
66923
  }
66285
66924
  }
66286
66925
  const wiring = await computeWiring(chainDetails, parsed.model);
66287
- results.push({
66926
+ results2.push({
66288
66927
  model: modelInput,
66289
66928
  nativeProvider: parsed.provider,
66290
66929
  isExplicit: parsed.isExplicitProvider,
@@ -66295,7 +66934,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
66295
66934
  wiring
66296
66935
  });
66297
66936
  }
66298
- console.log(JSON.stringify(results, null, 2));
66937
+ console.log(JSON.stringify(results2, null, 2));
66299
66938
  } finally {
66300
66939
  if (liveProxy2) {
66301
66940
  try {
@@ -66439,7 +67078,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
66439
67078
  }
66440
67079
  const isLiveProbe = !!liveProxy;
66441
67080
  const printable = [];
66442
- const results = [];
67081
+ const results2 = [];
66443
67082
  for (const { modelInput, parsed, chain, chainDetails } of modelChains) {
66444
67083
  const wiring = await computeWiring(chainDetails, parsed.model);
66445
67084
  const directProbe = directProbeResults.get(modelInput);
@@ -66461,7 +67100,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
66461
67100
  directProbe,
66462
67101
  wiring
66463
67102
  });
66464
- results.push({
67103
+ results2.push({
66465
67104
  model: modelInput,
66466
67105
  nativeProvider: parsed.provider,
66467
67106
  isExplicit: parsed.isExplicitProvider,
@@ -66480,7 +67119,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
66480
67119
  } catch {}
66481
67120
  liveProxy = null;
66482
67121
  }
66483
- tui.store.setResults(results);
67122
+ tui.store.setResults(results2);
66484
67123
  await tui.waitForQuit();
66485
67124
  await tui.shutdown();
66486
67125
  } else {
@@ -67856,8 +68495,8 @@ async function pingLocalProvider(catalogName, timeoutMs = PING_TIMEOUT_MS) {
67856
68495
  }
67857
68496
  }
67858
68497
  async function pingLocalProviders(catalogNames, timeoutMs = PING_TIMEOUT_MS) {
67859
- const results = await Promise.all(catalogNames.map(async (name) => [name, await pingLocalProvider(name, timeoutMs)]));
67860
- return Object.fromEntries(results);
68498
+ const results2 = await Promise.all(catalogNames.map(async (name) => [name, await pingLocalProvider(name, timeoutMs)]));
68499
+ return Object.fromEntries(results2);
67861
68500
  }
67862
68501
  var PING_TIMEOUT_MS = 2000, HEALTH_PATH;
67863
68502
  var init_local_liveness = __esm(() => {
@@ -71764,6 +72403,7 @@ var init_RoutingContent = __esm(() => {
71764
72403
  "minimax-coding": "MiniMax Coding Plan",
71765
72404
  glm: "Native GLM API",
71766
72405
  "glm-coding": "GLM Coding Plan",
72406
+ "qwen-cloud": "Qwen Plan",
71767
72407
  google: "Direct Gemini API",
71768
72408
  openai: "Direct OpenAI API",
71769
72409
  "openai-codex": "OpenAI Codex (Responses API)",
@@ -74334,13 +74974,20 @@ __export(exports_claude_runner, {
74334
74974
  resolveContextWindowEnv: () => resolveContextWindowEnv,
74335
74975
  managedSettingsForcesClaudeAi: () => managedSettingsForcesClaudeAi,
74336
74976
  isProxyAuthMode: () => isProxyAuthMode,
74977
+ initializeTokenFile: () => initializeTokenFile,
74978
+ discoverUserStatusLineCommand: () => discoverUserStatusLineCommand,
74337
74979
  createTempSettingsFile: () => createTempSettingsFile,
74338
74980
  createStatusLineScript: () => createStatusLineScript,
74339
74981
  computeMainThreadContextWindow: () => computeMainThreadContextWindow,
74982
+ cleanupStaleTokenFiles: () => cleanupStaleTokenFiles,
74340
74983
  checkClaudeInstalled: () => checkClaudeInstalled,
74341
74984
  buildClaudishSettingsOverlay: () => buildClaudishSettingsOverlay,
74985
+ buildChainedStatusCommand: () => buildChainedStatusCommand,
74986
+ USER_STATUS_LINE_TIMEOUT_SECONDS: () => USER_STATUS_LINE_TIMEOUT_SECONDS,
74987
+ STALE_TOKEN_FILE_MS: () => STALE_TOKEN_FILE_MS,
74342
74988
  MIN_AUTO_COMPACT_WINDOW: () => MIN_AUTO_COMPACT_WINDOW,
74343
- CLAUDE_CODE_DEFAULT_MAX_CONTEXT: () => CLAUDE_CODE_DEFAULT_MAX_CONTEXT
74989
+ CLAUDE_CODE_DEFAULT_MAX_CONTEXT: () => CLAUDE_CODE_DEFAULT_MAX_CONTEXT,
74990
+ CATALOG_WINDOW_TIMEOUT_MS: () => CATALOG_WINDOW_TIMEOUT_MS
74344
74991
  });
74345
74992
  import { spawn as spawn4 } from "child_process";
74346
74993
  import {
@@ -74349,11 +74996,13 @@ import {
74349
74996
  mkdirSync as mkdirSync16,
74350
74997
  openSync as openSync5,
74351
74998
  readFileSync as readFileSync24,
74999
+ readdirSync as readdirSync6,
75000
+ statSync as statSync5,
74352
75001
  unlinkSync as unlinkSync9,
74353
75002
  writeFileSync as writeFileSync18
74354
75003
  } from "fs";
74355
75004
  import { homedir as homedir29, tmpdir as tmpdir2 } from "os";
74356
- import { join as join31 } from "path";
75005
+ import { dirname as dirname11, join as join31 } from "path";
74357
75006
  import { isatty } from "tty";
74358
75007
  function releaseTerminalIsolation() {
74359
75008
  if (!restoreTerminal)
@@ -74395,9 +75044,9 @@ function managedSettingsPath() {
74395
75044
  }
74396
75045
  return "/etc/claude-code/managed-settings.json";
74397
75046
  }
74398
- function managedSettingsForcesClaudeAi(readFile = readFileSync24) {
75047
+ function managedSettingsForcesClaudeAi(readFile2 = readFileSync24) {
74399
75048
  try {
74400
- const raw2 = readFile(managedSettingsPath(), "utf-8");
75049
+ const raw2 = readFile2(managedSettingsPath(), "utf-8");
74401
75050
  const parsed = JSON.parse(raw2);
74402
75051
  return parsed.forceLoginMethod === "claudeai";
74403
75052
  } catch {
@@ -74532,7 +75181,111 @@ process.stdin.on('end', () => {
74532
75181
  writeFileSync18(scriptPath, script, "utf-8");
74533
75182
  return scriptPath;
74534
75183
  }
74535
- function createTempSettingsFile(_modelDisplay, port, proxyAuthMode) {
75184
+ function initializeTokenFile(tokenFilePath) {
75185
+ try {
75186
+ mkdirSync16(dirname11(tokenFilePath), { recursive: true });
75187
+ writeFileSync18(tokenFilePath, JSON.stringify({
75188
+ input_tokens: 0,
75189
+ output_tokens: 0,
75190
+ total_tokens: 0,
75191
+ total_cost: 0,
75192
+ context_window: "unknown",
75193
+ context_left_percent: -1,
75194
+ updated_at: Date.now(),
75195
+ is_free: false,
75196
+ is_estimated: false
75197
+ }), "utf-8");
75198
+ } catch (e) {
75199
+ log(`[claude-runner] Could not initialize token file ${tokenFilePath}: ${e}`);
75200
+ }
75201
+ }
75202
+ function cleanupStaleTokenFiles(dir, now = Date.now(), maxAgeMs = STALE_TOKEN_FILE_MS) {
75203
+ let removed = 0;
75204
+ let entries;
75205
+ try {
75206
+ entries = readdirSync6(dir);
75207
+ } catch {
75208
+ return 0;
75209
+ }
75210
+ const cutoff = now - maxAgeMs;
75211
+ let scanned = 0;
75212
+ for (const name of entries) {
75213
+ if (scanned >= MAX_TOKEN_FILES_SCANNED)
75214
+ break;
75215
+ if (!name.startsWith("tokens-") || !name.endsWith(".json"))
75216
+ continue;
75217
+ scanned++;
75218
+ const full = join31(dir, name);
75219
+ try {
75220
+ if (statSync5(full).mtimeMs >= cutoff)
75221
+ continue;
75222
+ unlinkSync9(full);
75223
+ removed++;
75224
+ } catch {}
75225
+ }
75226
+ if (removed > 0) {
75227
+ log(`[claude-runner] Removed ${removed} stale token file(s) from ${dir}`);
75228
+ }
75229
+ return removed;
75230
+ }
75231
+ function parseSettingsArg(value) {
75232
+ if (value.trimStart().startsWith("{")) {
75233
+ return JSON.parse(value);
75234
+ }
75235
+ return JSON.parse(readFileSync24(value, "utf-8"));
75236
+ }
75237
+ function parseSettingsArgSafe(value) {
75238
+ try {
75239
+ const parsed = parseSettingsArg(value);
75240
+ return parsed && typeof parsed === "object" ? parsed : null;
75241
+ } catch {
75242
+ return null;
75243
+ }
75244
+ }
75245
+ function userSettingsFileCandidates(cwd) {
75246
+ return [
75247
+ join31(homedir29(), ".claude", "settings.json"),
75248
+ join31(cwd, ".claude", "settings.json"),
75249
+ join31(cwd, ".claude", "settings.local.json")
75250
+ ];
75251
+ }
75252
+ function discoverUserStatusLineCommand(claudeArgs = [], cwd = process.cwd()) {
75253
+ const sources = userSettingsFileCandidates(cwd).filter((file2) => existsSync25(file2));
75254
+ const idx = claudeArgs.indexOf("--settings");
75255
+ const settingsArg = idx === -1 ? undefined : claudeArgs[idx + 1];
75256
+ if (settingsArg)
75257
+ sources.push(settingsArg);
75258
+ let effective;
75259
+ for (const source of sources) {
75260
+ const layer = parseSettingsArgSafe(source);
75261
+ if (layer && "statusLine" in layer)
75262
+ effective = layer.statusLine;
75263
+ }
75264
+ return chainableCommandOf(effective);
75265
+ }
75266
+ function chainableCommandOf(statusLine) {
75267
+ if (!statusLine || typeof statusLine !== "object")
75268
+ return null;
75269
+ const { type, command } = statusLine;
75270
+ if (type !== "command" || typeof command !== "string")
75271
+ return null;
75272
+ const trimmed = command.trim();
75273
+ if (!trimmed)
75274
+ return null;
75275
+ if (trimmed.includes("CLAUDISH_ACTIVE_MODEL_NAME") || trimmed.includes("CLAUDISH_IS_LOCAL")) {
75276
+ return null;
75277
+ }
75278
+ return trimmed;
75279
+ }
75280
+ function buildChainedStatusCommand(userCommand, claudishBody, claudishSegment) {
75281
+ const quotedUser = `'${userCommand.replace(/'/g, `'\\''`)}'`;
75282
+ const ESC2 = "\x1B";
75283
+ const separator = `SEP=' ${ESC2}[2m\u2022${ESC2}[0m '`;
75284
+ const runUser = `if command -v timeout >/dev/null 2>&1; then _CT="timeout ${USER_STATUS_LINE_TIMEOUT_SECONDS}"; elif command -v gtimeout >/dev/null 2>&1; then _CT="gtimeout ${USER_STATUS_LINE_TIMEOUT_SECONDS}"; else _CT=""; fi; USER_OUT=$(printf '%s' "$JSON" | $_CT bash -c ${quotedUser} 2>/dev/null)`;
75285
+ const emit2 = `if [ -n "$USER_OUT" ]; then LAST="\${USER_OUT##*$'\\n'}"; if [ "$LAST" = "$USER_OUT" ]; then printf '%s%s%s\\n' "$USER_OUT" "$SEP" "$SEG"; else printf '%s\\n%s%s%s\\n' "\${USER_OUT%$'\\n'*}" "$LAST" "$SEP" "$SEG"; fi; else printf '%s\\n' "$SEG"; fi`;
75286
+ return `JSON=$(cat); ${runUser}; ${claudishBody}; SEG=$(${claudishSegment}); ${separator}; ${emit2}`;
75287
+ }
75288
+ function createTempSettingsFile(_modelDisplay, port, proxyAuthMode, userStatusLineCommand) {
74536
75289
  const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
74537
75290
  const claudishDir = join31(homeDir, ".claudish");
74538
75291
  try {
@@ -74541,6 +75294,8 @@ function createTempSettingsFile(_modelDisplay, port, proxyAuthMode) {
74541
75294
  const timestamp = Date.now();
74542
75295
  const tempPath = join31(claudishDir, `settings-${timestamp}.json`);
74543
75296
  const tokenFilePath = join31(claudishDir, `tokens-${port}.json`);
75297
+ cleanupStaleTokenFiles(claudishDir);
75298
+ initializeTokenFile(tokenFilePath);
74544
75299
  let statusCommand;
74545
75300
  if (isWindows2()) {
74546
75301
  const scriptPath = createStatusLineScript(tokenFilePath);
@@ -74555,7 +75310,13 @@ function createTempSettingsFile(_modelDisplay, port, proxyAuthMode) {
74555
75310
  const BOLD4 = "\\033[1m";
74556
75311
  const formatTokensBash = `fmt_tok() { local n=\${1:-0}; if [ "$n" -ge 1000000 ]; then echo "$((n/1000000))M"; elif [ "$n" -ge 1000 ]; then echo "$((n/1000))k"; else echo "$n"; fi; }`;
74557
75312
  const effWinBash = `eff_win() { local w=\${1:-0}; local m=\${CLAUDE_CODE_MAX_CONTEXT_TOKENS:-}; local a=\${CLAUDE_CODE_AUTO_COMPACT_WINDOW:-}; case "$m" in ''|*[!0-9]*) m=${CLAUDE_CODE_DEFAULT_MAX_CONTEXT};; esac; case "$a" in ''|*[!0-9]*) a=0;; esac; case "$w" in ''|*[!0-9]*) w=0;; esac; if [ "$w" -gt 0 ]; then if [ "$m" -gt 0 ] && [ "$m" -lt "$w" ]; then w=$m; fi; if [ "$a" -gt 0 ] && [ "$a" -lt "$w" ]; then w=$a; fi; fi; echo "$w"; }`;
74558
- statusCommand = `JSON=$(cat) && DIR=$(basename "$(pwd)") && [ \${#DIR} -gt 15 ] && DIR="\${DIR:0:12}..." || true && CTX=-1 && COST="0" && IS_FREE="false" && IS_EST="false" && PROVIDER="" && TOKEN_MODEL="" && IN_TOK=0 && CTX_WIN=0 && ${formatTokensBash} && ${effWinBash} && if [ -f "${tokenFilePath}" ]; then TOKENS=$(cat "${tokenFilePath}" 2>/dev/null | tr -d ' \\n') && REAL_CTX=$(echo "$TOKENS" | grep -o '"context_left_percent":-\\?[0-9]*' | grep -o '\\-\\?[0-9]*') && if [ ! -z "$REAL_CTX" ]; then CTX="$REAL_CTX"; fi && REAL_COST=$(echo "$TOKENS" | grep -o '"total_cost":[0-9.]*' | cut -d: -f2) && if [ ! -z "$REAL_COST" ]; then COST="$REAL_COST"; fi && IN_TOK=$(echo "$TOKENS" | grep -o '"input_tokens":[0-9]*' | grep -o '[0-9]*') && CTX_WIN=$(echo "$TOKENS" | grep -o '"context_window":[0-9]*' | grep -o '[0-9]*') && IS_FREE=$(echo "$TOKENS" | grep -o '"is_free":[a-z]*' | cut -d: -f2) && IS_EST=$(echo "$TOKENS" | grep -o '"is_estimated":[a-z]*' | cut -d: -f2) && PROVIDER=$(echo "$TOKENS" | grep -o '"provider_name":"[^"]*"' | cut -d'"' -f4) && TOKEN_MODEL=$(echo "$TOKENS" | grep -o '"model_name":"[^"]*"' | cut -d'"' -f4); fi && if [ "$CLAUDISH_IS_LOCAL" = "true" ]; then COST_DISPLAY="LOCAL"; elif [ "$IS_FREE" = "true" ]; then COST_DISPLAY="FREE"; elif [ "$IS_EST" = "true" ]; then COST_DISPLAY=$(printf "~\\$%.3f" "$COST"); else COST_DISPLAY=$(printf "\\$%.3f" "$COST"); fi && MODEL_DISPLAY="\${TOKEN_MODEL:-$CLAUDISH_ACTIVE_MODEL_NAME}" && if [ ! -z "$PROVIDER" ]; then MODEL_DISPLAY="$PROVIDER $MODEL_DISPLAY"; fi && EFF_WIN=$(eff_win $CTX_WIN) && if [ "$EFF_WIN" -gt 0 ] 2>/dev/null && [ "$IN_TOK" -gt 0 ] 2>/dev/null; then CTX=$(( ((EFF_WIN - IN_TOK) * 200 / EFF_WIN + 1) / 2 )); if [ "$CTX" -lt 0 ]; then CTX=0; fi; fi && if [ "$CTX" -lt 0 ] 2>/dev/null || [ "$EFF_WIN" -le 0 ] 2>/dev/null; then if [ "$IN_TOK" -gt 0 ] 2>/dev/null; then CTX_DISPLAY="$(fmt_tok $IN_TOK) tokens"; else CTX_DISPLAY="N/A"; fi; elif [ "$IN_TOK" -gt 0 ] 2>/dev/null; then if [ "$EFF_WIN" -lt "$CTX_WIN" ] 2>/dev/null; then CTX_DISPLAY="$CTX% ($(fmt_tok $IN_TOK)/$(fmt_tok $EFF_WIN) of $(fmt_tok $CTX_WIN))"; else CTX_DISPLAY="$CTX% ($(fmt_tok $IN_TOK)/$(fmt_tok $EFF_WIN))"; fi; else CTX_DISPLAY="$CTX%"; fi && printf "${CYAN4}${BOLD4}%s${RESET4} ${DIM4}\u2022${RESET4} ${YELLOW3}%s${RESET4} ${DIM4}\u2022${RESET4} ${GREEN4}%s${RESET4} ${DIM4}\u2022${RESET4} ${MAGENTA3}%s${RESET4}\\n" "$DIR" "$MODEL_DISPLAY" "$COST_DISPLAY" "$CTX_DISPLAY"`;
75313
+ const dirPrelude = `DIR=$(basename "$(pwd)"); [ \${#DIR} -gt 15 ] && DIR="\${DIR:0:12}..." || true; `;
75314
+ const readState = `CTX=-1; COST="0"; IS_FREE="false"; IS_EST="false"; PROVIDER=""; TOKEN_MODEL=""; IN_TOK=0; CTX_WIN=0; ${formatTokensBash}; ${effWinBash}; if [ -f "${tokenFilePath}" ]; then TOKENS=$(cat "${tokenFilePath}" 2>/dev/null | tr -d '\\n\\r'); V=$(echo "$TOKENS" | grep -o '"context_left_percent": *-\\?[0-9]*' | grep -o '\\-\\?[0-9]*'); [ -n "$V" ] && CTX="$V"; V=$(echo "$TOKENS" | grep -o '"total_cost": *[0-9.]*' | cut -d: -f2 | tr -d ' '); [ -n "$V" ] && COST="$V"; V=$(echo "$TOKENS" | grep -o '"input_tokens": *[0-9]*' | grep -o '[0-9]*'); [ -n "$V" ] && IN_TOK="$V"; V=$(echo "$TOKENS" | grep -o '"context_window": *[0-9]*' | grep -o '[0-9]*'); [ -n "$V" ] && CTX_WIN="$V"; V=$(echo "$TOKENS" | grep -o '"is_free": *[a-z]*' | cut -d: -f2 | tr -d ' '); [ -n "$V" ] && IS_FREE="$V"; V=$(echo "$TOKENS" | grep -o '"is_estimated": *[a-z]*' | cut -d: -f2 | tr -d ' '); [ -n "$V" ] && IS_EST="$V"; V=$(echo "$TOKENS" | grep -o '"provider_name": *"[^"]*"' | cut -d'"' -f4); [ -n "$V" ] && PROVIDER="$V"; V=$(echo "$TOKENS" | grep -o '"model_name": *"[^"]*"' | cut -d'"' -f4); [ -n "$V" ] && TOKEN_MODEL="$V"; fi; if [ "$CLAUDISH_IS_LOCAL" = "true" ]; then COST_DISPLAY="LOCAL"; elif [ "$IS_FREE" = "true" ]; then COST_DISPLAY="FREE"; elif [ "$IS_EST" = "true" ]; then COST_DISPLAY=$(printf "~\\$%.3f" "$COST"); else COST_DISPLAY=$(printf "\\$%.3f" "$COST"); fi; MODEL_DISPLAY="\${TOKEN_MODEL:-$CLAUDISH_ACTIVE_MODEL_NAME}"; if [ -n "$PROVIDER" ]; then MODEL_DISPLAY="$PROVIDER $MODEL_DISPLAY"; fi; EFF_WIN=$(eff_win $CTX_WIN); if [ "$EFF_WIN" -gt 0 ] 2>/dev/null && [ "$IN_TOK" -gt 0 ] 2>/dev/null; then CTX=$(( ((EFF_WIN - IN_TOK) * 200 / EFF_WIN + 1) / 2 )); if [ "$CTX" -lt 0 ]; then CTX=0; fi; fi; if [ "$CTX" -lt 0 ] 2>/dev/null || [ "$EFF_WIN" -le 0 ] 2>/dev/null; then if [ "$IN_TOK" -gt 0 ] 2>/dev/null; then CTX_DISPLAY="$(fmt_tok $IN_TOK) tokens"; else CTX_DISPLAY="N/A"; fi; elif [ "$IN_TOK" -gt 0 ] 2>/dev/null; then if [ "$EFF_WIN" -lt "$CTX_WIN" ] 2>/dev/null; then CTX_DISPLAY="$CTX% ($(fmt_tok $IN_TOK)/$(fmt_tok $EFF_WIN) of $(fmt_tok $CTX_WIN))"; else CTX_DISPLAY="$CTX% ($(fmt_tok $IN_TOK)/$(fmt_tok $EFF_WIN))"; fi; else CTX_DISPLAY="$CTX%"; fi`;
75315
+ const segmentWithDir = `printf "${CYAN4}${BOLD4}%s${RESET4} ${DIM4}\u2022${RESET4} ${YELLOW3}%s${RESET4} ${DIM4}\u2022${RESET4} ${GREEN4}%s${RESET4} ${DIM4}\u2022${RESET4} ${MAGENTA3}%s${RESET4}\\n" "$DIR" "$MODEL_DISPLAY" "$COST_DISPLAY" "$CTX_DISPLAY"`;
75316
+ const segmentNoDirWithProvider = `printf "${YELLOW3}%s${RESET4} ${DIM4}\u2022${RESET4} ${GREEN4}%s${RESET4} ${DIM4}\u2022${RESET4} ${MAGENTA3}%s${RESET4}\\n" "$PROVIDER" "$COST_DISPLAY" "$CTX_DISPLAY"`;
75317
+ const segmentNoDirNoProvider = `printf "${GREEN4}%s${RESET4} ${DIM4}\u2022${RESET4} ${MAGENTA3}%s${RESET4}\\n" "$COST_DISPLAY" "$CTX_DISPLAY"`;
75318
+ const segmentNoDir = `if [ -n "$PROVIDER" ]; then ${segmentNoDirWithProvider}; else ${segmentNoDirNoProvider}; fi`;
75319
+ statusCommand = userStatusLineCommand ? buildChainedStatusCommand(userStatusLineCommand, readState, segmentNoDir) : `JSON=$(cat); ${dirPrelude}${readState}; ${segmentWithDir}`;
74559
75320
  }
74560
75321
  const statusLine = {
74561
75322
  type: "command",
@@ -74564,7 +75325,7 @@ function createTempSettingsFile(_modelDisplay, port, proxyAuthMode) {
74564
75325
  };
74565
75326
  const settings = buildClaudishSettingsOverlay(statusLine, proxyAuthMode);
74566
75327
  writeFileSync18(tempPath, JSON.stringify(settings, null, 2), "utf-8");
74567
- return { path: tempPath, statusLine };
75328
+ return { path: tempPath, statusLine, tokenFilePath };
74568
75329
  }
74569
75330
  function buildClaudishSettingsOverlay(statusLine, proxyAuthMode) {
74570
75331
  const settings = { statusLine, disableClaudeAiConnectors: true };
@@ -74580,13 +75341,7 @@ function mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine, proxy
74580
75341
  }
74581
75342
  const userSettingsValue = config3.claudeArgs[idx + 1];
74582
75343
  try {
74583
- let userSettings;
74584
- if (userSettingsValue.trimStart().startsWith("{")) {
74585
- userSettings = JSON.parse(userSettingsValue);
74586
- } else {
74587
- const rawUserSettings = readFileSync24(userSettingsValue, "utf-8");
74588
- userSettings = JSON.parse(rawUserSettings);
74589
- }
75344
+ const userSettings = parseSettingsArg(userSettingsValue);
74590
75345
  userSettings.statusLine = statusLine;
74591
75346
  if (!("disableClaudeAiConnectors" in userSettings)) {
74592
75347
  userSettings.disableClaudeAiConnectors = true;
@@ -74602,27 +75357,64 @@ function mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine, proxy
74602
75357
  }
74603
75358
  config3.claudeArgs.splice(idx, 2);
74604
75359
  }
75360
+ function withDeadline(promise3, ms) {
75361
+ return new Promise((resolve4) => {
75362
+ const timer = setTimeout(() => resolve4(null), ms);
75363
+ timer.unref?.();
75364
+ promise3.then((value) => {
75365
+ clearTimeout(timer);
75366
+ resolve4(value);
75367
+ }).catch(() => {
75368
+ clearTimeout(timer);
75369
+ resolve4(null);
75370
+ });
75371
+ });
75372
+ }
75373
+ async function resolveLocalContextWindow(spec, cachePath) {
75374
+ const parsed = parseModelSpec(spec);
75375
+ let provider = parsed.provider;
75376
+ if (!parsed.isExplicitProvider) {
75377
+ const plan = await route(spec);
75378
+ if (plan.kind !== "ok")
75379
+ return null;
75380
+ provider = plan.primary.provider;
75381
+ }
75382
+ const win = await discoverContextWindow(provider, parsed.model) ?? lookupModelForProvider(parsed.model, provider, cachePath);
75383
+ return {
75384
+ modelId: parsed.model,
75385
+ window: typeof win === "number" && win > 0 ? win : null
75386
+ };
75387
+ }
75388
+ async function catalogWindowMin(modelIds) {
75389
+ const misses = [...new Set(modelIds)];
75390
+ if (misses.length === 0)
75391
+ return Number.POSITIVE_INFINITY;
75392
+ const windows = await withDeadline(Promise.all(misses.map((id) => resolveCatalogContextWindow(id).catch(() => null))), CATALOG_WINDOW_TIMEOUT_MS);
75393
+ let min = Number.POSITIVE_INFINITY;
75394
+ for (const win of windows ?? []) {
75395
+ if (typeof win === "number" && win > 0)
75396
+ min = Math.min(min, win);
75397
+ }
75398
+ return min;
75399
+ }
74605
75400
  async function computeMainThreadContextWindow(config3, cachePath) {
74606
75401
  const specs = [config3.model, config3.modelOpus, config3.modelSonnet].filter((s) => typeof s === "string" && s.length > 0);
74607
75402
  if (specs.length === 0)
74608
75403
  return 0;
74609
75404
  let min = Number.POSITIVE_INFINITY;
75405
+ const unresolved = [];
74610
75406
  for (const spec of specs) {
74611
75407
  try {
74612
- const parsed = parseModelSpec(spec);
74613
- let provider = parsed.provider;
74614
- if (!parsed.isExplicitProvider) {
74615
- const plan = await route(spec);
74616
- if (plan.kind !== "ok")
74617
- continue;
74618
- provider = plan.primary.provider;
74619
- }
74620
- const win = await discoverContextWindow(provider, parsed.model) ?? lookupModelForProvider(parsed.model, provider, cachePath);
74621
- if (typeof win === "number" && win > 0) {
74622
- min = Math.min(min, win);
74623
- }
75408
+ const resolved = await resolveLocalContextWindow(spec, cachePath);
75409
+ if (!resolved)
75410
+ continue;
75411
+ if (resolved.window !== null)
75412
+ min = Math.min(min, resolved.window);
75413
+ else
75414
+ unresolved.push(resolved.modelId);
74624
75415
  } catch {}
74625
75416
  }
75417
+ min = Math.min(min, await catalogWindowMin(unresolved));
74626
75418
  return Number.isFinite(min) ? min : 0;
74627
75419
  }
74628
75420
  function resolveContextWindowEnv(realWindow, processEnv = process.env) {
@@ -74659,7 +75451,12 @@ async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
74659
75451
  onCleanup?.();
74660
75452
  return 1;
74661
75453
  }
74662
- const { path: tempSettingsPath, statusLine } = createTempSettingsFile(modelId ?? "default", port, proxyAuthMode);
75454
+ const userStatusLineCommand = discoverUserStatusLineCommand(config3.claudeArgs);
75455
+ const {
75456
+ path: tempSettingsPath,
75457
+ statusLine,
75458
+ tokenFilePath
75459
+ } = createTempSettingsFile(modelId ?? "default", port, proxyAuthMode, userStatusLineCommand);
74663
75460
  mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine, proxyAuthMode);
74664
75461
  const claudeArgs = [];
74665
75462
  claudeArgs.push("--settings", tempSettingsPath);
@@ -74692,8 +75489,16 @@ async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
74692
75489
  ...process.env,
74693
75490
  ANTHROPIC_BASE_URL: proxyUrl,
74694
75491
  [ENV.CLAUDISH_ACTIVE_MODEL_NAME]: modelDisplayName,
74695
- CLAUDISH_IS_LOCAL: isLocalModel2 ? "true" : "false"
75492
+ CLAUDISH_IS_LOCAL: isLocalModel2 ? "true" : "false",
75493
+ [ENV.CLAUDISH_TOKEN_FILE]: tokenFilePath
74696
75494
  };
75495
+ if (modelId) {
75496
+ const parsedSpec = parseModelSpec(modelId);
75497
+ const providerDisplayName = parsedSpec.isExplicitProvider ? getProviderByName(parsedSpec.provider)?.displayName : undefined;
75498
+ if (providerDisplayName) {
75499
+ env[ENV.CLAUDISH_PROVIDER_NAME] = providerDisplayName;
75500
+ }
75501
+ }
74697
75502
  let hidAnthropicApiKey = false;
74698
75503
  delete env.CLAUDECODE;
74699
75504
  if (config3.monitor) {
@@ -74904,17 +75709,20 @@ async function checkClaudeInstalled() {
74904
75709
  const binary = await findClaudeBinary();
74905
75710
  return binary !== null;
74906
75711
  }
74907
- var restoreTerminal = null, CLAUDE_CODE_DEFAULT_MAX_CONTEXT = 200000, MIN_AUTO_COMPACT_WINDOW = 200000;
75712
+ var restoreTerminal = null, STALE_TOKEN_FILE_MS, MAX_TOKEN_FILES_SCANNED = 4000, CLAUDE_CODE_DEFAULT_MAX_CONTEXT = 200000, USER_STATUS_LINE_TIMEOUT_SECONDS = 3, MIN_AUTO_COMPACT_WINDOW = 200000, CATALOG_WINDOW_TIMEOUT_MS = 1500;
74908
75713
  var init_claude_runner = __esm(() => {
74909
75714
  init_model_catalog();
74910
75715
  init_config2();
75716
+ init_context_window_fallback();
74911
75717
  init_logger();
74912
75718
  init_profile_config();
74913
75719
  init_model_discovery();
74914
75720
  init_model_parser();
75721
+ init_provider_definitions();
74915
75722
  init_routing_rules();
74916
75723
  init_telemetry();
74917
75724
  init_terminal_isolation();
75725
+ STALE_TOKEN_FILE_MS = 7 * 24 * 60 * 60 * 1000;
74918
75726
  });
74919
75727
 
74920
75728
  // src/diag-output.ts
@@ -75148,7 +75956,7 @@ import { spawn as spawn5 } from "child_process";
75148
75956
  import { execSync as execSync2 } from "child_process";
75149
75957
  import { existsSync as existsSync26, readFileSync as readFileSync25, writeFileSync as writeFileSync20 } from "fs";
75150
75958
  import { connect as netConnect } from "net";
75151
- import { dirname as dirname11, join as join33 } from "path";
75959
+ import { dirname as dirname12, join as join33 } from "path";
75152
75960
  import { setTimeout as wait } from "timers/promises";
75153
75961
  import { fileURLToPath as fileURLToPath3 } from "url";
75154
75962
  function resolveRouteInfo(modelId) {
@@ -75241,7 +76049,7 @@ function buildPaneHeader(model, prompt, bg) {
75241
76049
  }
75242
76050
  function findMagmuxBinary() {
75243
76051
  const thisFile = fileURLToPath3(import.meta.url);
75244
- const thisDir = dirname11(thisFile);
76052
+ const thisDir = dirname12(thisFile);
75245
76053
  const pkgRoot = join33(thisDir, "..");
75246
76054
  const platform3 = process.platform;
75247
76055
  const arch = process.arch;
@@ -75255,7 +76063,7 @@ function findMagmuxBinary() {
75255
76063
  const candidate = join33(searchDir, "node_modules", pkgName, "bin", "magmux");
75256
76064
  if (existsSync26(candidate))
75257
76065
  return candidate;
75258
- const parent = dirname11(searchDir);
76066
+ const parent = dirname12(searchDir);
75259
76067
  if (parent === searchDir)
75260
76068
  break;
75261
76069
  searchDir = parent;
@@ -75316,12 +76124,12 @@ async function subscribeToMagmux(sockPath, onEvent) {
75316
76124
  client.once("error", done);
75317
76125
  });
75318
76126
  }
75319
- function buildTeamStatus(manifest, startedAt, results) {
76127
+ function buildTeamStatus(manifest, startedAt, results2) {
75320
76128
  const anonIds = Object.keys(manifest.models);
75321
76129
  const models = {};
75322
76130
  for (let i = 0;i < anonIds.length; i++) {
75323
76131
  const anonId = anonIds[i];
75324
- const result = results?.find((r) => r.pane === i);
76132
+ const result = results2?.find((r) => r.pane === i);
75325
76133
  if (!result) {
75326
76134
  models[anonId] = {
75327
76135
  state: "TIMEOUT",
@@ -75390,8 +76198,8 @@ async function runWithGrid(sessionPath, models, input, opts) {
75390
76198
  proc.on("exit", () => resolve4());
75391
76199
  proc.on("error", () => resolve4());
75392
76200
  });
75393
- const [{ results }] = await Promise.all([subscription, procExit]);
75394
- const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
76201
+ const [{ results: results2 }] = await Promise.all([subscription, procExit]);
76202
+ const status = buildTeamStatus(manifest, startedAt, results2?.panes ?? null);
75395
76203
  const statusPath = join33(sessionPath, "status.json");
75396
76204
  writeFileSync20(statusPath, JSON.stringify(status, null, 2), "utf-8");
75397
76205
  return status;