Skip to content

feat(codex)!: migrate ACP adapter#1353

Open
vixenclawsastraagent wants to merge 16 commits into
openabdev:mainfrom
vixenclawsastraagent:feat/1352-codex-acp-migration
Open

feat(codex)!: migrate ACP adapter#1353
vixenclawsastraagent wants to merge 16 commits into
openabdev:mainfrom
vixenclawsastraagent:feat/1352-codex-acp-migration

Conversation

@vixenclawsastraagent

@vixenclawsastraagent vixenclawsastraagent commented Jul 11, 2026

Copy link
Copy Markdown

What problem does this solve?

OpenAB's Codex image currently uses @zed-industries/codex-acp@0.16.0. That adapter embeds Codex core 0.137.0, so upgrading the globally installed Codex CLI in the image does not affect the runtime that actually handles ACP turns. Requests using gpt-5.6-sol therefore still fail with requires 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

Before
OpenAB ──ACP──> Zed adapter 0.16.0 ──> embedded Codex 0.137.0
                                                │
                                                └─> gpt-5.6-sol: 400

After
OpenAB ──ACP v1──> agentclientprotocol adapter 1.1.2
                         │
                         └──> Codex app-server 0.144.1
                                      │
                                      └─> gpt-5.6-sol: end_turn

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-server while continuing to use the global Codex CLI's codex login authentication 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-acp binary name and uses the app-server architecture (Agent Client Protocol, 2026a). It also exposes INITIAL_AGENT_MODE for selecting read-only, agent, or agent-full-access when the adapter starts (Agent Client Protocol, 2026b). The Zed v0.16.0 lockfile pins Codex core 0.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.

  1. Agent Client Protocol. 2026a. codex-acp v1.1.2. [Source code]. Available from: agentclientprotocol/codex-acp v1.1.2. [Accessed 11 July 2026].
  2. Agent Client Protocol. 2026b. Runtime options. [Online]. Available from: codex-acp v1.1.2 runtime options. [Accessed 11 July 2026].
  3. Nous Research. 2026a. Codex app-server runtime. [Online]. Available from: Hermes Agent runtime guide. [Accessed 11 July 2026].
  4. Nous Research. 2026b. Codex app-server transport. [Source code]. Available from: Hermes Agent transport source. [Accessed 11 July 2026].
  5. OpenClaw. 2026a. Codex app-server stdio transport. [Source code]. Available from: OpenClaw transport source. [Accessed 11 July 2026].
  6. OpenClaw. 2026b. Codex extension dependencies. [Source code]. Available from: OpenClaw package manifest. [Accessed 11 July 2026].
  7. OpenClaw. 2026c. Codex harness runtime boundary. [Online]. Available from: OpenClaw runtime documentation. [Accessed 11 July 2026].
  8. University of Leeds Library. n.d. Leeds Harvard: Website or webpage. [Online]. Available from: Leeds Harvard referencing examples. [Accessed 11 July 2026].
  9. Zed Industries. 2026a. codex-acp v0.16.0 Cargo.lock. [Source code]. Available from: Zed adapter lockfile. [Accessed 11 July 2026].
  10. Zed Industries. 2026b. Maintainer migration guidance for codex-acp. [GitHub issue comment]. Available from: zed-industries/codex-acp#333. [Accessed 11 July 2026].

Proposed Solution

  • Pin @agentclientprotocol/codex-acp@1.1.2 and @openai/codex@0.144.1 in Dockerfile.codex, Dockerfile.unified, and Dockerfile.package.
  • Install both packages in the same npm transaction. The adapter's declared @openai/codex ^0.144.0 dependency is deduplicated to the pinned global 0.144.1, while the global CLI remains available for codex login --device-auth.
  • Do not set CODEX_PATH. OpenAB launches agents with a minimal environment, so Docker ENV values are not inherited automatically. The resulting npm installation already ensures that the adapter and global CLI resolve to the same Codex version.
  • Start codex-acp through /usr/bin/env INITIAL_AGENT_MODE=agent-full-access. This sets one non-secret adapter option after OpenAB's env_clear() boundary, treats the outer container or VM as the default sandbox, and avoids requiring nested unprivileged user namespaces.
  • Map the existing OpenAB pool default auto to agent and full-access to agent-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.
  • Update the Codex documentation, setup wizard, and Docker smoke workflows. Codex initialization now uses the ACP integer protocolVersion: 1, and the workflows verify the installed adapter and CLI versions.
  • Add 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.
  • Update the English and Traditional Chinese README architecture diagrams to show built-in and gateway adapters, standalone and unified deployments, and the local and AgentCore runtime boundaries.

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-acp binary 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_option directly. Only legacy values supplied through OpenAB's [pool].default_config_options are mapped automatically.

At maintainer request, OpenAB's Codex images override the upstream adapter's conservative agent default with agent-full-access. This makes the outer container or VM the default security boundary and avoids a nested bubblewrap sandbox that cannot start in common unprivileged Docker runtimes. An explicit read-only, agent, or agent-full-access pool value is applied after session/new and therefore overrides the image default; mapped legacy values do the same.

Alternatives Considered

  • Upgrade only the global Codex CLI: The Zed adapter uses an embedded core, so this does not update the runtime that handles ACP turns.
  • Continue updating the Zed adapter: Its maintainer has directed future integration work toward agentclientprotocol/codex-acp; continuing with Zed would preserve the cost of maintaining two implementations.
  • Remove the global Codex CLI: codex-acp login and OpenAB's existing authentication command require the codex binary on PATH.
  • Point CODEX_PATH at the global CLI: OpenAB's env_clear() boundary does not preserve Docker ENV values. Installing both packages in one npm transaction provides a simpler and directly verifiable single-version resolution.
  • Keep the upstream agent default in OpenAB images: The first preview canary reproduced a bubblewrap namespace failure below the ACP boundary in an unprivileged Docker runtime. OpenAB therefore defaults its Codex images to agent-full-access, documents the outer-isolation requirement, and retains explicit mode overrides for deployments that can support or require the inner sandbox.
  • Rewrite legacy modes unconditionally: This could break other ACP agents or adapters that still advertise the old IDs, so the mapping is capability-gated.

Validation

  • cargo check
  • cargo test (15 root-package tests)
  • cargo test -p openab-core acp::pool (18 tests, including three mode-compatibility regression tests)
  • cargo clippy -- -D warnings
  • cargo build --release
  • Formatting regression check: every Rust file modified by this PR passes rustfmt --check. cargo fmt --all -- --check reports the same pre-existing 6,104-line drift on an unmodified origin/main and on this branch. This PR introduces no additional formatting failures and therefore does not reformat unrelated files.
  • Lockfile-based dependency audit: generated an npm lockfile with adapter 1.1.2 and Codex 0.144.1 pinned, then ran npm audit --omit=dev --audit-level=high; the result was found 0 vulnerabilities.
  • Successfully built Dockerfile.codex, Dockerfile.unified --target codex, and Dockerfile.package --target codex.
  • At runtime commit df91ae8, all three local image forms expose OPENAB_AGENT_COMMAND=/usr/bin/env INITIAL_AGENT_MODE=agent-full-access codex-acp; an ACP session/new reports agent-full-access when no mode override is supplied.
  • Regression coverage confirms that explicit read-only, agent, and agent-full-access values remain unchanged before OpenAB sends them through session/set_config_option.
  • Containers report adapter 1.1.2 and Codex CLI 0.144.1; npm list confirms that the adapter dependency is deduplicated to the same Codex version.
  • Both the per-agent and unified Codex images complete ACP v1 initialize using OpenAB client information.
  • Created a live ACP session, selected gpt-5.6-sol, and completed a prompt with stopReason: end_turn and response ACP_OK.
  • Both modified workflows parse successfully with yq at the current head.
  • The current docs-only head c6aa59c passed 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.
  • Upstream runtime validation passed at d072a1b: CI, Docker Smoke Test, and Docker Smoke Test (Unified).
  • Fork-local GitHub Actions validation: vixenclawsastraagent/openab#1 ran PR commit 99f0415 against fork main at ca64f87, which matched upstream main at the time of the run.
  • Docker Smoke Test completed on GitHub-hosted ubuntu-24.04 runners with all 15 matrix jobs passing.
  • Docker Smoke Test (Unified) completed on GitHub-hosted runners with the builder and all 15 targets passing (16/16 jobs).
  • Codex-specific GitHub Actions evidence: the per-agent job and unified job both verified adapter 1.1.2, Codex CLI 0.144.1, and the ACP v1 initialization handshake.
  • Cross-checked the English and Traditional Chinese README architecture diagrams against docs/adr/unified-binary.md, the Cargo feature list, and the AgentCore documentation.
  • git diff --check
  • cargo 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 unmodified origin/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 at d072a1b, which added the version-source refactor. Runtime commit df91ae8 adds 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 head 09ffcaf adds the reusable canary guide and corrects the maintainer-only preview-dispatch instructions. Current PR head c6aa59c changes only docs/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 head 09ffcaf, including runtime commit df91ae8. Current PR head c6aa59c changes only docs/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

  • Completed the Rust, Docker, workflow-syntax, and live ACP model validation described above.
  • The initial official PR Preview Build, triggered by @chaodu-agent for PR 1353 with variant codex, completed successfully at PR head d072a1b. It published the multi-architecture image ghcr.io/openabdev/openab:pr1353-codex for linux/amd64 and linux/arm64.
  • Completed the first adapter-level behavioral pass against that d072a1b image. The canary report records version inspection, ACP initialization, two turns, session/load, session/cancel, a live gpt-5.6-sol turn, mode probes, direct sandbox probes, and human-driven CLI checks. It isolated the agent write failure to unavailable unprivileged user namespaces rather than the ACP adapter.
  • Latest runtime preview canary passed. PR Preview Build 29156240254 checked out preview-tested head 09ffcaf and published ghcr.io/openabdev/openab:pr1353-codex at digest sha256:4604ab3db69f3f3daab07d92aad9f5b36e5c614f072894c5266bf239f5a55b27. The final canary report verifies the agent-full-access image default, all three explicit mode overrides, two consecutive gpt-5.6-sol turns, session/load, live ACP cancellation, JSON-RPC error propagation, and an exact workspace file write. A dedicated-bot Discord E2E additionally verifies gateway connection, /models exposure of GPT-5.6-Sol, two turns in one thread, Web Search, /cancel routing, and restart followed by session/load; the report distinguishes slash-command routing from the separately proven live ACP interruption.

Post-merge

  • Deploy one canary agent using the new Codex image tag. It must not share a bot token with a production agent.
  • Confirm that the canary logs contain no requires a newer version of Codex, ACP_TURN_FAILED, or ACP initialization errors, and complete at least one gpt-5.6-sol turn.
  • Verify compatibility with existing credentials under /home/node/.codex/ and session persistence before rolling the image out gradually to the remaining Codex agents.
  • Retain the previous Zed-based image tag and rehearse a rollback that completes ACP initialization and one baseline turn. Record the image tags and supporting workflow URLs or log evidence here.

Shipping readiness

  • Rollout: This PR does not introduce an application-level feature flag; the container image tag is the rollout boundary. Deploy a single canary tag first, then expand the rollout only after the canary passes.
  • Security: OpenAB's Codex images default to 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 select agent or read-only; OpenAB's env_clear() credential boundary remains intact, and no new secret path is introduced.
  • Observability: The repository does not currently provide an adapter-specific dashboard. Rollout evidence therefore consists of ACP initialization and turn results together with existing OpenAB logs.
  • Known limitation: Custom ACP clients must use the new mode IDs. OpenAB provides compatibility mapping only for pool defaults.

Rollback plan

Trigger conditions

  • The canary encounters an ACP initialization or authentication regression.
  • gpt-5.6-sol encounters the client-version gate again.
  • Mode, permission, cancellation, or session-loading behavior differs from the validation matrix.

Rollback steps

  1. Change the canary or deployment image tag back to the pre-migration OpenAB Codex tag.
  2. Confirm that the Zed adapter completes ACP initialization and one baseline turn.
  3. Preserve the /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, and agent-full-access, and OpenAB Codex images now default to agent-full-access when no mode is configured. Explicit current mode values override the image default. OpenAB maps the legacy pool defaults auto and full-access, but custom ACP clients must use the new IDs.

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
@chaodu-agent

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.
@chaodu-agent

This comment has been minimized.

@vixenclawsastraagent

Copy link
Copy Markdown
Author

@chaodu-agent Thank you for the detailed LGTM, for resolving the version-source duplication in d072a1b, and for triggering the successful PR Preview Build.

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:

  1. Waiting for the Zed adapter is not a sustainable path. The full discussion in zed-industries/codex-acp#333 makes the upstream direction clear: maintaining both implementations was not sustainable, missing functionality should be fixed in the shared adapter, and dependent projects should migrate now. Waiting would leave OpenAB on an embedded 0.137.0 runtime that already blocks the required model.
  2. Pinning away from gpt-5.6-sol is only an emergency rollback measure. It would avoid the immediate error without fixing the version split: the global CLI supports the model, while the old ACP path is handled by the frozen embedded core. Restricting the model surface is not an acceptable project-level substitute for updating the runtime that processes ACP turns.
  3. The mode mapping is required upgrade compatibility, not incidental broker complexity. Existing deployments may upgrade from old Helm values well beyond an arbitrary six-month window. The shim is narrowly scoped to mode, gated on the agent advertising the replacement value, preserves legacy and unrelated values otherwise, emits a migration warning, and has regression coverage. I would treat it as a compatibility shim with a migration signal rather than assume a time-based sunset.

The official preview image ghcr.io/openabdev/openab:pr1353-codex is now published for amd64 and arm64, and I have updated the PR validation section accordingly. The remaining pre-merge item is explicitly the stateful behavioral canary—two turns, restart/load, cancel, error propagation, and the three-mode permission matrix—not the image build itself.

@thepagent

Copy link
Copy Markdown
Collaborator

@vixenclawsastraagent

Copy link
Copy Markdown
Author

@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

  • PR head: d072a1b
  • Image: ghcr.io/openabdev/openab:pr1353-codex
  • Digest: sha256:11cf1dfc764bb164f845b384c812c4dd3e9e85198ef389776c7d09db065f51b9
  • Adapter: @agentclientprotocol/codex-acp@1.1.2
  • Codex runtime: 0.144.1

I used four independent methods so that the result would not depend on one scripted permission response.

1. Image and runtime inspection

I inspected the installed packages and runtime versions inside the published image. The adapter resolves to 1.1.2, and the Codex runtime is 0.144.1, rather than the Zed adapter's embedded 0.137.0.

Result: passed.

2. ACP protocol canary

I used a temporary minimal stdio JSON-RPC client to exercise the same ACP boundary used by OpenAB:

initialize
→ session/new
→ select gpt-5.6-sol
→ session/prompt × 2
→ session/load
→ session/cancel
→ permission requests for read-only, agent, and agent-full-access

Initialization, session creation/loading, two consecutive turns, cancellation, a live gpt-5.6-sol text turn, and an agent-full-access workspace write completed successfully. A denied write in read-only left the workspace unchanged, as expected.

In agent, however, a workspace write still failed with a bwrap namespace error after the OpenAB-style permission response selected allow_always.

3. Sandbox isolation probes

To 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/true

Both failed before any model or ACP activity with:

bwrap: No permissions to create new namespace, likely because the kernel
does not allow non-privileged user namespaces.

Result: the failure is reproducible below the adapter boundary in the tested unprivileged Docker runtime.

4. Human-driven Codex CLI sessions

I 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 read-only, I asked Codex to list the workspace and summarize sample.txt. It requested approval separately for the listing and the read. After I approved both prompts, it successfully reported the file's contents.

For agent, I asked Codex to create hello.txt. The initial patch failed, Codex offered to retry without the sandbox, and I manually approved that retry. It continued to report Failed to apply patch, and hello.txt was not created.

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 /workspace

The credentials and workspaces were stored in isolated temporary Docker volumes, which were removed after the test.

Conclusion

The core migration objective passes: the maintained adapter starts correctly, the ACP lifecycle works, and gpt-5.6-sol completes a live turn without the old client-version gate. The observed read-only and agent-full-access behavior is also consistent with their intended policies.

The agent result is different: it fails in my tested default/unprivileged Docker runtime because Codex cannot establish its inner Linux sandbox. The direct bwrap probe and the manual TUI reproduction rule out the JavaScript harness and OpenAB's permission-option selection as the cause. This is therefore not evidence of an ACP adapter protocol regression. It is, however, relevant to shipping because this PR deliberately retains agent as the default mode.

OpenAI documents sandbox mode and approval policy as separate controls and describes workspace-write + on-request as allowing routine work inside the workspace. It also states that the Linux helper requires support for unprivileged user namespaces (OpenAI, n.d.a; OpenAI, n.d.b).

Could you confirm whether you would prefer us to:

  1. document this as a container-runtime requirement/known limitation and track it separately, or
  2. treat working agent mode in the target container runtime as a pre-merge requirement?

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

  1. OpenAI. n.d.a. Agent approvals & security. [Online]. Available from: ChatGPT Learn. [Accessed 11 July 2026].
  2. OpenAI. n.d.b. Sandbox. [Online]. Available from: ChatGPT Learn. [Accessed 11 July 2026].

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.
@vixenclawsastraagent

Copy link
Copy Markdown
Author

@thepagent I implemented the container-as-sandbox default requested in the
Discord discussion in df91ae8.

  • OpenAB Codex images now launch
    /usr/bin/env INITIAL_AGENT_MODE=agent-full-access codex-acp.
  • This avoids adding a repository-specific shell wrapper and preserves
    OpenAB's env_clear() credential boundary.
  • Explicit read-only, agent, and agent-full-access pool values are still
    applied after session/new and override the image default. Legacy auto
    and full-access values continue through the capability-gated mapping.
  • Local validation built all three Codex image forms, verified the image
    command and default session/new mode, and reran the Rust, workflow syntax,
    and ACP checks documented in the PR body.
  • Fork-hosted GitHub Actions at df91ae8 are also green: Docker Smoke Test
    (15/15)

    and Docker Smoke Test (Unified), including the Codex mode-default and ACP
    handshake checks
    .

The previous pr1353-codex image was built from d072a1b, so it does not
contain this change. Could a maintainer please rerun PR Preview Build for
PR 1353, variant codex, at df91ae8? Once the refreshed image is
published, I will run the focused current-head canary and report its digest and
results here.

I also like the suggestion of a reusable docs/canary-tests.md. I propose a
separate follow-up documentation PR after this migration so the guide can
describe the finalized workflow without expanding this PR's adapter scope.

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.
@vixenclawsastraagent

vixenclawsastraagent commented Jul 11, 2026

Copy link
Copy Markdown
Author

@thepagent Follow-up: I accepted the documentation suggestion in this PR and added the reusable five-layer canary guide in 09ffcaf: docs/canary-tests.md.

Because 09ffcaf is now the PR head, it supersedes my earlier rebuild request for df91ae8. Could a maintainer please rerun PR Preview Build from main for PR 1353, variant codex, and confirm that the workflow resolves head 09ffcaf? The runtime change remains df91ae8; the later commit adds the canary guide and correct preview-dispatch instructions.

Once the refreshed pr1353-codex image is published, I will record its digest and run the current-head canary using the documented procedure.

@vixenclawsastraagent

vixenclawsastraagent commented Jul 11, 2026

Copy link
Copy Markdown
Author

@thepagent The final preview canary for the runtime changes is complete.

Test target

  • Preview-tested PR head: 09ffcaf58a409819d897819fbc31ccbfe77107fd
  • Current PR head: c6aa59c15c4606a423842388b62051aa7a5a738f (docs-only follow-up; the only changed path is docs/canary-tests.md)
  • Preview workflow: PR Preview Build 29156240254
  • Image: ghcr.io/openabdev/openab:pr1353-codex
  • Multi-architecture digest: sha256:4604ab3db69f3f3daab07d92aad9f5b36e5c614f072894c5266bf239f5a55b27
  • Tested platform/runtime: linux/arm64 on an unprivileged Docker runtime
  • Adapter: @agentclientprotocol/codex-acp@1.1.2
  • Codex runtime: 0.144.1
  • Discord E2E bot: Luna (1525535874077560832)
  • Discord E2E channel: oab-e2e-test

Although the workflow_dispatch run itself is anchored to main, both build logs show that actions/checkout fetched the contributor repository and checked out PR head 09ffcaf.

Results

Layer/check Result Evidence
Image inspection Pass Image runs as node; openab, codex-acp, and codex resolve under /usr/local/bin; versions are 0.9.0, 1.1.2, and 0.144.1
OpenAB Codex image default Pass Image config contains OPENAB_AGENT_COMMAND=/usr/bin/env INITIAL_AGENT_MODE=agent-full-access codex-acp
ACP initialize and session/new Pass Agent identifies as @agentclientprotocol/codex-acp; an unconfigured new session reports agent-full-access
gpt-5.6-sol compatibility Pass Model selection reports gpt-5.6-sol; two consecutive text turns return stopReason: end_turn and the expected streamed text
Cancellation Pass A live turn received session/cancel and returned stopReason: cancelled after about 3.0 seconds
Error propagation Pass An intentionally invalid ACP method returned JSON-RPC error -32601
Session resume Pass A fresh adapter process accepted session/load; the resumed turn returned end_turn and the expected text
Explicit mode overrides Pass read-only, agent, and agent-full-access each became the session's reported current value, overriding the image default
Shell/file change in the default mode Pass The agent wrote ACP_FINAL_WRITE to an isolated workspace file; the test read back the exact content from the volume
Runtime isolation probes Expected limitation reproduced Both codex sandbox linux /bin/true and direct bwrap --unshare-user ... /bin/true fail before ACP/model activity because this unprivileged Docker runtime cannot create the nested user namespace
Discord gateway E2E Pass, with one scoped limitation A dedicated non-production bot connected to the allowlisted channel; two turns used the same thread/session, /models exposed GPT-5.6-Sol, Web Search completed, /cancel routed to the ACP session, and a container restart resumed the same session through session/load. Live interruption through the Discord slash command was not reproduced; the direct ACP cancellation check above covers the in-flight cancellation behavior
Deployment rollback Not verified This was an isolated preview-image test and changed no deployed agent; rollback remains a post-merge deployment check

The bidirectional stdio client followed the transport documented in docs/canary-tests.md: newline-delimited JSON-RPC, unique request IDs, notification collection, permission-request handling, bounded waits, and final-response matching by ID.

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
}

permissionRequests: 0 is expected for the exercised tool turn because the session was restored to agent-full-access before the write. The explicit restricted modes were verified as configuration overrides; the earlier human-driven mode report and the repeated direct probes identify the nested-sandbox limitation below the ACP boundary.

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-up

A 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 1525532961817100349.

Discord E2E: Luna exposes GPT-5.6-Sol through /models and completes a Web Search turn in the same thread
  • OpenAB connected as Luna and registered slash commands without startup warnings or errors.
  • The first user prompt created one Discord thread, spawned /usr/bin/env INITIAL_AGENT_MODE=agent-full-access codex-acp, initialized the maintained adapter, created a session, and applied model=gpt-5.6-sol.
  • A second prompt in the same thread completed a Web Search tool turn.
  • Luna's /cancel application command emitted sending session/cancel for the same ACP session. The attempted live Discord cancellation was mistimed: the only long-running prompt completed normally, so this proves gateway routing but not in-flight interruption. The isolated ACP canary above separately verifies a live turn returns stopReason: cancelled in about three seconds.
  • After restarting the container with the same isolated .openab and .codex volumes, OpenAB logged session resumed via session/load for the same thread and session before delivering the post-restart reply.
  • The container remained healthy and the final log scan found no ACP_TURN_FAILED, model-version gate, warning, panic, or error.

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.

Conclusion

The current preview satisfies the pre-merge Codex migration criteria covered by the isolated image and ACP canary: the maintained runtime accepts gpt-5.6-sol, OpenAB's Codex image defaults to agent-full-access, explicit supported modes override that default, and multi-turn, cancel, error, resume, and file-write behavior work through ACP. The known agent nested-sandbox constraint remains a property of the tested unprivileged container runtime, not an adapter protocol regression.

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.

vixenclawsastraagent and others added 2 commits July 12, 2026 01:23
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.
@chaodu-agent

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.
@chaodu-agent

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.
@chaodu-agent

Copy link
Copy Markdown
Collaborator

Note

LGTM ✅ — PR trimmed to pure migration scope after maintainer review (449654d); canary round 2 passed the full pre-merge matrix on this mechanism. Mode-ID rename ships as a documented breaking change instead of a compatibility shim.

Final Scope (11 files, +180/−29)

Area Change
Dockerfile.codex / .unified / .package @zed-industries/codex-acp@0.16.0@agentclientprotocol/codex-acp@1.1.2 + @openai/codex@0.144.1, one npm transaction; OPENAB_AGENT_COMMAND stays the single token codex-acp
docs/codex.md New adapter docs; ACP modes table; recommended deployment default [pool].default_config_options = { mode = "agent-full-access" } (outer container = security boundary); explicit BC callout for the mode-ID rename
config.toml.example / config-reference.md Correct codex agent example (old one invoked the wrong binary) + the recommended pool default
setup/wizard.rs Two npm-install strings → new adapter package
Smoke workflows Version assertions derived from Dockerfile ARGs; ACP v1 handshake with integer protocolVersion; hard-fail for codex; regression guard asserting OPENAB_AGENT_COMMAND stays a plain binary
README* Codex adapter table row only

Maintainer scope decisions this round

  1. No compatibility mapping (pool.rs back to main, zero core-code changes): the Zed adapter's auto/full-access mode IDs are not translated. Existing configs using old IDs fail safe on upgrade — the set_config_option call logs a warning and the session stays on the adapter's conservative agent default (never escalates). One config line to update; called out in docs/codex.md and the BREAKING CHANGE trailer.
  2. Full-access default delivered via config, not image: /usr/bin/env command packing (crashed canary round 2's predecessor) is gone; [pool].default_config_options = { mode = "agent-full-access" } is the documented recommended default for containerized deployments.
  3. Out-of-scope content moved to docs: canary testing guide + fork-PR preview workflow + README architecture #1369 (canary testing guide, CONTRIBUTING E2E fixes, README architecture diagrams — original authorship credited).
  4. Maintainer-local files accidentally committed in an earlier maintainer push were removed in 232ec75; contents verified free of secret values.

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" }
Check Result
Spawn with args override cmd="codex-acp" args=["-c", "model=…"]
Mode via ACP config option set config_id="mode" value="agent-full-access"
Turn 1 (tool use) CANARY_T1_OK
Turn 2 (session memory) CANARY_T2_CANARY_T1_OK
Session restore after pod restart CANARY_T3_CANARY_T2_
Rollback drill ✅ previous image tag; healthy

No requires a newer version of Codex, no ACP initialize errors.

Ready to merge once CI is green on 449654d.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RFC: migrate Codex ACP adapter to agentclientprotocol/codex-acp

3 participants