codeep 3.4.1 → 3.5.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.
@@ -17,11 +17,15 @@ export const PROVIDERS = {
17
17
  supportsNativeTools: true,
18
18
  },
19
19
  },
20
+ // The plan accepts exactly these two ("Only the following two models can be
21
+ // called: GLM-5.3, GLM-5.3-Flash" — docs.z.ai/devpack/faq, 2026-09-23). It
22
+ // routes GLM-5.2/5.1 to 5.3, so a 5.2 entry labelled 5.3 as an older model,
23
+ // and Turbo is not on the plan at all: Z.AI warns other models risk
24
+ // "unexpected charges", which a flat-fee surface would never show in /cost.
25
+ // Stored ids migrate via RETIRED_MODEL_REPLACEMENTS.
20
26
  models: [
21
27
  { id: 'glm-5.3', name: 'GLM-5.3', description: 'Latest flagship for project-scale engineering (1M context)' },
22
28
  { id: 'glm-5.3-flash', name: 'GLM-5.3 Flash', description: 'Same 1M context at roughly a ninth of the price' },
23
- { id: 'glm-5.2', name: 'GLM-5.2', description: 'Previous flagship for project-scale engineering (1M context)' },
24
- { id: 'glm-5-turbo', name: 'GLM-5 Turbo', description: 'Fast GLM-5 variant, available to all users' },
25
29
  ],
26
30
  defaultModel: 'glm-5.3',
27
31
  defaultProtocol: 'openai',
@@ -48,14 +52,16 @@ export const PROVIDERS = {
48
52
  },
49
53
  },
50
54
  models: [
51
- // GLM-5.3 reached the standalone pay-per-use API on 2026-08-19 and is
52
- // priced on docs.z.ai/guides/overview/pricing. The China platform
53
- // (`z.ai-cn*`) bills separately and is not covered by that page, so it is
54
- // left alone until its own listing is checked.
55
+ // GLM-5.3 reached the standalone pay-per-use API on 2026-08-18 and is
56
+ // priced on docs.z.ai/guides/overview/pricing, as is FlashX (not on
57
+ // either Coding Plan). GLM-5-Turbo is no longer on that price list, the
58
+ // models overview or the OpenAPI model enum — it was last listed on
59
+ // 2026-07-31, with no deprecation notice — so it is not offered here;
60
+ // stored configs move to Flash. China (`z.ai-cn-api`) still sells it.
55
61
  { id: 'glm-5.3', name: 'GLM-5.3', description: 'Latest flagship for project-scale engineering (1M context)' },
56
62
  { id: 'glm-5.3-flash', name: 'GLM-5.3 Flash', description: 'Same 1M context at roughly a ninth of the price' },
63
+ { id: 'glm-5.3-flashx', name: 'GLM-5.3 FlashX', description: 'Faster GLM-5.3 Flash (about 200 tokens/s), 1M context' },
57
64
  { id: 'glm-5.2', name: 'GLM-5.2', description: 'Previous flagship for project-scale engineering (1M context)' },
58
- { id: 'glm-5-turbo', name: 'GLM-5 Turbo', description: 'Fast GLM-5 variant' },
59
65
  ],
60
66
  defaultModel: 'glm-5.3',
61
67
  defaultProtocol: 'openai',
@@ -80,12 +86,13 @@ export const PROVIDERS = {
80
86
  supportsNativeTools: true,
81
87
  },
82
88
  },
83
- // GLM-5.3 is on the China gateway and the China Coding Plan (bigmodel.cn).
84
- // Flash is listed only on China pay-per-use, so the plan does not offer it.
89
+ // The China plan now carries the same two models on every tier ("所有套餐均
90
+ // 支持 GLM-5.3、GLM-5.3-Flash" — docs.bigmodel.cn/cn/coding-plan/overview) and
91
+ // switches GLM-5.2/5.1 to 5.3 and GLM-5-Turbo to 5.3-Flash. FlashX is not on
92
+ // the plan. Stored ids migrate via RETIRED_MODEL_REPLACEMENTS.
85
93
  models: [
86
94
  { id: 'glm-5.3', name: 'GLM-5.3', description: 'Latest flagship for project-scale engineering (1M context)' },
87
- { id: 'glm-5.2', name: 'GLM-5.2', description: 'Previous flagship for project-scale engineering (1M context)' },
88
- { id: 'glm-5-turbo', name: 'GLM-5 Turbo', description: 'Fast GLM-5 variant, available to all users' },
95
+ { id: 'glm-5.3-flash', name: 'GLM-5.3 Flash', description: 'Same 1M context, much lighter on plan quota' },
89
96
  ],
90
97
  defaultModel: 'glm-5.3',
91
98
  defaultProtocol: 'openai',
@@ -114,7 +121,10 @@ export const PROVIDERS = {
114
121
  models: [
115
122
  { id: 'glm-5.3', name: 'GLM-5.3', description: 'Latest flagship for project-scale engineering (1M context)' },
116
123
  { id: 'glm-5.3-flash', name: 'GLM-5.3 Flash', description: 'Same 1M context at a tenth of the price' },
124
+ { id: 'glm-5.3-flashx', name: 'GLM-5.3 FlashX', description: 'Faster GLM-5.3 Flash (about 200 tokens/s), 1M context' },
117
125
  { id: 'glm-5.2', name: 'GLM-5.2', description: 'Previous flagship for project-scale engineering (1M context)' },
126
+ // Still listed and priced on BigModel pay-per-use (CNY 5/22 below 32K
127
+ // input); only the China Coding Plan reroutes it.
118
128
  { id: 'glm-5-turbo', name: 'GLM-5 Turbo', description: 'Fast GLM-5 variant' },
119
129
  ],
120
130
  defaultModel: 'glm-5.3',
@@ -212,17 +222,23 @@ export const PROVIDERS = {
212
222
  supportsNativeTools: true,
213
223
  },
214
224
  },
215
- // One model. DeepSeek's own pricing page names `deepseek-flash` (V4.1 Flash)
216
- // as the model to use and says it outperforms V4 Pro. `deepseek-v4-flash`
217
- // is already served by it, and from 2026-09-14 12:00 Beijing time every
218
- // `deepseek-v4-pro` request is routed to it and billed at its price until
219
- // a V4.1 Pro exists — so a Pro entry would be a label on a different model.
220
- // Both old ids migrate via RETIRED_MODEL_REPLACEMENTS.
225
+ // DeepSeek's pricing page names `deepseek-flash` (V4.1 Flash) as the model
226
+ // to use, so it stays the default; the retired `deepseek-v4-flash` ids are
227
+ // served by it and migrate via RETIRED_MODEL_REPLACEMENTS.
228
+ //
229
+ // V4 Pro was announced to route to Flash from 2026-09-14, and Codeep 3.3.0
230
+ // migrated it away. DeepSeek reversed that on 2026-09-11, before the
231
+ // cutover: the changelog's 09-10 entry now says V4 Pro service continues
232
+ // "with the billing method remaining unchanged", and the pricing page and
233
+ // API reference list `deepseek-v4-pro` (DeepSeek-V4-Pro-0813) beside Flash.
234
+ // The 09-10 news post still carries the old routing text; the changelog is
235
+ // the newer source. So Pro is a separate, separately billed model again.
221
236
  //
222
- // Note the id differs on OpenRouter, which lists the same model as
223
- // `deepseek/deepseek-v4.1-flash`.
237
+ // The ids differ on OpenRouter: `deepseek/deepseek-v4.1-flash`, and
238
+ // `deepseek/deepseek-v4-pro-0813` for this Pro (see the fallback list).
224
239
  models: [
225
240
  { id: 'deepseek-flash', name: 'DeepSeek V4.1 Flash', description: 'Current DeepSeek model — thinking on by default, vision, 1M context, 384K output' },
241
+ { id: 'deepseek-v4-pro', name: 'DeepSeek V4 Pro', description: 'Larger V4 model (V4-Pro-0813) — thinking on, 1M context, no vision, about 4x Flash\'s price' },
226
242
  ],
227
243
  defaultModel: 'deepseek-flash',
228
244
  defaultProtocol: 'openai',
@@ -243,11 +259,18 @@ export const PROVIDERS = {
243
259
  protocols: {
244
260
  openai: { baseUrl: 'https://api.kimi.com/coding/v1', authHeader: 'Bearer', supportsNativeTools: true },
245
261
  },
262
+ // Kimi Code upgraded `kimi-for-coding` in place to K2.8 Preview on
263
+ // 2026-09-11 (1,048,576 context on every tier, effort low/high/max). The
264
+ // plans were then renamed: Plus ≈ Moderato, Pro ≈ Allegretto, and the new Go
265
+ // tier has no coding quota. K3 reaches 1M context only on Pro/Allegretto; on
266
+ // Plus/Moderato it is capped at 256K, and going past that returns HTTP 401
267
+ // "Your current plan supports only kimi-k3 up to 256K context"
268
+ // (kimi.com/code/docs/en/kimi-code/models.html, error-reference.html).
246
269
  models: [
247
- { id: 'kimi-for-coding', name: 'Kimi Code', description: 'Available on every Kimi Code plan — maps to K2.7 Code' },
248
- { id: 'k3', name: 'Kimi K3', description: '1M-context flagship — Moderato plan or higher' },
249
- { id: 'k3-256k', name: 'Kimi K3 (256K)', description: 'K3 with a smaller context window — Moderato plan or higher' },
250
- { id: 'kimi-for-coding-highspeed', name: 'Kimi Code (High-Speed)', description: 'Low-latency K2.7 Code — Allegretto plan or higher' },
270
+ { id: 'kimi-for-coding', name: 'Kimi Code', description: 'K2.8 Preview, 1M context — every Kimi Code plan (Plus/Andante and above)' },
271
+ { id: 'k3', name: 'Kimi K3', description: 'Flagship — 1M context on Pro/Allegretto and above; Plus/Moderato is capped at 256K (use K3 256K there)' },
272
+ { id: 'k3-256k', name: 'Kimi K3 (256K)', description: 'K3 with a 256K context window — Plus/Moderato plan or higher' },
273
+ { id: 'kimi-for-coding-highspeed', name: 'Kimi Code (High-Speed)', description: 'K2.7 Code HighSpeed, 256K context — Pro/Allegretto plan or higher' },
251
274
  ],
252
275
  defaultModel: 'kimi-for-coding',
253
276
  defaultProtocol: 'openai',
@@ -309,15 +332,17 @@ export const PROVIDERS = {
309
332
  openai: { baseUrl: 'https://api.x.ai/v1', authHeader: 'Bearer', supportsNativeTools: true },
310
333
  },
311
334
  models: [
312
- { id: 'grok-4.6', name: 'Grok 4.6', description: 'Flagship reasoning model — xAI recommends it for code, 500K context' },
313
- { id: 'grok-4.5', name: 'Grok 4.5', description: 'Previous flagship reasoning model, 500K context' },
335
+ { id: 'grok-4.7', name: 'Grok 4.7', description: 'Flagship reasoning model — xAI recommends it for code, 500K context' },
336
+ { id: 'grok-4.6', name: 'Grok 4.6', description: 'Previous flagship reasoning model, 500K context' },
337
+ { id: 'grok-4.5', name: 'Grok 4.5', description: 'Older flagship reasoning model, 500K context' },
314
338
  { id: 'grok-build-0.1', name: 'Grok Build 0.1', description: 'Agentic coding model — fast, 256K context' },
315
339
  { id: 'grok-4.3', name: 'Grok 4.3', description: 'Older flagship, 1M context' },
316
340
  ],
317
- // Stays on the agentic coder, not the new flagship. grok-4.6 is the better
318
- // model and xAI recommends it for code, but it bills 2x input and 3x output
319
- // against grok-build-0.1 — moving every unpinned user onto it silently is
320
- // not ours to decide. It is one `/model` away for anyone who wants it.
341
+ // Stays on the agentic coder, not the new flagship. grok-4.7 is the better
342
+ // model — xAI's models page recommends only it, "including code", and it is
343
+ // the default of xAI's own Grok Build agent — but it bills 2x input and 3x
344
+ // output against grok-build-0.1, so moving every unpinned user onto it
345
+ // silently is not ours to decide. It is one `/model` away.
321
346
  defaultModel: 'grok-build-0.1',
322
347
  defaultProtocol: 'openai',
323
348
  useMaxCompletionTokens: true, // reasoning models reject max_tokens
@@ -358,8 +383,7 @@ export const PROVIDERS = {
358
383
  openai: { baseUrl: 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1', authHeader: 'Bearer', supportsNativeTools: true },
359
384
  },
360
385
  // 3.8 Max and Flash are GA on Model Studio international and cost less
361
- // than the 3.7 Max / 3.6 Flash they sit above. China (`qwen-cn-api`) is a
362
- // separate listing and is not changed on this evidence.
386
+ // than the 3.7 Max / 3.6 Flash they sit above.
363
387
  models: [
364
388
  { id: 'qwen3.8-max', name: 'Qwen3.8-Max', description: 'Latest flagship for complex coding and reasoning (1M context)' },
365
389
  { id: 'qwen3.8-flash', name: 'Qwen3.8-Flash', description: 'Low-latency, low-cost multimodal model (1M context)' },
@@ -382,14 +406,22 @@ export const PROVIDERS = {
382
406
  protocols: {
383
407
  openai: { baseUrl: 'https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1', authHeader: 'Bearer', supportsNativeTools: true },
384
408
  },
409
+ // qwen3.8-max-preview is retired here: Alibaba routes it to qwen3.8-max,
410
+ // bills it at that rate and says to update the id (token-plan-personal-
411
+ // overview, 2026-09-23); stored configs migrate. The plan now has Personal
412
+ // and Team editions on this same URL and key format, with different
413
+ // allowlists — qwen3.6-plus is Team-only, and a Personal key gets
414
+ // "403 AccessDenied.Unpurchased" for it. Nothing here can tell the two
415
+ // editions apart, so the entry says so rather than disappearing on Team.
385
416
  models: [
386
- { id: 'qwen3.8-max-preview', name: 'Qwen3.8-Max Preview', description: 'Newest Token Plan flagship for complex agentic work' },
387
- { id: 'qwen3.7-max', name: 'Qwen3.7-Max', description: 'Production flagship for complex coding and reasoning' },
417
+ { id: 'qwen3.8-max', name: 'Qwen3.8-Max', description: 'Token Plan flagship for complex agentic work (1M context)' },
418
+ { id: 'qwen3.8-flash', name: 'Qwen3.8-Flash', description: 'Low-latency, credit-efficient model (1M context)' },
419
+ { id: 'qwen3.7-max', name: 'Qwen3.7-Max', description: 'Previous flagship for complex coding and reasoning' },
388
420
  { id: 'qwen3.7-plus', name: 'Qwen3.7-Plus', description: 'Balanced quality and throughput' },
389
- { id: 'qwen3.6-plus', name: 'Qwen3.6-Plus', description: 'Fast multimodal model with a 1M context window' },
390
- { id: 'qwen3.6-flash', name: 'Qwen3.6-Flash', description: 'Low-latency, credit-efficient model' },
421
+ { id: 'qwen3.6-plus', name: 'Qwen3.6-Plus', description: 'Team edition only — a Personal plan returns 403 for it' },
422
+ { id: 'qwen3.6-flash', name: 'Qwen3.6-Flash', description: 'Previous low-latency, credit-efficient model' },
391
423
  ],
392
- defaultModel: 'qwen3.8-max-preview',
424
+ defaultModel: 'qwen3.8-max',
393
425
  defaultProtocol: 'openai',
394
426
  maxOutputTokens: 131_072,
395
427
  noStreamWithTools: true,
@@ -426,10 +458,18 @@ export const PROVIDERS = {
426
458
  protocols: {
427
459
  openai: { baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1', authHeader: 'Bearer', supportsNativeTools: true },
428
460
  },
461
+ // 3.8 Max and Flash have been GA in China (Beijing) since 2026-08-02 and
462
+ // 2026-08-26, at CNY 12/36 and 0.8/2.7 (help.aliyun.com/zh/model-studio/
463
+ // qwen3-8-max, qwen3-8-flash). Pricing is keyed by id, so they reuse the
464
+ // international USD rows — an over-estimate, the same policy as z.ai-cn.
465
+ // The default is unchanged: 3.8 Max costs the same as 3.7 Max in Beijing,
466
+ // and moving unpinned users was not part of adding it.
429
467
  models: [
430
- { id: 'qwen3.7-max', name: 'Qwen3.7-Max', description: 'Latest flagship for complex coding and reasoning' },
468
+ { id: 'qwen3.8-max', name: 'Qwen3.8-Max', description: 'Latest flagship for complex coding and reasoning (1M context)' },
469
+ { id: 'qwen3.8-flash', name: 'Qwen3.8-Flash', description: 'Low-latency, low-cost multimodal model (1M context)' },
470
+ { id: 'qwen3.7-max', name: 'Qwen3.7-Max', description: 'Previous flagship for complex coding and reasoning' },
431
471
  { id: 'qwen3.7-plus', name: 'Qwen3.7-Plus', description: 'Balanced quality, speed, and price (1M context)' },
432
- { id: 'qwen3.6-flash', name: 'Qwen3.6-Flash', description: 'Low-latency, low-cost multimodal model' },
472
+ { id: 'qwen3.6-flash', name: 'Qwen3.6-Flash', description: 'Previous low-latency, low-cost multimodal model' },
433
473
  ],
434
474
  defaultModel: 'qwen3.7-max',
435
475
  defaultProtocol: 'openai',
@@ -446,10 +486,16 @@ export const PROVIDERS = {
446
486
  protocols: {
447
487
  openai: { baseUrl: 'https://api-inference.modelscope.cn/v1', authHeader: 'Bearer', supportsNativeTools: true },
448
488
  },
489
+ // Shown only until the live catalogue loads. The previous fallback,
490
+ // Qwen/Qwen3-Coder-480B-A35B-Instruct, is no longer served: it is absent from
491
+ // api-inference.modelscope.cn/v1/models (2026-09-23) and its hub entry has
492
+ // SupportApiInference false. ModelScope names no successor, so this pick is
493
+ // Codeep's: the largest Qwen in the live list, whose model card documents
494
+ // tool calling ("Qwen3.5 excels in tool calling capabilities").
449
495
  models: [
450
- { id: 'Qwen/Qwen3-Coder-480B-A35B-Instruct', name: 'Qwen3-Coder 480B', description: 'Fallback model shown until the live catalog loads' },
496
+ { id: 'Qwen/Qwen3.5-397B-A17B', name: 'Qwen3.5 397B', description: 'Fallback model shown until the live catalog loads' },
451
497
  ],
452
- defaultModel: 'Qwen/Qwen3-Coder-480B-A35B-Instruct',
498
+ defaultModel: 'Qwen/Qwen3.5-397B-A17B',
453
499
  defaultProtocol: 'openai',
454
500
  maxOutputTokens: 65_536,
455
501
  noStreamWithTools: true,
@@ -462,7 +508,7 @@ export const PROVIDERS = {
462
508
  },
463
509
  'openai': {
464
510
  name: 'OpenAI',
465
- description: 'GPT and o-series models',
511
+ description: 'GPT models',
466
512
  protocols: {
467
513
  openai: {
468
514
  baseUrl: 'https://api.openai.com/v1',
@@ -470,9 +516,21 @@ export const PROVIDERS = {
470
516
  supportsNativeTools: true,
471
517
  },
472
518
  },
519
+ // Chat Completions is the only OpenAI endpoint Codeep calls, and GPT-6 has a
520
+ // documented tool restriction there (developers.openai.com guides/
521
+ // latest-model and reasoning): Sol and Luna call tools only with
522
+ // reasoning_effort "none", and "Chat Completions does not support function
523
+ // calling with GPT-6 Astra" at all. So Astra is not offered until a
524
+ // Responses API transport exists — stored configs move to Sol — and Sol and
525
+ // Luna run agent turns with reasoning off (see reasoningParamsFor).
526
+ //
527
+ // The default stays 5.6 Sol: it is the newest model OpenAI documents with
528
+ // reasoning AND tool calls on Chat Completions ("The Chat Completions
529
+ // examples use GPT-5.6 for compatibility" — function-calling guide).
473
530
  models: [
474
- { id: 'gpt-6-astra', name: 'GPT-6 Astra', description: 'Frontier GPT — 1M context, 128K output. Rolling out by organization, so a key may not have it yet. 2x the price of 5.6 Sol' },
475
- { id: 'gpt-5.6-sol', name: 'GPT-5.6 Sol', description: 'Most capable GPT — best for coding & agentic work' },
531
+ { id: 'gpt-5.6-sol', name: 'GPT-5.6 Sol', description: 'Most capable GPT for coding & agentic work on Codeep\'s transport' },
532
+ { id: 'gpt-6-sol', name: 'GPT-6 Sol', description: 'GPT-6 at $2/$10, 1M context — agent turns run with reasoning off (tools need effort "none" here); /thinking applies to plain chat' },
533
+ { id: 'gpt-6-luna', name: 'GPT-6 Luna', description: 'Cheapest GPT-6 ($0.10/$0.50), 1M context — agent turns run with reasoning off, as for Sol' },
476
534
  { id: 'gpt-5.6-terra', name: 'GPT-5.6 Terra', description: 'Balanced — GPT-5.5 quality at about half the price' },
477
535
  { id: 'gpt-5.6-luna', name: 'GPT-5.6 Luna', description: 'Fast and cheap — high-volume workloads' },
478
536
  ],
@@ -497,12 +555,20 @@ export const PROVIDERS = {
497
555
  },
498
556
  models: [
499
557
  { id: 'claude-fable-5-1', name: 'Claude Fable 5.1', description: 'Most capable — hardest reasoning & long-horizon agentic work' },
558
+ { id: 'claude-opus-5-5', name: 'Claude Opus 5.5', description: 'Complex agentic coding & deep reasoning — $4/$20, 1M context' },
500
559
  { id: 'claude-fable-5', name: 'Claude Fable 5', description: 'Superseded by 5.1 — same price, kept for pinned configs' },
501
- { id: 'claude-opus-5', name: 'Claude Opus 5', description: 'Complex agentic coding & deep reasoning — the Opus workhorse' },
560
+ { id: 'claude-opus-5', name: 'Claude Opus 5', description: 'Legacy since Opus 5.5 — kept for pinned configs' },
502
561
  { id: 'claude-sonnet-5', name: 'Claude Sonnet 5', description: 'Best balance of speed and intelligence' },
503
562
  { id: 'claude-haiku-4-5-20251001', name: 'Claude Haiku', description: 'Fastest and most affordable' },
504
563
  ],
505
- defaultModel: 'claude-opus-5',
564
+ // Anthropic's models overview now says to "start with Claude Opus 5.5 for
565
+ // most workloads", and it is 20% cheaper than Opus 5 (now "Active
566
+ // (legacy)", retiring no sooner than 2027-07-24 — so no migration). One
567
+ // behaviour change comes with it: Opus 5.5 defaults to MEDIUM effort where
568
+ // Opus 5 defaulted to high, and /thinking auto sends no effort, so an
569
+ // unpinned user runs one level lower than before. It also thinks more per
570
+ // turn at a given effort — see minResponseTokensFor.
571
+ defaultModel: 'claude-opus-5-5',
506
572
  defaultProtocol: 'anthropic',
507
573
  envKey: 'ANTHROPIC_API_KEY',
508
574
  groupLabel: 'Anthropic',
@@ -543,28 +609,38 @@ export const PROVIDERS = {
543
609
  supportsNativeTools: true,
544
610
  },
545
611
  },
546
- // Top 12 — the full catalog (100+) is fetched lazily via
612
+ // A short fallback — the full catalog (100+) is fetched lazily via
547
613
  // fetchOpenRouterModels() because dynamicModels is true.
548
614
  // We keep these hardcoded so first-time users without network
549
615
  // get a working dropdown.
550
616
  models: [
551
617
  { id: 'openrouter/auto', name: 'Auto-route', description: 'OpenRouter picks the best model for the task' },
552
- { id: 'anthropic/claude-fable-5-1', name: 'Claude Fable 5.1', description: 'Anthropic — most capable' },
618
+ // Dotted, as OpenRouter's /api/v1/models lists them. The hyphenated
619
+ // `anthropic/claude-fable-5-1` this used to carry resolves on OpenRouter's
620
+ // metadata lookup but is absent from the models list, so a pick made
621
+ // before the live catalogue loaded matched nothing in it.
622
+ { id: 'anthropic/claude-fable-5.1', name: 'Claude Fable 5.1', description: 'Anthropic — most capable' },
623
+ { id: 'anthropic/claude-opus-5.5', name: 'Claude Opus 5.5', description: 'Anthropic — current Opus' },
553
624
  { id: 'anthropic/claude-fable-5', name: 'Claude Fable 5', description: 'Anthropic — superseded by 5.1' },
554
- { id: 'anthropic/claude-opus-5', name: 'Claude Opus 5', description: 'Anthropic — flagship Opus tier' },
625
+ { id: 'anthropic/claude-opus-5', name: 'Claude Opus 5', description: 'Anthropic — legacy Opus' },
555
626
  { id: 'anthropic/claude-sonnet-5', name: 'Claude Sonnet 5', description: 'Anthropic — balanced' },
556
627
  { id: 'openai/gpt-6-astra', name: 'GPT-6 Astra', description: 'OpenAI — frontier' },
628
+ { id: 'openai/gpt-6-sol', name: 'GPT-6 Sol', description: 'OpenAI — GPT-6, balanced' },
629
+ { id: 'openai/gpt-6-luna', name: 'GPT-6 Luna', description: 'OpenAI — GPT-6, fast/cheap' },
557
630
  { id: 'openai/gpt-5.6-sol', name: 'GPT-5.6 Sol', description: 'OpenAI — flagship' },
558
631
  { id: 'openai/gpt-5.6-luna', name: 'GPT-5.6 Luna', description: 'OpenAI — fast/efficient' },
559
632
  { id: 'google/gemini-3.8-flash', name: 'Gemini 3.8 Flash', description: 'Google — latest production Flash' },
560
633
  { id: 'google/gemini-3.7-flash', name: 'Gemini 3.7 Flash', description: 'Google — previous production Flash' },
561
634
  { id: 'deepseek/deepseek-v4.1-flash', name: 'DeepSeek V4.1 Flash', description: 'DeepSeek — current model' },
635
+ // The GA Pro, with a DeepSeek-hosted endpoint. `deepseek/deepseek-v4-pro`
636
+ // is the April 0423 preview, served only by third parties.
637
+ { id: 'deepseek/deepseek-v4-pro-0813', name: 'DeepSeek V4 Pro', description: 'DeepSeek — larger V4 model' },
562
638
  { id: 'moonshotai/kimi-k3', name: 'Kimi K3', description: 'Moonshot — long-horizon coding' },
563
639
  // Dated on purpose: OpenRouter lists only the snapshot. The undated
564
640
  // `qwen/qwen3.8-max` this used to carry does not exist there, so picking
565
641
  // it before the live catalogue loaded was an error.
566
642
  { id: 'qwen/qwen3.8-max-0902', name: 'Qwen 3.8 Max', description: 'Alibaba — latest flagship' },
567
- { id: 'x-ai/grok-4.6', name: 'Grok 4.6', description: 'xAI — flagship reasoning' },
643
+ { id: 'x-ai/grok-4.7', name: 'Grok 4.7', description: 'xAI — flagship reasoning' },
568
644
  ],
569
645
  defaultModel: 'openrouter/auto',
570
646
  defaultProtocol: 'openai',
@@ -620,22 +696,36 @@ export function getProvider(id) {
620
696
  * Exact migrations for curated model ids that vendors replaced.
621
697
  *
622
698
  * Keep this deliberately narrower than the provider catalogue. Dynamic
623
- * OpenRouter/Ollama/custom ids are user-controlled and must never be rewritten.
699
+ * OpenRouter/Ollama/custom ids are user-controlled and must never be rewritten;
700
+ * ModelScope's one entry is the exception, explained where it sits.
624
701
  */
625
702
  const RETIRED_MODEL_REPLACEMENTS = {
626
- 'z.ai': { 'glm-5.1': 'glm-5.2', 'glm-5': 'glm-5.2' },
627
- 'z.ai-api': { 'glm-5.1': 'glm-5.2', 'glm-5': 'glm-5.2' },
628
- 'z.ai-cn': { 'glm-5.1': 'glm-5.2', 'glm-5': 'glm-5.2' },
703
+ // Both GLM Coding Plans accept exactly GLM-5.3 and GLM-5.3-Flash. Z.AI routes
704
+ // 5.2 and 5.1 to 5.3 on both, and the China plan routes Turbo to 5.3-Flash.
705
+ // Plain glm-5, and Turbo on the international plan, are Codeep's extension of
706
+ // the same routing: neither plan's text names them.
707
+ 'z.ai': { 'glm-5.2': 'glm-5.3', 'glm-5.1': 'glm-5.3', 'glm-5': 'glm-5.3', 'glm-5-turbo': 'glm-5.3-flash' },
708
+ // Pay-per-use still sells 5.2 as its own id. Turbo left the international
709
+ // price list and model enum with no notice and no named successor, so it
710
+ // follows the China plan's routing to Flash.
711
+ 'z.ai-api': { 'glm-5.1': 'glm-5.2', 'glm-5': 'glm-5.2', 'glm-5-turbo': 'glm-5.3-flash' },
712
+ 'z.ai-cn': { 'glm-5.2': 'glm-5.3', 'glm-5.1': 'glm-5.3', 'glm-5': 'glm-5.3', 'glm-5-turbo': 'glm-5.3-flash' },
629
713
  'z.ai-cn-api': { 'glm-5.1': 'glm-5.2', 'glm-5': 'glm-5.2' },
630
714
  google: {
631
715
  'gemini-3.1-flash-lite': 'gemini-3.5-flash-lite',
716
+ // Google's deprecations table names 3.6 Flash (no shutdown date yet). Its
717
+ // /whats-new-gemini-3.5 guide suggests 3.5 Flash instead; the lifecycle
718
+ // table is the dedicated source, and 3.5 Flash would cost twice as much.
719
+ 'gemini-3-flash-preview': 'gemini-3.6-flash',
720
+ // Shut down 2026-03-09; the server already aliases the id to 3.1 Pro.
721
+ 'gemini-3-pro-preview': 'gemini-3.1-pro-preview',
632
722
  },
633
- // DeepSeek named V4.1 Flash the model to use: the V4 Flash ids are retired
634
- // and served by it, and V4 Pro is routed to it from 2026-09-14.
723
+ // The V4 Flash ids are retired and served by V4.1 Flash. There is no V4 Pro
724
+ // entry: DeepSeek cancelled its routing to Flash (see the catalogue above),
725
+ // and rewriting it moved users off a live, separately billed model.
635
726
  deepseek: {
636
727
  'deepseek-v4-flash': 'deepseek-flash',
637
728
  'deepseek-v4-flash-vision-exp': 'deepseek-flash',
638
- 'deepseek-v4-pro': 'deepseek-flash',
639
729
  },
640
730
  grok: {
641
731
  'grok-code-fast-1': 'grok-build-0.1',
@@ -645,6 +735,13 @@ const RETIRED_MODEL_REPLACEMENTS = {
645
735
  'gpt-5.5': 'gpt-5.6-sol',
646
736
  'gpt-5.4': 'gpt-5.6-terra',
647
737
  'gpt-5.4-mini': 'gpt-5.6-luna',
738
+ // Not retired by OpenAI. Astra cannot call tools on Chat Completions at all
739
+ // ("Chat Completions does not support function calling with GPT-6 Astra"),
740
+ // and agentChat turns a 400 on a tools request into the text-tool fallback,
741
+ // so every agent turn on it silently ran without native tools. Sol is the
742
+ // GPT-6 that can call tools here. Drop this entry once a Responses API
743
+ // transport exists and Astra is offered again.
744
+ 'gpt-6-astra': 'gpt-6-sol',
648
745
  },
649
746
  'kimi-api': {
650
747
  'kimi-k3-code': 'kimi-k3',
@@ -658,30 +755,66 @@ const RETIRED_MODEL_REPLACEMENTS = {
658
755
  'kimi-k3-thinking': 'kimi-k3',
659
756
  'kimi-k2.5': 'kimi-k2.6',
660
757
  },
758
+ // Alibaba retires qwen3-coder-plus, qwen3-coder-next and bare qwen3-max on
759
+ // 2026-10-10, on the plans as well (notices 1949 and 1950). It names
760
+ // qwen3.7-plus for both coders and qwen3.7-max for qwen3-max — but 3.7 Max is
761
+ // not on the Coding Plan allowlist, so the two Coding Plan surfaces land
762
+ // qwen3-max on 3.7 Plus, exactly as their own qwen3.7-max entry does.
661
763
  qwen: {
662
764
  'qwen3-coder-plus': 'qwen3.7-plus',
663
765
  'qwen3-coder-next': 'qwen3.7-plus',
664
766
  'qwen3.7-max': 'qwen3.7-plus',
767
+ 'qwen3-max': 'qwen3.7-plus',
665
768
  },
769
+ // The coders used to go to qwen3.7-max here, which bills 6.25x the input rate
770
+ // of the qwen3.7-plus Alibaba names. Configs already moved stay where they
771
+ // are: 3.7 Max is a valid model.
666
772
  'qwen-api': {
667
- 'qwen3-coder-plus': 'qwen3.7-max',
668
- 'qwen3-coder-next': 'qwen3.7-max',
773
+ 'qwen3-coder-plus': 'qwen3.7-plus',
774
+ 'qwen3-coder-next': 'qwen3.7-plus',
669
775
  'qwen3-coder-flash': 'qwen3.6-flash',
776
+ 'qwen3-max': 'qwen3.7-max',
777
+ },
778
+ // The preview is retired and routed to qwen3.8-max, which Alibaba says to use.
779
+ 'qwen-token-plan': {
780
+ 'qwen3.8-max-preview': 'qwen3.8-max',
781
+ 'qwen3-coder-plus': 'qwen3.7-plus',
782
+ 'qwen3-coder-next': 'qwen3.7-plus',
783
+ 'qwen3-max': 'qwen3.7-max',
670
784
  },
671
785
  'qwen-cn': {
672
786
  'qwen3-coder-plus': 'qwen3.7-plus',
673
787
  'qwen3-coder-next': 'qwen3.7-plus',
674
788
  'qwen3.7-max': 'qwen3.7-plus',
789
+ 'qwen3-max': 'qwen3.7-plus',
675
790
  },
676
791
  'qwen-cn-api': {
677
- 'qwen3-coder-plus': 'qwen3.7-max',
678
- 'qwen3-coder-next': 'qwen3.7-max',
792
+ 'qwen3-coder-plus': 'qwen3.7-plus',
793
+ 'qwen3-coder-next': 'qwen3.7-plus',
679
794
  'qwen3-coder-flash': 'qwen3.6-flash',
795
+ 'qwen3-max': 'qwen3.7-max',
796
+ },
797
+ // The one entry on a dynamic catalogue, and the exception to the rule above:
798
+ // the 480B coder was Codeep's own fallback default, so most configs holding
799
+ // it never chose it, and ModelScope stopped serving it (see the catalogue
800
+ // entry). Exact id only; every other ModelScope id stays the user's. Drop
801
+ // this if ModelScope serves the 480B again.
802
+ modelscope: {
803
+ 'Qwen/Qwen3-Coder-480B-A35B-Instruct': 'Qwen/Qwen3.5-397B-A17B',
680
804
  },
681
805
  };
806
+ /**
807
+ * One exact lookup, never followed further: every target must already be a
808
+ * model its own provider offers (providers.test.ts holds the map to that), so a
809
+ * chain can never be needed and the macOS mirror stays a flat table too.
810
+ */
682
811
  export function replacementModelFor(providerId, modelId) {
683
812
  return RETIRED_MODEL_REPLACEMENTS[providerId]?.[modelId];
684
813
  }
814
+ /** The whole map, read-only — for the invariant test and nothing else. */
815
+ export function retiredModelReplacements() {
816
+ return RETIRED_MODEL_REPLACEMENTS;
817
+ }
685
818
  /**
686
819
  * Curated display order for the first-run login flow + `/provider` /
687
820
  * `/login` pickers. Headline / popular providers float to the top so
@@ -811,11 +944,16 @@ export function providerNoStreamWithTools(providerId) {
811
944
  * omission as default. Kimi K2.x code/thinking models fix temperature
812
945
  * internally and 400 on any custom value, so they're here too. Google removed
813
946
  * the deprecated sampling parameters outright in the Gemini 3.7 generation.
947
+ * GPT-6 takes temperature/top_p only at reasoning_effort "none" (Astra never):
948
+ * the direct `openai` provider already omits them for every model through
949
+ * requiresDefaultTemperature, so this entry is for `openai/gpt-6-*` on
950
+ * OpenRouter, the one path where a GPT-6 id could still be sent one.
814
951
  */
815
952
  const SAMPLING_PARAMS_REJECTED = [
816
953
  'claude-fable-5', 'claude-opus-5', 'claude-opus-4-8', 'claude-opus-4-7', 'claude-sonnet-5',
817
954
  'kimi-k3', 'kimi-k2.7-code', 'kimi-for-coding', 'k3',
818
955
  'gemini-3.7-flash', 'gemini-3.8-flash',
956
+ 'gpt-6',
819
957
  ];
820
958
  export function modelRejectsSamplingParams(model) {
821
959
  // Canonicalize both sides. OpenRouter routes these as `google/gemini-3.7-flash`
@@ -837,6 +975,25 @@ export function getEffectiveMaxTokens(providerId, requested) {
837
975
  return requested;
838
976
  return Math.min(requested, provider.maxOutputTokens);
839
977
  }
978
+ /**
979
+ * The smallest response budget (`max_tokens`) worth sending this model, or 0
980
+ * for no floor. Callers take the larger of this and whatever they would have
981
+ * sent, then apply getEffectiveMaxTokens as usual.
982
+ *
983
+ * Claude Opus 5.5 thinks on every request — adaptive thinking cannot be turned
984
+ * off — and "tends to think more per turn than Claude Opus 5" at the same
985
+ * effort; Anthropic's notes say to "leave room in max_tokens for the thinking",
986
+ * which spends the same limit as the answer. The task planner's 2048, or a
987
+ * maxTokens lowered in /settings, could go on thinking alone and cut the reply
988
+ * off. 32K is Codeep's own default; the Max tier gets 64K, where Anthropic's
989
+ * advice for max effort on Opus 5 is "starting at 64k tokens". Matched on the
990
+ * canonical id, so `anthropic/claude-opus-5.5` on OpenRouter is covered too.
991
+ */
992
+ export function minResponseTokensFor(model, tier) {
993
+ if (!idMatches(canonicalModelId(model), 'claude-opus-5-5'))
994
+ return 0;
995
+ return tier === 'max' ? 65_536 : 32_768;
996
+ }
840
997
  export const REASONING_TIERS = ['auto', 'low', 'medium', 'high', 'max'];
841
998
  /**
842
999
  * Canonicalize a model id for capability matching: lowercase, drop any
@@ -855,6 +1012,64 @@ export function canonicalModelId(model) {
855
1012
  function idMatches(id, prefix) {
856
1013
  return id === prefix || id.startsWith(`${prefix}-`);
857
1014
  }
1015
+ /**
1016
+ * GPT-6 Sol and Luna on Chat Completions support function calling "only with
1017
+ * `reasoning_effort` set to `none`" (developers.openai.com models/gpt-6-sol,
1018
+ * guides/latest-model). So a request that carries tools to them must send
1019
+ * "none", whatever /thinking says. Direct `openai` only: OpenRouter may reach
1020
+ * OpenAI through the Responses API, where the rule does not apply, and Astra
1021
+ * rejects "none" with a 400 (it is not offered on `openai` at all).
1022
+ */
1023
+ export function toolsForceReasoningOff(providerId, model) {
1024
+ if (providerId !== 'openai')
1025
+ return false;
1026
+ const id = canonicalModelId(model);
1027
+ return idMatches(id, 'gpt-6-sol') || idMatches(id, 'gpt-6-luna');
1028
+ }
1029
+ /**
1030
+ * What /thinking must tell the user when the tier does not reach every request
1031
+ * for this model, or null. Without it the setting would look applied while
1032
+ * agent turns quietly ran at "none".
1033
+ */
1034
+ export function agentTurnReasoningNote(providerId, model) {
1035
+ if (!toolsForceReasoningOff(providerId, model))
1036
+ return null;
1037
+ return `Agent turns on ${model} send reasoning_effort "none" whatever the tier — OpenAI's Chat Completions API lets GPT-6 Sol and Luna call tools only with reasoning off. The tier applies to plain chat.`;
1038
+ }
1039
+ /**
1040
+ * What our Max tier sends as OpenRouter's unified `reasoning.effort`.
1041
+ *
1042
+ * OpenRouter accepts "xhigh" and "max", but only where the model does. Its
1043
+ * /api/v1/models `reasoning.supported_efforts` (read 2026-09-23) lists "max"
1044
+ * for GPT-5.6 and GPT-6, the Claude 5 family and Opus 4.7/4.8, DeepSeek V4.1
1045
+ * Flash and V4 Pro 0813, and Kimi K3; "xhigh" is the ceiling for GPT-5.4/5.5,
1046
+ * Grok 4.6/4.7 and Qwen 3.8 Max. Everything else keeps the old "high" cap —
1047
+ * including the 0423 `deepseek/deepseek-v4-pro` preview (xhigh|high only) and
1048
+ * Grok 4.5/4.3, which list no xhigh — because OpenRouter does not document what
1049
+ * happens to a level a model does not list.
1050
+ */
1051
+ function openRouterMaxEffort(model) {
1052
+ const id = canonicalModelId(model);
1053
+ const max = ['gpt-5-6', 'gpt-6', 'claude-opus-5', 'claude-fable-5', 'claude-sonnet-5',
1054
+ 'claude-opus-4-7', 'claude-opus-4-8', 'deepseek-v4-1-flash', 'deepseek-v4-pro-0813', 'kimi-k3'];
1055
+ if (max.some(prefix => idMatches(id, prefix)))
1056
+ return 'max';
1057
+ const xhigh = ['gpt-5-5', 'gpt-5-4', 'grok-4-6', 'grok-4-7', 'qwen3-8-max'];
1058
+ if (xhigh.some(prefix => idMatches(id, prefix)))
1059
+ return 'xhigh';
1060
+ return 'high';
1061
+ }
1062
+ /**
1063
+ * Grok models whose ceiling is "xhigh". xAI's reasoning guide: "`xhigh` is
1064
+ * available on `grok-4.6` and later", and grok-4.7 lists it. grok-4.5 and
1065
+ * grok-4.3 are disputed — their model pages list xhigh, while the guide says 4.5
1066
+ * treats it as "high" and the May-15 page gives 4.3 four levels ending at high —
1067
+ * so they keep the high ceiling until a live request settles it.
1068
+ */
1069
+ function grokHasXhigh(model) {
1070
+ const id = canonicalModelId(model);
1071
+ return idMatches(id, 'grok-4-6') || idMatches(id, 'grok-4-7');
1072
+ }
858
1073
  /**
859
1074
  * Does this provider+model expose a GRADED thinking-effort control we can drive?
860
1075
  * Used to gate the `/thinking` UI — hidden entirely for models without one.
@@ -890,16 +1105,21 @@ export function modelSupportsReasoningEffort(providerId, model) {
890
1105
  // Turbo is a plain toggle.
891
1106
  return idMatches(id, 'glm-5-2') || idMatches(id, 'glm-5-3');
892
1107
  case 'kimi':
893
- return idMatches(id, 'k3');
1108
+ // `kimi-for-coding` is K2.8 Preview since 2026-09-11 and takes low/high/max
1109
+ // (default max). Matched EXACTLY: idMatches would also take in
1110
+ // `kimi-for-coding-highspeed`, which is K2.7 Code HighSpeed — thinking
1111
+ // always on, no effort ladder.
1112
+ return idMatches(id, 'k3') || id === 'kimi-for-coding';
894
1113
  case 'kimi-api':
895
1114
  case 'kimi-cn':
896
1115
  return idMatches(id, 'kimi-k3');
897
1116
  case 'grok':
898
- // Grok reasoning models accept reasoning_effort (none/low/medium/high).
899
- // The coders (grok-code-fast, grok-build — the default) are NON-reasoning
900
- // and 400 on reasoning_effort; a 400 here silently drops the whole turn
901
- // into the weaker text-tool fallback (agentChat.ts), so exclude them
902
- // alongside the explicit *-non-reasoning variants.
1117
+ // Grok reasoning models accept reasoning_effort (low/medium/high, plus
1118
+ // xhigh from grok-4.6; 4.3 also takes none, which no tier sends). The
1119
+ // coders (grok-code-fast, grok-build — the default) reason internally but
1120
+ // have no effort control and 400 on the parameter; a 400 here silently
1121
+ // drops the whole turn into the weaker text-tool fallback (agentChat.ts),
1122
+ // so exclude them alongside the explicit *-non-reasoning variants.
903
1123
  if (id.startsWith('grok-build') || id.startsWith('grok-code'))
904
1124
  return false;
905
1125
  return id.startsWith('grok') && !id.includes('non-reasoning');
@@ -919,7 +1139,14 @@ export function modelSupportsReasoningEffort(providerId, model) {
919
1139
  * models, or providers without a graded knob — so callers can spread it
920
1140
  * unconditionally. Keep in lockstep with macOS `ModelTuning.reasoningParams`.
921
1141
  */
922
- export function reasoningParamsFor(providerId, model, tier) {
1142
+ export function reasoningParamsFor(providerId, model, tier,
1143
+ /** The request carries a non-empty `tools` array (native tool calling). */
1144
+ opts = {}) {
1145
+ // Before the 'auto' return on purpose: auto sends nothing, GPT-6 Sol/Luna then
1146
+ // run at their default "medium", and that is exactly the combination OpenAI
1147
+ // documents as unable to call tools on Chat Completions.
1148
+ if (opts.tools && toolsForceReasoningOff(providerId, model))
1149
+ return { reasoning_effort: 'none' };
923
1150
  // 'auto', or any unexpected value from an older/garbled config, → no param.
924
1151
  // (Guards against ever emitting e.g. `effort: undefined`, which could 400.)
925
1152
  if (tier === 'auto' || !REASONING_TIERS.includes(tier))
@@ -930,9 +1157,14 @@ export function reasoningParamsFor(providerId, model, tier) {
930
1157
  case 'anthropic':
931
1158
  // low / medium / high / max — all valid on the capable Claude models.
932
1159
  return { output_config: { effort: tier } };
933
- case 'openai':
934
- // none/low/medium/high/xhigh — no "max"; map our Max → xhigh (the ceiling).
935
- return { reasoning_effort: tier === 'max' ? 'xhigh' : tier };
1160
+ case 'openai': {
1161
+ // Chat Completions takes none/minimal/low/medium/high/xhigh/max, but "max"
1162
+ // is per model: it arrived with GPT-5.6 (changelog 2026-07-09) and every
1163
+ // GPT-6 page lists it. GPT-5.5 and earlier top out at xhigh.
1164
+ const id = canonicalModelId(model);
1165
+ const hasMax = idMatches(id, 'gpt-5-6') || idMatches(id, 'gpt-6');
1166
+ return { reasoning_effort: tier === 'max' ? (hasMax ? 'max' : 'xhigh') : tier };
1167
+ }
936
1168
  case 'google':
937
1169
  // Gemini's OpenAI-compat layer maps reasoning_effort onto thinking_level
938
1170
  // and documents low | medium | high. (Medium 400'd on Gemini 3 Preview,
@@ -941,13 +1173,12 @@ export function reasoningParamsFor(providerId, model, tier) {
941
1173
  // 'minimal' is deliberately not emitted: 3.7 Flash rejects it outright.
942
1174
  return { reasoning_effort: tier === 'max' ? 'high' : tier };
943
1175
  case 'deepseek':
944
- // V4.1 Flash distinguishes low / high / max (DeepSeek's mapping table:
945
- // minimal+low→low, medium+high+xhigh→high, max+ultra→max). The retired V4
946
- // ids graded only high|max. Always an effort, never a disabled block.
947
- if (idMatches(canonicalModelId(model), 'deepseek-flash')) {
948
- return { reasoning_effort: tier === 'low' ? 'low' : tier === 'max' ? 'max' : 'high' };
949
- }
950
- return { reasoning_effort: tier === 'max' ? 'max' : 'high' };
1176
+ // V4.1 Flash and V4 Pro both distinguish low / high / max — "The thinking
1177
+ // modes of V4-Pro and V4-Flash now support three thinking effort levels"
1178
+ // (changelog 2026-08-13; mapping table: minimal+low→low,
1179
+ // medium+high+xhigh→high, max+ultra→max). Always an effort, never a
1180
+ // disabled block.
1181
+ return { reasoning_effort: tier === 'low' ? 'low' : tier === 'max' ? 'max' : 'high' };
951
1182
  case 'z.ai':
952
1183
  case 'z.ai-api':
953
1184
  case 'z.ai-cn':
@@ -965,11 +1196,12 @@ export function reasoningParamsFor(providerId, model, tier) {
965
1196
  // Kimi K3 accepts low/high/max; collapse our medium tier to high.
966
1197
  return { reasoning_effort: tier === 'low' ? 'low' : tier === 'max' ? 'max' : 'high' };
967
1198
  case 'grok':
968
- // none/low/medium/high — no "max"; map our Max → high (the ceiling).
969
- return { reasoning_effort: tier === 'max' ? 'high' : tier };
1199
+ // No "max" on xAI; our Max maps to the model's own ceiling — xhigh from
1200
+ // grok-4.6, high below it (see grokHasXhigh).
1201
+ return { reasoning_effort: tier === 'max' ? (grokHasXhigh(model) ? 'xhigh' : 'high') : tier };
970
1202
  case 'openrouter':
971
- // Unified reasoning object; no "max" effort → cap at high.
972
- return { reasoning: { effort: tier === 'max' ? 'high' : tier } };
1203
+ // Unified reasoning object; Max goes as high as the model lists.
1204
+ return { reasoning: { effort: tier === 'max' ? openRouterMaxEffort(model) : tier } };
973
1205
  default:
974
1206
  return {};
975
1207
  }
@@ -977,8 +1209,11 @@ export function reasoningParamsFor(providerId, model, tier) {
977
1209
  /**
978
1210
  * The DISTINCT tiers a given provider+model actually exposes — used to build a
979
1211
  * per-model picker that only offers levels the model can tell apart (e.g.
980
- * GLM-5.2/DeepSeek grade only high|max; Gemini via the OpenAI-compat layer only
981
- * low|high). Always leads with 'auto'. `[]` for models with no graded knob.
1212
+ * GLM-5.2 grades only high|max; Gemini via the OpenAI-compat layer has no max).
1213
+ * Always leads with 'auto'. `[]` for models with no graded knob.
1214
+ *
1215
+ * GPT-6 Sol/Luna list their full set: it is what they run on plain chat. Agent
1216
+ * turns send "none" regardless (toolsForceReasoningOff), and /thinking says so.
982
1217
  *
983
1218
  * Kept in lockstep with `reasoningParamsFor` (the providers-test asserts every
984
1219
  * listed tier yields a DISTINCT param, so this can't silently drift). Mirrors
@@ -997,9 +1232,7 @@ export function availableReasoningTiers(providerId, model) {
997
1232
  // coding, so collapsing it hid the setting most users want.
998
1233
  return ['auto', 'low', 'medium', 'high'];
999
1234
  case 'deepseek':
1000
- return idMatches(canonicalModelId(model), 'deepseek-flash')
1001
- ? ['auto', 'low', 'high', 'max']
1002
- : ['auto', 'high', 'max'];
1235
+ return ['auto', 'low', 'high', 'max'];
1003
1236
  case 'z.ai':
1004
1237
  case 'z.ai-api':
1005
1238
  case 'z.ai-cn':
@@ -1013,9 +1246,13 @@ export function availableReasoningTiers(providerId, model) {
1013
1246
  case 'kimi-cn':
1014
1247
  return ['auto', 'low', 'high', 'max'];
1015
1248
  case 'grok':
1016
- return ['auto', 'low', 'medium', 'high'];
1249
+ return grokHasXhigh(model)
1250
+ ? ['auto', 'low', 'medium', 'high', 'max']
1251
+ : ['auto', 'low', 'medium', 'high'];
1017
1252
  case 'openrouter':
1018
- return ['auto', 'low', 'medium', 'high'];
1253
+ return openRouterMaxEffort(model) === 'high'
1254
+ ? ['auto', 'low', 'medium', 'high']
1255
+ : ['auto', 'low', 'medium', 'high', 'max'];
1019
1256
  default:
1020
1257
  return [];
1021
1258
  }