Skip to content

feat!: convert the mixin into a standalone agent kit - #2

Open
akalipetis wants to merge 1 commit into
feature-codex-claude-subagentsfrom
feature-agent-kit
Open

feat!: convert the mixin into a standalone agent kit#2
akalipetis wants to merge 1 commit into
feature-codex-claude-subagentsfrom
feature-agent-kit

Conversation

@akalipetis

Copy link
Copy Markdown
Contributor

Stacked on #1 — merge that first. Base is feature-codex-claude-subagents, so this PR's diff is just the conversion.

What changed

Lambda was a kind: mixin that required the built-in codex agent and hijacked its entrypoint to launch Pi, exiling the native CLIs to sbx-codex and sbx-claude. That whole arrangement existed for one reason: keeping host-managed OpenAI OAuth, which SBX gates on built-in provenance.

It's now a kind: sandbox agent kit on docker/sandbox-templates:shell-docker, with lambda as its own agent binary — a symlink to pi, with Pi's provider and model defaults carried in sandbox.command rather than a wrapper script. codex and claude are the real upstream CLIs again and every shim is gone.

Before After
Kind mixin, requires: agent: codex sandbox
Agent binary codex (hijacked to run Pi) lambda
Native Codex sbx-codex codex
Native Claude sbx-claude claude
Launch sbx run codex --kit ... sbx run lambda --kit ...

The OAuth problem, and how auth actually works now

Proxy-managed OAuth does not activate for a third-party sandbox kit. Docker documents this in two places, and it held up under test: a request carrying the oai-oat01-proxy-managed sentinel to chatgpt.com/backend-api/codex/responses returned a 401 byte-identical to one carrying totally-not-a-token. The proxy records an OAuth binding and then never substitutes. This is a provenance gate, not a syntax problem — the declaration validates fine.

Neither subscription can fall back to an API key: ChatGPT Plus/Pro and Claude Pro/Max are OAuth-only, and an OpenAI or Anthropic API key is a separate pay-per-token account.

API-key injection does work, so host-managed auth is rebuilt on top of the mechanism that does. The host mints and refreshes the token; the proxy substitutes it per request. No token enters the sandbox, and nothing is per-project — all four secrets are global, so every future project sandbox picks them up with zero logins.

Consumer Emits Injected into Credential
Pi (lambda) header via sbx-codex.ts chatgpt.com chatgpt-codex
Native codex header via the sandboxd model provider in ~/.codex/config.toml chatgpt.com chatgpt-codex
Native claude CLAUDE_CODE_OAUTH_TOKEN api.anthropic.com claude-code
Pi / OpenCode Go OPENCODE_API_KEY opencode.ai opencode-go
gh, git GH_TOKEN api.github.com, github.com, raw.githubusercontent.com github

Both ChatGPT consumers only need to emit an Authorization header — apiKey.inject overwrites whatever it contains, so the placeholders in sbx-codex.ts and config.toml are not secrets and never reach upstream.

Two subtleties worth reviewing closely

  • chatgpt-codex must not be renamed openai. sbx secret set --command cannot combine with --oauth, so reusing that service id would replace the built-in Codex agent's OAuth registration. The built-in kit only injects openai into api.openai.com/openai.com, not chatgpt.com, so plain sbx run codex would start failing. Confirmed intact after setup.
  • No apiKeyHelper in ~/.claude/settings.json. The built-in claude kit seeds it so Claude Code presents the proxy sentinel, but that only works with provenance-backed interception. Here it would make Claude Code send a dead sentinel and never prompt, breaking the in-sandbox /login fallback. Gating on SBX_CRED_ANTHROPIC_MODE doesn't help either — that variable reports apikey for an OAuth-only declaration.

Verification

Smoke sandbox created from the kit, secrets stored, bindings approved:

  • Native claude — completion on claude-opus-5; rate-limit event reported isUsingOverage: false, so it draws on the subscription plan.
  • run_subagent's exact Claude invocation — completes and emits the stream-json shape the extension parses. --effort confirmed a valid flag.
  • Pi and native codex — both reach the account, returning "usage limit has been reached" / "out of credits". Account billing state, not auth failures, and the diagnostic difference from the pre-fix Could not parse your authentication token.
  • opencode-go — authenticated, workspace-scoped response (used as the control proving injection works).
  • sbx kit validate . passes.

Still unverified: whether --refresh on-demand keeps pace with ChatGPT token lifetimes over a long session.

Migration

Every existing sandbox is agent codex and must be recreated as lambda. Volumes are keyed to sandbox name, so nothing carries over automatically.

sbx secret set chatgpt-codex \
  --command 'pi auth print-bearer-token --provider openai-codex' --refresh on-demand
claude setup-token && sbx secret set claude-code --token
sbx rm --force lambda-<project> && sbx run --name lambda-<project> --kit . lambda

The lambda() shell helper in the README is updated to pass lambda instead of codex.

Also in here

  • Two previously-uncommitted items from the schema v2 migration: files/home/.pi/agent/models.json (maps Pi's max to upstream ultra for gpt-5.6-terra) and agents/plans/2026-08-28-v2-kit-repair.md.
  • TypeScript indentation switched to spaces, with .editorconfig to enforce it. This makes native-subagents.ts look larger than it is — the logic change is executableFor() plus deleting a now-unused PATH probe.

🤖 Generated with Claude Code

Lambda was a `kind: mixin` that required the built-in `codex` agent and
hijacked its entrypoint to launch Pi, exiling the native CLIs to `sbx-codex`
and `sbx-claude`. That existed only to keep host-managed OpenAI OAuth, which
is gated on built-in provenance.

Lambda is now a `kind: sandbox` agent kit built on
`docker/sandbox-templates:shell-docker`, with `lambda` as its own agent binary
(a symlink to `pi`, with Pi's provider and model defaults in
`sandbox.command`). `codex` and `claude` are the real upstream CLIs again and
every shim is gone.

Proxy-managed OAuth does not activate for a third-party sandbox kit: the proxy
never substitutes the sentinel, verified by a request carrying it returning a
401 byte-identical to one carrying a garbage token. API-key injection does
work, so host-managed auth is rebuilt on top of it. The host mints and
refreshes tokens and the proxy substitutes them per request, so no token enters
the sandbox and no credential is per-project:

- `chatgpt-codex` injects into `chatgpt.com`, sourced from
  `pi auth print-bearer-token --provider openai-codex` with `--refresh
  on-demand`. Deliberately not named `openai`, because `--command` cannot
  combine with `--oauth` and reusing that service id would break plain
  `sbx run codex`.
- `claude-code` injects into `api.anthropic.com` from a `claude setup-token`
  token.
- `opencode-go` and `github` are unchanged.

Verified end to end: native `claude` returns a completion on `claude-opus-5`
drawing on the plan rather than overage; Pi and native `codex` both reach the
account and return account-scoped billing messages rather than auth failures.

Also switches TypeScript indentation to spaces and adds `.editorconfig` to
enforce it.

BREAKING CHANGE: the agent is now `lambda`, not `codex`. Launch with
`sbx run lambda --kit ...`, and recreate existing sandboxes to pick it up.
The `sbx-codex` and `sbx-claude` commands no longer exist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T10:29:12.636269Z a3759fa PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a3759fa24b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread README.md
Comment thread spec.yaml
Comment thread spec.yaml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant