@kenkaiiii/gg-core 5.90.0 → 5.91.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -40,14 +40,18 @@ __export(index_exports, {
40
40
  HIDDEN_PROVIDERS: () => HIDDEN_PROVIDERS,
41
41
  LOCAL_API_KEY_PLACEHOLDER: () => LOCAL_API_KEY_PLACEHOLDER,
42
42
  LOCAL_AUTH_KEY_PREFIX: () => LOCAL_AUTH_KEY_PREFIX,
43
+ MINIMAX_CN_BASE_URL: () => MINIMAX_CN_BASE_URL,
44
+ MINIMAX_GLOBAL_BASE_URL: () => MINIMAX_GLOBAL_BASE_URL,
43
45
  MODELS: () => MODELS,
44
46
  MOONSHOT_OAUTH_KEY: () => MOONSHOT_OAUTH_KEY,
45
47
  NotLoggedInError: () => NotLoggedInError,
48
+ OPENROUTER_DEFAULT_BASE_URL: () => OPENROUTER_DEFAULT_BASE_URL,
46
49
  PLAN_MODE_THINKING_CAP: () => PLAN_MODE_THINKING_CAP,
47
50
  SubscriptionUsageError: () => SubscriptionUsageError,
48
51
  TelegramBot: () => TelegramBot,
49
52
  XAI_OAUTH_KEY: () => XAI_OAUTH_KEY,
50
53
  XIAOMI_CREDITS_KEY: () => XIAOMI_CREDITS_KEY,
54
+ XIAOMI_TOKEN_PLAN_BASE_URLS: () => XIAOMI_TOKEN_PLAN_BASE_URLS,
51
55
  authPriorityNote: () => authPriorityNote,
52
56
  clampThinkingForPlanMode: () => clampThinkingForPlanMode,
53
57
  clearLocalDiscoveryCache: () => clearLocalDiscoveryCache,
@@ -56,6 +60,8 @@ __export(index_exports, {
56
60
  createAutoUpdater: () => createAutoUpdater,
57
61
  decodeOggOpus: () => decodeOggOpus,
58
62
  describeAuthMethods: () => describeAuthMethods,
63
+ detectMiniMaxBaseUrl: () => detectMiniMaxBaseUrl,
64
+ detectXiaomiTokenPlanBaseUrl: () => detectXiaomiTokenPlanBaseUrl,
59
65
  discoverLocalModels: () => discoverLocalModels,
60
66
  downmixToMono: () => downmixToMono,
61
67
  dualAuthProvider: () => dualAuthProvider,
@@ -81,6 +87,7 @@ __export(index_exports, {
81
87
  getModel: () => getModel,
82
88
  getModelsForProvider: () => getModelsForProvider,
83
89
  getNextThinkingLevel: () => getNextThinkingLevel,
90
+ getProviderCatalog: () => getProviderCatalog,
84
91
  getSessionId: () => getSessionId,
85
92
  getSummaryModel: () => getSummaryModel,
86
93
  getSupportedThinkingLevels: () => getSupportedThinkingLevels,
@@ -110,6 +117,7 @@ __export(index_exports, {
110
117
  ollamaServedContext: () => ollamaServedContext,
111
118
  openLog: () => openLog,
112
119
  parseLocalModelId: () => parseLocalModelId,
120
+ parseOpenRouterCatalog: () => parseOpenRouterCatalog,
113
121
  pendingClaudeCodeVersionRefresh: () => import_gg_ai.pendingClaudeCodeVersionRefresh,
114
122
  probeEndpoint: () => probeEndpoint,
115
123
  providerStorageKeys: () => providerStorageKeys,
@@ -123,6 +131,7 @@ __export(index_exports, {
123
131
  registerRuntimeModels: () => registerRuntimeModels,
124
132
  resample: () => resample,
125
133
  setProgressCallback: () => setProgressCallback,
134
+ setProviderCatalog: () => setProviderCatalog,
126
135
  toModelInfo: () => toModelInfo,
127
136
  transcribeVoice: () => transcribeVoice,
128
137
  usesOpenAICodexTransport: () => usesOpenAICodexTransport,
@@ -1329,9 +1338,13 @@ async function acquireLock(lockPath) {
1329
1338
  continue;
1330
1339
  }
1331
1340
  } catch {
1332
- await import_promises.default.unlink(lockPath).catch(() => {
1333
- });
1334
- continue;
1341
+ const stat = await import_promises.default.stat(lockPath).catch(() => null);
1342
+ if (!stat) continue;
1343
+ if (Date.now() - stat.mtimeMs > STALE_TIMEOUT_MS) {
1344
+ await import_promises.default.unlink(lockPath).catch(() => {
1345
+ });
1346
+ continue;
1347
+ }
1335
1348
  }
1336
1349
  if (Date.now() - startTime > MAX_WAIT_MS) {
1337
1350
  await import_promises.default.unlink(lockPath).catch(() => {
@@ -1528,6 +1541,90 @@ function isHiddenProvider(provider) {
1528
1541
 
1529
1542
  // src/auth-storage.ts
1530
1543
  var import_gg_ai3 = require("@kenkaiiii/gg-ai");
1544
+
1545
+ // src/minimax-region.ts
1546
+ var MINIMAX_GLOBAL_BASE_URL = "https://api.minimax.io/anthropic";
1547
+ var MINIMAX_CN_BASE_URL = "https://api.minimax.cn/anthropic";
1548
+ var REGION_HOSTS = [
1549
+ { host: "https://api.minimax.io", baseUrl: MINIMAX_GLOBAL_BASE_URL },
1550
+ { host: "https://api.minimax.cn", baseUrl: MINIMAX_CN_BASE_URL }
1551
+ ];
1552
+ var KEY_KNOWN_CODES = /* @__PURE__ */ new Set([0, 2062]);
1553
+ var KEY_REJECTED_CODES = /* @__PURE__ */ new Set([1004, 2049]);
1554
+ async function probeRegion(host, apiKey, fetchImpl, timeoutMs) {
1555
+ const url = apiKey.startsWith("sk-api-") ? `${host}/account/query_balance` : `${host}/v1/token_plan/remains`;
1556
+ let sawRejection = false;
1557
+ const authHeaders2 = [
1558
+ { Authorization: `Bearer ${apiKey}` },
1559
+ { "x-api-key": apiKey }
1560
+ ];
1561
+ for (const auth of authHeaders2) {
1562
+ let res;
1563
+ try {
1564
+ res = await fetchImpl(url, {
1565
+ headers: { ...auth, "Content-Type": "application/json" },
1566
+ signal: AbortSignal.timeout(timeoutMs)
1567
+ });
1568
+ } catch {
1569
+ return "inconclusive";
1570
+ }
1571
+ if (res.status === 401 || res.status === 403) {
1572
+ sawRejection = true;
1573
+ continue;
1574
+ }
1575
+ if (!res.ok) return "inconclusive";
1576
+ let code;
1577
+ try {
1578
+ code = (await res.json()).base_resp?.status_code;
1579
+ } catch {
1580
+ return "inconclusive";
1581
+ }
1582
+ if (typeof code === "number" && KEY_KNOWN_CODES.has(code)) return "authorized";
1583
+ if (typeof code === "number" && KEY_REJECTED_CODES.has(code)) {
1584
+ sawRejection = true;
1585
+ continue;
1586
+ }
1587
+ return "inconclusive";
1588
+ }
1589
+ return sawRejection ? "unauthorized" : "inconclusive";
1590
+ }
1591
+ async function detectMiniMaxBaseUrl(apiKey, options = {}) {
1592
+ const fetchImpl = options.fetch ?? fetch;
1593
+ const timeoutMs = options.timeoutMs ?? 5e3;
1594
+ const results = await Promise.all(
1595
+ REGION_HOSTS.map((r) => probeRegion(r.host, apiKey, fetchImpl, timeoutMs))
1596
+ );
1597
+ const index = results.indexOf("authorized");
1598
+ return index === -1 ? void 0 : REGION_HOSTS[index].baseUrl;
1599
+ }
1600
+
1601
+ // src/xiaomi-region.ts
1602
+ var XIAOMI_TOKEN_PLAN_BASE_URLS = [
1603
+ "https://token-plan-sgp.xiaomimimo.com/v1",
1604
+ "https://token-plan-ams.xiaomimimo.com/v1",
1605
+ "https://token-plan-cn.xiaomimimo.com/v1"
1606
+ ];
1607
+ async function detectXiaomiTokenPlanBaseUrl(apiKey, options = {}) {
1608
+ const fetchImpl = options.fetch ?? fetch;
1609
+ const timeoutMs = options.timeoutMs ?? 5e3;
1610
+ const accepted = await Promise.all(
1611
+ XIAOMI_TOKEN_PLAN_BASE_URLS.map(async (baseUrl) => {
1612
+ try {
1613
+ const res = await fetchImpl(`${baseUrl}/models`, {
1614
+ headers: { Authorization: `Bearer ${apiKey}` },
1615
+ signal: AbortSignal.timeout(timeoutMs)
1616
+ });
1617
+ return res.ok;
1618
+ } catch {
1619
+ return false;
1620
+ }
1621
+ })
1622
+ );
1623
+ const index = accepted.indexOf(true);
1624
+ return index === -1 ? void 0 : XIAOMI_TOKEN_PLAN_BASE_URLS[index];
1625
+ }
1626
+
1627
+ // src/auth-storage.ts
1531
1628
  var MOONSHOT_OAUTH_KEY = "moonshot-oauth";
1532
1629
  var XAI_OAUTH_KEY = "xai-oauth";
1533
1630
  var DUAL_AUTH_PROVIDERS = [
@@ -1577,6 +1674,15 @@ function activeBaseUrlEntry(data, provider) {
1577
1674
  }
1578
1675
  return data[provider];
1579
1676
  }
1677
+ var MINIMAX_REGION_RETRY_MS = 5 * 60 * 1e3;
1678
+ var miniMaxRegionProbes = /* @__PURE__ */ new Map();
1679
+ function cachedMiniMaxRegion(apiKey) {
1680
+ const cached = miniMaxRegionProbes.get(apiKey);
1681
+ if (cached && Date.now() - cached.at < MINIMAX_REGION_RETRY_MS) return cached.result;
1682
+ const result = detectMiniMaxBaseUrl(apiKey).catch(() => void 0);
1683
+ miniMaxRegionProbes.set(apiKey, { at: Date.now(), result });
1684
+ return result;
1685
+ }
1580
1686
  function readStoredBaseUrlSync(authFile, provider) {
1581
1687
  try {
1582
1688
  const data = JSON.parse((0, import_node_fs3.readFileSync)(authFile, "utf-8"));
@@ -1623,8 +1729,12 @@ var AuthStorage = class {
1623
1729
  snapshotIno = 0;
1624
1730
  /** Per-provider lock to serialize concurrent refresh calls. */
1625
1731
  refreshLocks = /* @__PURE__ */ new Map();
1626
- constructor(filePath) {
1732
+ detectMiniMaxRegion;
1733
+ detectXiaomiCluster;
1734
+ constructor(filePath, options) {
1627
1735
  this.filePath = filePath ?? getAppPaths().authFile;
1736
+ this.detectMiniMaxRegion = options?.detectMiniMaxBaseUrl ?? cachedMiniMaxRegion;
1737
+ this.detectXiaomiCluster = options?.detectXiaomiTokenPlanBaseUrl ?? detectXiaomiTokenPlanBaseUrl;
1628
1738
  }
1629
1739
  /** Path to the on-disk auth file. Useful for status output. */
1630
1740
  get path() {
@@ -1823,11 +1933,48 @@ var AuthStorage = class {
1823
1933
  await this.ensureFresh();
1824
1934
  return this.data[provider];
1825
1935
  }
1936
+ /**
1937
+ * A MiniMax key saved without an endpoint (every key, until its first use)
1938
+ * gets the endpoint of the region that issued it, saved so the check runs
1939
+ * once. Without this a Mainland China key went to the Global API, got a 401,
1940
+ * and was deleted as invalid.
1941
+ */
1942
+ async withMiniMaxRegion(creds) {
1943
+ const baseUrl = await this.detectMiniMaxRegion(creds.accessToken);
1944
+ if (!baseUrl) return creds;
1945
+ log("INFO", "auth", `MiniMax key belongs to ${baseUrl} \u2014 saved for future requests`);
1946
+ await this.mutateLatest((latest) => {
1947
+ const current = latest["minimax"];
1948
+ if (current?.accessToken === creds.accessToken && !current.baseUrl) {
1949
+ current.baseUrl = baseUrl;
1950
+ }
1951
+ });
1952
+ return { ...creds, baseUrl };
1953
+ }
1826
1954
  async setCredentials(provider, creds) {
1827
1955
  await this.mutateLatest((latest) => {
1828
1956
  latest[provider] = creds;
1829
1957
  });
1830
1958
  }
1959
+ /**
1960
+ * After a 401, move a regional key to the cluster that issued it instead of
1961
+ * treating it as invalid. Only the Xiaomi Token Plan is regional: every key
1962
+ * used to go to Singapore, so Europe and China keys were rejected and deleted.
1963
+ * Returns the new base URL, or undefined when no other cluster accepts the key.
1964
+ */
1965
+ async relocateRegionalApiKey(storageKeys) {
1966
+ const key = await this.pickStorageKey(storageKeys);
1967
+ const creds = key === "xiaomi" ? this.data[key] : void 0;
1968
+ if (!creds) return void 0;
1969
+ const baseUrl = await this.detectXiaomiCluster(creds.accessToken);
1970
+ if (!baseUrl || baseUrl === creds.baseUrl) return void 0;
1971
+ await this.mutateLatest((latest) => {
1972
+ const current = latest["xiaomi"];
1973
+ if (current?.accessToken === creds.accessToken) current.baseUrl = baseUrl;
1974
+ });
1975
+ log("INFO", "auth", `Xiaomi Token Plan key belongs to ${baseUrl} \u2014 saved for future requests`);
1976
+ return baseUrl;
1977
+ }
1831
1978
  // Keeps local-endpoint keys: they're the user's own config, and a 401 there is often a busy gateway.
1832
1979
  async clearRejectedApiKey(storageKey) {
1833
1980
  if (storageKey.startsWith(LOCAL_AUTH_KEY_PREFIX)) return false;
@@ -1947,6 +2094,7 @@ var AuthStorage = class {
1947
2094
  throw new NotLoggedInError(provider);
1948
2095
  }
1949
2096
  if (STATIC_API_KEY_PROVIDERS.has(provider)) {
2097
+ if (provider === "minimax" && !creds.baseUrl) return await this.withMiniMaxRegion(creds);
1950
2098
  return creds;
1951
2099
  }
1952
2100
  if (!opts?.forceRefresh && Date.now() < creds.expiresAt - refreshThresholdMs(creds)) {
@@ -2597,6 +2745,23 @@ var MODELS = [
2597
2745
  maxThinkingLevel: "max"
2598
2746
  },
2599
2747
  // ── MiniMax ────────────────────────────────────────────
2748
+ {
2749
+ // The only text model M Plan / Token Plan keys cover; MiniMax-M3 on those
2750
+ // keys fails with "insufficient balance (1008)". Pay-as-you-go serves both.
2751
+ // Always thinks (`thinking: disabled` 400s; omitted = on) with a real
2752
+ // low→max effort ladder on output_config.effort, API default max.
2753
+ id: "MiniMax-M3.1-Flash",
2754
+ name: "MiniMax M3.1 Flash",
2755
+ provider: "minimax",
2756
+ contextWindow: 1e6,
2757
+ maxOutputTokens: 131072,
2758
+ supportsThinking: true,
2759
+ supportsImages: true,
2760
+ supportsVideo: true,
2761
+ maxVideoBytes: 50 * 1024 * 1024,
2762
+ costTier: "low",
2763
+ maxThinkingLevel: "max"
2764
+ },
2600
2765
  {
2601
2766
  id: "MiniMax-M3",
2602
2767
  name: "MiniMax M3",
@@ -2803,11 +2968,44 @@ function clearRuntimeModels(predicate) {
2803
2968
  if (predicate(model)) runtimeModels.delete(id);
2804
2969
  }
2805
2970
  }
2971
+ var providerCatalogs = /* @__PURE__ */ new Map();
2972
+ function setProviderCatalog(provider, models) {
2973
+ if (!models) {
2974
+ providerCatalogs.delete(provider);
2975
+ return [];
2976
+ }
2977
+ const installed = /* @__PURE__ */ new Map();
2978
+ for (const model of models) {
2979
+ if (model.provider !== provider) continue;
2980
+ if (MODELS.some((m) => m.id === model.id && m.provider !== provider)) continue;
2981
+ installed.set(model.id, model);
2982
+ }
2983
+ providerCatalogs.set(provider, installed);
2984
+ return [...installed.values()];
2985
+ }
2986
+ function getProviderCatalog(provider) {
2987
+ const catalog = providerCatalogs.get(provider);
2988
+ return catalog ? [...catalog.values()] : void 0;
2989
+ }
2806
2990
  function getAllModels() {
2807
- return [...MODELS, ...runtimeModels.values()];
2991
+ const synced = [...providerCatalogs.values()].flatMap((c) => [...c.values()]);
2992
+ return [
2993
+ ...MODELS.filter((m) => !providerCatalogs.has(m.provider)),
2994
+ ...synced,
2995
+ ...runtimeModels.values()
2996
+ ];
2808
2997
  }
2809
2998
  function getModel(id) {
2810
- return MODELS.find((m) => m.id === id) ?? runtimeModels.get(id);
2999
+ const fixed = MODELS.find((m) => m.id === id);
3000
+ if (fixed && !providerCatalogs.has(fixed.provider)) return fixed;
3001
+ return findSynced(id) ?? fixed ?? runtimeModels.get(id);
3002
+ }
3003
+ function findSynced(id) {
3004
+ for (const catalog of providerCatalogs.values()) {
3005
+ const hit = catalog.get(id);
3006
+ if (hit) return hit;
3007
+ }
3008
+ return void 0;
2811
3009
  }
2812
3010
  function getModelsForProvider(provider) {
2813
3011
  return getAllModels().filter((m) => m.provider === provider);
@@ -2847,7 +3045,7 @@ var DEFAULT_MODEL_IDS = {
2847
3045
  gemini: "gemini-3.1-flash-lite",
2848
3046
  glm: "glm-5.3",
2849
3047
  moonshot: "kimi-k3",
2850
- minimax: "MiniMax-M3",
3048
+ minimax: "MiniMax-M3.1-Flash",
2851
3049
  deepseek: "deepseek-v4-pro",
2852
3050
  huggingface: "moonshotai/Kimi-K2.7-Code",
2853
3051
  openrouter: "qwen/qwen3.8-max",
@@ -2913,6 +3111,59 @@ function getSummaryModel(provider, currentModelId) {
2913
3111
  return tagged ?? getModel(currentModelId) ?? getDefaultModel(provider);
2914
3112
  }
2915
3113
 
3114
+ // src/openrouter-catalog.ts
3115
+ var OPENROUTER_DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
3116
+ var EFFORT_LADDER = ["low", "medium", "high", "xhigh", "max"];
3117
+ function positiveInt(value) {
3118
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : void 0;
3119
+ }
3120
+ function strings(value) {
3121
+ return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
3122
+ }
3123
+ function costTier(prompt) {
3124
+ const perToken = typeof prompt === "string" ? Number(prompt) : NaN;
3125
+ if (!Number.isFinite(perToken) || perToken < 5e-7) return "low";
3126
+ return perToken < 3e-6 ? "medium" : "high";
3127
+ }
3128
+ function parseOpenRouterCatalog(body) {
3129
+ const data = body?.data;
3130
+ if (!Array.isArray(data)) return { models: [], skipped: 0 };
3131
+ const models = [];
3132
+ let skipped = 0;
3133
+ const seen = /* @__PURE__ */ new Set();
3134
+ for (const raw of data) {
3135
+ const id = typeof raw?.id === "string" ? raw.id.trim() : "";
3136
+ const params = strings(raw?.supported_parameters);
3137
+ const contextWindow = positiveInt(raw?.top_provider?.context_length) ?? positiveInt(raw?.context_length);
3138
+ if (!id || seen.has(id) || !params.includes("tools") || !contextWindow) {
3139
+ skipped++;
3140
+ continue;
3141
+ }
3142
+ seen.add(id);
3143
+ const efforts = strings(raw.reasoning?.supported_efforts);
3144
+ const ladder = EFFORT_LADDER.filter((rung) => efforts.includes(rung));
3145
+ const supportsThinking = ladder.length > 0 || params.includes("reasoning") || params.includes("reasoning_effort");
3146
+ const defaultEffort = raw.reasoning?.default_effort;
3147
+ const maxOutput = positiveInt(raw.top_provider?.max_completion_tokens) ?? Math.min(contextWindow, 16384);
3148
+ models.push({
3149
+ id,
3150
+ name: typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : id,
3151
+ provider: "openrouter",
3152
+ contextWindow,
3153
+ maxOutputTokens: Math.min(maxOutput, contextWindow),
3154
+ supportsThinking,
3155
+ supportsImages: strings(raw.architecture?.input_modalities).includes("image"),
3156
+ // Video needs a per-transport payload cap the catalog doesn't publish.
3157
+ supportsVideo: false,
3158
+ costTier: costTier(raw.pricing?.prompt),
3159
+ maxThinkingLevel: ladder[ladder.length - 1] ?? "high",
3160
+ ...ladder.length > 0 ? { thinkingLevels: ladder } : {},
3161
+ ...typeof defaultEffort === "string" && ladder.includes(defaultEffort) ? { defaultThinkingLevel: defaultEffort } : {}
3162
+ });
3163
+ }
3164
+ return { models, skipped };
3165
+ }
3166
+
2916
3167
  // src/thinking-level.ts
2917
3168
  var OPENAI_GPT_THINKING_LEVELS = ["medium", "high", "xhigh"];
2918
3169
  var OPENAI_GPT_56_THINKING_LEVELS = [
@@ -2941,6 +3192,13 @@ var ANTHROPIC_ADAPTIVE_THINKING_LEVELS = [
2941
3192
  ];
2942
3193
  var MOONSHOT_K3_THINKING_LEVELS = ["low", "high", "max"];
2943
3194
  var GLM_THINKING_LEVELS = ["low", "medium", "high", "xhigh", "max"];
3195
+ var MINIMAX_EFFORT_THINKING_LEVELS = [
3196
+ "low",
3197
+ "medium",
3198
+ "high",
3199
+ "xhigh",
3200
+ "max"
3201
+ ];
2944
3202
  var LOCAL_THINKING_LEVELS = ["low", "medium", "high", "max"];
2945
3203
  function isOpenAIGptModel(provider, model) {
2946
3204
  return provider === "openai" && model.startsWith("gpt-");
@@ -2957,6 +3215,9 @@ function isMoonshotK3Model(provider, model) {
2957
3215
  function isGlmModel(provider) {
2958
3216
  return provider === "glm";
2959
3217
  }
3218
+ function isMiniMaxEffortModel(provider, model) {
3219
+ return provider === "minimax" && model.startsWith("MiniMax-M3.1");
3220
+ }
2960
3221
  function isAnthropicXhighModel(provider, model) {
2961
3222
  return provider === "anthropic" && /opus-5|opus-4-8|opus-4-7|sonnet-5[-.]5|haiku-5[-.]5/.test(model);
2962
3223
  }
@@ -2970,6 +3231,8 @@ function getSupportedThinkingLevels(provider, model) {
2970
3231
  const maxIndex2 = LOCAL_THINKING_LEVELS.indexOf(info.maxThinkingLevel);
2971
3232
  return maxIndex2 === -1 ? LOCAL_THINKING_LEVELS.slice(0, 3) : LOCAL_THINKING_LEVELS.slice(0, maxIndex2 + 1);
2972
3233
  }
3234
+ const declared = getModel(model)?.thinkingLevels;
3235
+ if (declared && declared.length > 0) return declared;
2973
3236
  const maxLevel = getMaxThinkingLevel(model);
2974
3237
  if (isAnthropicAdaptiveModel(provider, model)) {
2975
3238
  const levels2 = isAnthropicXhighModel(provider, model) ? ANTHROPIC_XHIGH_THINKING_LEVELS : ANTHROPIC_ADAPTIVE_THINKING_LEVELS;
@@ -2989,6 +3252,7 @@ function getSupportedThinkingLevels(provider, model) {
2989
3252
  }
2990
3253
  if (isMoonshotK3Model(provider, model)) return MOONSHOT_K3_THINKING_LEVELS;
2991
3254
  if (provider === "deepseek") return DEEPSEEK_THINKING_LEVELS;
3255
+ if (isMiniMaxEffortModel(provider, model)) return MINIMAX_EFFORT_THINKING_LEVELS;
2992
3256
  if (isGlmModel(provider)) {
2993
3257
  const maxIndex2 = GLM_THINKING_LEVELS.indexOf(maxLevel);
2994
3258
  if (maxIndex2 === -1) return GLM_THINKING_LEVELS;
@@ -3009,7 +3273,7 @@ function isThinkingLevelSupported(provider, model, level) {
3009
3273
  }
3010
3274
  function getNextThinkingLevel(provider, model, current) {
3011
3275
  const supportedLevels = getSupportedThinkingLevels(provider, model);
3012
- const shouldCycleLevels = isOpenAIGptModel(provider, model) || isAnthropicAdaptiveModel(provider, model) || isSakanaModel(provider) || isXaiModel(provider) || isMoonshotK3Model(provider, model) || isGlmModel(provider) || provider === "deepseek" || // Local servers take a real effort level, not just on/off: Ollama accepts
3276
+ const shouldCycleLevels = isOpenAIGptModel(provider, model) || isAnthropicAdaptiveModel(provider, model) || isSakanaModel(provider) || isXaiModel(provider) || isMoonshotK3Model(provider, model) || isGlmModel(provider) || isMiniMaxEffortModel(provider, model) || provider === "deepseek" || // Local servers take a real effort level, not just on/off: Ollama accepts
3013
3277
  // low/medium/high on `reasoning_effort` (verified against 0.32) and the
3014
3278
  // other OpenAI-compatible servers use the same three. A model that can't
3015
3279
  // reason at all already has no supported levels, so it never gets here.
@@ -3124,7 +3388,9 @@ async function probeEndpoint(endpoint, { timeoutMs = DEFAULT_PROBE_TIMEOUT_MS, s
3124
3388
  const entries = (list.data ?? []).filter(
3125
3389
  (entry) => typeof entry.id === "string" && entry.id.length > 0 && !NON_CHAT_ID_PATTERN.test(entry.id)
3126
3390
  );
3127
- const models = await enrich(endpoint, entries, { timeoutMs, signal });
3391
+ const enriched = await enrich(endpoint, entries, { timeoutMs, signal });
3392
+ const pinned = contextLength(endpoint.contextLength);
3393
+ const models = pinned === void 0 ? enriched : enriched.map((m) => ({ ...m, contextWindow: pinned, contextWindowKnown: true }));
3128
3394
  log("INFO", "local-models", `Probed ${endpoint.label}`, {
3129
3395
  baseUrl: endpoint.baseUrl,
3130
3396
  models: String(models.length)
@@ -3150,7 +3416,7 @@ async function enrichCustom(endpoint, entries, options) {
3150
3416
  return generic;
3151
3417
  }
3152
3418
  function genericModel(endpoint, entry) {
3153
- const declared = contextLength(entry.max_model_len) ?? contextLength(entry.context_length);
3419
+ const declared = contextLength(entry.max_model_len) ?? contextLength(entry.context_length) ?? contextLength(entry.meta?.n_ctx);
3154
3420
  return {
3155
3421
  rawId: entry.id,
3156
3422
  endpointId: endpoint.id,
@@ -3330,7 +3596,7 @@ function toModelInfo(model, endpoint) {
3330
3596
  }
3331
3597
  var cache;
3332
3598
  function cacheKey(endpoints) {
3333
- return endpoints.map((e) => `${e.id}@${e.baseUrl}`).join("|");
3599
+ return endpoints.map((e) => `${e.id}@${e.baseUrl}#${e.contextLength ?? ""}`).join("|");
3334
3600
  }
3335
3601
  async function discoverLocalModels(endpoints = DEFAULT_LOCAL_ENDPOINTS, options = {}) {
3336
3602
  const key = cacheKey(endpoints);
@@ -3451,7 +3717,7 @@ var ALL_AUTH_PROVIDERS = [
3451
3717
  {
3452
3718
  value: "minimax",
3453
3719
  label: "MiniMax",
3454
- description: "MiniMax M3",
3720
+ description: "MiniMax M3.1 Flash, M3",
3455
3721
  methods: ["apikey"],
3456
3722
  apiKeyLabel: "MiniMax"
3457
3723
  },
@@ -4284,14 +4550,18 @@ function createAutoUpdater(config) {
4284
4550
  HIDDEN_PROVIDERS,
4285
4551
  LOCAL_API_KEY_PLACEHOLDER,
4286
4552
  LOCAL_AUTH_KEY_PREFIX,
4553
+ MINIMAX_CN_BASE_URL,
4554
+ MINIMAX_GLOBAL_BASE_URL,
4287
4555
  MODELS,
4288
4556
  MOONSHOT_OAUTH_KEY,
4289
4557
  NotLoggedInError,
4558
+ OPENROUTER_DEFAULT_BASE_URL,
4290
4559
  PLAN_MODE_THINKING_CAP,
4291
4560
  SubscriptionUsageError,
4292
4561
  TelegramBot,
4293
4562
  XAI_OAUTH_KEY,
4294
4563
  XIAOMI_CREDITS_KEY,
4564
+ XIAOMI_TOKEN_PLAN_BASE_URLS,
4295
4565
  authPriorityNote,
4296
4566
  clampThinkingForPlanMode,
4297
4567
  clearLocalDiscoveryCache,
@@ -4300,6 +4570,8 @@ function createAutoUpdater(config) {
4300
4570
  createAutoUpdater,
4301
4571
  decodeOggOpus,
4302
4572
  describeAuthMethods,
4573
+ detectMiniMaxBaseUrl,
4574
+ detectXiaomiTokenPlanBaseUrl,
4303
4575
  discoverLocalModels,
4304
4576
  downmixToMono,
4305
4577
  dualAuthProvider,
@@ -4325,6 +4597,7 @@ function createAutoUpdater(config) {
4325
4597
  getModel,
4326
4598
  getModelsForProvider,
4327
4599
  getNextThinkingLevel,
4600
+ getProviderCatalog,
4328
4601
  getSessionId,
4329
4602
  getSummaryModel,
4330
4603
  getSupportedThinkingLevels,
@@ -4354,6 +4627,7 @@ function createAutoUpdater(config) {
4354
4627
  ollamaServedContext,
4355
4628
  openLog,
4356
4629
  parseLocalModelId,
4630
+ parseOpenRouterCatalog,
4357
4631
  pendingClaudeCodeVersionRefresh,
4358
4632
  probeEndpoint,
4359
4633
  providerStorageKeys,
@@ -4367,6 +4641,7 @@ function createAutoUpdater(config) {
4367
4641
  registerRuntimeModels,
4368
4642
  resample,
4369
4643
  setProgressCallback,
4644
+ setProviderCatalog,
4370
4645
  toModelInfo,
4371
4646
  transcribeVoice,
4372
4647
  usesOpenAICodexTransport,