feat!: convert the mixin into a standalone agent kit - #2
Open
akalipetis wants to merge 1 commit into
Open
Conversation
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>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 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".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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: mixinthat required the built-incodexagent and hijacked its entrypoint to launch Pi, exiling the native CLIs tosbx-codexandsbx-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: sandboxagent kit ondocker/sandbox-templates:shell-docker, withlambdaas its own agent binary — a symlink topi, with Pi's provider and model defaults carried insandbox.commandrather than a wrapper script.codexandclaudeare the real upstream CLIs again and every shim is gone.mixin,requires: agent: codexsandboxcodex(hijacked to run Pi)lambdasbx-codexcodexsbx-claudeclaudesbx 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-managedsentinel tochatgpt.com/backend-api/codex/responsesreturned a 401 byte-identical to one carryingtotally-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.
lambda)sbx-codex.tschatgpt.comchatgpt-codexcodexsandboxdmodel provider in~/.codex/config.tomlchatgpt.comchatgpt-codexclaudeCLAUDE_CODE_OAUTH_TOKENapi.anthropic.comclaude-codeOPENCODE_API_KEYopencode.aiopencode-gogh,gitGH_TOKENapi.github.com,github.com,raw.githubusercontent.comgithubBoth ChatGPT consumers only need to emit an
Authorizationheader —apiKey.injectoverwrites whatever it contains, so the placeholders insbx-codex.tsandconfig.tomlare not secrets and never reach upstream.Two subtleties worth reviewing closely
chatgpt-codexmust not be renamedopenai.sbx secret set --commandcannot combine with--oauth, so reusing that service id would replace the built-in Codex agent's OAuth registration. The built-in kit only injectsopenaiintoapi.openai.com/openai.com, notchatgpt.com, so plainsbx run codexwould start failing. Confirmed intact after setup.apiKeyHelperin~/.claude/settings.json. The built-inclaudekit 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/loginfallback. Gating onSBX_CRED_ANTHROPIC_MODEdoesn't help either — that variable reportsapikeyfor an OAuth-only declaration.Verification
Smoke sandbox created from the kit, secrets stored, bindings approved:
claude— completion onclaude-opus-5; rate-limit event reportedisUsingOverage: false, so it draws on the subscription plan.run_subagent's exact Claude invocation — completes and emits the stream-json shape the extension parses.--effortconfirmed a valid flag.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-fixCould 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-demandkeeps pace with ChatGPT token lifetimes over a long session.Migration
Every existing sandbox is agent
codexand must be recreated aslambda. Volumes are keyed to sandbox name, so nothing carries over automatically.The
lambda()shell helper in the README is updated to passlambdainstead ofcodex.Also in here
files/home/.pi/agent/models.json(maps Pi'smaxto upstreamultraforgpt-5.6-terra) andagents/plans/2026-08-28-v2-kit-repair.md..editorconfigto enforce it. This makesnative-subagents.tslook larger than it is — the logic change isexecutableFor()plus deleting a now-unused PATH probe.🤖 Generated with Claude Code