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