13 KiB
v0.3.0 ChatGPT Codex Provider Implementation Plan
Goal
This release adds a first-class ChatGPT Codex provider preset so users who are already signed in to Codex with a ChatGPT subscription can run Cassady without creating a separate API-key environment variable. The preset should call https://chatgpt.com/backend-api/codex/responses and resolve its bearer token from the local Codex auth config by default.
Success statement:
A user who has already run
codex loginor signed in through the Codex app can selectChatGPT Codexincass login, passcass check, and send Cassady turns through their Codex subscription without copying tokens into Cassady config.
Scope
In scope
- Add
ChatGPT Codexas a built-in provider preset in the setup/login catalog. - Add a provider kind/client for the ChatGPT Codex responses endpoint rather than forcing it through
/chat/completionsURL construction. - Read the default access token from the local Codex auth file, normally
$CODEX_HOME/auth.jsonor~/.codex/auth.json. - Support the observed Codex auth shape with
tokens.access_token, while keeping token values out of Cassady config, logs, check output, and error text. - Prefer the Codex-configured model from
$CODEX_HOME/config.tomlwhen available, with a safe manual model fallback. - Teach
cass checkto validate that local Codex auth is present and usable for the activeChatGPT Codexprovider. - Add docs explaining prerequisites, setup flow, token-source behavior, expiration troubleshooting, and the distinction between ChatGPT subscription access and API-key providers.
- Add focused tests with temporary Codex-home fixtures and mocked streaming responses.
Out of scope
- Implementing Cassady's own browser OAuth/device-login flow for ChatGPT.
- Storing or refreshing ChatGPT/Codex tokens in Cassady-owned config files.
- Reverse engineering unrelated ChatGPT backend endpoints beyond the requested Codex responses endpoint.
- Guaranteeing compatibility if the private ChatGPT backend endpoint or Codex auth file format changes.
- Replacing OpenAI-compatible provider support or changing existing provider presets.
- Release tagging, packaging, or GitHub release creation.
Context or Current State
Cassady's provider stack is currently centered on OpenAI-compatible chat completions:
src/setup.rsowns the built-in provider catalog, setup/login prompts, model discovery viaGET {base_url}/models, and writes toproviders.json/models.json/config.json.src/config.rsdefinesProviderDefinition, validates provider registries, resolvesapi_keyvalues from literals or environment-variable references, and currently accepts onlykind = "openai-compatible".src/agent.rsconstructsOpenAiCompatibleProviderdirectly from resolved config.src/providers/openai_compatible.rsappends/chat/completions, sends OpenAI-compatible chat payloads, and parses OpenAI-compatible streaming deltas.docs/providers.md,docs/configuration.md,docs/commands.md,docs/workflows.md, andREADME.mddescribe provider setup as API-key/environment-variable based.
The new preset differs in two important ways:
- Authentication should come from Codex's local login state, not from a Cassady environment-variable API key.
- The endpoint is a Codex-specific responses endpoint (
https://chatgpt.com/backend-api/codex/responses), so Cassady needs an endpoint-specific provider client or a more general provider dispatch layer.
Local Codex auth is expected to live under Codex home, normally ~/.codex/auth.json, with a shape like:
{
"auth_mode": "chatgpt",
"tokens": {
"access_token": "...",
"refresh_token": "...",
"account_id": "..."
},
"last_refresh": "..."
}
Cassady should treat this file as sensitive input: read it only when resolving the active provider token, never copy the access token into Cassady-owned JSON, and never print token contents.
Design Principles
- Use Codex login state, do not own ChatGPT auth. Cassady should integrate with an existing Codex login and tell users to run
codex loginor sign in to Codex when auth is missing or expired. - Keep provider protocols explicit. Do not pretend the ChatGPT Codex endpoint is OpenAI-compatible if it needs different URL construction, request shape, or stream parsing.
- Avoid token leakage. Token values must not be stored in
~/.cass, included in transcripts, surfaced incass check, or embedded in test snapshots. - Keep existing setup stable. Existing providers should continue to use environment-variable API keys and
/modelsdiscovery without extra Codex dependencies. - Fail with clear recovery steps. Missing Codex auth should produce actionable messages, not generic provider errors.
Design
Provider catalog and setup UX
Add a built-in catalog entry:
| Provider | Provider id | Kind | Endpoint | Token source |
|---|---|---|---|---|
| ChatGPT Codex | chatgpt-codex |
chatgpt-codex |
https://chatgpt.com/backend-api/codex/responses |
Local Codex auth |
In cass login/cass setup, selecting this provider should skip the normal API key environment variable prompt and instead show a prerequisite check:
ChatGPT Codex uses your local Codex login.
✓ Found Codex auth at ~/.codex/auth.json
If the file or access token is missing:
ChatGPT Codex needs a local Codex login.
Run `codex login` or sign in with the Codex app, then run `cass login` again.
Model selection should prefer, in order:
- The
modelvalue from$CODEX_HOME/config.tomlwhen present. - A known default Codex model constant only if the project already has a current default available.
- Manual model id entry.
Do not call GET /models for chatgpt-codex unless a verified endpoint is added later; model discovery remains an OpenAI-compatible setup behavior.
Provider configuration shape
Extend provider config without breaking existing files. One possible JSON shape is:
{
"id": "chatgpt-codex",
"name": "ChatGPT Codex",
"kind": "chatgpt-codex",
"base_url": "https://chatgpt.com/backend-api/codex/responses",
"auth": { "type": "codex_local" },
"default_model": "gpt-5.5",
"models": ["gpt-5.5"]
}
Implementation may choose an equivalent internal representation, but it should preserve these properties:
- Existing
api_keystring behavior remains valid for OpenAI-compatible providers. chatgpt-codexproviders can omit environment-variable API keys.cass checkcan distinguish missing local Codex auth from missing API-key env vars.- Serialized config does not contain the Codex access token.
Codex auth resolution
Add a small resolver module, for example src/codex_auth.rs, with helpers like:
codex_home() -> PathBuf:$CODEX_HOMEwhen set, otherwise~/.codex.codex_auth_path() -> PathBuf:$CODEX_AUTH_FILEfor tests/overrides when set, otherwise{codex_home}/auth.json.load_codex_access_token() -> Result<CodexAccessToken>: parsetokens.access_tokenand return a redacted/display-safe token wrapper.check_codex_auth() -> CodexAuthStatus: report path found, auth mode, access-token presence, optional JWT expiration, and recovery hints.
If the access token looks like a JWT, parse the exp claim without validating the signature so Cassady can warn or fail early when the token is expired. Token refresh itself should stay out of scope unless Codex exposes a stable documented local refresh interface.
Read the token at request time rather than caching it during setup. This allows a separate Codex process to refresh auth.json between Cassady turns.
Provider dispatch
Refactor provider construction so src/agent.rs does not instantiate only OpenAiCompatibleProvider. A simple first step is an enum:
enum ProviderClient {
OpenAiCompatible(OpenAiCompatibleProvider),
ChatGptCodex(ChatGptCodexProvider),
}
Both variants should expose a common complete(messages, tools, tx) async method returning the existing CompletionResult. This preserves the current agent loop, tool execution, conversation storage, and TUI behavior.
ChatGPT Codex responses client
Add a new provider module, for example src/providers/chatgpt_codex.rs, that:
- Posts to the exact configured endpoint, defaulting to
https://chatgpt.com/backend-api/codex/responses. - Sends
Authorization: Bearer <local Codex access token>. - Includes the active model and converted message/tool context in the endpoint's expected responses format.
- Streams assistant text into
AgentEvent::AssistantChunk. - Streams reasoning summaries into
AgentEvent::ReasoningChunkonly when the endpoint provides a safe reasoning summary field. - Converts function/tool call deltas into Cassady
StoredToolCallvalues. - Converts Cassady tool results back into the endpoint's function-call-output input shape on the next turn.
- Redacts authentication details from non-success response errors.
The exact request/stream schema should be verified against the endpoint during implementation and captured in mocked fixtures. If the endpoint rejects a field used by OpenAI-compatible providers, keep the Codex payload minimal rather than adding compatibility shims that risk breaking the flow.
Check and troubleshooting behavior
For active chatgpt-codex providers, cass check should report status like:
✓ active provider: chatgpt-codex
✓ endpoint: https://chatgpt.com/backend-api/codex/responses
✓ Codex auth: ~/.codex/auth.json contains an access token
Failure should point to recovery:
✗ Codex auth: no access token found in ~/.codex/auth.json
Run `codex login` or sign in with the Codex app, then rerun `cass check`.
Do not print the token, account id, refresh token, or full auth JSON.
Implementation Steps
- Add the v0.3.0 roadmap entry and this implementation plan.
- Extend provider config types/validation to support
kind = "chatgpt-codex"and a non-env local Codex auth source while preserving existing OpenAI-compatible files. - Add
src/codex_auth.rsfor Codex home discovery, auth-file parsing, redacted status reporting, and optional JWT expiration checks. - Add
ChatGPT Codextosrc/setup.rsprovider catalog and branch setup behavior so it skips API-key env prompts and/modelsdiscovery. - Refactor provider construction in
src/agent.rsbehind a small provider dispatch enum or trait. - Implement
src/providers/chatgpt_codex.rswith endpoint-specific request conversion, streaming parsing, tool-call conversion, and redacted errors. - Update
cass checkso active and inactive provider checks understand Codex-local auth separately from environment-variable API keys. - Update README and bundled docs for the new preset, prerequisites, config example, troubleshooting, and known endpoint/auth caveats.
- Add unit tests for config parsing/validation, Codex auth fixtures, setup catalog behavior, and provider dispatch.
- Add mocked streaming tests for the ChatGPT Codex client, including text, tool calls, tool outputs, auth failures, and redaction.
Tests
providers.jsonwith existing OpenAI-compatible providers still parses and validates.- A
chatgpt-codexprovider with local Codex auth validates withoutapi_key/env-var availability. - Missing
~/.codex/auth.jsonproduces a clearcass checkerror for an activechatgpt-codexprovider. - A fixture
auth.jsonwithtokens.access_tokenresolves a token but redacts it in display and errors. - Expired JWT-like access tokens are detected when possible and produce a recovery hint.
- Setup/login catalog includes
ChatGPT Codexand skips the API-key env-var prompt for that provider. - Model selection uses
$CODEX_HOME/config.tomlmodelwhen available, with manual fallback. - Provider dispatch selects
ChatGptCodexProvideronly forkind = "chatgpt-codex". - Mocked Codex streaming responses produce assistant chunks and final
CompletionResult.content. - Mocked Codex function-call streams produce Cassady
StoredToolCallvalues and accept subsequent tool output messages. - Provider error messages never include access tokens, refresh tokens, or raw auth JSON.
cargo fmtpasses.cargo test --locked --all-targetspasses when practical.
Documentation
- Update
README.mdsetup/provider sections withChatGPT Codexas a subscription-backed option. - Update
docs/providers.mdwith the new preset, endpoint, token-source behavior, and private-endpoint caveat. - Update
docs/configuration.mdwith the extended provider schema and a safe example that uses local Codex auth. - Update
docs/commands.mdanddocs/workflows.mdforcass login,cass check, and troubleshooting steps. - Update
docs/troubleshooting.mdwith missing/expired Codex auth, unsupported model, and backend endpoint failure guidance.
Acceptance Criteria
cass loginoffersChatGPT Codexas a provider preset.- Selecting
ChatGPT Codexdoes not ask for an API-key environment variable by default. - Cassady reads the access token from local Codex auth at request/check time and never stores that token under
~/.cass. - Active
chatgpt-codexsessions callhttps://chatgpt.com/backend-api/codex/responsesinstead of appending/chat/completions. - Normal OpenAI-compatible providers continue to work unchanged.
cass checkgives clear success/failure output for local Codex auth without leaking secrets.- README and bundled docs explain the prerequisite of signing in to Codex first.
cargo fmtandcargo test --locked --all-targetspass.