From b73763d9443be71bbec89bc332c4720ee920902c Mon Sep 17 00:00:00 2001 From: Yeon Gil Kang Date: Wed, 15 Apr 2026 11:23:02 +0900 Subject: [PATCH] fix(pi-ai): hide unsupported ChatGPT codex oauth models ChatGPT-backed Codex sign-in no longer exposes the removed 5.1/5.2 Codex variants. Filter those models from openai-codex OAuth so GSD stops surfacing selections that immediately fail while leaving API-key-backed OpenAI models available. (cherry picked from commit 1aedba583916826fc5c6129037f61e9802010e46) --- .../pi-ai/src/utils/oauth/openai-codex.ts | 15 ++++++++ .../src/core/model-registry-auth-mode.test.ts | 36 ++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/packages/pi-ai/src/utils/oauth/openai-codex.ts b/packages/pi-ai/src/utils/oauth/openai-codex.ts index 8c5bd56bd..f506a2e5b 100644 --- a/packages/pi-ai/src/utils/oauth/openai-codex.ts +++ b/packages/pi-ai/src/utils/oauth/openai-codex.ts @@ -26,6 +26,14 @@ const TOKEN_URL = "https://auth.openai.com/oauth/token"; const REDIRECT_URI = "http://localhost:1455/auth/callback"; const SCOPE = "openid profile email offline_access"; const JWT_CLAIM_PATH = "https://api.openai.com/auth"; +const CHATGPT_UNSUPPORTED_MODEL_IDS = new Set([ + "gpt-5.2-codex", + "gpt-5.1-codex-mini", + "gpt-5.1-codex-max", + "gpt-5.1-codex", + "gpt-5.1", + "gpt-5", +]); const SUCCESS_HTML = ` @@ -454,4 +462,11 @@ export const openaiCodexOAuthProvider: OAuthProviderInterface = { getApiKey(credentials: OAuthCredentials): string { return credentials.access; }, + + modifyModels(models) { + return models.filter((model) => ( + model.provider !== "openai-codex" + || !CHATGPT_UNSUPPORTED_MODEL_IDS.has(model.id) + )); + }, }; diff --git a/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts b/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts index c8c8774c5..7a4b6e3a6 100644 --- a/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +++ b/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts @@ -2,7 +2,7 @@ import assert from "node:assert/strict"; import { describe, it } from "node:test"; import type { Api, Model, SimpleStreamOptions, Context, AssistantMessageEventStream } from "@singularity-forge/pi-ai"; import { getApiProvider } from "@singularity-forge/pi-ai"; -import type { AuthStorage } from "./auth-storage.js"; +import { AuthStorage, type AuthStorageData } from "./auth-storage.js"; import { ModelRegistry } from "./model-registry.js"; function createRegistry(hasAuthFn?: (provider: string) => boolean): ModelRegistry { @@ -18,6 +18,10 @@ function createRegistry(hasAuthFn?: (provider: string) => boolean): ModelRegistr return new ModelRegistry(authStorage, undefined); } +function createInMemoryRegistry(data: AuthStorageData = {}): ModelRegistry { + return new ModelRegistry(AuthStorage.inMemory(data), undefined); +} + function createProviderModel(id: string, api?: string): NonNullable[1]["models"]>[number] { return { id, @@ -389,6 +393,36 @@ describe("ModelRegistry authMode — getAvailable", () => { const available = registry.getAvailable(); assert.equal(available.length, 0); }); + + it("prunes Codex models removed from ChatGPT-backed openai-codex OAuth", () => { + const registry = createInMemoryRegistry({ + "openai-codex": { + type: "oauth", + access: "oauth-access", + refresh: "oauth-refresh", + expires: Date.now() + 60_000, + accountId: "acct_123", + }, + }); + + assert.equal(registry.find("openai-codex", "gpt-5.1-codex-max"), undefined); + assert.equal(registry.find("openai-codex", "gpt-5.1"), undefined); + assert.equal(findModel(registry, "openai-codex", "gpt-5.2-codex"), undefined); + assert.ok(registry.find("openai-codex", "gpt-5.4")); + assert.ok(findModel(registry, "openai-codex", "gpt-5.4")); + }); + + it("keeps API-backed OpenAI Codex-capable models available", () => { + const registry = createInMemoryRegistry({ + openai: { + type: "api_key", + key: "sk-test", + }, + }); + + assert.ok(registry.find("openai", "gpt-5.2-codex")); + assert.ok(findModel(registry, "openai", "gpt-5.2-codex")); + }); }); // ─── getApiKey ────────────────────────────────────────────────────────────────