Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 42 additions & 14 deletions packages/opencode/src/plugin/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,36 @@ const CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses"
const OAUTH_PORT = 1455
const OAUTH_POLLING_SAFETY_MARGIN_MS = 3000

/** Non-codex ChatGPT-subscription (OAuth) allowlist. Any modelId
* containing "codex" is auto-allowed by ``shouldAllowOAuthModel`` below,
* so this set only enumerates the plain non-codex main/mini variants
* OpenAI exposes on Codex-tier accounts. Bump whenever a new gpt-5.N
* is generally available on the subscription. Exported for unit-test
* coverage — see test/plugin/codex-allowlist.test.ts. */
export const OAUTH_ALLOWED_MODELS = new Set([
"gpt-5.2",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.5",
"gpt-5.6",
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Add gpt-5.6-luna, gpt-5.6-sol, and gpt-5.6-terra to OAUTH_ALLOWED_MODELS; the OAuth filter currently deletes these supported Codex models before they reach the picker.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/plugin/codex.ts, line 34:

<comment>Add `gpt-5.6-luna`, `gpt-5.6-sol`, and `gpt-5.6-terra` to `OAUTH_ALLOWED_MODELS`; the OAuth filter currently deletes these supported Codex models before they reach the picker.</comment>

<file context>
@@ -16,6 +16,24 @@ const CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses"
+  "gpt-5.4",
+  "gpt-5.4-mini",
+  "gpt-5.5",
+  "gpt-5.6",
+])
+
</file context>
Suggested change
"gpt-5.6",
"gpt-5.6",
"gpt-5.6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not addressing in this PR. Adding gpt-5.6-luna/sol/terra requires confirmed evidence that ChatGPT-Codex subscription actually accepts them — the finding asserts they're supported but doesn't cite that source. The current defensive posture (reject unless known-good) is safer than admitting variants that would surface as request-time 4xx. If OpenAI's Codex-tier documentation confirms coverage, one-line addition per variant + a link in the comment. Happy to reopen if you can share a source.

Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
])

/** OAuth (ChatGPT-subscription) model-filter policy for the ACTIVE plugin
* (this file — wired via plugin/index.ts). A model is kept if either
* (a) its id contains ``"codex"`` (all codex variants ship on the
* subscription), or (b) its id is an exact member of
* ``OAUTH_ALLOWED_MODELS`` (the curated non-codex releases).
*
* The sibling file plugin/openai/codex.ts (an in-progress refactor,
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
* currently NOT wired) has its own separate filter with a
* ``parseFloat(match[1]) > 5.4`` fallback. Adopting this helper is
* followup work on that refactor — do NOT assume the two files share
* this policy today. */
export function shouldAllowOAuthModel(modelId: string): boolean {
if (modelId.includes("codex")) return true
return OAUTH_ALLOWED_MODELS.has(modelId)
}

interface PkceCodes {
verifier: string
challenge: string
Expand Down Expand Up @@ -398,21 +428,19 @@ export async function CodexAuthPlugin(input: PluginInput): Promise<Hooks> {
const auth = await getAuth()
if (auth.type !== "oauth") return {}

// Filter models to only allowed Codex models for OAuth
const allowedModels = new Set([
"gpt-5.1-codex",
"gpt-5.1-codex-max",
"gpt-5.1-codex-mini",
"gpt-5.2",
"gpt-5.2-codex",
"gpt-5.3-codex",
"gpt-5.4",
"gpt-5.4-mini",
])
// Filter models to only those the ChatGPT-subscription (Codex) tier
// accepts. Delegates to ``shouldAllowOAuthModel`` (module-level,
// above). See OAUTH_ALLOWED_MODELS + shouldAllowOAuthModel for the
// criteria + how to add new gpt-5.N releases.
//
// NOTE: this file is the ACTIVE plugin (wired via plugin/index.ts).
// The sibling plugin/openai/codex.ts is an unwired in-progress
// refactor that keeps its OWN ALLOWED_MODELS + parseFloat > 5.4
// fallback — this filter does NOT share a source of truth with it.
// Adopting shouldAllowOAuthModel there is followup on that refactor.
// (Closes #1132 — GPT 5.6 missing from picker.)
for (const modelId of Object.keys(provider.models)) {
if (modelId.includes("codex")) continue
if (allowedModels.has(modelId)) continue
delete provider.models[modelId]
if (!shouldAllowOAuthModel(modelId)) delete provider.models[modelId]
}

// Zero out costs for Codex (included with ChatGPT subscription)
Expand Down
129 changes: 129 additions & 0 deletions packages/opencode/test/plugin/codex-allowlist.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Regression coverage for the ChatGPT-subscription (OAuth) model filter
// in packages/opencode/src/plugin/codex.ts — the ACTIVE plugin wired
// via plugin/index.ts.
//
// Filed as issue #1132: GPT 5.6 was released but the allowlist stopped
// at 5.4, so users on ChatGPT Pro/Plus (Codex tier) couldn't pick it in
// the model picker even though the underlying models.dev catalog had it.
//
// Sibling file plugin/openai/codex.ts is an in-progress refactor of the
// same plugin, currently NOT wired via plugin/index.ts, and NOT covered
// by this file — it has its own separate ``ALLOWED_MODELS`` + a
// ``parseFloat > 5.4`` fallback in its ``models()`` filter, and its
// existing test file (test/plugin/codex.test.ts) already covers its
// OAuth-flow + JWT parsing internals. When that refactor is wired,
// adopting ``shouldAllowOAuthModel`` here (and expanding this file's
// coverage to the newly-active filter) is followup work.
import { describe, expect, test } from "bun:test"
import { OAUTH_ALLOWED_MODELS, shouldAllowOAuthModel } from "../../src/plugin/codex"

describe("OAUTH_ALLOWED_MODELS — subscription model picker regression barrier", () => {
test("issue #1132: gpt-5.6 is present", () => {
// If this fails again, someone dropped gpt-5.6 from the non-codex
// allowlist without moving forward to a newer generation — rejecting
// a shipped OpenAI model users have subscription access to.
expect(OAUTH_ALLOWED_MODELS.has("gpt-5.6")).toBe(true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This barrier tests the exported constant's members but not that the loader applies it. A refactor that stops consuming OAUTH_ALLOWED_MODELS (the two-allowlist maintenance risk this PR itself calls out) would pass every test while gpt-5.5/5.6 silently disappear from the OAuth picker. Add a behavior-level test that runs the codex.ts loader filter against a fake provider.models: assert gpt-5.5 and gpt-5.6 survive, and gpt-5.4-pro / gpt-5.6-luna etc. are removed.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/test/plugin/codex-allowlist.test.ts, line 19:

<comment>This barrier tests the exported constant's members but not that the loader applies it. A refactor that stops consuming OAUTH_ALLOWED_MODELS (the two-allowlist maintenance risk this PR itself calls out) would pass every test while gpt-5.5/5.6 silently disappear from the OAuth picker. Add a behavior-level test that runs the codex.ts loader filter against a fake provider.models: assert gpt-5.5 and gpt-5.6 survive, and gpt-5.4-pro / gpt-5.6-luna etc. are removed.</comment>

<file context>
@@ -0,0 +1,64 @@
+    // If this test starts failing again, someone dropped gpt-5.6 from
+    // the allowlist without moving forward to a newer generation —
+    // rejecting a shipped OpenAI model users have subscription access to.
+    expect(OAUTH_ALLOWED_MODELS.has("gpt-5.6")).toBe(true)
+  })
+
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bc4f1c19. The test file now has TWO describe blocks: the constant-membership assertions (the original barrier) AND a behavior block that exercises shouldAllowOAuthModel directly against a range of inputs (allowlist hits, codex-tagged auto-allow, API-tier-only rejections, unrelated-provider rejections). Catches the exact regression class you flagged: a refactor that stops consuming OAUTH_ALLOWED_MODELS would fail the behavior tests even if the constant were untouched.

})

test("gpt-5.5 is present (added alongside 5.6 for parity)", () => {
expect(OAUTH_ALLOWED_MODELS.has("gpt-5.5")).toBe(true)
})

test("prior non-codex generations stay allowlisted (no accidental removal)", () => {
for (const id of ["gpt-5.2", "gpt-5.4", "gpt-5.4-mini"]) {
expect(OAUTH_ALLOWED_MODELS.has(id)).toBe(true)
}
})

test("codex-tagged variants are NOT in the non-codex set (they're auto-allowed instead)", () => {
// The non-codex set is deliberately minimal — every codex-tagged id
// is auto-allowed by shouldAllowOAuthModel's `includes("codex")` check
// below, so listing them here would be redundant + a maintenance trap.
for (const id of ["gpt-5.1-codex", "gpt-5.2-codex", "gpt-5.3-codex"]) {
expect(OAUTH_ALLOWED_MODELS.has(id)).toBe(false)
}
})

test("allowlist does NOT include API-tier-only variants (defensive)", () => {
// Pro / luna / sol / terra variants ship on models.dev but aren't
// confirmed available on the ChatGPT-subscription (Codex) tier —
// showing them in the picker would surface a request-time failure.
// If OpenAI extends subscription coverage to them, add them here
// deliberately (with a link to the announcement).
for (const id of [
"gpt-5.4-pro",
"gpt-5.5-pro",
"gpt-5.6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
]) {
expect(OAUTH_ALLOWED_MODELS.has(id)).toBe(false)
}
})

test("allowlist size never regresses below current baseline", () => {
// Trip-wire: if someone truncates the allowlist by mistake (or in
// a bad rebase), the count drops and this test catches it before
// shipping. Bump when a real new addition lands.
expect(OAUTH_ALLOWED_MODELS.size).toBeGreaterThanOrEqual(5)
})
})

describe("shouldAllowOAuthModel — behavior of the filter itself", () => {
// Behavior-level coverage: even if a refactor stops passing
// OAUTH_ALLOWED_MODELS through, the filter function is what the
// loader actually calls, so this catches breakage the constant-only
// tests above would miss. (cubic P3 catch.)

test("allowlist members pass (spot-check each generation)", () => {
for (const id of ["gpt-5.2", "gpt-5.4", "gpt-5.4-mini", "gpt-5.5", "gpt-5.6"]) {
expect(shouldAllowOAuthModel(id)).toBe(true)
}
})

test("codex-tagged ids pass regardless of exact allowlist membership", () => {
// Any id containing "codex" auto-passes — covers gpt-5.1-codex,
// gpt-5.3-codex-spark, plus any future codex variant OpenAI ships.
for (const id of [
"gpt-5.1-codex",
"gpt-5.1-codex-max",
"gpt-5.1-codex-mini",
"gpt-5.2-codex",
"gpt-5.3-codex",
"gpt-5.3-codex-spark",
"gpt-5.3-codex-xhigh",
"codex-hypothetical-future-name",
]) {
expect(shouldAllowOAuthModel(id)).toBe(true)
}
})

test("API-tier-only variants are rejected (the whole point of the filter)", () => {
// These are the models the previous parseFloat > 5.4 fallback in
// plugin/openai/codex.ts was incorrectly admitting; the shared
// filter must reject them so the picker stays honest about what
// the subscription actually accepts.
for (const id of [
"gpt-5.4-pro",
"gpt-5.4-nano",
"gpt-5.5-pro",
"gpt-5.6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
]) {
expect(shouldAllowOAuthModel(id)).toBe(false)
}
})

test("completely unrelated ids are rejected", () => {
for (const id of [
"claude-3.5-sonnet",
"gemini-2.5-pro",
"gpt-4o",
"gpt-4-turbo",
"",
]) {
expect(shouldAllowOAuthModel(id)).toBe(false)
}
})
})
Loading