feat(codex)!: migrate ACP adapter#1353
Conversation
Preserve existing default_config_options values when an ACP agent advertises the renamed Codex modes. Leave other agents and custom values unchanged. Refs openabdev#1352
Install agentclientprotocol/codex-acp 1.1.2 with Codex 0.144.1 across every Codex image path. Verify pinned versions and the ACP v1 initialize handshake in Docker smoke tests. BREAKING CHANGE: Codex ACP mode IDs are now read-only, agent, and agent-full-access. Refs openabdev#1352
Point installation guidance at the maintained ACP adapter and document the renamed modes, compatibility mapping, security tradeoffs, and rollback path. Refs openabdev#1352
Keep accepting legacy OpenAB pool defaults, but warn operators with the old and replacement values so they can migrate their configuration. Refs openabdev#1352
Show built-in and gateway platform paths, unified and standalone deployment modes, the shared OpenAB core, and local or AgentCore ACP runtimes in both READMEs. Refs openabdev#1352
Restore the three-column platform, thin OpenAB broker, and agent runtime overview while adding unified gateway, newer platforms, Codex app-server, native agent, and AgentCore paths. Refs openabdev#1352
Label webhook-based platform edges as webhook/API and Feishu/Lark as WS/webhook so the architecture diagram communicates integration semantics rather than only the HTTP transport. Refs openabdev#1352
This comment has been minimized.
This comment has been minimized.
The expected adapter/CLI versions were hardcoded in the workflow greps, duplicating the Dockerfile ARG defaults in 5 places. Extract them from the Dockerfile each job actually builds so a Codex version bump only touches the Dockerfiles.
This comment has been minimized.
This comment has been minimized.
|
@chaodu-agent Thank you for the detailed LGTM, for resolving the version-source duplication in On the “Three Reasons We Might Not Need This PR,” I agree that all three pressure tests reinforce the migration rather than provide a viable reason to defer it:
The official preview image |
|
@thepagent Thanks for waiting. I have completed the first behavioral canary pass against the official preview image. The corresponding Discord report is here: https://discord.com/channels/1491295327620169908/1525352558829371483/1525474619497254953 Test target
I used four independent methods so that the result would not depend on one scripted permission response. 1. Image and runtime inspectionI inspected the installed packages and runtime versions inside the published image. The adapter resolves to Result: passed. 2. ACP protocol canaryI used a temporary minimal stdio JSON-RPC client to exercise the same ACP boundary used by OpenAB: Initialization, session creation/loading, two consecutive turns, cancellation, a live In 3. Sandbox isolation probesTo remove OpenAB, ACP, the test client, and the model from the path, I ran the sandbox components directly in the same image: codex sandbox linux /bin/true
bwrap --unshare-user --uid 0 --gid 0 /bin/trueBoth failed before any model or ACP activity with: Result: the failure is reproducible below the adapter boundary in the tested unprivileged Docker runtime. 4. Human-driven Codex CLI sessionsI then ran Codex interactively in the preview image, entered natural-language requests, read each permission prompt, and chose the response manually rather than pre-programming it. For For The interactive commands were equivalent to: codex --sandbox read-only --ask-for-approval on-request -C /workspace
codex --sandbox workspace-write --ask-for-approval on-request -C /workspaceThe credentials and workspaces were stored in isolated temporary Docker volumes, which were removed after the test. ConclusionThe core migration objective passes: the maintained adapter starts correctly, the ACP lifecycle works, and The OpenAI documents sandbox mode and approval policy as separate controls and describes Could you confirm whether you would prefer us to:
I have not marked the entire behavioral preview canary complete: a formal Discord/OpenAB gateway deployment, actionable error-propagation check, and deployment rollback rehearsal remain separate from the adapter-level and direct-runtime evidence above. References
|
Start codex-acp with the maintained adapter mode environment setting so the outer container remains the default security boundary. Explicit ACP configuration continues to override the image default, including capability-gated legacy mode mappings. BREAKING CHANGE: OpenAB Codex images now default to agent-full-access when no mode is configured.
|
@thepagent I implemented the container-as-sandbox default requested in the
The previous I also like the suggestion of a reusable |
Document a repeatable five-layer workflow for preview images, ACP protocol smoke tests, runtime isolation, interactive validation, and Discord gateway E2E. Clarify that maintainers dispatch PR preview builds from main while the workflow checks out the current pull request head.
|
@thepagent Follow-up: I accepted the documentation suggestion in this PR and added the reusable five-layer canary guide in Because Once the refreshed |
|
@thepagent The final preview canary for the runtime changes is complete. Test target
Although the Results
The bidirectional stdio client followed the transport documented in The final machine-readable result was: {
"defaultMode": "agent-full-access",
"model": "gpt-5.6-sol",
"turns": [
{ "stopReason": "end_turn", "expectedTextSeen": true },
{ "stopReason": "end_turn", "expectedTextSeen": true }
],
"cancel": {
"notificationSent": true,
"elapsedMs": 3018,
"stopReason": "cancelled",
"hasError": false
},
"errorPropagation": { "hasError": true, "code": -32601 },
"load": {
"accepted": true,
"resumedStopReason": "end_turn",
"expectedTextSeen": true
},
"modeOverrides": {
"read-only": "read-only",
"agent": "agent",
"agent-full-access": "agent-full-access"
},
"fileWrite": {
"stopReason": "end_turn",
"responseTextSeen": true,
"exactFileContent": true
},
"permissionRequests": 0
}
The first harness start exposed a root-owned temporary auth-volume directory, which prevented Codex from creating its SQLite state. After correcting only that disposable fixture's ownership to the image user's UID/GID, the final run completed cleanly. Host credentials were never modified. Discord gateway E2E follow-upA newly created, non-production bot was used so the test did not share a token with Nova or any deployed OpenAB instance. The local preview container was pinned to the manifest digest above and allowlisted only channel
All temporary containers, the copied-auth, workspace, and session-state volumes, both temporary clients, the canary config, and the credential file were removed after the tests. ConclusionThe current preview satisfies the pre-merge Codex migration criteria covered by the isolated image and ACP canary: the maintained runtime accepts Discord gateway E2E is now verified for connection, model selection, multi-turn delivery, a Web Search tool call, slash-command cancellation routing, and restart/session-load behavior. The scoped live-interruption gap is covered at the direct ACP layer and is called out above rather than overstated. Deployment rollback remains a post-merge gate because no deployed agent was changed during this isolated preview test. |
Add copyable commands for preview builds, ACP transport, runtime probes, Discord E2E, cleanup, and rollback. Reflow prose and add a table of contents so the rendered guide remains easy to scan.
This comment has been minimized.
This comment has been minimized.
The df91ae8 approach packed INITIAL_AGENT_MODE into OPENAB_AGENT_COMMAND through /usr/bin/env. Canary on a real deployment showed this breaks every config that overrides [agent].args only (the documented model-pin pattern): OAB keeps token 1 as the command and replaces the args, yielding '/usr/bin/env -c model=…' → spawn crash. Keep the intent (containerized deployments run full access by default; the outer container is the security boundary) but deliver it through the PR's own ACP mechanism: [pool].default_config_options = { mode = "agent-full-access" }, now the documented recommended default in docs/codex.md, config.toml.example, and config-reference.md. Nothing is baked into the image; the command stays a single token. The smoke-test mode-default steps become a regression guard asserting OPENAB_AGENT_COMMAND stays a plain binary. Also fixes the stale old- adapter codex example in config.toml.example (command was 'codex' with args ['--acp']). BREAKING CHANGE: unchanged from the base migration (mode IDs). The image no longer forces agent-full-access; deployments opt in via the documented [pool] default.
This comment has been minimized.
This comment has been minimized.
3cee49c was staged with 'git add -A' from a working tree that contained maintainer-local artifacts unrelated to this PR: a 21MB prebuilt binary (.pre-built/), local caches, a deployment manifest, and local skill notes. Remove them; none contain secret values (the manifest references Secrets Manager ARNs only).
Maintainer decision: keep the broker thin. Instead of carrying a compatibility shim in pool.rs that translates the Zed adapter's mode ids (auto/full-access) to the new adapter's (agent/agent-full-access), revert pool.rs to main and call the rename out as an explicit breaking change in docs/codex.md. Existing configs using the old ids fail safe on upgrade: the set_config_option call fails with a warning and the session stays on the adapter's conservative 'agent' default — it never escalates. Operators update one config line. BREAKING CHANGE: [pool].default_config_options mode values auto / full-access are not translated; use agent / agent-full-access.
…tead of mapping Maintainer scope decisions: - Drop the pool.rs legacy mode-id compatibility mapping. Keep the broker thin: the Zed adapter's auto/full-access ids are called out as an explicit breaking change in docs/codex.md instead of being translated at session setup. Old values fail safe on upgrade — the set_config_option call warns and the session stays on the adapter's conservative 'agent' default; operators update one config line. - Move the canary-testing guide (docs/canary-tests.md) and the CONTRIBUTING.md E2E section rewrite to a separate docs PR — they are a standalone process-documentation topic, not part of the adapter migration. - Revert the README architecture-diagram rewrites for the same reason, keeping only the Codex adapter table row (zed → agentclientprotocol). BREAKING CHANGE: [pool].default_config_options mode values auto / full-access are not translated; use agent / agent-full-access.
|
Note LGTM ✅ — PR trimmed to pure migration scope after maintainer review ( Final Scope (11 files, +180/−29)
Maintainer scope decisions this round
Canary Evidence (round 2, isolated codex bot, ECS)Config exercised the exact pattern that crashed round 1 plus the new default: [agent]
args = ["-c", "model=\"gpt-5.5\""]
[pool]
default_config_options = { mode = "agent-full-access" }
No Ready to merge once CI is green on |

What problem does this solve?
OpenAB's Codex image currently uses
@zed-industries/codex-acp@0.16.0. That adapter embeds Codex core0.137.0, so upgrading the globally installed Codex CLI in the image does not affect the runtime that actually handles ACP turns. Requests usinggpt-5.6-soltherefore still fail withrequires a newer version of Codex.This PR migrates the Codex ACP runtime to the actively maintained adapter while preserving the behavior of existing OpenAB mode settings when upstream mode IDs have changed.
Closes #1352
Discord Discussion URL: https://discord.com/channels/1491295327620169908/1525352558829371483
Table of Contents
At a Glance
Prior Art & Industry Research
Citations follow the Leeds Harvard author-date style. Software repositories and documentation are treated as works by corporate authors. The online-source format described by the University of Leeds Library (n.d.) includes the author, year, title, medium, access date, and URL.
OpenClaw:
OpenClaw's native Codex plugin uses Codex app-server directly, pins
@openai/codex@0.144.1, and keeps the channel integration separate from the runtime boundary. This supports retaining OpenAB's existing ACP boundary while moving the underlying Codex runtime to the app-server architecture (OpenClaw, 2026a; OpenClaw, 2026b; OpenClaw, 2026c). OpenClaw's older ACPX path still uses the Zed adapter; this PR follows the direction established by its native Codex plugin instead.Hermes Agent:
Hermes Agent provides an opt-in Codex app-server runtime that launches
codex app-serverwhile continuing to use the global Codex CLI'scodex loginauthentication flow. This demonstrates that app-server can coexist with established CLI credentials. It also requires users to choose workspace/default permissions explicitly instead of silently relaxing the sandbox (Nous Research, 2026a; Nous Research, 2026b).Other references:
The maintained adapter preserves the
codex-acpbinary name and uses the app-server architecture (Agent Client Protocol, 2026a). It also exposesINITIAL_AGENT_MODEfor selectingread-only,agent, oragent-full-accesswhen the adapter starts (Agent Client Protocol, 2026b). The Zed v0.16.0 lockfile pins Codex core0.137.0, and its maintainer recommends adding missing functionality to the new implementation instead of maintaining two adapters indefinitely (Zed Industries, 2026a; Zed Industries, 2026b).References
Numbered for review navigation; sorted alphabetically by corporate author and, for works by the same author in the same year, by title.
Proposed Solution
@agentclientprotocol/codex-acp@1.1.2and@openai/codex@0.144.1inDockerfile.codex,Dockerfile.unified, andDockerfile.package.@openai/codex ^0.144.0dependency is deduplicated to the pinned global0.144.1, while the global CLI remains available forcodex login --device-auth.CODEX_PATH. OpenAB launches agents with a minimal environment, so DockerENVvalues are not inherited automatically. The resulting npm installation already ensures that the adapter and global CLI resolve to the same Codex version.codex-acpthrough/usr/bin/env INITIAL_AGENT_MODE=agent-full-access. This sets one non-secret adapter option after OpenAB'senv_clear()boundary, treats the outer container or VM as the default sandbox, and avoids requiring nested unprivileged user namespaces.autotoagentandfull-accesstoagent-full-access, but only when the agent advertises the replacement values in its configuration capabilities. Values used with the old adapter, other ACP agents, and custom configurations remain unchanged. When a legacy value is mapped, OpenAB logs the old value, its replacement, and instructions for updating the configuration.protocolVersion: 1, and the workflows verify the installed adapter and CLI versions.docs/canary-tests.md, a reusable five-layer guide covering preview-image inspection, bidirectional ACP stdio smoke tests, runtime isolation probes, human-driven validation, Discord gateway E2E, acceptance criteria, evidence reporting, and cleanup.This replaces an implementation within the existing agent subprocess and ACP subsystem. It does not introduce a new subsystem or public OpenAB API, so the project's ADR policy does not require a new ADR. The architectural rationale, risks, and rollback plan are documented in #1352 and this PR.
Why this approach?
The maintained adapter preserves the
codex-acpbinary and the ACP methods OpenAB already uses, but delegates Codex execution to the upstream app-server. This avoids the version split between the old Rust adapter's embedded core and the separately installed CLI. The capability-gated mode mapping is applied only when the replacement values are actually advertised by the agent, preserving existing OpenAB configurations without guessing what similarly named options mean for other agents.The accepted tradeoff is that the renamed ACP mode IDs remain a breaking change for custom clients that call
session/set_config_optiondirectly. Only legacy values supplied through OpenAB's[pool].default_config_optionsare mapped automatically.At maintainer request, OpenAB's Codex images override the upstream adapter's conservative
agentdefault withagent-full-access. This makes the outer container or VM the default security boundary and avoids a nestedbubblewrapsandbox that cannot start in common unprivileged Docker runtimes. An explicitread-only,agent, oragent-full-accesspool value is applied aftersession/newand therefore overrides the image default; mapped legacy values do the same.Alternatives Considered
agentclientprotocol/codex-acp; continuing with Zed would preserve the cost of maintaining two implementations.codex-acp loginand OpenAB's existing authentication command require thecodexbinary onPATH.CODEX_PATHat the global CLI: OpenAB'senv_clear()boundary does not preserve DockerENVvalues. Installing both packages in one npm transaction provides a simpler and directly verifiable single-version resolution.agentdefault in OpenAB images: The first preview canary reproduced abubblewrapnamespace failure below the ACP boundary in an unprivileged Docker runtime. OpenAB therefore defaults its Codex images toagent-full-access, documents the outer-isolation requirement, and retains explicit mode overrides for deployments that can support or require the inner sandbox.Validation
cargo checkcargo test(15 root-package tests)cargo test -p openab-core acp::pool(18 tests, including three mode-compatibility regression tests)cargo clippy -- -D warningscargo build --releaserustfmt --check.cargo fmt --all -- --checkreports the same pre-existing 6,104-line drift on an unmodifiedorigin/mainand on this branch. This PR introduces no additional formatting failures and therefore does not reformat unrelated files.1.1.2and Codex0.144.1pinned, then rannpm audit --omit=dev --audit-level=high; the result wasfound 0 vulnerabilities.Dockerfile.codex,Dockerfile.unified --target codex, andDockerfile.package --target codex.df91ae8, all three local image forms exposeOPENAB_AGENT_COMMAND=/usr/bin/env INITIAL_AGENT_MODE=agent-full-access codex-acp; an ACPsession/newreportsagent-full-accesswhen no mode override is supplied.read-only,agent, andagent-full-accessvalues remain unchanged before OpenAB sends them throughsession/set_config_option.1.1.2and Codex CLI0.144.1;npm listconfirms that the adapter dependency is deduplicated to the same Codex version.initializeusing OpenAB client information.gpt-5.6-sol, and completed a prompt withstopReason: end_turnand responseACP_OK.yqat the current head.c6aa59cpassed check. The canary guide has balanced fences, 14 valid TOC anchors, syntax-valid Bash, JSON, and TOML examples, valid repository-relative links, a GitHub-rendered GFM review, and commands smoke-tested against the preview image.d072a1b: CI, Docker Smoke Test, and Docker Smoke Test (Unified).99f0415against forkmainatca64f87, which matched upstreammainat the time of the run.ubuntu-24.04runners with all 15 matrix jobs passing.1.1.2, Codex CLI0.144.1, and the ACP v1 initialization handshake.docs/adr/unified-binary.md, the Cargo feature list, and the AgentCore documentation.git diff --checkcargo test --workspace: the macOS baseline is 657 passed and 1 failed. The sole failure is a pre-existing test that assumes/bin/false, which does not exist on macOS. The same test fails on an unmodifiedorigin/main, so this is unrelated to the PR.The fork-hosted Docker runs above validated commit
99f0415. Upstream CI and both Docker smoke workflows subsequently passed atd072a1b, which added the version-source refactor. Runtime commitdf91ae8adds the OpenAB image mode default, its smoke assertions, explicit-mode regression coverage, and updated security guidance. Its fork runs passed: Docker Smoke Test completed 15/15 jobs, and Docker Smoke Test (Unified) passed its builder and all 15 targets. Preview-tested head09ffcafadds the reusable canary guide and corrects the maintainer-only preview-dispatch instructions. Current PR headc6aa59cchanges onlydocs/canary-tests.md: it removes prose hard wraps, adds a TOC, and expands reproducible commands and examples. The refreshed runtime preview image and final canary both passed, as tracked below.Test plan
The rollout is gate-based rather than tied to an unconfirmed calendar date. Local evidence is complete. The initial preview and behavioral pass cover
d072a1b; the refreshed preview, isolated ACP canary, and dedicated-bot Discord E2E cover preview-tested head09ffcaf, including runtime commitdf91ae8. Current PR headc6aa59cchanges onlydocs/canary-tests.md, so the earlier image is not described as a current-head build. Deployment rollback remains in the post-merge gate because no deployed agent was changed during the isolated tests. Owners, triggers, and detailed acceptance criteria are maintained in #1352.Pre-merge
PR Preview Build, triggered by@chaodu-agentfor PR1353with variantcodex, completed successfully at PR headd072a1b. It published the multi-architecture imageghcr.io/openabdev/openab:pr1353-codexforlinux/amd64andlinux/arm64.d072a1bimage. The canary report records version inspection, ACP initialization, two turns,session/load,session/cancel, a livegpt-5.6-solturn, mode probes, direct sandbox probes, and human-driven CLI checks. It isolated theagentwrite failure to unavailable unprivileged user namespaces rather than the ACP adapter.PR Preview Build 29156240254checked out preview-tested head09ffcafand publishedghcr.io/openabdev/openab:pr1353-codexat digestsha256:4604ab3db69f3f3daab07d92aad9f5b36e5c614f072894c5266bf239f5a55b27. The final canary report verifies theagent-full-accessimage default, all three explicit mode overrides, two consecutivegpt-5.6-solturns,session/load, live ACP cancellation, JSON-RPC error propagation, and an exact workspace file write. A dedicated-bot Discord E2E additionally verifies gateway connection,/modelsexposure ofGPT-5.6-Sol, two turns in one thread, Web Search,/cancelrouting, and restart followed bysession/load; the report distinguishes slash-command routing from the separately proven live ACP interruption.Post-merge
requires a newer version of Codex,ACP_TURN_FAILED, or ACP initialization errors, and complete at least onegpt-5.6-solturn./home/node/.codex/and session persistence before rolling the image out gradually to the remaining Codex agents.Shipping readiness
agent-full-access, removing Codex's inner sandbox and approval prompts. The documentation requires a dedicated outer container or VM boundary, warns against host filesystem and Docker socket mounts, and calls for scoped credentials, volumes, service accounts, and network access. Users can explicitly selectagentorread-only; OpenAB'senv_clear()credential boundary remains intact, and no new secret path is introduced.Rollback plan
Trigger conditions
gpt-5.6-solencounters the client-version gate again.Rollback steps
/home/node/.codex/PVC without migrating credential or session data, and record the rollback evidence in RFC: migrate Codex ACP adapter to agentclientprotocol/codex-acp #1352 and this PR.Database considerations
Not applicable. This PR includes no database schema or data migration.
Time to rollback
Not verified; this depends on the image rollout time of the target deployment platform.
BREAKING CHANGE: Codex ACP mode IDs become
read-only,agent, andagent-full-access, and OpenAB Codex images now default toagent-full-accesswhen no mode is configured. Explicit current mode values override the image default. OpenAB maps the legacy pool defaultsautoandfull-access, but custom ACP clients must use the new IDs.