codex-multi-auth 2.10.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.codex-plugin/plugin.json +1 -1
  2. package/README.md +1 -1
  3. package/config/README.md +3 -1
  4. package/config/codex-legacy.json +300 -0
  5. package/config/codex-modern.json +96 -0
  6. package/dist/lib/capability-policy.d.ts.map +1 -1
  7. package/dist/lib/capability-policy.js +18 -2
  8. package/dist/lib/config.d.ts.map +1 -1
  9. package/dist/lib/config.js +5 -2
  10. package/dist/lib/constants.d.ts +9 -0
  11. package/dist/lib/constants.d.ts.map +1 -1
  12. package/dist/lib/constants.js +29 -0
  13. package/dist/lib/context-budget/model-context-windows.d.ts +1 -1
  14. package/dist/lib/context-budget/model-context-windows.d.ts.map +1 -1
  15. package/dist/lib/context-budget/model-context-windows.js +12 -0
  16. package/dist/lib/entitlement-cache.d.ts.map +1 -1
  17. package/dist/lib/entitlement-cache.js +2 -1
  18. package/dist/lib/model-capability-matrix.d.ts.map +1 -1
  19. package/dist/lib/model-capability-matrix.js +9 -1
  20. package/dist/lib/policy/runtime-policy.d.ts +9 -1
  21. package/dist/lib/policy/runtime-policy.d.ts.map +1 -1
  22. package/dist/lib/policy/runtime-policy.js +1 -0
  23. package/dist/lib/request/error-classification.d.ts.map +1 -1
  24. package/dist/lib/request/error-classification.js +45 -4
  25. package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
  26. package/dist/lib/request/helpers/model-map.js +181 -0
  27. package/dist/lib/usage/ledger.d.ts +1 -1
  28. package/dist/lib/usage/ledger.d.ts.map +1 -1
  29. package/dist/lib/usage/ledger.js +9 -0
  30. package/dist/lib/usage/pricing.d.ts +17 -2
  31. package/dist/lib/usage/pricing.d.ts.map +1 -1
  32. package/dist/lib/usage/pricing.js +72 -1
  33. package/dist/lib/usage/redaction.d.ts +1 -1
  34. package/dist/lib/usage/redaction.d.ts.map +1 -1
  35. package/dist/lib/usage/redaction.js +13 -0
  36. package/dist/lib/usage/types.d.ts +33 -0
  37. package/dist/lib/usage/types.d.ts.map +1 -1
  38. package/dist/lib/usage/types.js +28 -1
  39. package/dist/lib/usage/usage-extraction.d.ts +2 -1
  40. package/dist/lib/usage/usage-extraction.d.ts.map +1 -1
  41. package/dist/lib/usage/usage-extraction.js +44 -4
  42. package/package.json +1 -1
  43. package/scripts/codex.js +153 -2
@@ -35,7 +35,15 @@ export function buildModelCapabilityMatrix(input) {
35
35
  });
36
36
  const entitlementKeys = [accountKey, entitlementKey];
37
37
  for (const model of models) {
38
- const profile = MODEL_PROFILES[model] ?? MODEL_PROFILES[resolveNormalizedModel(model)];
38
+ // `Object.hasOwn` before the index: `models` comes from `--model` on
39
+ // the CLI, so `constructor` and friends reach this lookup and a bare
40
+ // index returns the matching `Object.prototype` member. It is truthy,
41
+ // so the `!profile` guard passes it through and the matrix emits a row
42
+ // whose normalizedModel, promptFamily and capabilities are all
43
+ // undefined.
44
+ const profile = Object.hasOwn(MODEL_PROFILES, model)
45
+ ? MODEL_PROFILES[model]
46
+ : MODEL_PROFILES[resolveNormalizedModel(model)];
39
47
  if (!profile)
40
48
  continue;
41
49
  const entitlement = getEntitlementBlock(input.entitlements, entitlementKeys, profile.normalizedModel, now);
@@ -2,7 +2,7 @@ import type { CapabilityPolicyStore } from "../capability-policy.js";
2
2
  import { type AccountPolicyStore } from "../account-policy.js";
3
3
  import { type BudgetGuardEvaluation, type BudgetGuardStore } from "../budget-guard.js";
4
4
  import { type ProjectRoutingProfileContext } from "../routing-profiles.js";
5
- import { appendUsageLedgerRow, type UsageLedgerOperation, type UsageLedgerOutcome, type UsageLedgerSource } from "../usage/index.js";
5
+ import { appendUsageLedgerRow, type UsageLedgerOperation, type UsageLedgerOutcome, type UsageLedgerSource, type UsageServiceTier } from "../usage/index.js";
6
6
  export interface RuntimePolicyAccount {
7
7
  index: number;
8
8
  accountId?: string | null;
@@ -46,6 +46,14 @@ export interface RuntimeUsageRecordInput {
46
46
  cachedInputTokens?: number | null;
47
47
  reasoningTokens?: number | null;
48
48
  totalTokens?: number | null;
49
+ /**
50
+ * Billed service tier, when upstream reported one. Carried explicitly
51
+ * because the row below is rebuilt field by field: a spread from the
52
+ * usage deferral reaches this input, but anything not named here is
53
+ * dropped before the ledger, which would price a Fast response at the
54
+ * standard rate.
55
+ */
56
+ serviceTier?: UsageServiceTier;
49
57
  }
50
58
  export declare function loadRuntimePolicyState(startDir?: string): Promise<RuntimePolicyState>;
51
59
  export declare function evaluateRuntimePolicy(input: {
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-policy.d.ts","sourceRoot":"","sources":["../../../lib/policy/runtime-policy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime-policy.d.ts","sourceRoot":"","sources":["../../../lib/policy/runtime-policy.ts"],"names":[],"mappings":"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"}
@@ -187,6 +187,7 @@ export function createRuntimeUsageRecorder(input) {
187
187
  cachedInputTokens: recordInput.cachedInputTokens,
188
188
  reasoningTokens: recordInput.reasoningTokens,
189
189
  totalTokens: recordInput.totalTokens,
190
+ serviceTier: recordInput.serviceTier,
190
191
  };
191
192
  await append(row).catch(() => undefined);
192
193
  },
@@ -1 +1 @@
1
- {"version":3,"file":"error-classification.d.ts","sourceRoot":"","sources":["../../../lib/request/error-classification.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,WAAW,gBAAgB;IACzB,aAAa,EAAE,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACvB;AAED,sGAAsG;AACtG,eAAO,MAAM,oCAAoC,6CAA6C,CAAC;AAQ/F,eAAO,MAAM,wCAAwC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAoB7E,CAAC;AAEF,MAAM,WAAW,yBAAyB;IACzC,aAAa,EAAE,OAAO,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,sCAAsC;IACtD,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnC,+BAA+B,EAAE,OAAO,CAAC;IACzC,iCAAiC,EAAE,OAAO,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACvC;AA0CD,wBAAgB,oCAAoC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgBzF;AAED,sGAAsG;AACtG,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQ3F;AAED,wBAAgB,4BAA4B,CAC3C,SAAS,EAAE,OAAO,GAChB,yBAAyB,CAqD3B;AAED,wBAAgB,oCAAoC,CACnD,OAAO,EAAE,sCAAsC,GAC7C,MAAM,GAAG,SAAS,CAgCpB;AAED;;GAEG;AACH,wBAAgB,uCAAuC,CACtD,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,SAAS,EAAE,OAAO,GAChB,OAAO,CAgBT;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK1E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,MAAM,GACrB,OAAO,CAoDT;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,CAmB1E"}
1
+ {"version":3,"file":"error-classification.d.ts","sourceRoot":"","sources":["../../../lib/request/error-classification.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,WAAW,gBAAgB;IACzB,aAAa,EAAE,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACvB;AAED,sGAAsG;AACtG,eAAO,MAAM,oCAAoC,6CAA6C,CAAC;AAQ/F,eAAO,MAAM,wCAAwC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAoD7E,CAAC;AAEF,MAAM,WAAW,yBAAyB;IACzC,aAAa,EAAE,OAAO,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,sCAAsC;IACtD,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnC,+BAA+B,EAAE,OAAO,CAAC;IACzC,iCAAiC,EAAE,OAAO,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACvC;AAiDD,wBAAgB,oCAAoC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgBzF;AAED,sGAAsG;AACtG,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQ3F;AAED,wBAAgB,4BAA4B,CAC3C,SAAS,EAAE,OAAO,GAChB,yBAAyB,CAqD3B;AAED,wBAAgB,oCAAoC,CACnD,OAAO,EAAE,sCAAsC,GAC7C,MAAM,GAAG,SAAS,CAkCpB;AAED;;GAEG;AACH,wBAAgB,uCAAuC,CACtD,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,SAAS,EAAE,OAAO,GAChB,OAAO,CAgBT;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK1E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,MAAM,GACrB,OAAO,CAoDT;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,CAmB1E"}
@@ -4,13 +4,45 @@
4
4
  * re-exports the public symbols so existing importers are unchanged.
5
5
  */
6
6
  import { isRecord } from "../utils.js";
7
- import { HTTP_STATUS } from "../constants.js";
7
+ import { HTTP_STATUS, stripModelEffortSuffix } from "../constants.js";
8
8
  /** @internal Exported only for sibling lib/request modules; import via fetch-helpers.ts elsewhere. */
9
9
  export const CHATGPT_CODEX_UNSUPPORTED_MODEL_CODE = "model_not_supported_with_chatgpt_account";
10
10
  const CHATGPT_CODEX_UNSUPPORTED_MODEL_PATTERN = /model is not supported when using codex with a chatgpt account/i;
11
11
  const NORMALIZED_UNSUPPORTED_MODEL_PATTERN = /the model ['"]([^'"]+)['"] is not currently available for this chatgpt account/i;
12
12
  const MODEL_ACCESS_DENIED_PATTERN = /the model [`'"]([^`'"]+)[`'"] does not exist or you do not have access to it/i;
13
13
  export const DEFAULT_UNSUPPORTED_CODEX_FALLBACK_CHAIN = {
14
+ // GPT-6 Astra rolls out org by org, so an account that is not entitled yet
15
+ // gets a real unsupported-model response for it. This chain only fires when
16
+ // the user has opted into `fallbackOnUnsupportedCodexModel` (default
17
+ // `false`), and only on that response, so it never silently swaps the model
18
+ // out from under a request the account could have served.
19
+ //
20
+ // This table is walked ONE HOP AT A TIME, not as a per-request candidate
21
+ // list. index.ts reassigns `model` to whatever came back and passes that as
22
+ // `requestedModel` on the next unsupported response, so reaching `gpt-5.5`
23
+ // from Astra requires `gpt-5.6-sol` to carry its own entry below. A second
24
+ // element here is only ever consulted when the first is already in
25
+ // `attemptedModels` for that same call.
26
+ //
27
+ // Depth is not free: every hop spends one of the shared per-request outbound
28
+ // attempts (`tryConsumeOutboundRequestAttempt`). A single-account balanced
29
+ // session gets a budget of 5, and the aeon walk needs exactly 5, so it fits
30
+ // with nothing spare. Spend an attempt on a retry or a stream failover and
31
+ // the tail hops become unreachable, ending as an attempt-budget-exhausted
32
+ // 503 rather than `gpt-5.4`. Hops are ordered most-valuable-first so what is
33
+ // lost first matters least. Do not add a hop to a GPT-6 row without
34
+ // re-checking that budget; `test/gpt6-astra-models.test.ts` asserts it.
35
+ "gpt-6-astra": ["gpt-5.6-sol", "gpt-5.5"],
36
+ // aeon steps to the flagship first: still GPT-6, still Astra, just without
37
+ // the long-horizon behaviour.
38
+ "gpt-6-astra-aeon": ["gpt-6-astra", "gpt-5.6-sol"],
39
+ // The hop that makes the Astra path reach a model every account has. GPT-5.6
40
+ // shipped without a chain entry, so an unsupported 5.6 response ended the
41
+ // walk; with Astra above it that would have stranded the fallback one rung
42
+ // short of the floor it documents. Terra and Luna are deliberately still
43
+ // absent: nothing steps into them, so giving them a hop would change 5.6
44
+ // behaviour beyond completing this path.
45
+ "gpt-5.6-sol": ["gpt-5.5"],
14
46
  "gpt-5": ["gpt-5.5"],
15
47
  "gpt-5-pro": ["gpt-5.5-pro"],
16
48
  "gpt-5-chat-latest": ["gpt-5.5"],
@@ -40,10 +72,17 @@ function canonicalizeModelName(model) {
40
72
  const stripped = trimmed.includes("/")
41
73
  ? (trimmed.split("/").pop() ?? trimmed)
42
74
  : trimmed;
43
- return stripped.replace(/-(none|minimal|low|medium|high|xhigh)$/i, "");
75
+ return stripModelEffortSuffix(stripped);
44
76
  }
45
77
  function normalizeFallbackChain(customChain) {
46
- const normalized = {};
78
+ // Null-prototype, because both the keys written here and the keys read out of
79
+ // it are caller-controlled. On a plain object, a `customChain` entry named
80
+ // `__proto__` reassigns this object's prototype instead of adding a row, and
81
+ // a lookup for `constructor` returns `Object.prototype.constructor`, a
82
+ // truthy non-array whose `.length` is 1, so the emptiness check passes and
83
+ // the `for...of` below throws `targets is not iterable` inside the request
84
+ // path.
85
+ const normalized = Object.create(null);
47
86
  for (const [key, values] of Object.entries(DEFAULT_UNSUPPORTED_CODEX_FALLBACK_CHAIN)) {
48
87
  const normalizedKey = canonicalizeModelName(key);
49
88
  if (!normalizedKey)
@@ -158,7 +197,9 @@ export function resolveUnsupportedCodexFallbackModel(options) {
158
197
  }
159
198
  const chain = normalizeFallbackChain(options.customChain);
160
199
  const targets = chain[currentModel] ?? [];
161
- if (targets.length === 0)
200
+ // Belt and braces with the null-prototype chain above: a `customChain` the
201
+ // caller passed straight through could still carry a non-array value.
202
+ if (!Array.isArray(targets) || targets.length === 0)
162
203
  return undefined;
163
204
  for (const target of targets) {
164
205
  if (!options.fallbackToGpt52OnUnsupportedGpt53 &&
@@ -1 +1 @@
1
- {"version":3,"file":"model-map.d.ts","sourceRoot":"","sources":["../../../../lib/request/helpers/model-map.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EACX,oBAAoB,EACpB,mBAAmB,EACnB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,CAAC;AAE1D,MAAM,MAAM,iBAAiB,GAC1B,aAAa,GACb,WAAW,GACX,OAAO,GACP,SAAS,GACT,SAAS,CAAC;AAEb;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,iBAAiB,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,WAAW,EAMvC,CAAC;AAEX,MAAM,WAAW,iBAAiB;IACjC,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,iBAAiB,CAAC;IAChC,sBAAsB,EAAE,oBAAoB,CAAC;IAC7C,yBAAyB,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAC3D,YAAY,EAAE,iBAAiB,CAAC;CAChC;AA6CD,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AACnD,eAAO,MAAM,aAAa,YAAY,CAAC;AAQvC,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AAOjD,eAAO,MAAM,uBAAuB,iGAO1B,CAAC;AA4EX;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CA6G9C,CAAC;AAEX,QAAA,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,CAAC;AAqG7C,OAAO,EAAE,SAAS,EAAE,CAAC;AAoKrB;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQtE;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CA2BxE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,CAavE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,CAEjF;AAeD;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAC1C,KAAK,EAAE,MAAM,GAAG,SAAS,GACvB,mBAAmB,CASrB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAErD"}
1
+ {"version":3,"file":"model-map.d.ts","sourceRoot":"","sources":["../../../../lib/request/helpers/model-map.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EACX,oBAAoB,EACpB,mBAAmB,EACnB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,CAAC;AAE1D,MAAM,MAAM,iBAAiB,GAC1B,aAAa,GACb,WAAW,GACX,OAAO,GACP,SAAS,GACT,SAAS,CAAC;AAEb;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,iBAAiB,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,WAAW,EAMvC,CAAC;AAEX,MAAM,WAAW,iBAAiB;IACjC,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,iBAAiB,CAAC;IAChC,sBAAsB,EAAE,oBAAoB,CAAC;IAC7C,yBAAyB,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAC3D,YAAY,EAAE,iBAAiB,CAAC;CAChC;AA6CD,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AACnD,eAAO,MAAM,aAAa,YAAY,CAAC;AAevC,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AAOjD,eAAO,MAAM,uBAAuB,iGAO1B,CAAC;AAoIX;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CA+I9C,CAAC;AAEX,QAAA,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,CAAC;AAgI7C,OAAO,EAAE,SAAS,EAAE,CAAC;AAqOrB;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQtE;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAuCxE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,CAavE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,CAEjF;AAeD;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAC1C,KAAK,EAAE,MAAM,GAAG,SAAS,GACvB,mBAAmB,CASrB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAErD"}
@@ -60,6 +60,13 @@ export const DEFAULT_MODEL = "gpt-5.5";
60
60
  // actual request routing and the legacy `gpt-5` alias remain opt-in per 2.5.0.
61
61
  // Bare `gpt-5.6` aliases to Sol; we pin the canonical id so the probe display
62
62
  // and report `modelSelection` read `gpt-5.6-sol` without a remap arrow.
63
+ //
64
+ // GPT-6 Astra deliberately does NOT lead the probe as of its 2026-09-03 launch.
65
+ // A probe only needs a response's quota headers, and Astra is still rolling out
66
+ // org by org, so leading with it would spend one failed request per probe for
67
+ // every account without entitlement yet and buy nothing. Move DEFAULT_PROBE_MODEL
68
+ // to `gpt-6-astra` once it is broadly available, and put `gpt-5.6-sol` directly
69
+ // behind it in QUOTA_PROBE_MODEL_CHAIN.
63
70
  export const DEFAULT_PROBE_MODEL = "gpt-5.6-sol";
64
71
  // Single source of truth for the live/quota probe fallback chain. Both the
65
72
  // manager probe (lib/quota-probe.ts) and the runtime probe (lib/runtime/quota-probe.ts)
@@ -102,6 +109,57 @@ const GPT_5_6_LUNA_EFFORTS = [
102
109
  "xhigh",
103
110
  "max",
104
111
  ];
112
+ /**
113
+ * GPT-6 Astra, OpenAI's 2026-09-03 frontier release.
114
+ *
115
+ * There is no Sol/Terra/Luna split this generation: the announced lineup is the
116
+ * flagship plus `aeon`, a long-horizon variant built for runs measured in days.
117
+ * `gpt-6-astra` is the API model name OpenAI published at launch;
118
+ * `gpt-6-astra-aeon` is the second slug that shipped beside it in the Codex
119
+ * model list. "Astra Pro" is a plan tier, not a separate slug we have seen, so
120
+ * it is deliberately not registered as its own canonical model — the GPT-6
121
+ * resolver below claims `gpt-6-astra-pro` and every other unrecognised GPT-6 id
122
+ * for the flagship rather than letting it fall through to GPT-5.5.
123
+ */
124
+ const GPT_6_ASTRA_MODEL = "gpt-6-astra";
125
+ const GPT_6_ASTRA_AEON_MODEL = "gpt-6-astra-aeon";
126
+ /** Bare `gpt-6` resolves to the flagship, mirroring bare `gpt-5.6` -> Sol. */
127
+ const GPT_6_FLAGSHIP_ALIAS = "gpt-6";
128
+ /**
129
+ * Astra inherits the GPT-5.6 frontier effort ladder: no `none`/`minimal`, and
130
+ * `ultra` at the top. OpenAI has published no default effort for Astra, so the
131
+ * flagship follows the tier it succeeds (Sol, `low`) and the long-horizon
132
+ * variant follows the catalog's other long-running models (`medium`).
133
+ */
134
+ const GPT_6_ASTRA_EFFORTS = [
135
+ "low",
136
+ "medium",
137
+ "high",
138
+ "xhigh",
139
+ "max",
140
+ "ultra",
141
+ ];
142
+ /**
143
+ * Cyber-specialty models from the upstream Codex catalog
144
+ * (openai/codex `codex-rs/models-manager/models.json`).
145
+ *
146
+ * They are hidden in the Codex picker and gated behind the Daybreak program,
147
+ * but the catalog marks them `supported_in_api`, so a client can and does name
148
+ * them. Until now every `gpt-daybreak-*` id missed the codex resolver (no
149
+ * `codex` token) and the general GPT-5 resolver (no `gpt 5` tokens) and landed
150
+ * on `DEFAULT_MODEL` — asking for the cyber-permissive model silently ran
151
+ * GPT-5.5. Reasoning ladders and defaults below are the catalog's own values.
152
+ */
153
+ const DAYBREAK_BLUE_MODEL = "gpt-daybreak-blue-latest";
154
+ const DAYBREAK_RED_MODEL = "gpt-daybreak-red-latest";
155
+ const DAYBREAK_EFFORTS = [
156
+ "low",
157
+ "medium",
158
+ "high",
159
+ "xhigh",
160
+ "max",
161
+ "ultra",
162
+ ];
105
163
  const GPT_5_5_CANONICAL_MODEL = "gpt-5.5";
106
164
  const GPT_5_5_PRO_CANONICAL_MODEL = "gpt-5.5-pro";
107
165
  const GPT_5_5_RELEASE_MODEL = "gpt-5.5-2026-04-23";
@@ -180,6 +238,40 @@ export const MODEL_PROFILES = {
180
238
  supportedReasoningEfforts: ["medium"],
181
239
  capabilities: TOOL_CAPABILITIES.compactOnly,
182
240
  },
241
+ // Like GPT-5.6, GPT-6 Astra ships its base instructions inline in the
242
+ // upstream model catalog rather than as a `gpt_6_prompt.md`, so it stays on
243
+ // the GPT-5.2 prompt family with every other post-5.2 general model. Adding
244
+ // a `gpt-6` prompt family here would also widen MODEL_FAMILIES, which is a
245
+ // persisted key space (`activeIndexByFamily`) and would need a storage
246
+ // migration to grow.
247
+ [GPT_6_ASTRA_MODEL]: {
248
+ normalizedModel: GPT_6_ASTRA_MODEL,
249
+ promptFamily: "gpt-5.2",
250
+ defaultReasoningEffort: "low",
251
+ supportedReasoningEfforts: GPT_6_ASTRA_EFFORTS,
252
+ capabilities: TOOL_CAPABILITIES.full,
253
+ },
254
+ [GPT_6_ASTRA_AEON_MODEL]: {
255
+ normalizedModel: GPT_6_ASTRA_AEON_MODEL,
256
+ promptFamily: "gpt-5.2",
257
+ defaultReasoningEffort: "medium",
258
+ supportedReasoningEfforts: GPT_6_ASTRA_EFFORTS,
259
+ capabilities: TOOL_CAPABILITIES.full,
260
+ },
261
+ [DAYBREAK_BLUE_MODEL]: {
262
+ normalizedModel: DAYBREAK_BLUE_MODEL,
263
+ promptFamily: "gpt-5.2",
264
+ defaultReasoningEffort: "low",
265
+ supportedReasoningEfforts: DAYBREAK_EFFORTS,
266
+ capabilities: TOOL_CAPABILITIES.full,
267
+ },
268
+ [DAYBREAK_RED_MODEL]: {
269
+ normalizedModel: DAYBREAK_RED_MODEL,
270
+ promptFamily: "gpt-5.2",
271
+ defaultReasoningEffort: "medium",
272
+ supportedReasoningEfforts: DAYBREAK_EFFORTS,
273
+ capabilities: TOOL_CAPABILITIES.full,
274
+ },
183
275
  // GPT-5.6 ships its base instructions inline in the upstream model catalog
184
276
  // rather than as a `gpt_5_6_prompt.md`, so these stay on the GPT-5.2 prompt
185
277
  // family alongside the other post-5.2 general models.
@@ -282,6 +374,21 @@ function addGpt56Aliases() {
282
374
  addEffortAliases(GPT_5_6_LUNA_MODEL, GPT_5_6_LUNA_MODEL, GPT_5_6_LUNA_EFFORTS);
283
375
  addEffortAliases(GPT_5_6_FLAGSHIP_ALIAS, GPT_5_6_SOL_MODEL, GPT_5_6_SOL_TERRA_EFFORTS);
284
376
  }
377
+ function addGpt6Aliases() {
378
+ addEffortAliases(GPT_6_ASTRA_MODEL, GPT_6_ASTRA_MODEL, GPT_6_ASTRA_EFFORTS);
379
+ addEffortAliases(GPT_6_ASTRA_AEON_MODEL, GPT_6_ASTRA_AEON_MODEL, GPT_6_ASTRA_EFFORTS);
380
+ addEffortAliases(GPT_6_FLAGSHIP_ALIAS, GPT_6_ASTRA_MODEL, GPT_6_ASTRA_EFFORTS);
381
+ // `astra` on its own is how the model is spoken about everywhere; accept it
382
+ // rather than letting it fall through to GPT-5.5.
383
+ addEffortAliases("astra", GPT_6_ASTRA_MODEL, GPT_6_ASTRA_EFFORTS);
384
+ addEffortAliases("astra-aeon", GPT_6_ASTRA_AEON_MODEL, GPT_6_ASTRA_EFFORTS);
385
+ }
386
+ function addDaybreakAliases() {
387
+ addEffortAliases(DAYBREAK_BLUE_MODEL, DAYBREAK_BLUE_MODEL, DAYBREAK_EFFORTS);
388
+ addEffortAliases(DAYBREAK_RED_MODEL, DAYBREAK_RED_MODEL, DAYBREAK_EFFORTS);
389
+ addEffortAliases("daybreak-blue", DAYBREAK_BLUE_MODEL, DAYBREAK_EFFORTS);
390
+ addEffortAliases("daybreak-red", DAYBREAK_RED_MODEL, DAYBREAK_EFFORTS);
391
+ }
285
392
  function addGeneralAliases() {
286
393
  addReasoningAliases(GPT_5_5_CANONICAL_MODEL, GPT_5_5_CANONICAL_MODEL);
287
394
  addReasoningAliases(GPT_5_5_RELEASE_MODEL, GPT_5_5_CANONICAL_MODEL);
@@ -320,6 +427,8 @@ function addCodexAliases() {
320
427
  addCodexAliases();
321
428
  addGeneralAliases();
322
429
  addGpt56Aliases();
430
+ addGpt6Aliases();
431
+ addDaybreakAliases();
323
432
  export { MODEL_MAP };
324
433
  function stripProviderPrefix(modelId) {
325
434
  return modelId.includes("/") ? (modelId.split("/").pop() ?? modelId) : modelId;
@@ -395,6 +504,68 @@ function resolveCodexCatalogModel(modelId) {
395
504
  * model than the caller asked for. Unrecognised tiers resolve to Sol, matching
396
505
  * OpenAI's bare `gpt-5.6` alias.
397
506
  */
507
+ /**
508
+ * Resolve GPT-6 identifiers that are not exact aliases — a dated snapshot id
509
+ * (`gpt-6-astra-2026-09-03`), the "Astra Pro" plan tier (`gpt-6-astra-pro`), or
510
+ * any tier name OpenAI adds after this file was written.
511
+ *
512
+ * This is the same guard the 5.6 resolver exists for, one major version up:
513
+ * without it, `resolveGeneralGpt5CatalogModel` never matches (it requires a
514
+ * `gpt 5` token pair) and every unrecognised GPT-6 id lands on `DEFAULT_MODEL`,
515
+ * running GPT-5.5 for a caller who asked for the frontier model. `aeon` keeps
516
+ * its own canonical id because it is a behaviourally different model (long
517
+ * horizon), not a rename of the flagship; everything else resolves to the
518
+ * flagship, matching OpenAI's bare `gpt-6` alias.
519
+ *
520
+ * Ids carrying a `codex` token are left to `resolveCodexCatalogModel`, exactly
521
+ * as the 5.6 resolver defers them.
522
+ */
523
+ function resolveGpt6CatalogModel(modelId) {
524
+ const tokens = tokenizeModelId(modelId);
525
+ const gptIndex = tokens.indexOf("gpt");
526
+ const versionToken = gptIndex === -1 ? undefined : tokens[gptIndex + 1];
527
+ // `gpt6` with no separator tokenizes as ONE token, so the `gpt` + `6` pair
528
+ // never forms and it used to fall through to GPT-5.5 while still passing
529
+ // capability-policy's catalog gate. Gate and resolver have to agree on the
530
+ // same id or the policy store keys state a request never reads.
531
+ const isGpt6 = versionToken === "6" || tokens.includes("gpt6");
532
+ // A bare `astra` token counts too. OpenAI's own launch material and every
533
+ // picker label say "Astra" without the `gpt-6` prefix, so `Astra Pro` and
534
+ // `astra-fast` reach this resolver with no version tokens at all; without
535
+ // this clause they miss every branch and land on GPT-5.5.
536
+ //
537
+ // It is anchored, not a free-floating substring: an id that names a
538
+ // DIFFERENT GPT major version does not get claimed for the frontier model
539
+ // just because `astra` appears in it, so `gpt-4-astra-x` is declined here
540
+ // rather than silently running GPT-6.
541
+ const namesOtherGptVersion = versionToken !== undefined &&
542
+ /^\d+$/.test(versionToken) &&
543
+ versionToken !== "6";
544
+ const isAstra = tokens.includes("astra") && !namesOtherGptVersion;
545
+ if ((!isGpt6 && !isAstra) || tokens.includes("codex")) {
546
+ return undefined;
547
+ }
548
+ if (tokens.includes("aeon"))
549
+ return GPT_6_ASTRA_AEON_MODEL;
550
+ return GPT_6_ASTRA_MODEL;
551
+ }
552
+ /**
553
+ * Resolve the Daybreak cyber models, including ids that are not exact aliases
554
+ * (a pinned `gpt-daybreak-red-2026-08-14`, say).
555
+ *
556
+ * `red` is the cyber-permissive variant and `blue` the defensive one; an
557
+ * unrecognised Daybreak id resolves to `blue`, the more restricted of the two,
558
+ * so a typo cannot silently upgrade a caller into the permissive model.
559
+ */
560
+ function resolveDaybreakCatalogModel(modelId) {
561
+ const tokens = tokenizeModelId(modelId);
562
+ if (!tokens.includes("daybreak")) {
563
+ return undefined;
564
+ }
565
+ if (tokens.includes("red"))
566
+ return DAYBREAK_RED_MODEL;
567
+ return DAYBREAK_BLUE_MODEL;
568
+ }
398
569
  function resolveGpt56CatalogModel(modelId) {
399
570
  const tokens = tokenizeModelId(modelId);
400
571
  const gptIndex = tokens.indexOf("gpt");
@@ -476,10 +647,20 @@ export function resolveNormalizedModel(model) {
476
647
  if (mappedModel) {
477
648
  return mappedModel;
478
649
  }
650
+ // Daybreak first: its slugs carry neither a `codex` nor a `gpt 5` token, so
651
+ // every other resolver declines them and they would reach DEFAULT_MODEL.
652
+ const daybreakCatalogModel = resolveDaybreakCatalogModel(modelId);
653
+ if (daybreakCatalogModel) {
654
+ return daybreakCatalogModel;
655
+ }
479
656
  const codexCatalogModel = resolveCodexCatalogModel(modelId);
480
657
  if (codexCatalogModel) {
481
658
  return codexCatalogModel;
482
659
  }
660
+ const gpt6CatalogModel = resolveGpt6CatalogModel(modelId);
661
+ if (gpt6CatalogModel) {
662
+ return gpt6CatalogModel;
663
+ }
483
664
  const gpt56CatalogModel = resolveGpt56CatalogModel(modelId);
484
665
  if (gpt56CatalogModel) {
485
666
  return gpt56CatalogModel;
@@ -1,4 +1,4 @@
1
- import type { UsageLedgerAppendInput, UsageLedgerPaths, UsageLedgerQuery, UsageLedgerRow, UsageSummary, UsageSummaryQuery } from "./types.js";
1
+ import { type UsageLedgerAppendInput, type UsageLedgerPaths, type UsageLedgerQuery, type UsageLedgerRow, type UsageSummary, type UsageSummaryQuery } from "./types.js";
2
2
  export declare function getUsageLedgerPaths(): UsageLedgerPaths;
3
3
  export declare function appendUsageLedgerRow(input: UsageLedgerAppendInput): Promise<UsageLedgerRow>;
4
4
  export declare function readUsageLedgerRows(query?: UsageLedgerQuery): Promise<UsageLedgerRow[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../../lib/usage/ledger.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACX,sBAAsB,EAEtB,gBAAgB,EAChB,gBAAgB,EAEhB,cAAc,EAEd,YAAY,EAGZ,iBAAiB,EAEjB,MAAM,YAAY,CAAC;AAqOpB,wBAAgB,mBAAmB,IAAI,gBAAgB,CAMtD;AAED,wBAAsB,oBAAoB,CACzC,KAAK,EAAE,sBAAsB,GAC3B,OAAO,CAAC,cAAc,CAAC,CASzB;AA+JD,wBAAsB,mBAAmB,CACxC,KAAK,GAAE,gBAAqB,GAC1B,OAAO,CAAC,cAAc,EAAE,CAAC,CAc3B;AAuDD,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,cAAc,EAAE,EACtB,KAAK,GAAE,iBAAsB,GAC3B,YAAY,CAwBd;AAED,wBAAsB,oBAAoB,CACzC,KAAK,GAAE,iBAAsB,GAC3B,OAAO,CAAC,YAAY,CAAC,CAEvB;AAED,wBAAsB,iBAAiB,CAAC,OAAO,GAAE;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2B9B;AAED,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD"}
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../../lib/usage/ledger.ts"],"names":[],"mappings":"AASA,OAAO,EAEN,KAAK,sBAAsB,EAE3B,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EAErB,KAAK,cAAc,EAEnB,KAAK,YAAY,EAGjB,KAAK,iBAAiB,EAEtB,MAAM,YAAY,CAAC;AAqOpB,wBAAgB,mBAAmB,IAAI,gBAAgB,CAMtD;AAED,wBAAsB,oBAAoB,CACzC,KAAK,EAAE,sBAAsB,GAC3B,OAAO,CAAC,cAAc,CAAC,CASzB;AAuKD,wBAAsB,mBAAmB,CACxC,KAAK,GAAE,gBAAqB,GAC1B,OAAO,CAAC,cAAc,EAAE,CAAC,CAc3B;AAuDD,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,cAAc,EAAE,EACtB,KAAK,GAAE,iBAAsB,GAC3B,YAAY,CAwBd;AAED,wBAAsB,oBAAoB,CACzC,KAAK,GAAE,iBAAsB,GAC3B,OAAO,CAAC,YAAY,CAAC,CAEvB;AAED,wBAAsB,iBAAiB,CAAC,OAAO,GAAE;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2B9B;AAED,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD"}
@@ -4,6 +4,7 @@ import { getCodexMultiAuthDir } from "../runtime-paths.js";
4
4
  import { logWarn } from "../logger.js";
5
5
  import { isRecord, sleep } from "../utils.js";
6
6
  import { normalizeUsageLedgerRow, usageRowToJsonLine, } from "./redaction.js";
7
+ import { isKnownServiceTier, } from "./types.js";
7
8
  const USAGE_DIR_NAME = "usage";
8
9
  const USAGE_LEDGER_FILE_NAME = "usage-ledger.jsonl";
9
10
  const RETRYABLE_FS_CODES = new Set(["EBUSY", "EPERM"]);
@@ -287,6 +288,14 @@ function normalizeParsedUsageRow(value) {
287
288
  Number.isFinite(value.tokens.totalTokens)
288
289
  ? Math.max(0, Math.trunc(value.tokens.totalTokens))
289
290
  : 0,
291
+ // Validated against the union rather than passed through: the file is
292
+ // on disk and can be edited, and an unrecognised string would reach the
293
+ // pricer as a tier it has no rate for. Rebuilding only the numeric
294
+ // fields dropped this entirely, so a persisted Fast row read back as
295
+ // standard in `usage` reports.
296
+ ...(isKnownServiceTier(value.tokens.serviceTier)
297
+ ? { serviceTier: value.tokens.serviceTier }
298
+ : {}),
290
299
  };
291
300
  const account = isRecord(value.account)
292
301
  ? {
@@ -1,9 +1,24 @@
1
- import type { UsageTokenCounts } from "./types.js";
1
+ import type { UsageServiceTier, UsageTokenCounts } from "./types.js";
2
2
  export interface UsageModelPricing {
3
3
  inputUsdPerMillion: number;
4
4
  outputUsdPerMillion: number;
5
5
  cachedInputUsdPerMillion?: number;
6
6
  reasoningUsdPerMillion?: number;
7
+ /**
8
+ * Rates for non-standard service tiers, keyed by `UsageServiceTier`.
9
+ *
10
+ * Every entry in `MODEL_PRICING` is a STANDARD-tier rate. OpenAI's Fast tier
11
+ * costs more (2x for GPT-6 Astra), so pricing a Fast session off the standard
12
+ * row under-counts it by half and lets a `maxCostUsd` cap overrun.
13
+ *
14
+ * A tier absent from this map is deliberately NOT approximated from the
15
+ * standard rate. Only Astra's Fast multiplier is published; inventing one for
16
+ * the rest would move a budget's trip point on a guess, which is the failure
17
+ * this file exists to avoid. `estimateUsageCostUsd` reports an unlisted tier
18
+ * as unknown cost instead, so a budget fails closed the same way it does for
19
+ * an unpriced model.
20
+ */
21
+ serviceTiers?: Partial<Record<UsageServiceTier, UsageModelPricing>>;
7
22
  }
8
23
  /**
9
24
  * Models the router can normalize to that have no published rate here yet.
@@ -19,7 +34,7 @@ export interface UsageModelPricing {
19
34
  * `test/usage-pricing-coverage.test.ts` fails if a NEW routable model appears
20
35
  * in neither list.
21
36
  */
22
- export declare const UNPRICED_ROUTABLE_MODELS: readonly ["gpt-5.1", "gpt-5.2-pro", "gpt-5.4-mini", "gpt-5.4-nano", "gpt-5.4-pro", "gpt-5.5-pro", "gpt-5-mini", "gpt-5-nano"];
37
+ export declare const UNPRICED_ROUTABLE_MODELS: readonly ["gpt-6-astra-aeon", "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.1", "gpt-5.2-pro", "gpt-5.4-mini", "gpt-5.4-nano", "gpt-5.4-pro", "gpt-5.5-pro", "gpt-5-mini", "gpt-5-nano"];
23
38
  export declare function getUsageModelPricing(model: string | null | undefined): UsageModelPricing | null;
24
39
  export declare function estimateUsageCostUsd(model: string | null | undefined, tokens: UsageTokenCounts): number | null;
25
40
  export declare function listUsageModelPricing(): Record<string, UsageModelPricing>;
@@ -1 +1 @@
1
- {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../../lib/usage/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CAChC;AA2DD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,+HAS3B,CAAC;AAOX,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,iBAAiB,GAAG,IAAI,CAM1B;AAED,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,MAAM,EAAE,gBAAgB,GACtB,MAAM,GAAG,IAAI,CAqBf;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAEzE"}
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../../lib/usage/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAErE,MAAM,WAAW,iBAAiB;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,CAAC,CAAC;CACpE;AAyFD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,0MAkB3B,CAAC;AAOX,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,iBAAiB,GAAG,IAAI,CAiB1B;AAmBD,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,MAAM,EAAE,gBAAgB,GACtB,MAAM,GAAG,IAAI,CA8Bf;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAEzE"}
@@ -1,4 +1,34 @@
1
1
  const MODEL_PRICING = {
2
+ // GPT-6 Astra, published at the 2026-09-03 launch: $10 / 1M input,
3
+ // $50 / 1M output on the standard service tier, and $20 / $100 on Fast,
4
+ // carried in `serviceTiers` below.
5
+ //
6
+ // The cached-input rate is the platform-wide 90% cached discount, which every
7
+ // other row in this table already encodes at exactly input/10. It shipped
8
+ // absent at first, on the reasoning that no Astra-specific cached figure was
9
+ // published; that made Astra the only model billing cached tokens at the FULL
10
+ // input rate, over-stating a cache-heavy session tenfold and tripping a
11
+ // `maxCostUsd` cap far too early. Over-stating is the safer direction than
12
+ // under-stating, but a 10x error blocks legitimate work, and the discount is
13
+ // a uniform platform rate rather than a per-model guess.
14
+ "gpt-6-astra": {
15
+ inputUsdPerMillion: 10,
16
+ outputUsdPerMillion: 50,
17
+ cachedInputUsdPerMillion: 1,
18
+ reasoningUsdPerMillion: 50,
19
+ serviceTiers: {
20
+ // 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.
24
+ priority: {
25
+ inputUsdPerMillion: 20,
26
+ outputUsdPerMillion: 100,
27
+ cachedInputUsdPerMillion: 2,
28
+ reasoningUsdPerMillion: 100,
29
+ },
30
+ },
31
+ },
2
32
  "gpt-5-codex": {
3
33
  inputUsdPerMillion: 1.25,
4
34
  outputUsdPerMillion: 10,
@@ -69,6 +99,15 @@ const MODEL_PRICING = {
69
99
  * in neither list.
70
100
  */
71
101
  export const UNPRICED_ROUTABLE_MODELS = [
102
+ // OpenAI published a rate for the Astra flagship at launch but not for the
103
+ // long-horizon `aeon` variant, and the Daybreak cyber models are sold under
104
+ // a separate controlled-access agreement with no public per-token rate.
105
+ // Pricing `aeon` off the flagship would be a guess on the model whose whole
106
+ // purpose is running for days, which is exactly where a wrong rate does the
107
+ // most damage.
108
+ "gpt-6-astra-aeon",
109
+ "gpt-daybreak-blue-latest",
110
+ "gpt-daybreak-red-latest",
72
111
  "gpt-5.1",
73
112
  "gpt-5.2-pro",
74
113
  "gpt-5.4-mini",
@@ -87,10 +126,42 @@ export function getUsageModelPricing(model) {
87
126
  if (!normalized) {
88
127
  return null;
89
128
  }
129
+ // `Object.hasOwn`, not a bare index. The model string arrives raw from the
130
+ // client (`createUsageLedgerRow` only trims it), so `constructor`,
131
+ // `toString` and friends reach this lookup and a bare index hands back the
132
+ // matching `Object.prototype` member. That object is truthy, so it is
133
+ // returned as a rate, and every field on it is undefined: the cost comes out
134
+ // `NaN` instead of `null`. A NaN cost is worse than an unknown one, because
135
+ // `NaN >= limit` is false, so it silently makes a `maxCostUsd` budget
136
+ // unenforceable rather than failing closed the way an unpriced model does.
137
+ if (!Object.hasOwn(MODEL_PRICING, normalized)) {
138
+ return null;
139
+ }
90
140
  return MODEL_PRICING[normalized] ?? null;
91
141
  }
142
+ /**
143
+ * Pick the rate that applies to a response's service tier.
144
+ *
145
+ * Returns `null` when the tier is real but this table has no rate for it, which
146
+ * the caller turns into an unknown cost. `standard` and an absent tier both use
147
+ * the base row, because every entry in `MODEL_PRICING` is a standard-tier rate.
148
+ */
149
+ function resolveServiceTierPricing(pricing, serviceTier) {
150
+ if (!serviceTier || serviceTier === "standard") {
151
+ return pricing;
152
+ }
153
+ return pricing.serviceTiers?.[serviceTier] ?? null;
154
+ }
92
155
  export function estimateUsageCostUsd(model, tokens) {
93
- const pricing = getUsageModelPricing(model);
156
+ const basePricing = getUsageModelPricing(model);
157
+ if (!basePricing) {
158
+ return null;
159
+ }
160
+ // Resolve the tier BEFORE any arithmetic. A response billed at a tier this
161
+ // table has no rate for must report unknown cost, not a standard-tier
162
+ // figure: under-counting is what lets a `maxCostUsd` cap overrun, and
163
+ // `evaluateBudgetGuard` already knows how to fail closed on `null`.
164
+ const pricing = resolveServiceTierPricing(basePricing, tokens.serviceTier);
94
165
  if (!pricing) {
95
166
  return null;
96
167
  }
@@ -1,4 +1,4 @@
1
- import type { UsageLedgerAccountRef, UsageLedgerAppendInput, UsageLedgerRow } from "./types.js";
1
+ import { type UsageLedgerAccountRef, type UsageLedgerAppendInput, type UsageLedgerRow } from "./types.js";
2
2
  export declare function hashUsageIdentifier(value: string): string;
3
3
  export declare function createUsageAccountRef(input: {
4
4
  accountId?: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../../../lib/usage/redaction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,qBAAqB,EACrB,sBAAsB,EAGtB,cAAc,EAGd,MAAM,YAAY,CAAC;AA2EpB,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC5C,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GAAG,qBAAqB,GAAG,IAAI,CAkB/B;AAsBD,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,sBAAsB,GAC3B,cAAc,CA0BhB;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAE9D"}
1
+ {"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../../../lib/usage/redaction.ts"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAG3B,KAAK,cAAc,EAGnB,MAAM,YAAY,CAAC;AA2EpB,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC5C,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GAAG,qBAAqB,GAAG,IAAI,CAkB/B;AAkCD,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,sBAAsB,GAC3B,cAAc,CA0BhB;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAE9D"}
@@ -1,4 +1,5 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
+ import { isKnownServiceTier, } from "./types.js";
2
3
  import { estimateUsageCostUsd } from "./pricing.js";
3
4
  const VALID_SOURCES = new Set([
4
5
  "runtime-proxy",
@@ -98,6 +99,18 @@ function normalizeTokens(input) {
98
99
  totalTokens: providedTotal === null
99
100
  ? computedTotal
100
101
  : Math.max(0, Math.trunc(providedTotal)),
102
+ // Carried through so `estimateUsageCostUsd` below can refuse to price a
103
+ // tier it has no rate for. Dropping it here would silently restore the
104
+ // standard-rate under-count this field exists to prevent.
105
+ //
106
+ // Validated, not passed through on truthiness. Every other field in this
107
+ // normaliser is checked against its allowed set (`normalizeSource`,
108
+ // `normalizeOperation`, `normalizeOutcome`), and the ledger READ path
109
+ // already rejects an unknown tier. Accepting one here made the write
110
+ // path the only unvalidated way onto a row.
111
+ ...(isKnownServiceTier(input.serviceTier)
112
+ ? { serviceTier: input.serviceTier }
113
+ : {}),
101
114
  };
102
115
  }
103
116
  export function normalizeUsageLedgerRow(input) {