Refresh Codex OAuth model defaults

Stop seeding stale Codex OAuth model metadata so connected accounts use the upstream /models response instead of the retired gpt-5.2-codex default. Use gpt-5.5 as the Codex proxy fallback model, omit the fake legacy client version when Codex CLI is unavailable, and remove the stale Copilot fallback entry. Add focused OAuth regressions for the current default model, upstream model-list behavior, and Copilot fallback ordering.

Alessandro committed Jul 2, 2026 at 16:08 UTC 446031429de88f77e8a8f4e0884e0cc209b216c9
5 files changed +58 -10
plugins/_oauth/helpers/codex.py
+6 -4
@@ -35,7 +35,7 @@ except ImportError:
35 AUTH_FILENAME = "auth.json"
36 ACCESS_EXPIRY_MARGIN = timedelta(minutes=5)
37 REFRESH_INTERVAL = timedelta(minutes=55)
38 -FALLBACK_CODEX_VERSION = "0.124.0"
38 +DEFAULT_CODEX_MODEL = "gpt-5.5"
39 OAUTH_ERROR_KEYS = ("error_description", "error")
40 DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60
41 WINDOWS_LOCK_RETRY_SECONDS = 0.05
@@ -590,9 +590,11 @@ def fetch_models() -> list[str]:
590 if configured:
591 return configured
592
593 + client_version = resolve_codex_version()
594 + params = {"client_version": client_version} if client_version else None
595 response = request_codex(
596 "/models",
595 - params={"client_version": resolve_codex_version()},
597 + params=params,
598 )
599 if not response.ok:
600 raise RuntimeError(upstream_error_message(response, "Failed to load Codex models."))
@@ -772,7 +774,7 @@ def chat_messages_to_response_body(body: dict[str, Any]) -> dict[str, Any]:
774 )
775
776 response_body: dict[str, Any] = {
775 - "model": body.get("model") or "gpt-5.2",
777 + "model": body.get("model") or DEFAULT_CODEX_MODEL,
778 "input": response_input,
779 "instructions": "\n\n".join(instructions),
780 "store": False,
@@ -980,7 +982,7 @@ def resolve_codex_version() -> str:
982 return version
983 except Exception:
984 pass
983 - return FALLBACK_CODEX_VERSION
985 + return ""
986
987
988 def resolve_auth_file_candidates() -> list[Path]:
plugins/_oauth/helpers/providers/codex.py
+3 -3
@@ -20,7 +20,7 @@ from plugins._oauth.helpers.state import (
20 )
21
22
23 -CODEX_DEFAULT_MODELS = ["gpt-5.2-codex", "gpt-5.2"]
23 +CODEX_DEFAULT_MODEL = "gpt-5.5"
24 CODEX_FALLBACK_CONFIG = {
25 "enabled": True,
26 "models": [],
@@ -35,7 +35,7 @@ class CodexOAuthProvider:
35
36 def metadata(self) -> OAuthProviderMetadata:
37 cfg = _codex_config()
38 - models = cfg["models"] or CODEX_DEFAULT_MODELS
38 + models = list(cfg["models"])
39 return OAuthProviderMetadata(
40 provider_id=CODEX_PROVIDER_ID,
41 display_name="Codex/ChatGPT",
@@ -43,7 +43,7 @@ class CodexOAuthProvider:
43 model_provider_id=CODEX_PROVIDER_ID,
44 icon="openai",
45 auth_flow="device_code",
46 - default_model=models[0] if models else "gpt-5.2-codex",
46 + default_model=models[0] if models else CODEX_DEFAULT_MODEL,
47 default_models=models,
48 proxy_base_path=cfg["proxy_base_path"],
49 callback_path=cfg["callback_path"],
plugins/_oauth/helpers/providers/github_copilot.py
+1 -2
@@ -35,7 +35,6 @@ COPILOT_HEADERS = {
35 "Copilot-Integration-Id": "vscode-chat",
36 }
37 CURATED_MODELS = [
38 - "gpt-5.2-codex",
38 "gpt-5.2",
39 "claude-sonnet-4.5",
40 "claude-opus-4.5",
@@ -203,7 +202,7 @@ class GitHubCopilotOAuthProvider:
202 model_provider_id=GITHUB_COPILOT_PROVIDER_ID,
203 icon="github",
204 auth_flow="device_code",
206 - default_model="gpt-5.2-codex",
205 + default_model=CURATED_MODELS[0],
206 default_models=list(CURATED_MODELS),
207 proxy_base_path="/oauth/github-copilot",
208 supports_enterprise_domain=True,
tests/test_oauth_codex.py
+47
@@ -15,6 +15,7 @@ import yaml
15 sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
16 from plugins._oauth.helpers import codex
17 from plugins._oauth.helpers import routes
18 +from plugins._oauth.helpers.providers import codex as codex_provider
19 from plugins._oauth.extensions.python._functions.models.get_api_key.end import (
20 _20_oauth_account_dummy_key as oauth_dummy_key,
21 )
@@ -90,6 +91,52 @@ def test_chat_messages_to_response_body_extracts_instructions():
91 assert body["reasoning"] == {"effort": "high"}
92
93
94 +def test_chat_messages_to_response_body_uses_current_codex_default_model():
95 + body = codex.chat_messages_to_response_body(
96 + {
97 + "messages": [
98 + {"role": "user", "content": "Hello"},
99 + ],
100 + }
101 + )
102 +
103 + assert body["model"] == "gpt-5.5"
104 +
105 +
106 +def test_codex_provider_metadata_uses_upstream_models_only_by_default(monkeypatch):
107 + monkeypatch.setattr(
108 + codex_provider,
109 + "_codex_config",
110 + lambda: {
111 + "models": [],
112 + "proxy_base_path": "/oauth/codex",
113 + "callback_path": "/auth/callback",
114 + },
115 + )
116 +
117 + metadata = codex_provider.CodexOAuthProvider().metadata()
118 +
119 + assert metadata.default_model == "gpt-5.5"
120 + assert metadata.default_models == []
121 +
122 +
123 +def test_codex_fetch_models_omits_fake_client_version(monkeypatch):
124 + calls = []
125 +
126 + class FakeResponse:
127 + ok = True
128 +
129 + def json(self):
130 + return {"models": [{"slug": "upstream-model"}]}
131 +
132 + monkeypatch.setattr(codex, "codex_config", lambda: {"models": []})
133 + monkeypatch.setattr(codex, "resolve_codex_version", lambda: "")
134 + monkeypatch.setattr(codex, "request_codex", lambda path, params=None: calls.append((path, params)) or FakeResponse())
135 +
136 + assert codex.fetch_models() == ["upstream-model"]
137 + assert calls == [("/models", None)]
138 +
139 +
140 def test_chat_messages_to_response_body_preserves_image_parts_for_responses():
141 data_url = "data:image/png;base64,abcd"
142
tests/test_oauth_github_copilot.py
+1 -1
@@ -120,7 +120,7 @@ def test_models_returns_curated_list_without_network(monkeypatch):
120
121 models = provider.models()
122
123 - assert models[:3] == ["gpt-5.2-codex", "gpt-5.2", "claude-sonnet-4.5"]
123 + assert models[:3] == ["gpt-5.2", "claude-sonnet-4.5", "claude-opus-4.5"]
124 assert "grok-code-fast-1" in models
125
126