Skip to content

Codex robustness: logged-out detection + model listing#378

Merged
chadbyte merged 5 commits into
mainfrom
fix/codex-auth-required
Jul 1, 2026
Merged

Codex robustness: logged-out detection + model listing#378
chadbyte merged 5 commits into
mainfrom
fix/codex-auth-required

Conversation

@chadbyte

@chadbyte chadbyte commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Two Codex adapter fixes.

1. Detect logged-out Codex → trigger the login modal

Claude reports "not logged in" as a login-prompt message; Codex instead emits an unauthorized / token-revoked error event (codexErrorInfo: "unauthorized", HTTP 401 token_revoked). That fell through the adapter as an unhandled runtime_specific event, so auth_required never fired and the login modal never opened.

  • codex.js: map the top-level unauthorized/token-revoked error event to a neutral auth_required yokeType (vendor: codex); other top-level errors surface as normal error.
  • sdk-message-processor.js: handle yokeType === "auth_required" via a shared, per-session-deduped emitAuthRequired(session) helper (also used by the Claude login-prompt path). Codex-specific detection stays in the adapter.

Result: codex logout + send a message now pops the login modal running codex login --device-auth.

2. New Codex session showed a Claude model in the picker

get_vendor_models awaited Codex init(); when the app-server initialize handshake timed out, model listing threw and the picker stayed on the previous vendor's model (e.g. claude-opus-4-8).

  • codex.js: Codex models are a fixed list — hoist to a CODEX_MODELS constant that supportedModels() returns directly, independent of init.
  • project.js: get_vendor_models no longer lets a failed init() skip supportedModels().

Result: a new Codex session lists the gpt-5.x models immediately, even if the app-server init is slow/unavailable.

Validation

node --check + require smoke on all touched files; relay purity preserved (no codex error strings in the processor). Runtime pending: confirm on a logged-out Codex session (modal pops) and that a fresh Codex session shows a gpt model.

chadbyte added 2 commits July 1, 2026 12:14
Codex doesn't emit a Claude-style "not logged in" message; when its
token is revoked the app-server sends an unauthorized/token-revoked
error event, which fell through as an unhandled runtime_specific event —
so the login modal never opened.

Map that error to a neutral auth_required yokeType in the Codex adapter,
and handle auth_required in the message processor by emitting the same
login flow (WS message + notification) used for Claude. Extract the
auth-required emission into a shared, per-session-deduped helper so both
vendors go through one path. Codex-specific error detection stays in the
adapter; the relay only sees the neutral yokeType.
A new Codex session showed a Claude model in the picker: get_vendor_models
awaited codex init(), and when the app-server `initialize` handshake timed
out, model listing threw and the picker stayed on the previous vendor's
model.

Codex models are a fixed list, so decouple them from init: hoist the list
to a CODEX_MODELS constant that supportedModels() returns directly, and in
get_vendor_models don't let a failed init() skip supportedModels().
@chadbyte chadbyte changed the title Detect Codex logged-out state and trigger the login modal Codex robustness: logged-out detection + model listing Jul 1, 2026
chadbyte added 3 commits July 1, 2026 14:12
The logged-out modal only fired for the clean unauthorized error event.
When Codex retries the responses websocket it instead surfaces the 401 as
turn/failed and item errors ("401 Unauthorized", "Missing bearer", "sign
in again"), which slipped through as generic errors — no login modal.

Add a shared isCodexAuthError() and apply it at every error emit point
(turn/failed, item error, top-level error, run-loop catch), mapping auth
failures to the neutral auth_required yokeType. The processor already
dedupes auth_required per session, so repeated 401 retries collapse to a
single login modal.
Opening a Codex session while the last model was a Claude one left the
chip showing the Claude model: the model_info handler kept the existing
selection whenever vendorSelectionLocked was set, even if that model
didn't belong to the new vendor. Now the existing selection is kept only
when it's actually in the vendor's model list; otherwise it snaps to the
server-provided default (mirrors the server's modelToSend logic).
The definitive Codex "not logged in" signal is a 401 on its responses
endpoint, which only shows up on the app-server's stderr — the JSON-RPC
error events carry a generic message, so the login modal never fired.

Watch stderr for the 401/auth patterns and synthesize an unauthorized
error event into the adapter's event handler, which maps to the neutral
auth_required yokeType. Deduped (15s) so repeated 401 retries collapse to
a single login modal.
@chadbyte chadbyte merged commit a3314b0 into main Jul 1, 2026
1 check passed
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

This issue has been resolved in version 2.45.0-beta.4 (main).

To update, run:

npx clay-server@2.45.0-beta.4

-- Clay Deploy Bot

Build anything, with anyone, in one place.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

This issue has been resolved in version 2.45.0 (stable).

To update, run:

npx clay-server@2.45.0

-- Clay Deploy Bot

Build anything, with anyone, in one place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant