From 8d1a89d4ebbe20b074d5075a5984d655858035d8 Mon Sep 17 00:00:00 2001 From: Guillermo Casanova Date: Tue, 29 Sep 2026 16:27:45 -0300 Subject: [PATCH 1/2] fix(codex): Pro 500 accounts no longer break the Codex provider Codex 0.159 reports ChatGPT Pro 500 accounts as planType "promax". Our generated protocol decoded plan types as a closed enum, so account/read failed to decode and the provider showed as unavailable. Generate PlanType as an open string, matching how Codex itself reads plans it does not know, and label promax as ChatGPT Pro 500. --- .../src/provider/Layers/CodexProvider.ts | 2 + .../scripts/generate.ts | 8 +- .../src/_generated/schema.gen.ts | 205 ++---------------- .../src/schema.test.ts | 18 +- 4 files changed, 37 insertions(+), 196 deletions(-) diff --git a/apps/server/src/provider/Layers/CodexProvider.ts b/apps/server/src/provider/Layers/CodexProvider.ts index 57b0cab334bb..c8efe8135b81 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 500 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..dbcf55c6c663 100644 --- a/packages/effect-codex-app-server/scripts/generate.ts +++ b/packages/effect-codex-app-server/scripts/generate.ts @@ -145,6 +145,12 @@ const ManualSchemas: Record = { }, }; +// Codex ships new ChatGPT plans between protocol refreshes and reads unknown +// ones as `unknown`. Accept any plan so a new one cannot fail account reads. +const DefinitionSchemaOverrides: 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 +649,7 @@ const generateFiles = Effect.fn("generateFiles")(function* () { aggregateSchemas[localDefinitionNames.get(definitionName)!] = stripNullDefaults( normalizeNullableTypes( rewriteExternalRefs( - definitionSchema, + DefinitionSchemaOverrides[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..9f5e2c7beaf7 100644 --- a/packages/effect-codex-app-server/src/schema.test.ts +++ b/packages/effect-codex-app-server/src/schema.test.ts @@ -4,6 +4,10 @@ import * as Schema from "effect/Schema"; import * as CodexSchema from "./schema.ts"; const isGetAccountResponse = Schema.is(CodexSchema.V2GetAccountResponse); +const isAccountUpdatedNotification = Schema.is(CodexSchema.V2AccountUpdatedNotification); +const isAccountRateLimitsUpdatedNotification = Schema.is( + CodexSchema.V2AccountRateLimitsUpdatedNotification, +); const isThreadReadResponse = Schema.is(CodexSchema.V2ThreadReadResponse); const isThreadResumeResponse = Schema.is(CodexSchema.V2ThreadResumeResponse); const isThreadForkResponse = Schema.is(CodexSchema.V2ThreadForkResponse); @@ -201,16 +205,8 @@ it("accepts Codex misalignment policy errors for thread responses", () => { ); }); -it("accepts Codex 0.150 account plan values", () => { - const planTypes = [ - "self_serve_business_prolite", - "ent26", - "enterprise_cbp_automation", - "edu_plus", - "edu_pro", - ]; - - for (const planType of planTypes) { +it("accepts account plans newer than the generated protocol", () => { + for (const planType of ["promax", "a_plan_codex_adds_later"]) { const accountResponse = { account: { email: "user@example.com", @@ -221,5 +217,7 @@ it("accepts Codex 0.150 account plan values", () => { }; assert.equal(isGetAccountResponse(accountResponse), true); + assert.equal(isAccountUpdatedNotification({ authMode: "chatgpt", planType }), true); + assert.equal(isAccountRateLimitsUpdatedNotification({ rateLimits: { planType } }), true); } }); From 12ee8267dd7c9d71743d86b68621c83996db72c9 Mon Sep 17 00:00:00 2001 From: Guillermo Casanova Date: Tue, 29 Sep 2026 16:43:19 -0300 Subject: [PATCH 2/2] fix(codex): label Pro 500 accounts as Pro 25x --- apps/server/src/provider/Layers/CodexProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/provider/Layers/CodexProvider.ts b/apps/server/src/provider/Layers/CodexProvider.ts index c8efe8135b81..f65d4432ab6a 100644 --- a/apps/server/src/provider/Layers/CodexProvider.ts +++ b/apps/server/src/provider/Layers/CodexProvider.ts @@ -116,7 +116,7 @@ export function codexPlanLabel(planType: string | null | undefined): string | un case "prolite": return "ChatGPT Pro 5x Subscription"; case "promax": - return "ChatGPT Pro 500 Subscription"; + return "ChatGPT Pro 25x Subscription"; case "team": return "ChatGPT Team Subscription"; case "self_serve_business_prolite":