0050: Codex harness — ChatGPT subscription + in-app browser login #130

Merged
chad merged 13 commits from glen/0050-codex-harness into main 2026-10-18 22:29:17 -04:00
Collaborator

Adds Codex CLI as the fourth external harness (pi/claude/cursor/codex), ChatGPT-subscription auth, with in-app login. Design + as-built: docs/0050-codex-harness.md. Everything mirrored from cursor (0038); live-checked end-to-end on bee 2026-10-18/19.

What's in

  • Harness: @ai-sdk/harness-codex@1.0.143 (exact pin — pairs with our @ai-sdk/harness 1.0.141 / provider-utils 5.0.56, no stack bump; the tool-relay advisory GHSA-qw9h-448j-6rph affects ≤1.0.28 only). Bridge port 4002, codex({task}) chat tool, kind unions everywhere, /h/codex/<id> view, usage bucket, project allow-list, logo/labels. No DB migration (kind is text); no sandbox image change (pnpm already baked; adapter pnpm-installs its bridge per sandbox).
  • Auth (env-carried, no planting): credential at data/harness-auth/codex/auth.json (0600); CODEX_HOME wired at boot so the adapter reads AND refreshes the file in place; enterprise account id rides HarnessAgent({headers: {ChatGPT-Account-ID}}) (the designed seam — our sandbox lacks addRequestTransformations); runtime guard fails fast if any API-key env var would shadow the subscription.
  • Login (in-app, browser-based): primary = browser OAuth flow — authorize URL with PKCE S256 + state (client id app_EMoamEEZ73f0CkXaXp7hrann, redirect http://localhost:1455/auth/callback, byte-shape pinned to the shipped CLI), you paste the localhost callback back on /settings, server validates state constant-time and exchanges FORM-ENCODED; account id read from the nested https://api.openai.com/auth claim. Device-code flow secondary (Chad's enterprise workspace blocks it — proven live), paste-auth.json third. Tokens never enter logs, DB, or wire responses (test-enforced).
  • Models (live-verified on a team account): gpt-5.6-sol (default), gpt-5.6-terra/luna, gpt-6-sol, gpt-6.1-sol, gpt-6-luna, gpt-6-astra (transiently "at capacity" live). gpt-5.5 and gpt-5.5-codex are rejected by the backend for ChatGPT accounts — the adapter's own default is stale, so codex routes MUST pin a model (validated, with guidance). Effort knob (low..xhigh,max) like claude's.

Live evidence (bee, production path: real sandbox, real bridge, real turns)

  • Login: browser flow end-to-end (form-encoded exchange 200; device auth blocked by workspace; nested account claim discovered and handled).
  • Turns finished + file verified on 6 models; negative control (no credential) fails value-shaped (Missing environment variable: CODEX_API_KEY).
  • Login preserved into the app's credential dir on bee — codex works immediately post-deploy.

Review

Fresh-eyes review (review skill, separate context) returned merge-after-fixes: 2 should-fixes (runtime env-shadowing guard — was spike-only; modelless-route rejection — the API write path accepted a route that would send the rejected default) + 6 nits (misplaced comment, SANDBOXED_TOOLS miss, 1h→10min expiry margin so the adapter's own 5-min refresh window gets its chance, stale comments, boot warning on a diverging CODEX_HOME override, package.json churn). All 8 applied in a1038b5 and re-verified; reviewer's clean list: OAuth/PKCE/state handling, token hygiene across routes/UI/errors/tests, builder/headers seam, kind-union coverage, catalog/validation consistency, advisory claim.

Tests

814 passed / 16 skipped (was 790 on main; +90 codex tests across auth, login (fake issuer incl. byte-exact wire shapes), routes/validation, builder, tool gating, catalog view, usage). Opt-in spike: tests/codex-spike.integration.test.ts (CODEX_SPIKE=login_start|login_poll|turn).

Adds Codex CLI as the fourth external harness (pi/claude/cursor/codex), ChatGPT-subscription auth, with in-app login. Design + as-built: docs/0050-codex-harness.md. Everything mirrored from cursor (0038); live-checked end-to-end on bee 2026-10-18/19. ## What's in - **Harness**: `@ai-sdk/harness-codex@1.0.143` (exact pin — pairs with our `@ai-sdk/harness` 1.0.141 / `provider-utils` 5.0.56, no stack bump; the tool-relay advisory GHSA-qw9h-448j-6rph affects ≤1.0.28 only). Bridge port 4002, `codex({task})` chat tool, kind unions everywhere, `/h/codex/<id>` view, usage bucket, project allow-list, logo/labels. No DB migration (kind is text); no sandbox image change (pnpm already baked; adapter pnpm-installs its bridge per sandbox). - **Auth (env-carried, no planting)**: credential at `data/harness-auth/codex/auth.json` (0600); `CODEX_HOME` wired at boot so the adapter reads AND refreshes the file in place; enterprise account id rides `HarnessAgent({headers: {ChatGPT-Account-ID}})` (the designed seam — our sandbox lacks `addRequestTransformations`); runtime guard fails fast if any API-key env var would shadow the subscription. - **Login (in-app, browser-based)**: primary = browser OAuth flow — authorize URL with PKCE S256 + state (client id `app_EMoamEEZ73f0CkXaXp7hrann`, redirect `http://localhost:1455/auth/callback`, byte-shape pinned to the shipped CLI), you paste the localhost callback back on /settings, server validates state constant-time and exchanges FORM-ENCODED; account id read from the nested `https://api.openai.com/auth` claim. Device-code flow secondary (Chad's enterprise workspace blocks it — proven live), paste-auth.json third. Tokens never enter logs, DB, or wire responses (test-enforced). - **Models (live-verified on a team account)**: `gpt-5.6-sol` (default), `gpt-5.6-terra/luna`, `gpt-6-sol`, `gpt-6.1-sol`, `gpt-6-luna`, `gpt-6-astra` (transiently "at capacity" live). **gpt-5.5 and gpt-5.5-codex are rejected by the backend for ChatGPT accounts** — the adapter's own default is stale, so codex routes MUST pin a model (validated, with guidance). Effort knob (low..xhigh,max) like claude's. ## Live evidence (bee, production path: real sandbox, real bridge, real turns) - Login: browser flow end-to-end (form-encoded exchange 200; device auth blocked by workspace; nested account claim discovered and handled). - Turns finished + file verified on 6 models; negative control (no credential) fails value-shaped (`Missing environment variable: CODEX_API_KEY`). - Login preserved into the app's credential dir on bee — codex works immediately post-deploy. ## Review Fresh-eyes review (review skill, separate context) returned **merge-after-fixes**: 2 should-fixes (runtime env-shadowing guard — was spike-only; modelless-route rejection — the API write path accepted a route that would send the rejected default) + 6 nits (misplaced comment, SANDBOXED_TOOLS miss, 1h→10min expiry margin so the adapter's own 5-min refresh window gets its chance, stale comments, boot warning on a diverging CODEX_HOME override, package.json churn). **All 8 applied in a1038b5** and re-verified; reviewer's clean list: OAuth/PKCE/state handling, token hygiene across routes/UI/errors/tests, builder/headers seam, kind-union coverage, catalog/validation consistency, advisory claim. ## Tests 814 passed / 16 skipped (was 790 on main; +90 codex tests across auth, login (fake issuer incl. byte-exact wire shapes), routes/validation, builder, tool gating, catalog view, usage). Opt-in spike: `tests/codex-spike.integration.test.ts` (`CODEX_SPIKE=login_start|login_poll|turn`).
glen added 12 commits 2026-10-18 22:11:02 -04:00
chad approved these changes 2026-10-18 22:17:02 -04:00
merge: main into glen/0050-codex-harness — AGENTS.md resolved (codex edits re-applied over main's rewrite)
All checks were successful
deploy / checks (pull_request) Successful in 43s
deploy / deploy (pull_request) Has been skipped
b0c9a32987
chad merged commit a845ed3389 into main 2026-10-18 22:29:17 -04:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
chad/sweatshop-legacy!130
No description provided.