feat(llm): Add OpenAI Codex (ChatGPT subscription) as LLM provider#744
feat(llm): Add OpenAI Codex (ChatGPT subscription) as LLM provider#744Sanjeev-S wants to merge 4 commits intonearai:stagingfrom
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly expands the application's LLM provider capabilities by integrating OpenAI Codex, which enables users with a ChatGPT subscription (Pro/Plus) to leverage their existing accounts. The implementation includes a robust OAuth device code authentication flow for seamless login, a dedicated client for OpenAI's Responses API to handle chat interactions and tool usage, and an intelligent token-refreshing mechanism to maintain session continuity. This feature aims to reduce friction for a large segment of users by removing the need for a separate API key and providing a more integrated experience. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces OpenAI Codex (via ChatGPT subscription) as a new LLM provider. The implementation is extensive, covering configuration, authentication, and a robust API client with thoughtful details like token auto-refresh and error handling. However, sensitive session data lacks consistent protection across all supported platforms. A minor inconsistency was also noted in the environment variable documentation.
| #[cfg(unix)] | ||
| { | ||
| use std::os::unix::fs::PermissionsExt; | ||
| let perms = std::fs::Permissions::from_mode(0o600); | ||
| tokio::fs::set_permissions(&self.config.session_path, perms) | ||
| .await | ||
| .map_err(|e| { | ||
| LlmError::Io(std::io::Error::new( | ||
| e.kind(), | ||
| format!("Failed to set permissions: {}", e), | ||
| )) | ||
| })?; | ||
| } |
There was a problem hiding this comment.
The application attempts to set restrictive permissions (0o600) on the session file containing sensitive OAuth tokens, but this is only implemented for Unix-like systems via #[cfg(unix)]. On other platforms, such as Windows, the file may be created with default permissions, potentially allowing other users on the same machine to read the sensitive tokens. This violates the principle of secure data handling for sensitive credentials.
| # LLM_BACKEND=openai_codex | ||
| # OPENAI_CODEX_MODEL=gpt-5.3-codex # default | ||
| # OPENAI_CODEX_CLIENT_ID=app_EMoamEEZ73f0CkXaXp7hrann # override (rare) | ||
| # OPENAI_CODEX_AUTH_URL=https://auth.openai.com # override (rare) |
There was a problem hiding this comment.
For consistency with the implementation in src/config/llm.rs, it would be helpful to also document the OPENAI_CODEX_API_URL environment variable here. The code allows overriding the API base URL, but it's not mentioned in this example file, which could be confusing for users trying to configure a proxy.
# OPENAI_CODEX_AUTH_URL=https://auth.openai.com # override (rare)
# OPENAI_CODEX_API_URL=https://chatgpt.com/backend-api/codex # override (rare)
5fbfd22 to
de1bb7a
Compare
zmanian
left a comment
There was a problem hiding this comment.
Good implementation. Separate provider is justified -- targets a completely different API surface (Responses API at chatgpt.com with OAuth device code auth). Follows existing LlmProvider trait pattern correctly. Thorough test coverage.
Three blocking issues:
-
src/llm/CLAUDE.mdnot updated -- Project rules require updating specs when adding new behavior. The LLM module spec needs entries for the new provider, files, and the asyncbuild_provider_chain()change. -
Hardcoded
/tmp/path in test --token_refreshing.rsuses/tmp/test-codex-session.json. Project requirestempfilecrate for test files. -
build_provider_chain()sync-to-async is a breaking API change -- Consider making only theopenai_codexpath async within the function body, or document why the signature change is necessary.
Non-blocking:
- Debug-level logging of device code response body includes sensitive auth data
generate_pkce()function is dead code (defined, tested, never called)request_timeout_secsfrom config is ignored (hardcoded 300s)- Missing
Retry-Afterheader parsing for 429 responses - Missing strict-mode schema normalization for tool definitions
ilblackdragon
left a comment
There was a problem hiding this comment.
Code Review
Solid implementation — clean architecture following existing decorator patterns, good test coverage (25+ unit tests), and well-integrated into the setup wizard. A few items to address:
Critical
-
Hardcoded
/tmp/path in test (src/llm/token_refreshing.rs)session_path: std::path::PathBuf::from("/tmp/test-codex-session.json"),
Project rules require
tempfile::tempdir()— this will collide in parallel test runs. The session tests already do it correctly. -
generate_pkce()is dead code (src/llm/openai_codex_session.rs) — never called since the device code flow gets the PKCE pair from the server. Remove or#[allow(dead_code)]with a TODO for the browser PKCE fallback.
High
-
refresh_tokens()uses.json()but initial token exchange uses.form()(openai_codex_session.rs) — Auth0/OpenAI token endpoints expectapplication/x-www-form-urlencodedfor all grant types. The refresh call should use.form()to be consistent and avoid breakage with stricter server validation. -
Image attachments silently dropped (
openai_codex_provider.rsconvert_message) —msg.imagesis ignored for user messages. The Responses API supportsimage_urlcontent parts. At minimum log a warning; ideally convert them likenearai_chat.rsdoes. -
OpenAiCodexSessionstores tokens as plainString—Debugis correctly redacted, but the struct isClone + pubso tokens can leak through other paths. Consider removing theClonederive or making the structpub(crate).
Medium
-
Silent fallback on client builder failure (
openai_codex_session.rs:1640).build().unwrap_or_else(|_| Client::new())
If the builder fails (TLS error), the fallback client won't have configured headers/timeout, causing confusing auth failures. Propagate the error instead.
-
set_model()duplicated in bothOpenAiCodexProviderandTokenRefreshingProvider— the decorator should delegate toself.inner.set_model(). -
list_models()returns empty vec — means setup wizard model selection won't show models. Add a comment explaining this is expected for subscription-based access.
Low
-
.gitignorechange (.worktrees) is unrelated — should be a separate commit. -
include: ["reasoning.encrypted_content"]in request body is reasoning-model-specific — could cause issues with non-reasoning models. Consider making it conditional.
Positives
- Decorator pattern (
TokenRefreshingProvider) is clean and consistent withRetryProvider/CircuitBreakerProvider - Device code flow UX is clear with verification URL and code display
renewal_lockmutex properly prevents thundering herd on token refresh- Session persistence follows the NearAI pattern — consistent UX
- Comprehensive SSE parser with good edge case coverage (multiple tool calls, error events,
[DONE]marker)
Review fixes for the OpenAI Codex provider PR: - Remove dead `generate_pkce()` code (device flow gets PKCE from server) - Fix `refresh_tokens()` to use `.form()` instead of `.json()` per OAuth spec - Restore sync `build_provider_chain()` for backward compat; add async variant `build_provider_chain_async()` for codex (which needs async OAuth) - Remove Clone from `OpenAiCodexSession`, restrict fields to `pub(crate)` - Propagate HTTP client builder error instead of silent fallback - Redact device code response body from debug log - Change `set_model()` in TokenRefreshingProvider to delegate to inner - Replace hardcoded `/tmp/` test path with `tempfile::tempdir()` - Extract `assemble_provider_chain()` sync helper from `build_provider_chain()` - Accept `request_timeout_secs` from config instead of hardcoded 300s - Parse `Retry-After` header on 429 responses (matches nearai_chat.rs pattern) - Reuse `normalize_schema_strict()` for Codex tool definitions - Add warning log for dropped image attachments - Add doc comments on `list_models()` and `include` field - Add `OPENAI_CODEX_API_URL` to `.env.example` - Revert unrelated `.worktrees` addition to `.gitignore` - Update `src/llm/CLAUDE.md` with Codex provider docs and sync/async split - Update root `CLAUDE.md`: add Codex to features, providers, and config sections [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Converting to draft while I rework this and rebase |
zmanian
left a comment
There was a problem hiding this comment.
Re-review: Previous feedback well addressed, one new issue
The fix commit (af603be) systematically addressed all 16 items from both reviews. Good work. Specifically:
Blocking items -- all resolved:
src/llm/CLAUDE.mdupdated with Codex provider docs and sync/async split/tmp/test path replaced withtempfile::tempdir()build_provider_chain()sync signature preserved;build_provider_chain_async()added; shared logic extracted toassemble_provider_chain()
Non-blocking items -- all resolved:
generate_pkce()dead code removed- Device code response body redacted from debug log (now logs byte count)
refresh_tokens()switched from.json()to.form()per OAuth specCloneremoved fromOpenAiCodexSession, fields restricted topub(crate)- Client builder error propagated instead of silent fallback
set_model()inTokenRefreshingProviderdelegates to inner/tmp/test path intoken_refreshing.rsalso fixedrequest_timeout_secsfrom config passed through to providerRetry-Afterheader parsing implemented (both delay-seconds and HTTP-date)normalize_schema_strict()reused for Codex tool definitions- Warning log for dropped image attachments added
- Doc comments on
list_models()andincludefield added OPENAI_CODEX_API_URLadded to.env.example- Unrelated
.worktrees.gitignorechange reverted
One new blocking issue
Env var name mismatch between docs and config parser:
.env.example and src/llm/CLAUDE.md document:
OPENAI_CODEX_AUTH_URLOPENAI_CODEX_API_URL
But src/config/llm.rs reads:
OPENAI_CODEX_AUTH_ENDPOINTOPENAI_CODEX_API_BASE_URL
Users who follow the documented env var names will have their overrides silently ignored. Either rename the optional_env() calls in src/config/llm.rs to match the documented names, or update the docs to match the code. I'd suggest matching the docs since _URL is shorter and consistent with what's already published in .env.example.
Minor nit (non-blocking)
The body_text variable in device_code_login() parse error still includes the raw response body in the error message (line ~2040 of the session file). The debug log was correctly redacted, but on a parse failure the body leaks into the error string. Consider redacting there too, or at minimum truncating it.
Overall this is a solid implementation. Fix the env var name mismatch and it's good to go.
af603be to
6e0f803
Compare
Review fixes for the OpenAI Codex provider PR: - Remove dead `generate_pkce()` code (device flow gets PKCE from server) - Fix `refresh_tokens()` to use `.form()` instead of `.json()` per OAuth spec - Restore sync `build_provider_chain()` for backward compat; add async variant `build_provider_chain_async()` for codex (which needs async OAuth) - Remove Clone from `OpenAiCodexSession`, restrict fields to `pub(crate)` - Propagate HTTP client builder error instead of silent fallback - Redact device code response body from debug log - Change `set_model()` in TokenRefreshingProvider to delegate to inner - Replace hardcoded `/tmp/` test path with `tempfile::tempdir()` - Extract `assemble_provider_chain()` sync helper from `build_provider_chain()` - Accept `request_timeout_secs` from config instead of hardcoded 300s - Parse `Retry-After` header on 429 responses (matches nearai_chat.rs pattern) - Reuse `normalize_schema_strict()` for Codex tool definitions - Add warning log for dropped image attachments - Add doc comments on `list_models()` and `include` field - Add `OPENAI_CODEX_API_URL` to `.env.example` - Revert unrelated `.worktrees` addition to `.gitignore` - Update `src/llm/CLAUDE.md` with Codex provider docs and sync/async split - Update root `CLAUDE.md`: add Codex to features, providers, and config sections [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6e0f803 to
215825e
Compare
Review fixes for the OpenAI Codex provider PR: - Remove dead `generate_pkce()` code (device flow gets PKCE from server) - Fix `refresh_tokens()` to use `.form()` instead of `.json()` per OAuth spec - Inline codex dispatch into `build_provider_chain()` (single async function, no separate `assemble_provider_chain()` helper — matches main's pattern) - Remove Clone from `OpenAiCodexSession`, restrict fields to `pub(crate)` - Propagate HTTP client builder error instead of silent fallback - Redact device code response body from debug log - Change `set_model()` in TokenRefreshingProvider to delegate to inner - Replace hardcoded `/tmp/` test path with `tempfile::tempdir()` - Accept `request_timeout_secs` from config instead of hardcoded 300s - Parse `Retry-After` header on 429 responses (matches nearai_chat.rs pattern) - Reuse `normalize_schema_strict()` for Codex tool definitions - Add warning log for dropped image attachments - Add doc comments on `list_models()` and `include` field - Add `OPENAI_CODEX_API_URL` to `.env.example` - Fix codex error message in `create_llm_provider()` for clarity - Revert unrelated `.worktrees` addition to `.gitignore` - Update `src/llm/CLAUDE.md` with Codex provider docs [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add OpenAiCodex as a new LLM backend variant with config for auth endpoint, API base URL, client ID, and session persistence path. The session manager implements OpenAI's device code auth flow (headless-friendly, no browser required on the server) with automatic token refresh, following the same persistence pattern as the existing NEAR AI session manager. Closes nearai#742 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Native Responses API client for chatgpt.com/backend-api/codex/responses, the endpoint that works with ChatGPT subscription tokens. Handles SSE streaming, text completions, and tool call round-trips. Token-refreshing decorator wraps the provider to pre-emptively refresh OAuth tokens before API calls and retry once on auth failures. Reports zero cost since billing is through subscription. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…zard Connect the new provider to the LLM factory, add openai_codex to the CLI --backend flag, and add it as an option in the onboarding wizard. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Review fixes for the OpenAI Codex provider PR: - Remove dead `generate_pkce()` code (device flow gets PKCE from server) - Fix `refresh_tokens()` to use `.form()` instead of `.json()` per OAuth spec - Inline codex dispatch into `build_provider_chain()` (single async function, no separate `assemble_provider_chain()` helper — matches main's pattern) - Remove Clone from `OpenAiCodexSession`, restrict fields to `pub(crate)` - Propagate HTTP client builder error instead of silent fallback - Redact device code response body from debug log - Change `set_model()` in TokenRefreshingProvider to delegate to inner - Replace hardcoded `/tmp/` test path with `tempfile::tempdir()` - Accept `request_timeout_secs` from config instead of hardcoded 300s - Parse `Retry-After` header on 429 responses (matches nearai_chat.rs pattern) - Reuse `normalize_schema_strict()` for Codex tool definitions - Add warning log for dropped image attachments - Add doc comments on `list_models()` and `include` field - Add `OPENAI_CODEX_API_URL` to `.env.example` - Fix codex error message in `create_llm_provider()` for clarity - Revert unrelated `.worktrees` addition to `.gitignore` - Update `src/llm/CLAUDE.md` with Codex provider docs [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
215825e to
bd588e0
Compare
|
Thank you for the detailed feedback @zmanian and @ilblackdragon. I addressed the comments. @zmanian Since your last review, I changed back the build_provider_chain_async that I added since build_provider_chain already changed to async in a prior diff as I rebased. |
|
@Sanjeev-S Thanks for putting this PR together. |
|
Looking forward for its release!! |
|
@Sanjeev-S and @zmanian do you know what is preventing this from being merged? |
#744) Security: - Add SSRF validation (validate_base_url) on OPENAI_CODEX_AUTH_URL and OPENAI_CODEX_API_URL, matching the pattern used by all other base URL configs (regression test for #1103 included) Correctness: - Add missing cache_write_multiplier() and cache_read_discount() trait delegation in TokenRefreshingProvider - Cap device-code polling backoff at 60s to prevent unbounded interval growth on repeated 429 responses - Default expires_in to 3600s when server returns 0, preventing immediately-expired sessions - Fix pre-existing SseEvent::JobResult missing fallback_deliverable field in job_monitor.rs tests Cleanup: - Extract duplicated make_test_jwt() and test_codex_config() into shared codex_test_helpers module Co-Authored-By: Sanjeev-S <Sanjeev-S@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Thanks for the work on this, @Sanjeev-S! I've picked up your changes and continued them in #1461. The new PR includes your original work plus:
You're credited as co-author on all commits. Feel free to review the new PR! |
|
Superseded by #1461 (takeover with merge + fixes). Thank you @Sanjeev-S for the original implementation! |
…1461) * feat(llm): add OpenAI Codex backend config and OAuth session manager Add OpenAiCodex as a new LLM backend variant with config for auth endpoint, API base URL, client ID, and session persistence path. The session manager implements OpenAI's device code auth flow (headless-friendly, no browser required on the server) with automatic token refresh, following the same persistence pattern as the existing NEAR AI session manager. Closes #742 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(llm): add Responses API client and token-refreshing decorator Native Responses API client for chatgpt.com/backend-api/codex/responses, the endpoint that works with ChatGPT subscription tokens. Handles SSE streaming, text completions, and tool call round-trips. Token-refreshing decorator wraps the provider to pre-emptively refresh OAuth tokens before API calls and retry once on auth failures. Reports zero cost since billing is through subscription. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(llm): wire OpenAI Codex into provider factory, CLI, and setup wizard Connect the new provider to the LLM factory, add openai_codex to the CLI --backend flag, and add it as an option in the onboarding wizard. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(llm): address PR #744 review feedback (20 items) Review fixes for the OpenAI Codex provider PR: - Remove dead `generate_pkce()` code (device flow gets PKCE from server) - Fix `refresh_tokens()` to use `.form()` instead of `.json()` per OAuth spec - Inline codex dispatch into `build_provider_chain()` (single async function, no separate `assemble_provider_chain()` helper — matches main's pattern) - Remove Clone from `OpenAiCodexSession`, restrict fields to `pub(crate)` - Propagate HTTP client builder error instead of silent fallback - Redact device code response body from debug log - Change `set_model()` in TokenRefreshingProvider to delegate to inner - Replace hardcoded `/tmp/` test path with `tempfile::tempdir()` - Accept `request_timeout_secs` from config instead of hardcoded 300s - Parse `Retry-After` header on 429 responses (matches nearai_chat.rs pattern) - Reuse `normalize_schema_strict()` for Codex tool definitions - Add warning log for dropped image attachments - Add doc comments on `list_models()` and `include` field - Add `OPENAI_CODEX_API_URL` to `.env.example` - Fix codex error message in `create_llm_provider()` for clarity - Revert unrelated `.worktrees` addition to `.gitignore` - Update `src/llm/CLAUDE.md` with Codex provider docs [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address review feedback and harden OpenAI Codex provider (takeover #744) Security: - Add SSRF validation (validate_base_url) on OPENAI_CODEX_AUTH_URL and OPENAI_CODEX_API_URL, matching the pattern used by all other base URL configs (regression test for #1103 included) Correctness: - Add missing cache_write_multiplier() and cache_read_discount() trait delegation in TokenRefreshingProvider - Cap device-code polling backoff at 60s to prevent unbounded interval growth on repeated 429 responses - Default expires_in to 3600s when server returns 0, preventing immediately-expired sessions - Fix pre-existing SseEvent::JobResult missing fallback_deliverable field in job_monitor.rs tests Cleanup: - Extract duplicated make_test_jwt() and test_codex_config() into shared codex_test_helpers module Co-Authored-By: Sanjeev-S <Sanjeev-S@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review feedback on OpenAI Codex provider (#1461) - Login command now resolves OPENAI_CODEX_* env overrides even when LLM_BACKEND isn't set to openai_codex (Copilot review) - Setup wizard "Keep current provider?" for codex no longer re-triggers device code login — mirrors Bedrock's keep-and-return pattern (Copilot) - Revert provider init log from info back to debug (Copilot) - Add warning log when token expires_in=0, before defaulting to 3600s (Gemini review) Co-Authored-By: Sanjeev-S <Sanjeev-S@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Sanjeev Suresh <Sanjeev-S@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…1461) * feat(llm): add OpenAI Codex backend config and OAuth session manager Add OpenAiCodex as a new LLM backend variant with config for auth endpoint, API base URL, client ID, and session persistence path. The session manager implements OpenAI's device code auth flow (headless-friendly, no browser required on the server) with automatic token refresh, following the same persistence pattern as the existing NEAR AI session manager. Closes #742 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(llm): add Responses API client and token-refreshing decorator Native Responses API client for chatgpt.com/backend-api/codex/responses, the endpoint that works with ChatGPT subscription tokens. Handles SSE streaming, text completions, and tool call round-trips. Token-refreshing decorator wraps the provider to pre-emptively refresh OAuth tokens before API calls and retry once on auth failures. Reports zero cost since billing is through subscription. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(llm): wire OpenAI Codex into provider factory, CLI, and setup wizard Connect the new provider to the LLM factory, add openai_codex to the CLI --backend flag, and add it as an option in the onboarding wizard. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(llm): address PR #744 review feedback (20 items) Review fixes for the OpenAI Codex provider PR: - Remove dead `generate_pkce()` code (device flow gets PKCE from server) - Fix `refresh_tokens()` to use `.form()` instead of `.json()` per OAuth spec - Inline codex dispatch into `build_provider_chain()` (single async function, no separate `assemble_provider_chain()` helper — matches main's pattern) - Remove Clone from `OpenAiCodexSession`, restrict fields to `pub(crate)` - Propagate HTTP client builder error instead of silent fallback - Redact device code response body from debug log - Change `set_model()` in TokenRefreshingProvider to delegate to inner - Replace hardcoded `/tmp/` test path with `tempfile::tempdir()` - Accept `request_timeout_secs` from config instead of hardcoded 300s - Parse `Retry-After` header on 429 responses (matches nearai_chat.rs pattern) - Reuse `normalize_schema_strict()` for Codex tool definitions - Add warning log for dropped image attachments - Add doc comments on `list_models()` and `include` field - Add `OPENAI_CODEX_API_URL` to `.env.example` - Fix codex error message in `create_llm_provider()` for clarity - Revert unrelated `.worktrees` addition to `.gitignore` - Update `src/llm/CLAUDE.md` with Codex provider docs [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address review feedback and harden OpenAI Codex provider (takeover #744) Security: - Add SSRF validation (validate_base_url) on OPENAI_CODEX_AUTH_URL and OPENAI_CODEX_API_URL, matching the pattern used by all other base URL configs (regression test for #1103 included) Correctness: - Add missing cache_write_multiplier() and cache_read_discount() trait delegation in TokenRefreshingProvider - Cap device-code polling backoff at 60s to prevent unbounded interval growth on repeated 429 responses - Default expires_in to 3600s when server returns 0, preventing immediately-expired sessions - Fix pre-existing SseEvent::JobResult missing fallback_deliverable field in job_monitor.rs tests Cleanup: - Extract duplicated make_test_jwt() and test_codex_config() into shared codex_test_helpers module Co-Authored-By: Sanjeev-S <Sanjeev-S@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review feedback on OpenAI Codex provider (#1461) - Login command now resolves OPENAI_CODEX_* env overrides even when LLM_BACKEND isn't set to openai_codex (Copilot review) - Setup wizard "Keep current provider?" for codex no longer re-triggers device code login — mirrors Bedrock's keep-and-return pattern (Copilot) - Revert provider init log from info back to debug (Copilot) - Add warning log when token expires_in=0, before defaulting to 3600s (Gemini review) Co-Authored-By: Sanjeev-S <Sanjeev-S@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Sanjeev Suresh <Sanjeev-S@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…1461) * feat(llm): add OpenAI Codex backend config and OAuth session manager Add OpenAiCodex as a new LLM backend variant with config for auth endpoint, API base URL, client ID, and session persistence path. The session manager implements OpenAI's device code auth flow (headless-friendly, no browser required on the server) with automatic token refresh, following the same persistence pattern as the existing NEAR AI session manager. Closes #742 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(llm): add Responses API client and token-refreshing decorator Native Responses API client for chatgpt.com/backend-api/codex/responses, the endpoint that works with ChatGPT subscription tokens. Handles SSE streaming, text completions, and tool call round-trips. Token-refreshing decorator wraps the provider to pre-emptively refresh OAuth tokens before API calls and retry once on auth failures. Reports zero cost since billing is through subscription. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(llm): wire OpenAI Codex into provider factory, CLI, and setup wizard Connect the new provider to the LLM factory, add openai_codex to the CLI --backend flag, and add it as an option in the onboarding wizard. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(llm): address PR #744 review feedback (20 items) Review fixes for the OpenAI Codex provider PR: - Remove dead `generate_pkce()` code (device flow gets PKCE from server) - Fix `refresh_tokens()` to use `.form()` instead of `.json()` per OAuth spec - Inline codex dispatch into `build_provider_chain()` (single async function, no separate `assemble_provider_chain()` helper — matches main's pattern) - Remove Clone from `OpenAiCodexSession`, restrict fields to `pub(crate)` - Propagate HTTP client builder error instead of silent fallback - Redact device code response body from debug log - Change `set_model()` in TokenRefreshingProvider to delegate to inner - Replace hardcoded `/tmp/` test path with `tempfile::tempdir()` - Accept `request_timeout_secs` from config instead of hardcoded 300s - Parse `Retry-After` header on 429 responses (matches nearai_chat.rs pattern) - Reuse `normalize_schema_strict()` for Codex tool definitions - Add warning log for dropped image attachments - Add doc comments on `list_models()` and `include` field - Add `OPENAI_CODEX_API_URL` to `.env.example` - Fix codex error message in `create_llm_provider()` for clarity - Revert unrelated `.worktrees` addition to `.gitignore` - Update `src/llm/CLAUDE.md` with Codex provider docs [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address review feedback and harden OpenAI Codex provider (takeover #744) Security: - Add SSRF validation (validate_base_url) on OPENAI_CODEX_AUTH_URL and OPENAI_CODEX_API_URL, matching the pattern used by all other base URL configs (regression test for #1103 included) Correctness: - Add missing cache_write_multiplier() and cache_read_discount() trait delegation in TokenRefreshingProvider - Cap device-code polling backoff at 60s to prevent unbounded interval growth on repeated 429 responses - Default expires_in to 3600s when server returns 0, preventing immediately-expired sessions - Fix pre-existing SseEvent::JobResult missing fallback_deliverable field in job_monitor.rs tests Cleanup: - Extract duplicated make_test_jwt() and test_codex_config() into shared codex_test_helpers module Co-Authored-By: Sanjeev-S <Sanjeev-S@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review feedback on OpenAI Codex provider (#1461) - Login command now resolves OPENAI_CODEX_* env overrides even when LLM_BACKEND isn't set to openai_codex (Copilot review) - Setup wizard "Keep current provider?" for codex no longer re-triggers device code login — mirrors Bedrock's keep-and-return pattern (Copilot) - Revert provider init log from info back to debug (Copilot) - Add warning log when token expires_in=0, before defaulting to 3600s (Gemini review) Co-Authored-By: Sanjeev-S <Sanjeev-S@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Sanjeev Suresh <Sanjeev-S@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…earai#1461) * feat(llm): add OpenAI Codex backend config and OAuth session manager Add OpenAiCodex as a new LLM backend variant with config for auth endpoint, API base URL, client ID, and session persistence path. The session manager implements OpenAI's device code auth flow (headless-friendly, no browser required on the server) with automatic token refresh, following the same persistence pattern as the existing NEAR AI session manager. Closes nearai#742 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(llm): add Responses API client and token-refreshing decorator Native Responses API client for chatgpt.com/backend-api/codex/responses, the endpoint that works with ChatGPT subscription tokens. Handles SSE streaming, text completions, and tool call round-trips. Token-refreshing decorator wraps the provider to pre-emptively refresh OAuth tokens before API calls and retry once on auth failures. Reports zero cost since billing is through subscription. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(llm): wire OpenAI Codex into provider factory, CLI, and setup wizard Connect the new provider to the LLM factory, add openai_codex to the CLI --backend flag, and add it as an option in the onboarding wizard. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(llm): address PR nearai#744 review feedback (20 items) Review fixes for the OpenAI Codex provider PR: - Remove dead `generate_pkce()` code (device flow gets PKCE from server) - Fix `refresh_tokens()` to use `.form()` instead of `.json()` per OAuth spec - Inline codex dispatch into `build_provider_chain()` (single async function, no separate `assemble_provider_chain()` helper — matches main's pattern) - Remove Clone from `OpenAiCodexSession`, restrict fields to `pub(crate)` - Propagate HTTP client builder error instead of silent fallback - Redact device code response body from debug log - Change `set_model()` in TokenRefreshingProvider to delegate to inner - Replace hardcoded `/tmp/` test path with `tempfile::tempdir()` - Accept `request_timeout_secs` from config instead of hardcoded 300s - Parse `Retry-After` header on 429 responses (matches nearai_chat.rs pattern) - Reuse `normalize_schema_strict()` for Codex tool definitions - Add warning log for dropped image attachments - Add doc comments on `list_models()` and `include` field - Add `OPENAI_CODEX_API_URL` to `.env.example` - Fix codex error message in `create_llm_provider()` for clarity - Revert unrelated `.worktrees` addition to `.gitignore` - Update `src/llm/CLAUDE.md` with Codex provider docs [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address review feedback and harden OpenAI Codex provider (takeover nearai#744) Security: - Add SSRF validation (validate_base_url) on OPENAI_CODEX_AUTH_URL and OPENAI_CODEX_API_URL, matching the pattern used by all other base URL configs (regression test for nearai#1103 included) Correctness: - Add missing cache_write_multiplier() and cache_read_discount() trait delegation in TokenRefreshingProvider - Cap device-code polling backoff at 60s to prevent unbounded interval growth on repeated 429 responses - Default expires_in to 3600s when server returns 0, preventing immediately-expired sessions - Fix pre-existing SseEvent::JobResult missing fallback_deliverable field in job_monitor.rs tests Cleanup: - Extract duplicated make_test_jwt() and test_codex_config() into shared codex_test_helpers module Co-Authored-By: Sanjeev-S <Sanjeev-S@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review feedback on OpenAI Codex provider (nearai#1461) - Login command now resolves OPENAI_CODEX_* env overrides even when LLM_BACKEND isn't set to openai_codex (Copilot review) - Setup wizard "Keep current provider?" for codex no longer re-triggers device code login — mirrors Bedrock's keep-and-return pattern (Copilot) - Revert provider init log from info back to debug (Copilot) - Add warning log when token expires_in=0, before defaulting to 3600s (Gemini review) Co-Authored-By: Sanjeev-S <Sanjeev-S@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Sanjeev Suresh <Sanjeev-S@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…earai#1461) * feat(llm): add OpenAI Codex backend config and OAuth session manager Add OpenAiCodex as a new LLM backend variant with config for auth endpoint, API base URL, client ID, and session persistence path. The session manager implements OpenAI's device code auth flow (headless-friendly, no browser required on the server) with automatic token refresh, following the same persistence pattern as the existing NEAR AI session manager. Closes nearai#742 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(llm): add Responses API client and token-refreshing decorator Native Responses API client for chatgpt.com/backend-api/codex/responses, the endpoint that works with ChatGPT subscription tokens. Handles SSE streaming, text completions, and tool call round-trips. Token-refreshing decorator wraps the provider to pre-emptively refresh OAuth tokens before API calls and retry once on auth failures. Reports zero cost since billing is through subscription. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(llm): wire OpenAI Codex into provider factory, CLI, and setup wizard Connect the new provider to the LLM factory, add openai_codex to the CLI --backend flag, and add it as an option in the onboarding wizard. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(llm): address PR nearai#744 review feedback (20 items) Review fixes for the OpenAI Codex provider PR: - Remove dead `generate_pkce()` code (device flow gets PKCE from server) - Fix `refresh_tokens()` to use `.form()` instead of `.json()` per OAuth spec - Inline codex dispatch into `build_provider_chain()` (single async function, no separate `assemble_provider_chain()` helper — matches main's pattern) - Remove Clone from `OpenAiCodexSession`, restrict fields to `pub(crate)` - Propagate HTTP client builder error instead of silent fallback - Redact device code response body from debug log - Change `set_model()` in TokenRefreshingProvider to delegate to inner - Replace hardcoded `/tmp/` test path with `tempfile::tempdir()` - Accept `request_timeout_secs` from config instead of hardcoded 300s - Parse `Retry-After` header on 429 responses (matches nearai_chat.rs pattern) - Reuse `normalize_schema_strict()` for Codex tool definitions - Add warning log for dropped image attachments - Add doc comments on `list_models()` and `include` field - Add `OPENAI_CODEX_API_URL` to `.env.example` - Fix codex error message in `create_llm_provider()` for clarity - Revert unrelated `.worktrees` addition to `.gitignore` - Update `src/llm/CLAUDE.md` with Codex provider docs [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address review feedback and harden OpenAI Codex provider (takeover nearai#744) Security: - Add SSRF validation (validate_base_url) on OPENAI_CODEX_AUTH_URL and OPENAI_CODEX_API_URL, matching the pattern used by all other base URL configs (regression test for nearai#1103 included) Correctness: - Add missing cache_write_multiplier() and cache_read_discount() trait delegation in TokenRefreshingProvider - Cap device-code polling backoff at 60s to prevent unbounded interval growth on repeated 429 responses - Default expires_in to 3600s when server returns 0, preventing immediately-expired sessions - Fix pre-existing SseEvent::JobResult missing fallback_deliverable field in job_monitor.rs tests Cleanup: - Extract duplicated make_test_jwt() and test_codex_config() into shared codex_test_helpers module Co-Authored-By: Sanjeev-S <Sanjeev-S@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review feedback on OpenAI Codex provider (nearai#1461) - Login command now resolves OPENAI_CODEX_* env overrides even when LLM_BACKEND isn't set to openai_codex (Copilot review) - Setup wizard "Keep current provider?" for codex no longer re-triggers device code login — mirrors Bedrock's keep-and-return pattern (Copilot) - Revert provider init log from info back to debug (Copilot) - Add warning log when token expires_in=0, before defaulting to 3600s (Gemini review) Co-Authored-By: Sanjeev-S <Sanjeev-S@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Sanjeev Suresh <Sanjeev-S@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Summary
openai_codexLLM backend so ChatGPT Pro/Plus subscribers can use IronClaw without a separate API keyUsage
Commits
OpenAiCodexConfig,LlmBackend::OpenAiCodex, device code auth flow with token persistence and auto-refreshchatgpt.com/backend-api/codex/responses, SSE stream parsing, atomic token state, best-effort pre-emptive refresh--backend openai_codex, setup wizard integrationTest plan
cargo fmt— no changescargo clippy --all --benches --tests --examples --all-features— zero warningscargo test --lib— 1878 passed, 0 failedCloses #742
🤖 Generated with Claude Code