codex-multi-auth 2.15.0 → 2.16.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.
@@ -1,3 +1,10 @@
1
+ import { RETIRED_MODEL_REPLACEMENTS } from "../request/helpers/model-map.js";
2
+ /**
3
+ * Largest input still billed at the short-context rate. OpenAI's GPT-6 model
4
+ * pages say "Prompts with more than 272K input tokens are priced at 2x input
5
+ * and cache rates", so exactly 272,000 is short and 272,001 is long.
6
+ */
7
+ export const SHORT_CONTEXT_MAX_INPUT_TOKENS = 272_000;
1
8
  const MODEL_PRICING = {
2
9
  // GPT-6 Astra, published at the 2026-09-03 launch: $10 / 1M input,
3
10
  // $50 / 1M output on the standard service tier, and $20 / $100 on Fast,
@@ -16,48 +23,85 @@ const MODEL_PRICING = {
16
23
  outputUsdPerMillion: 50,
17
24
  cachedInputUsdPerMillion: 1,
18
25
  reasoningUsdPerMillion: 50,
26
+ // Long-context rows from the same page (read 2026-09-23).
27
+ longContext: {
28
+ inputUsdPerMillion: 20,
29
+ outputUsdPerMillion: 75,
30
+ cachedInputUsdPerMillion: 2,
31
+ reasoningUsdPerMillion: 75,
32
+ },
19
33
  serviceTiers: {
20
34
  // Published at launch alongside the standard rate: Fast mode is up to
21
- // 2.5x the speed at 2x the price, $20 / $100 per 1M. This is the only
22
- // tier multiplier OpenAI has published for any model in this table,
23
- // which is why it is the only one listed anywhere in this file.
35
+ // 2.5x the speed at 2x the price, $20 / $100 per 1M. Only the GPT-6
36
+ // rows list a Fast tier, because only GPT-6 has a published Fast rate.
24
37
  priority: {
25
38
  inputUsdPerMillion: 20,
26
39
  outputUsdPerMillion: 100,
27
40
  cachedInputUsdPerMillion: 2,
28
41
  reasoningUsdPerMillion: 100,
42
+ longContext: {
43
+ inputUsdPerMillion: 40,
44
+ outputUsdPerMillion: 150,
45
+ cachedInputUsdPerMillion: 4,
46
+ reasoningUsdPerMillion: 150,
47
+ },
29
48
  },
30
49
  },
31
50
  },
32
- "gpt-5-codex": {
33
- inputUsdPerMillion: 1.25,
34
- outputUsdPerMillion: 10,
35
- cachedInputUsdPerMillion: 0.125,
36
- reasoningUsdPerMillion: 10,
37
- },
38
- "gpt-5.1-codex": {
39
- inputUsdPerMillion: 1.25,
40
- outputUsdPerMillion: 10,
41
- cachedInputUsdPerMillion: 0.125,
42
- reasoningUsdPerMillion: 10,
43
- },
44
- "gpt-5.2": {
45
- inputUsdPerMillion: 1.25,
46
- outputUsdPerMillion: 10,
47
- cachedInputUsdPerMillion: 0.125,
48
- reasoningUsdPerMillion: 10,
49
- },
50
- "gpt-5.3-codex": {
51
- inputUsdPerMillion: 1.25,
51
+ // GPT-6 Sol and Luna, from the OpenAI API pricing page (read 2026-09-23;
52
+ // short- and long-context rows). Both publish a Fast tier at exactly 2x,
53
+ // like Astra.
54
+ "gpt-6-sol": {
55
+ inputUsdPerMillion: 2,
52
56
  outputUsdPerMillion: 10,
53
- cachedInputUsdPerMillion: 0.125,
57
+ cachedInputUsdPerMillion: 0.2,
54
58
  reasoningUsdPerMillion: 10,
59
+ longContext: {
60
+ inputUsdPerMillion: 4,
61
+ outputUsdPerMillion: 15,
62
+ cachedInputUsdPerMillion: 0.4,
63
+ reasoningUsdPerMillion: 15,
64
+ },
65
+ serviceTiers: {
66
+ priority: {
67
+ inputUsdPerMillion: 4,
68
+ outputUsdPerMillion: 20,
69
+ cachedInputUsdPerMillion: 0.4,
70
+ reasoningUsdPerMillion: 20,
71
+ longContext: {
72
+ inputUsdPerMillion: 8,
73
+ outputUsdPerMillion: 30,
74
+ cachedInputUsdPerMillion: 0.8,
75
+ reasoningUsdPerMillion: 30,
76
+ },
77
+ },
78
+ },
55
79
  },
56
- "gpt-5.4": {
57
- inputUsdPerMillion: 2,
58
- outputUsdPerMillion: 12,
59
- cachedInputUsdPerMillion: 0.2,
60
- reasoningUsdPerMillion: 12,
80
+ "gpt-6-luna": {
81
+ inputUsdPerMillion: 0.1,
82
+ outputUsdPerMillion: 0.5,
83
+ cachedInputUsdPerMillion: 0.01,
84
+ reasoningUsdPerMillion: 0.5,
85
+ longContext: {
86
+ inputUsdPerMillion: 0.2,
87
+ outputUsdPerMillion: 0.75,
88
+ cachedInputUsdPerMillion: 0.02,
89
+ reasoningUsdPerMillion: 0.75,
90
+ },
91
+ serviceTiers: {
92
+ priority: {
93
+ inputUsdPerMillion: 0.2,
94
+ outputUsdPerMillion: 1,
95
+ cachedInputUsdPerMillion: 0.02,
96
+ reasoningUsdPerMillion: 1,
97
+ longContext: {
98
+ inputUsdPerMillion: 0.4,
99
+ outputUsdPerMillion: 1.5,
100
+ cachedInputUsdPerMillion: 0.04,
101
+ reasoningUsdPerMillion: 1.5,
102
+ },
103
+ },
104
+ },
61
105
  },
62
106
  "gpt-5.5": {
63
107
  inputUsdPerMillion: 2,
@@ -108,14 +152,7 @@ export const UNPRICED_ROUTABLE_MODELS = [
108
152
  "gpt-6-astra-aeon",
109
153
  "gpt-daybreak-blue-latest",
110
154
  "gpt-daybreak-red-latest",
111
- "gpt-5.1",
112
- "gpt-5.2-pro",
113
- "gpt-5.4-mini",
114
- "gpt-5.4-nano",
115
- "gpt-5.4-pro",
116
155
  "gpt-5.5-pro",
117
- "gpt-5-mini",
118
- "gpt-5-nano",
119
156
  ];
120
157
  function normalizeModelName(model) {
121
158
  const trimmed = model?.trim().toLowerCase();
@@ -134,10 +171,19 @@ export function getUsageModelPricing(model) {
134
171
  // `NaN` instead of `null`. A NaN cost is worse than an unknown one, because
135
172
  // `NaN >= limit` is false, so it silently makes a `maxCostUsd` budget
136
173
  // unenforceable rather than failing closed the way an unpriced model does.
137
- if (!Object.hasOwn(MODEL_PRICING, normalized)) {
174
+ //
175
+ // A retired id is priced as the model it now runs on. The proxy records the
176
+ // raw client model string, so a new `gpt-5-codex` row is really a
177
+ // `gpt-5.6-sol` request; pricing it at the retired model's old rate would
178
+ // under-count a `maxCostUsd` budget. Rows already on disk are unaffected:
179
+ // the ledger stores `costUsd` when a row is written and never re-prices it.
180
+ const effective = Object.hasOwn(RETIRED_MODEL_REPLACEMENTS, normalized)
181
+ ? RETIRED_MODEL_REPLACEMENTS[normalized]
182
+ : normalized;
183
+ if (!effective || !Object.hasOwn(MODEL_PRICING, effective)) {
138
184
  return null;
139
185
  }
140
- return MODEL_PRICING[normalized] ?? null;
186
+ return MODEL_PRICING[effective] ?? null;
141
187
  }
142
188
  /**
143
189
  * Pick the rate that applies to a response's service tier.
@@ -152,6 +198,17 @@ function resolveServiceTierPricing(pricing, serviceTier) {
152
198
  }
153
199
  return pricing.serviceTiers?.[serviceTier] ?? null;
154
200
  }
201
+ function resolveContextLengthPricing(basePricing, tierPricing, inputTokens) {
202
+ if (inputTokens <= SHORT_CONTEXT_MAX_INPUT_TOKENS) {
203
+ return tierPricing;
204
+ }
205
+ if (tierPricing.longContext) {
206
+ return tierPricing.longContext;
207
+ }
208
+ // The model has long-context rates but not for this tier: unknown, never
209
+ // the cheaper short-context rate.
210
+ return basePricing.longContext ? null : tierPricing;
211
+ }
155
212
  export function estimateUsageCostUsd(model, tokens) {
156
213
  const basePricing = getUsageModelPricing(model);
157
214
  if (!basePricing) {
@@ -161,7 +218,11 @@ export function estimateUsageCostUsd(model, tokens) {
161
218
  // table has no rate for must report unknown cost, not a standard-tier
162
219
  // figure: under-counting is what lets a `maxCostUsd` cap overrun, and
163
220
  // `evaluateBudgetGuard` already knows how to fail closed on `null`.
164
- const pricing = resolveServiceTierPricing(basePricing, tokens.serviceTier);
221
+ const tierPricing = resolveServiceTierPricing(basePricing, tokens.serviceTier);
222
+ if (!tierPricing) {
223
+ return null;
224
+ }
225
+ const pricing = resolveContextLengthPricing(basePricing, tierPricing, tokens.inputTokens);
165
226
  if (!pricing) {
166
227
  return null;
167
228
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-multi-auth",
3
- "version": "2.15.0",
3
+ "version": "2.16.0",
4
4
  "description": "Codex CLI multi-account OAuth manager with account switching, health checks, runtime rotation, diagnostics, and recovery tools for @openai/codex",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/scripts/codex.js CHANGED
@@ -686,15 +686,15 @@ function resolveModelFamilyForStatus(model) {
686
686
  return "gpt-5.2";
687
687
  }
688
688
  if (normalized.includes("daybreak")) return "gpt-5.2";
689
- // GPT-5.6 general tiers share the gpt-5.2 prompt family (see
690
- // lib/request/helpers/model-map.ts MODEL_PROFILES). Check before the generic
691
- // gpt-5 catch-all, which would otherwise mis-bucket them as codex.
692
- if (normalized.startsWith("gpt-5.6")) return "gpt-5.2";
693
- if (normalized.startsWith("gpt-5.2")) return "gpt-5.2";
694
- if (normalized.startsWith("gpt-5.1")) return "gpt-5.1";
695
- if (normalized.includes("codex-max")) return "codex-max";
696
- if (normalized.includes("codex")) return "codex";
697
- if (normalized.startsWith("gpt-5")) return "gpt-5-codex";
689
+ // The proxy keys a /codex/responses request by `getModelFamily(model)`
690
+ // (lib/runtime-rotation-proxy.ts), i.e. the replacement's prompt family.
691
+ // Every codex model is retired and runs on `gpt-5.6-sol`/`terra`, and every
692
+ // live or retired general GPT-5 id runs on a model in the gpt-5.2 family, so
693
+ // status reads that family. The old `codex`/`codex-max`/`gpt-5.1`/
694
+ // `gpt-5-codex` answers named keys the proxy does not write for these ids.
695
+ if (normalized.includes("codex") || normalized.startsWith("gpt-5")) {
696
+ return "gpt-5.2";
697
+ }
698
698
  return null;
699
699
  }
700
700
 
@@ -1368,8 +1368,36 @@ const MODEL_ACCESS_DENIED_PATTERN =
1368
1368
  /the model [`'"]([^`'"]+)[`'"] does not exist or you do not have access to it/i;
1369
1369
  const DIRECT_UNSUPPORTED_MODEL_PATTERN =
1370
1370
  /['"]([^'"]+)['"]\s+model is not supported when using codex with a chatgpt account/i;
1371
- const CURRENT_CODEX_MODEL = "gpt-5.3-codex";
1372
- const LEGACY_CODEX_MODEL = "gpt-5-codex";
1371
+ // Every codex model is retired; codex ids run on the replacement OpenAI names
1372
+ // for them. Mirrors lib/request/helpers/model-map.ts CURRENT_CODEX_MODEL and
1373
+ // RETIRED_MODEL_REPLACEMENTS, pinned in parity by test/retired-models.test.ts.
1374
+ const CURRENT_CODEX_MODEL = "gpt-5.6-sol";
1375
+ const CODEX_MINI_REPLACEMENT_MODEL = "gpt-5.6-terra";
1376
+ const RETIRED_MODEL_REPLACEMENTS = {
1377
+ "gpt-5.4": "gpt-6-sol",
1378
+ "gpt-5.4-mini": "gpt-6-luna",
1379
+ "gpt-5.4-nano": "gpt-6-luna",
1380
+ "gpt-5.4-pro": "gpt-5.5-pro",
1381
+ "gpt-5.2": "gpt-5.6-sol",
1382
+ "gpt-5.2-pro": "gpt-5.5-pro",
1383
+ "gpt-5.1": "gpt-5.6-sol",
1384
+ "gpt-5-mini": "gpt-5.6-terra",
1385
+ "gpt-5-nano": "gpt-5.6-luna",
1386
+ "gpt-5-chat-latest": "gpt-5.6-sol",
1387
+ "gpt-5.1-chat-latest": "gpt-5.6-sol",
1388
+ "gpt-5.2-chat-latest": "gpt-5.6-sol",
1389
+ "gpt-5.3-chat-latest": "gpt-5.6-sol",
1390
+ "gpt-5.3-codex": CURRENT_CODEX_MODEL,
1391
+ "gpt-5.3-codex-spark": CURRENT_CODEX_MODEL,
1392
+ "gpt-5.2-codex": CURRENT_CODEX_MODEL,
1393
+ "gpt-5.1-codex": CURRENT_CODEX_MODEL,
1394
+ "gpt-5.1-codex-max": CURRENT_CODEX_MODEL,
1395
+ "gpt-5-codex": CURRENT_CODEX_MODEL,
1396
+ "codex-max": CURRENT_CODEX_MODEL,
1397
+ "gpt-5.1-codex-mini": CODEX_MINI_REPLACEMENT_MODEL,
1398
+ "gpt-5-codex-mini": CODEX_MINI_REPLACEMENT_MODEL,
1399
+ "codex-mini-latest": CODEX_MINI_REPLACEMENT_MODEL,
1400
+ };
1373
1401
  // Mirrors the GPT-6 rows of lib/request/error-classification.ts
1374
1402
  // `DEFAULT_UNSUPPORTED_CODEX_FALLBACK_CHAIN`. This table is walked differently
1375
1403
  // from lib's: `resolveUnsupportedModelRetryTarget` keys on the model named in
@@ -1381,25 +1409,25 @@ const LEGACY_CODEX_MODEL = "gpt-5-codex";
1381
1409
  const WRAPPER_UNSUPPORTED_MODEL_FALLBACK_CHAIN = {
1382
1410
  "gpt-6-astra": ["gpt-5.6-sol", "gpt-5.5"],
1383
1411
  "gpt-6-astra-aeon": ["gpt-6-astra", "gpt-5.6-sol"],
1412
+ "gpt-6-sol": ["gpt-5.6-sol"],
1413
+ "gpt-6-luna": ["gpt-5.6-luna"],
1384
1414
  "gpt-5.6-sol": ["gpt-5.5"],
1415
+ "gpt-5.6-luna": ["gpt-5.5"],
1416
+ "gpt-5.6-terra": ["gpt-5.5"],
1385
1417
  "gpt-5": ["gpt-5.5"],
1386
1418
  "gpt-5-pro": ["gpt-5.5-pro"],
1387
- "gpt-5-chat-latest": ["gpt-5.5"],
1388
- "gpt-5.5": ["gpt-5.4"],
1389
- "gpt-5.5-pro": ["gpt-5.4"],
1390
- "gpt-5.5-2026-04-23": ["gpt-5.4"],
1391
- "gpt-5.5-pro-2026-04-23": ["gpt-5.4"],
1392
- "gpt-5.5-20260423": ["gpt-5.4"],
1393
- "gpt-5.5-pro-20260423": ["gpt-5.4"],
1394
- "gpt-5.3-codex-spark": [CURRENT_CODEX_MODEL],
1395
- "codex-max": [CURRENT_CODEX_MODEL],
1396
- "gpt-5.1-codex-max": [CURRENT_CODEX_MODEL],
1397
- "codex-mini-latest": [CURRENT_CODEX_MODEL],
1398
- "gpt-5-codex-mini": [CURRENT_CODEX_MODEL],
1399
- "gpt-5.1-codex-mini": [CURRENT_CODEX_MODEL],
1400
- [LEGACY_CODEX_MODEL]: [CURRENT_CODEX_MODEL],
1401
- "gpt-5.2-codex": [CURRENT_CODEX_MODEL],
1402
- "gpt-5.1-codex": [CURRENT_CODEX_MODEL],
1419
+ "gpt-5.5-2026-04-23": ["gpt-5.5"],
1420
+ "gpt-5.5-20260423": ["gpt-5.5"],
1421
+ "gpt-5.5-pro-2026-04-23": ["gpt-5.5-pro"],
1422
+ "gpt-5.5-pro-20260423": ["gpt-5.5-pro"],
1423
+ // `gpt-5.5`/`gpt-5.5-pro` are the floor; `gpt-5.4` is retired. A retired id
1424
+ // steps once to its replacement.
1425
+ ...Object.fromEntries(
1426
+ Object.entries(RETIRED_MODEL_REPLACEMENTS).map(([retired, replacement]) => [
1427
+ retired,
1428
+ [replacement],
1429
+ ]),
1430
+ ),
1403
1431
  };
1404
1432
 
1405
1433
  function canonicalizeRequestedModelName(model) {
@@ -2022,9 +2050,10 @@ function hasCliAuthCredentialsStoreOverride(args) {
2022
2050
  // and its GPT-5 normalization helpers.
2023
2051
  // This wrapper runs before the TypeScript build, so it cannot import that source.
2024
2052
  const SUPPORTED_REASONING_EFFORTS_BY_MODEL = {
2025
- [CURRENT_CODEX_MODEL]: ["low", "medium", "high", "xhigh"],
2026
2053
  "gpt-6-astra": ["low", "medium", "high", "xhigh", "max", "ultra"],
2027
2054
  "gpt-6-astra-aeon": ["low", "medium", "high", "xhigh", "max", "ultra"],
2055
+ "gpt-6-sol": ["low", "medium", "high", "xhigh", "max", "ultra"],
2056
+ "gpt-6-luna": ["low", "medium", "high", "xhigh", "max"],
2028
2057
  "gpt-daybreak-blue-latest": ["low", "medium", "high", "xhigh", "max", "ultra"],
2029
2058
  "gpt-daybreak-red-latest": ["low", "medium", "high", "xhigh", "max", "ultra"],
2030
2059
  "gpt-5.6-sol": ["low", "medium", "high", "xhigh", "max", "ultra"],
@@ -2032,15 +2061,6 @@ const SUPPORTED_REASONING_EFFORTS_BY_MODEL = {
2032
2061
  "gpt-5.6-luna": ["low", "medium", "high", "xhigh", "max"],
2033
2062
  "gpt-5.5": ["none", "low", "medium", "high", "xhigh"],
2034
2063
  "gpt-5.5-pro": ["medium", "high", "xhigh"],
2035
- "gpt-5.4": ["none", "low", "medium", "high", "xhigh"],
2036
- "gpt-5.4-pro": ["medium", "high", "xhigh"],
2037
- "gpt-5.4-mini": ["medium"],
2038
- "gpt-5.4-nano": ["medium"],
2039
- "gpt-5.2-pro": ["medium", "high", "xhigh"],
2040
- "gpt-5.2": ["none", "low", "medium", "high", "xhigh"],
2041
- "gpt-5.1": ["none", "low", "medium", "high"],
2042
- "gpt-5-mini": ["medium"],
2043
- "gpt-5-nano": ["medium"],
2044
2064
  };
2045
2065
 
2046
2066
  const REASONING_FALLBACKS = {
@@ -2086,45 +2106,53 @@ const GPT_5_6_LUNA_MODEL = "gpt-5.6-luna";
2086
2106
  const GPT_5_6_FLAGSHIP_ALIAS = "gpt-5.6";
2087
2107
  const GPT_5_6_SOL_TERRA_EFFORTS = ["low", "medium", "high", "xhigh", "max", "ultra"];
2088
2108
  const GPT_5_6_LUNA_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
2089
- // GPT-6 Astra (2026-09-03). No Sol/Terra/Luna split this generation: the
2090
- // flagship plus `aeon`, a long-horizon variant. Same frontier effort ladder as
2091
- // 5.6 (no `none`/`minimal`, `ultra` at the top). Bare `gpt-6` -> flagship.
2109
+ // GPT-6 Astra (2026-09-03): the flagship plus `aeon`, a long-horizon variant.
2110
+ // Same frontier effort ladder as 5.6 (no `none`/`minimal`, `ultra` at the
2111
+ // top). Bare `gpt-6` -> flagship.
2092
2112
  const GPT_6_ASTRA_MODEL = "gpt-6-astra";
2093
2113
  const GPT_6_ASTRA_AEON_MODEL = "gpt-6-astra-aeon";
2094
2114
  const GPT_6_FLAGSHIP_ALIAS = "gpt-6";
2095
2115
  const GPT_6_ASTRA_EFFORTS = ["low", "medium", "high", "xhigh", "max", "ultra"];
2116
+ // GPT-6 Sol and Luna (upstream catalog, 2026-09-22). Sol reaches `ultra`, Luna
2117
+ // stops at `max`. No GPT-6 Terra. Mirrors lib/request/helpers/model-map.ts.
2118
+ const GPT_6_SOL_MODEL = "gpt-6-sol";
2119
+ const GPT_6_LUNA_MODEL = "gpt-6-luna";
2120
+ const GPT_6_SOL_EFFORTS = GPT_6_ASTRA_EFFORTS;
2121
+ const GPT_6_LUNA_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
2096
2122
  // Daybreak cyber models from the upstream Codex catalog. `red` is the
2097
2123
  // cyber-permissive variant, `blue` the defensive one.
2098
2124
  const DAYBREAK_BLUE_MODEL = "gpt-daybreak-blue-latest";
2099
2125
  const DAYBREAK_RED_MODEL = "gpt-daybreak-red-latest";
2100
2126
  const DAYBREAK_EFFORTS = ["low", "medium", "high", "xhigh", "max", "ultra"];
2127
+ // Retired minors map to the replacement OpenAI names for them. Mirrors lib.
2101
2128
  const GENERAL_GPT5_VERSION_CATALOG = {
2102
2129
  1: {
2103
- base: "gpt-5.1",
2130
+ base: GPT_5_6_SOL_MODEL,
2131
+ pro: GPT_5_5_PRO_CANONICAL_MODEL,
2104
2132
  },
2105
2133
  2: {
2106
- base: "gpt-5.2",
2107
- pro: "gpt-5.2-pro",
2134
+ base: GPT_5_6_SOL_MODEL,
2135
+ pro: GPT_5_5_PRO_CANONICAL_MODEL,
2108
2136
  },
2109
2137
  4: {
2110
- base: DEFAULT_GENERAL_GPT5_MODEL,
2111
- pro: "gpt-5.4-pro",
2112
- mini: "gpt-5.4-mini",
2113
- nano: "gpt-5.4-nano",
2138
+ base: GPT_6_SOL_MODEL,
2139
+ pro: GPT_5_5_PRO_CANONICAL_MODEL,
2140
+ mini: GPT_6_LUNA_MODEL,
2141
+ nano: GPT_6_LUNA_MODEL,
2114
2142
  },
2115
2143
  5: {
2116
2144
  base: GPT_5_5_CANONICAL_MODEL,
2117
2145
  pro: GPT_5_5_PRO_CANONICAL_MODEL,
2118
- mini: "gpt-5-mini",
2119
- nano: "gpt-5-nano",
2146
+ mini: GPT_5_6_TERRA_MODEL,
2147
+ nano: GPT_5_6_LUNA_MODEL,
2120
2148
  },
2121
2149
  };
2122
2150
  const GENERAL_GPT5_STABLE_VARIANTS = GENERAL_GPT5_VERSION_CATALOG[5];
2123
2151
  const GENERAL_GPT5_GENERIC_VARIANTS = {
2124
2152
  base: DEFAULT_GENERAL_GPT5_MODEL,
2125
2153
  pro: GPT_5_5_PRO_CANONICAL_MODEL,
2126
- mini: "gpt-5-mini",
2127
- nano: "gpt-5-nano",
2154
+ mini: GPT_5_6_TERRA_MODEL,
2155
+ nano: GPT_5_6_LUNA_MODEL,
2128
2156
  };
2129
2157
 
2130
2158
  function addRequestedModelAlias(alias, normalizedModel) {
@@ -2268,19 +2296,8 @@ function seedRequestedModelAliases() {
2268
2296
  GPT_5_5_PRO_RELEASE_COMPAT_MODEL,
2269
2297
  GPT_5_5_PRO_CANONICAL_MODEL,
2270
2298
  );
2271
- addRequestedModelReasoningAliases("gpt-5.4", "gpt-5.4");
2272
- addRequestedModelReasoningAliases("gpt-5.4-pro", "gpt-5.4-pro");
2273
- addRequestedModelReasoningAliases("gpt-5.4-mini", "gpt-5.4-mini");
2274
- addRequestedModelReasoningAliases("gpt-5.4-nano", "gpt-5.4-nano");
2275
- addRequestedModelReasoningAliases("gpt-5.2-pro", "gpt-5.2-pro");
2276
2299
  addRequestedModelReasoningAliases("gpt-5-pro", GPT_5_5_PRO_CANONICAL_MODEL);
2277
- addRequestedModelReasoningAliases("gpt-5.2", "gpt-5.2");
2278
- addRequestedModelReasoningAliases("gpt-5.1", "gpt-5.1");
2279
2300
  addRequestedModelReasoningAliases("gpt-5", DEFAULT_GENERAL_GPT5_MODEL);
2280
- addRequestedModelReasoningAliases("gpt-5-mini", "gpt-5-mini");
2281
- addRequestedModelReasoningAliases("gpt-5-nano", "gpt-5-nano");
2282
- addRequestedModelReasoningAliases("gpt-5.1-chat-latest", "gpt-5.1");
2283
- addRequestedModelReasoningAliases("gpt-5-chat-latest", DEFAULT_GENERAL_GPT5_MODEL);
2284
2301
  addRequestedModelEffortAliases(
2285
2302
  GPT_5_6_SOL_MODEL,
2286
2303
  GPT_5_6_SOL_MODEL,
@@ -2322,6 +2339,13 @@ function seedRequestedModelAliases() {
2322
2339
  GPT_6_ASTRA_AEON_MODEL,
2323
2340
  GPT_6_ASTRA_EFFORTS,
2324
2341
  );
2342
+ // No bare `sol`/`luna` aliases: those already mean the 5.6 tiers.
2343
+ addRequestedModelEffortAliases(GPT_6_SOL_MODEL, GPT_6_SOL_MODEL, GPT_6_SOL_EFFORTS);
2344
+ addRequestedModelEffortAliases(
2345
+ GPT_6_LUNA_MODEL,
2346
+ GPT_6_LUNA_MODEL,
2347
+ GPT_6_LUNA_EFFORTS,
2348
+ );
2325
2349
  addRequestedModelEffortAliases(
2326
2350
  DAYBREAK_BLUE_MODEL,
2327
2351
  DAYBREAK_BLUE_MODEL,
@@ -2342,17 +2366,10 @@ function seedRequestedModelAliases() {
2342
2366
  DAYBREAK_RED_MODEL,
2343
2367
  DAYBREAK_EFFORTS,
2344
2368
  );
2345
- addRequestedModelReasoningAliases(CURRENT_CODEX_MODEL, CURRENT_CODEX_MODEL);
2346
- addRequestedModelReasoningAliases("gpt-5.3-codex-spark", CURRENT_CODEX_MODEL);
2347
- addRequestedModelReasoningAliases(LEGACY_CODEX_MODEL, CURRENT_CODEX_MODEL);
2348
- addRequestedModelReasoningAliases("gpt-5.2-codex", CURRENT_CODEX_MODEL);
2349
- addRequestedModelReasoningAliases("gpt-5.1-codex", CURRENT_CODEX_MODEL);
2369
+ for (const [retired, replacement] of Object.entries(RETIRED_MODEL_REPLACEMENTS)) {
2370
+ addRequestedModelReasoningAliases(retired, replacement);
2371
+ }
2350
2372
  addRequestedModelAlias("gpt_5_codex", CURRENT_CODEX_MODEL);
2351
- addRequestedModelReasoningAliases("codex-max", CURRENT_CODEX_MODEL);
2352
- addRequestedModelReasoningAliases("gpt-5.1-codex-max", CURRENT_CODEX_MODEL);
2353
- addRequestedModelAlias("codex-mini-latest", CURRENT_CODEX_MODEL);
2354
- addRequestedModelReasoningAliases("gpt-5-codex-mini", CURRENT_CODEX_MODEL);
2355
- addRequestedModelReasoningAliases("gpt-5.1-codex-mini", CURRENT_CODEX_MODEL);
2356
2373
  }
2357
2374
 
2358
2375
  seedRequestedModelAliases();
@@ -2405,39 +2422,13 @@ function resolveStableGeneralGpt5Variant(variant) {
2405
2422
  // between the two, which is exactly what test/codex-model-resolution.test.ts
2406
2423
  // exists to prevent.
2407
2424
  function resolveCodexRequestedModel(normalized) {
2408
- if (
2409
- normalized.includes("gpt-5.1-codex-max") ||
2410
- normalized.includes("gpt 5.1 codex max") ||
2411
- normalized.includes("codex-max")
2412
- ) {
2413
- return CURRENT_CODEX_MODEL;
2425
+ // Every codex model is retired: a mini codex id goes to Terra, any other id
2426
+ // carrying `codex` to Sol. Mirrors lib resolveCodexCatalogModel.
2427
+ if (!normalized.includes("codex")) return "";
2428
+ if (/codex[- ]mini(?!mal)/.test(normalized)) {
2429
+ return CODEX_MINI_REPLACEMENT_MODEL;
2414
2430
  }
2415
- if (
2416
- normalized.includes("gpt-5.1-codex-mini") ||
2417
- normalized.includes("gpt 5.1 codex mini") ||
2418
- normalized.includes("gpt-5-codex-mini") ||
2419
- normalized.includes("gpt 5 codex mini") ||
2420
- normalized.includes("codex-mini-latest")
2421
- ) {
2422
- return CURRENT_CODEX_MODEL;
2423
- }
2424
- if (
2425
- normalized.includes("gpt-5.3-codex-spark") ||
2426
- normalized.includes("gpt 5.3 codex spark") ||
2427
- normalized.includes("gpt-5.3-codex") ||
2428
- normalized.includes("gpt 5.3 codex") ||
2429
- normalized.includes("gpt-5.2-codex") ||
2430
- normalized.includes("gpt 5.2 codex") ||
2431
- normalized.includes("gpt-5.1-codex") ||
2432
- normalized.includes("gpt 5.1 codex") ||
2433
- normalized.includes("gpt-5-codex") ||
2434
- normalized.includes("gpt 5 codex") ||
2435
- normalized.includes("codex")
2436
- ) {
2437
- return CURRENT_CODEX_MODEL;
2438
- }
2439
-
2440
- return "";
2431
+ return CURRENT_CODEX_MODEL;
2441
2432
  }
2442
2433
 
2443
2434
  // Resolve GPT-5.6 identifiers, including ones that are not exact aliases (e.g. a
@@ -2473,6 +2464,11 @@ function resolveGpt6RequestedModel(stripped) {
2473
2464
  return "";
2474
2465
  }
2475
2466
  if (tokens.includes("aeon")) return GPT_6_ASTRA_AEON_MODEL;
2467
+ if (isAstra) return GPT_6_ASTRA_MODEL;
2468
+ // `terra` goes to Sol: there is no GPT-6 Terra, and upstream migrates
2469
+ // `gpt-5.6-terra` users to Sol.
2470
+ if (tokens.includes("luna")) return GPT_6_LUNA_MODEL;
2471
+ if (tokens.includes("sol") || tokens.includes("terra")) return GPT_6_SOL_MODEL;
2476
2472
  return GPT_6_ASTRA_MODEL;
2477
2473
  }
2478
2474
 
@@ -2546,6 +2542,14 @@ function normalizeRequestedModel(model) {
2546
2542
  if (exactMatch) {
2547
2543
  return exactMatch;
2548
2544
  }
2545
+ // Mirrors lib: retry the alias lookup without a `-max`/`-ultra` suffix.
2546
+ const withoutTopEffort = normalized.replace(/-(max|ultra)$/, "");
2547
+ if (withoutTopEffort !== normalized) {
2548
+ const effortlessMatch = REQUESTED_MODEL_ALIASES.get(withoutTopEffort);
2549
+ if (effortlessMatch) {
2550
+ return effortlessMatch;
2551
+ }
2552
+ }
2549
2553
 
2550
2554
  const daybreakModel = resolveDaybreakRequestedModel(stripped);
2551
2555
  if (daybreakModel) {
@@ -6547,6 +6551,7 @@ export {
6547
6551
  resolveModelFamilyForStatus,
6548
6552
  canonicalizeRequestedModelName,
6549
6553
  WRAPPER_UNSUPPORTED_MODEL_FALLBACK_CHAIN,
6554
+ RETIRED_MODEL_REPLACEMENTS,
6550
6555
  };
6551
6556
 
6552
6557
  // Run the wrapper only when actually launched (as the `codex-multi-auth-codex`