From 60356f5c55f5ab44a6c7b60f222cde8e519bda8b Mon Sep 17 00:00:00 2001 From: Theo Browne Date: Tue, 29 Sep 2026 12:28:45 -0700 Subject: [PATCH 1/2] fix(codex): Pro Max accounts load, so Ultrafast shows up Codex 0.159 reports the `promax` plan, which our pinned 0.156 protocol rejects. That failed the whole `account/read` decode, so Codex never loaded for Pro Max accounts, the only plan with the Ultrafast tier. Generate `PlanType` as an open string, since we only use it for labels, and label `promax` accounts. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../src/provider/Layers/CodexProvider.ts | 2 + .../scripts/generate.ts | 9 +- .../src/_generated/schema.gen.ts | 205 ++---------------- .../src/schema.test.ts | 4 +- 4 files changed, 33 insertions(+), 187 deletions(-) diff --git a/apps/server/src/provider/Layers/CodexProvider.ts b/apps/server/src/provider/Layers/CodexProvider.ts index 57b0cab334bb..56d3fa21df1b 100644 --- a/apps/server/src/provider/Layers/CodexProvider.ts +++ b/apps/server/src/provider/Layers/CodexProvider.ts @@ -115,6 +115,8 @@ export function codexPlanLabel(planType: string | null | undefined): string | un return "ChatGPT Pro 20x Subscription"; case "prolite": return "ChatGPT Pro 5x Subscription"; + case "promax": + return "ChatGPT Pro Max Subscription"; case "team": return "ChatGPT Team Subscription"; case "self_serve_business_prolite": diff --git a/packages/effect-codex-app-server/scripts/generate.ts b/packages/effect-codex-app-server/scripts/generate.ts index b2696097ef8c..8825f6044fc2 100644 --- a/packages/effect-codex-app-server/scripts/generate.ts +++ b/packages/effect-codex-app-server/scripts/generate.ts @@ -145,6 +145,13 @@ const ManualSchemas: Record = { }, }; +// Codex adds plan slugs between our protocol refreshes (0.159 added `promax`). +// T3 Code only uses the plan for labels, so an unknown slug must not fail the +// whole `account/read` decode and take the provider down with it. +const DefinitionOverrides: Record = { + PlanType: { type: "string" }, +}; + const getGeneratedPaths = Effect.fn("getGeneratedPaths")(function* () { const path = yield* Path.Path; const generatedDir = path.join(import.meta.dirname, "..", "src", "_generated"); @@ -643,7 +650,7 @@ const generateFiles = Effect.fn("generateFiles")(function* () { aggregateSchemas[localDefinitionNames.get(definitionName)!] = stripNullDefaults( normalizeNullableTypes( rewriteExternalRefs( - definitionSchema, + DefinitionOverrides[definitionName] ?? definitionSchema, localDefinitionNames, file.namespace, exportNameByQualifiedName, diff --git a/packages/effect-codex-app-server/src/_generated/schema.gen.ts b/packages/effect-codex-app-server/src/_generated/schema.gen.ts index 9bc7b1ec1e9e..e9ac22ec120b 100644 --- a/packages/effect-codex-app-server/src/_generated/schema.gen.ts +++ b/packages/effect-codex-app-server/src/_generated/schema.gen.ts @@ -3204,43 +3204,10 @@ export const ServerNotification__AuthMode = Schema.Union( identifier: "ServerNotification__AuthMode", }); -export type ServerNotification__PlanType = - | "free" - | "go" - | "plus" - | "pro" - | "prolite" - | "team" - | "self_serve_business_prolite" - | "self_serve_business_usage_based" - | "business" - | "ent26" - | "enterprise_cbp_automation" - | "enterprise_cbp_usage_based" - | "enterprise" - | "edu" - | "edu_plus" - | "edu_pro" - | "unknown"; -export const ServerNotification__PlanType = Schema.Literals([ - "free", - "go", - "plus", - "pro", - "prolite", - "team", - "self_serve_business_prolite", - "self_serve_business_usage_based", - "business", - "ent26", - "enterprise_cbp_automation", - "enterprise_cbp_usage_based", - "enterprise", - "edu", - "edu_plus", - "edu_pro", - "unknown", -]).annotate({ identifier: "ServerNotification__PlanType" }); +export type ServerNotification__PlanType = string; +export const ServerNotification__PlanType = Schema.String.annotate({ + identifier: "ServerNotification__PlanType", +}); export type ServerNotification__CreditsSnapshot = { readonly balance?: string | null; @@ -4138,43 +4105,10 @@ export const V2AccountRateLimitsUpdatedNotification__SpendControlLimitSnapshot = used: Schema.String, }).annotate({ identifier: "V2AccountRateLimitsUpdatedNotification__SpendControlLimitSnapshot" }); -export type V2AccountRateLimitsUpdatedNotification__PlanType = - | "free" - | "go" - | "plus" - | "pro" - | "prolite" - | "team" - | "self_serve_business_prolite" - | "self_serve_business_usage_based" - | "business" - | "ent26" - | "enterprise_cbp_automation" - | "enterprise_cbp_usage_based" - | "enterprise" - | "edu" - | "edu_plus" - | "edu_pro" - | "unknown"; -export const V2AccountRateLimitsUpdatedNotification__PlanType = Schema.Literals([ - "free", - "go", - "plus", - "pro", - "prolite", - "team", - "self_serve_business_prolite", - "self_serve_business_usage_based", - "business", - "ent26", - "enterprise_cbp_automation", - "enterprise_cbp_usage_based", - "enterprise", - "edu", - "edu_plus", - "edu_pro", - "unknown", -]).annotate({ identifier: "V2AccountRateLimitsUpdatedNotification__PlanType" }); +export type V2AccountRateLimitsUpdatedNotification__PlanType = string; +export const V2AccountRateLimitsUpdatedNotification__PlanType = Schema.String.annotate({ + identifier: "V2AccountRateLimitsUpdatedNotification__PlanType", +}); export type V2AccountRateLimitsUpdatedNotification__RateLimitWindow = { readonly resetsAt?: number | null; @@ -4260,43 +4194,10 @@ export const V2AccountUpdatedNotification__AuthMode = Schema.Union( identifier: "V2AccountUpdatedNotification__AuthMode", }); -export type V2AccountUpdatedNotification__PlanType = - | "free" - | "go" - | "plus" - | "pro" - | "prolite" - | "team" - | "self_serve_business_prolite" - | "self_serve_business_usage_based" - | "business" - | "ent26" - | "enterprise_cbp_automation" - | "enterprise_cbp_usage_based" - | "enterprise" - | "edu" - | "edu_plus" - | "edu_pro" - | "unknown"; -export const V2AccountUpdatedNotification__PlanType = Schema.Literals([ - "free", - "go", - "plus", - "pro", - "prolite", - "team", - "self_serve_business_prolite", - "self_serve_business_usage_based", - "business", - "ent26", - "enterprise_cbp_automation", - "enterprise_cbp_usage_based", - "enterprise", - "edu", - "edu_plus", - "edu_pro", - "unknown", -]).annotate({ identifier: "V2AccountUpdatedNotification__PlanType" }); +export type V2AccountUpdatedNotification__PlanType = string; +export const V2AccountUpdatedNotification__PlanType = Schema.String.annotate({ + identifier: "V2AccountUpdatedNotification__PlanType", +}); export type V2AppListUpdatedNotification__AppReview = { readonly status: string }; export const V2AppListUpdatedNotification__AppReview = Schema.Struct({ @@ -5484,43 +5385,10 @@ export const V2GetAccountRateLimitsResponse__SpendControlLimitSnapshot = Schema. used: Schema.String, }).annotate({ identifier: "V2GetAccountRateLimitsResponse__SpendControlLimitSnapshot" }); -export type V2GetAccountRateLimitsResponse__PlanType = - | "free" - | "go" - | "plus" - | "pro" - | "prolite" - | "team" - | "self_serve_business_prolite" - | "self_serve_business_usage_based" - | "business" - | "ent26" - | "enterprise_cbp_automation" - | "enterprise_cbp_usage_based" - | "enterprise" - | "edu" - | "edu_plus" - | "edu_pro" - | "unknown"; -export const V2GetAccountRateLimitsResponse__PlanType = Schema.Literals([ - "free", - "go", - "plus", - "pro", - "prolite", - "team", - "self_serve_business_prolite", - "self_serve_business_usage_based", - "business", - "ent26", - "enterprise_cbp_automation", - "enterprise_cbp_usage_based", - "enterprise", - "edu", - "edu_plus", - "edu_pro", - "unknown", -]).annotate({ identifier: "V2GetAccountRateLimitsResponse__PlanType" }); +export type V2GetAccountRateLimitsResponse__PlanType = string; +export const V2GetAccountRateLimitsResponse__PlanType = Schema.String.annotate({ + identifier: "V2GetAccountRateLimitsResponse__PlanType", +}); export type V2GetAccountRateLimitsResponse__RateLimitWindow = { readonly resetsAt?: number | null; @@ -5563,43 +5431,10 @@ export const V2GetAccountRateLimitsResponse__RateLimitReachedType = Schema.Liter "workspace_member_usage_limit_reached", ]).annotate({ identifier: "V2GetAccountRateLimitsResponse__RateLimitReachedType" }); -export type V2GetAccountResponse__PlanType = - | "free" - | "go" - | "plus" - | "pro" - | "prolite" - | "team" - | "self_serve_business_prolite" - | "self_serve_business_usage_based" - | "business" - | "ent26" - | "enterprise_cbp_automation" - | "enterprise_cbp_usage_based" - | "enterprise" - | "edu" - | "edu_plus" - | "edu_pro" - | "unknown"; -export const V2GetAccountResponse__PlanType = Schema.Literals([ - "free", - "go", - "plus", - "pro", - "prolite", - "team", - "self_serve_business_prolite", - "self_serve_business_usage_based", - "business", - "ent26", - "enterprise_cbp_automation", - "enterprise_cbp_usage_based", - "enterprise", - "edu", - "edu_plus", - "edu_pro", - "unknown", -]).annotate({ identifier: "V2GetAccountResponse__PlanType" }); +export type V2GetAccountResponse__PlanType = string; +export const V2GetAccountResponse__PlanType = Schema.String.annotate({ + identifier: "V2GetAccountResponse__PlanType", +}); export type V2GetAccountResponse__AccountRoutingOverride = "NO_CONSTRAINT" | "us" | "us_cr"; export const V2GetAccountResponse__AccountRoutingOverride = Schema.Literals([ diff --git a/packages/effect-codex-app-server/src/schema.test.ts b/packages/effect-codex-app-server/src/schema.test.ts index b12b33e02854..cc88b9e38363 100644 --- a/packages/effect-codex-app-server/src/schema.test.ts +++ b/packages/effect-codex-app-server/src/schema.test.ts @@ -201,13 +201,15 @@ it("accepts Codex misalignment policy errors for thread responses", () => { ); }); -it("accepts Codex 0.150 account plan values", () => { +it("accepts account plan slugs newer than the pinned protocol", () => { const planTypes = [ "self_serve_business_prolite", "ent26", "enterprise_cbp_automation", "edu_plus", "edu_pro", + "promax", + "some_future_plan", ]; for (const planType of planTypes) { From 45333ae389b6d6fe15e1fa43c3dc188df329aaf7 Mon Sep 17 00:00:00 2001 From: Theo Browne Date: Tue, 29 Sep 2026 12:42:52 -0700 Subject: [PATCH 2/2] fix(codex): shorter Ultrafast description that fits on one line Co-Authored-By: Claude Opus 5.5 (1M context) --- .../src/provider/Layers/CodexProvider.test.ts | 10 ++++++++++ .../src/provider/Layers/CodexProvider.ts | 20 +++++++++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/apps/server/src/provider/Layers/CodexProvider.test.ts b/apps/server/src/provider/Layers/CodexProvider.test.ts index 0c7a40d9bd9e..2c67b6f144db 100644 --- a/apps/server/src/provider/Layers/CodexProvider.test.ts +++ b/apps/server/src/provider/Layers/CodexProvider.test.ts @@ -81,6 +81,11 @@ it("uses standard routing when the catalog has no default service tier", () => { name: "Fast", description: "1.5x speed, increased usage", }, + { + id: "ultrafast", + name: "Ultrafast", + description: "The fastest available responses for latency-sensitive work.", + }, ], supportedReasoningEfforts: [], }); @@ -97,6 +102,11 @@ it("uses standard routing when the catalog has no default service tier", () => { label: "Fast", description: "1.5x speed, increased usage", }, + { + id: "ultrafast", + label: "Ultrafast", + description: "Even faster, more expensive", + }, ], currentValue: "default", }, diff --git a/apps/server/src/provider/Layers/CodexProvider.ts b/apps/server/src/provider/Layers/CodexProvider.ts index 56d3fa21df1b..72ca3fd74f0d 100644 --- a/apps/server/src/provider/Layers/CodexProvider.ts +++ b/apps/server/src/provider/Layers/CodexProvider.ts @@ -90,6 +90,11 @@ const REASONING_EFFORT_LABELS: Readonly> = { const DEFAULT_SERVICE_TIER_ID = "default"; +/** Shorter copy for tiers whose catalog description wraps in the traits menu. */ +const SERVICE_TIER_DESCRIPTIONS: Readonly> = { + ultrafast: "Even faster, more expensive", +}; + function reasoningEffortLabel(reasoningEffort: string): string { return REASONING_EFFORT_LABELS[reasoningEffort] ?? reasoningEffort; } @@ -197,12 +202,15 @@ export function mapCodexModelCapabilities( label: "Standard", ...(defaultServiceTier === DEFAULT_SERVICE_TIER_ID ? { isDefault: true } : {}), }, - ...serviceTiers.map((tier) => ({ - id: tier.id, - label: tier.name, - ...(tier.description ? { description: tier.description } : {}), - ...(defaultServiceTier === tier.id ? { isDefault: true } : {}), - })), + ...serviceTiers.map((tier) => { + const description = SERVICE_TIER_DESCRIPTIONS[tier.id] ?? tier.description; + return { + id: tier.id, + label: tier.name, + ...(description ? { description } : {}), + ...(defaultServiceTier === tier.id ? { isDefault: true } : {}), + }; + }), ], currentValue: defaultServiceTier, });