Add ChatGPT Codex provider
CI / Build (push) Waiting to run
CI / Test (push) Waiting to run
CI / Build (push) Waiting to run
CI / Test (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
# 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 login` or signed in through the Codex app can select `ChatGPT Codex` in `cass login`, pass `cass check`, and send Cassady turns through their Codex subscription without copying tokens into Cassady config.
|
||||
|
||||
## Scope
|
||||
|
||||
### In scope
|
||||
|
||||
- Add `ChatGPT Codex` as 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/completions` URL construction.
|
||||
- Read the default access token from the local Codex auth file, normally `$CODEX_HOME/auth.json` or `~/.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.toml` when available, with a safe manual model fallback.
|
||||
- Teach `cass check` to validate that local Codex auth is present and usable for the active `ChatGPT Codex` provider.
|
||||
- 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.rs` owns the built-in provider catalog, setup/login prompts, model discovery via `GET {base_url}/models`, and writes to `providers.json`/`models.json`/`config.json`.
|
||||
- `src/config.rs` defines `ProviderDefinition`, validates provider registries, resolves `api_key` values from literals or environment-variable references, and currently accepts only `kind = "openai-compatible"`.
|
||||
- `src/agent.rs` constructs `OpenAiCompatibleProvider` directly from resolved config.
|
||||
- `src/providers/openai_compatible.rs` appends `/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`, and `README.md` describe provider setup as API-key/environment-variable based.
|
||||
|
||||
The new preset differs in two important ways:
|
||||
|
||||
1. Authentication should come from Codex's local login state, not from a Cassady environment-variable API key.
|
||||
2. 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:
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
1. **Use Codex login state, do not own ChatGPT auth.** Cassady should integrate with an existing Codex login and tell users to run `codex login` or sign in to Codex when auth is missing or expired.
|
||||
2. **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.
|
||||
3. **Avoid token leakage.** Token values must not be stored in `~/.cass`, included in transcripts, surfaced in `cass check`, or embedded in test snapshots.
|
||||
4. **Keep existing setup stable.** Existing providers should continue to use environment-variable API keys and `/models` discovery without extra Codex dependencies.
|
||||
5. **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:
|
||||
|
||||
```text
|
||||
ChatGPT Codex uses your local Codex login.
|
||||
|
||||
✓ Found Codex auth at ~/.codex/auth.json
|
||||
```
|
||||
|
||||
If the file or access token is missing:
|
||||
|
||||
```text
|
||||
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:
|
||||
|
||||
1. The `model` value from `$CODEX_HOME/config.toml` when present.
|
||||
2. A known default Codex model constant only if the project already has a current default available.
|
||||
3. 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:
|
||||
|
||||
```json
|
||||
{
|
||||
"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_key` string behavior remains valid for OpenAI-compatible providers.
|
||||
- `chatgpt-codex` providers can omit environment-variable API keys.
|
||||
- `cass check` can 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_HOME` when set, otherwise `~/.codex`.
|
||||
- `codex_auth_path() -> PathBuf`: `$CODEX_AUTH_FILE` for tests/overrides when set, otherwise `{codex_home}/auth.json`.
|
||||
- `load_codex_access_token() -> Result<CodexAccessToken>`: parse `tokens.access_token` and 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:
|
||||
|
||||
```rust
|
||||
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::ReasoningChunk` only when the endpoint provides a safe reasoning summary field.
|
||||
- Converts function/tool call deltas into Cassady `StoredToolCall` values.
|
||||
- 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:
|
||||
|
||||
```text
|
||||
✓ 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:
|
||||
|
||||
```text
|
||||
✗ 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
|
||||
|
||||
1. Add the v0.3.0 roadmap entry and this implementation plan.
|
||||
2. Extend provider config types/validation to support `kind = "chatgpt-codex"` and a non-env local Codex auth source while preserving existing OpenAI-compatible files.
|
||||
3. Add `src/codex_auth.rs` for Codex home discovery, auth-file parsing, redacted status reporting, and optional JWT expiration checks.
|
||||
4. Add `ChatGPT Codex` to `src/setup.rs` provider catalog and branch setup behavior so it skips API-key env prompts and `/models` discovery.
|
||||
5. Refactor provider construction in `src/agent.rs` behind a small provider dispatch enum or trait.
|
||||
6. Implement `src/providers/chatgpt_codex.rs` with endpoint-specific request conversion, streaming parsing, tool-call conversion, and redacted errors.
|
||||
7. Update `cass check` so active and inactive provider checks understand Codex-local auth separately from environment-variable API keys.
|
||||
8. Update README and bundled docs for the new preset, prerequisites, config example, troubleshooting, and known endpoint/auth caveats.
|
||||
9. Add unit tests for config parsing/validation, Codex auth fixtures, setup catalog behavior, and provider dispatch.
|
||||
10. Add mocked streaming tests for the ChatGPT Codex client, including text, tool calls, tool outputs, auth failures, and redaction.
|
||||
|
||||
## Tests
|
||||
|
||||
- `providers.json` with existing OpenAI-compatible providers still parses and validates.
|
||||
- A `chatgpt-codex` provider with local Codex auth validates without `api_key`/env-var availability.
|
||||
- Missing `~/.codex/auth.json` produces a clear `cass check` error for an active `chatgpt-codex` provider.
|
||||
- A fixture `auth.json` with `tokens.access_token` resolves 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 Codex` and skips the API-key env-var prompt for that provider.
|
||||
- Model selection uses `$CODEX_HOME/config.toml` `model` when available, with manual fallback.
|
||||
- Provider dispatch selects `ChatGptCodexProvider` only for `kind = "chatgpt-codex"`.
|
||||
- Mocked Codex streaming responses produce assistant chunks and final `CompletionResult.content`.
|
||||
- Mocked Codex function-call streams produce Cassady `StoredToolCall` values and accept subsequent tool output messages.
|
||||
- Provider error messages never include access tokens, refresh tokens, or raw auth JSON.
|
||||
- `cargo fmt` passes.
|
||||
- `cargo test --locked --all-targets` passes when practical.
|
||||
|
||||
## Documentation
|
||||
|
||||
- Update `README.md` setup/provider sections with `ChatGPT Codex` as a subscription-backed option.
|
||||
- Update `docs/providers.md` with the new preset, endpoint, token-source behavior, and private-endpoint caveat.
|
||||
- Update `docs/configuration.md` with the extended provider schema and a safe example that uses local Codex auth.
|
||||
- Update `docs/commands.md` and `docs/workflows.md` for `cass login`, `cass check`, and troubleshooting steps.
|
||||
- Update `docs/troubleshooting.md` with missing/expired Codex auth, unsupported model, and backend endpoint failure guidance.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- `cass login` offers `ChatGPT Codex` as a provider preset.
|
||||
- Selecting `ChatGPT Codex` does 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-codex` sessions call `https://chatgpt.com/backend-api/codex/responses` instead of appending `/chat/completions`.
|
||||
- Normal OpenAI-compatible providers continue to work unchanged.
|
||||
- `cass check` gives 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 fmt` and `cargo test --locked --all-targets` pass.
|
||||
Reference in New Issue
Block a user