@automatalabs/codex-acp
v2.8.0
Published
ACP server wrapping OpenAI Codex — Automata Labs fork of @agentclientprotocol/codex-acp adding turn-level outputSchema forwarding for structured output.
Readme
ACP adapter for Codex CLI (@automatalabs/codex-acp)
Use OpenAI Codex from Agent Client Protocol clients.
codex-acp is a stdio ACP agent server. It starts the Codex App Server, translates ACP requests into Codex operations, and maps Codex events back into the client.
This package is a fork of agentclientprotocol/codex-acp, regularly synced with upstream. On top of upstream it exposes Codex App Server features not (yet) piped through the ACP interface — turn-level structured output (outputSchema), per-session instruction overrides, native session steering, loaded-session turn-terminal state, and live session/fork. Outgoing metadata capabilities remain under agentCapabilities._meta; steering is separately advertised at top-level initialize metadata as _meta.steering.supported === true, so clients can feature-detect before sending _session/steering, and the _session/loaded_turn extension is advertised the same way (_meta.loadedTurn.supported === true).
Features
- ChatGPT, API key, and client-provided custom gateway authentication.
- Model, reasoning effort, fast mode, approval, and sandbox mode configuration.
- Concrete recommended model and reasoning-effort values through the opt-in AIR recommended config values capability.
- Text prompts, embedded context, images, resource links, and additional workspace directories.
- Turn-level structured output: a JSON Schema on the prompt's
_meta.outputSchemaconstrains the final assistant message (fork extension, see below). - Per-session base and developer instruction overrides via request
_meta(fork extension, see below). - Strict native session steering via
_session/steeringwhile the original prompt is active; an accepted instruction returns{ outcome: "injected" }, while an idle session or settlement race returns{ outcome: "promptRequired", reason: "noRunningTurn" }. Steering never starts or queues a turn, and unexpected failures reject the JSON-RPC request. - Loaded-session turn-terminal state via
_session/loaded_turn(the re-attach arm's authoritative completion evidence):_session/loaded_turn/query { sessionId }answers whether the loaded session's founding turn is still running right now —runningwhile a turn executes in-process (the_session/loaded_turn/endednotification then fires when that turn completes, with its stop reason or its error),completedwhen the loaded thread's last turn completed (the replayed final message is authoritative), andinterruptedwhen it ended without a terminal message (nothing is running — re-issue is safe). - Fork extensions advertised under
agentCapabilities._meta["@automatalabs/codex-acp"]for client-side feature detection. - Live
session/fork: the forked Codex thread stays subscribed —thread/forksubscribes the connection to the new thread exactly likethread/resumedoes, and this fork no longer unsubscribes it afterwards — and the new session publishes its available commands and MCP startup status like a resumed one, so the returned session id is promptable at once with nosession/resume/session/loadround trip (upstream unsubscribes the forked thread until it is reopened). - Shell command, file change, permission request, MCP tool call, terminal output, reasoning, plan, web search, image generation, image view, token usage, and review events.
- Client
fs.readTextFilecapability: when the client advertises it, file-change diff content is read throughfs/read_text_file(so diffs reflect unsaved editor buffers), with local file system fallback otherwise. File writes happen inside codex itself — the app-server delegates no file IO to the client. - Native ACP subagent sessions (after capability negotiation) with separate child histories and root-routed permissions; a legacy tool-call fallback otherwise. Legacy tool updates retain Codex thread identity and activity details in namespaced
_meta.codex.subagentmetadata. - Background terminal tasks in AIR, with task status and targeted stop support after capability negotiation.
- Session-scoped long-running goals through the provider-neutral goal extension.
- A per-turn agent file-change report after capability negotiation.
- Client-provided MCP servers over command-based stdio config and HTTP transport.
- Slash commands:
/status,/mcp,/skills,/goal,/review,/review-branch,/review-commit,/compact, and/logout, as well as configured skills.
Installation
Run the published package directly:
npx -y @automatalabs/codex-acpOr install it globally:
npm install -g @automatalabs/codex-acp
codex-acp --versionThe npm package includes a compatible @openai/codex dependency. Set CODEX_PATH only when you want the adapter to run a different Codex binary:
CODEX_PATH=/path/to/codex npx -y @automatalabs/codex-acpTo try changes that have landed on main but are not released yet, install from the
preview channel. Pushes to main trigger preview publishing without waiting
for CI or release-please; release commits are excluded, and newer pushes can
replace queued previews. See
docs/RELEASES.md.
npx -y @agentclientprotocol/codex-acp@previewAuthentication
The adapter advertises ACP auth methods during initialization. Clients can authenticate with:
- ChatGPT login. Set
NO_BROWSER=1to hide this method in remote or browserless environments. - API key via
CODEX_API_KEYorOPENAI_API_KEY. - A custom OpenAI-compatible gateway, when the client opts in to the gateway auth capability.
Runtime options
CODEX_API_KEY- API key used when the API-key auth method is selected. Takes precedence overOPENAI_API_KEY.OPENAI_API_KEY- fallback API key used when the API-key auth method is selected.CODEX_PATH- run a specific Codex executable instead of the bundled package dependency.CODEX_CONFIG- JSON object merged into the Codex session config.MODEL_PROVIDER- model provider to pass to Codex for new sessions.DEFAULT_AUTH_REQUEST- ACP auth request JSON used when Codex requires authentication.INITIAL_AGENT_MODE- initial mode id:read-only,agent, oragent-full-access.NO_BROWSER- hide browser-based ChatGPT auth when set.APP_SERVER_LOGS- directory for adapter logs.
Session instruction overrides
Clients can override Codex's thread instructions per session by setting bare keys on the ACP
session request's _meta (on session/new, session/load, session/resume, or session/fork).
They map directly onto the Codex thread/start / thread/resume / thread/fork parameters of
the same name — a forked session carries the instructions its own session/fork request named,
with no reattach needed (observed live: a forked thread honors a baseInstructions override, while
a developerInstructions override sent on thread/fork did not change the fork's behavior — the
forked thread kept its source thread's developer instructions; Codex app-server behavior):
| _meta key | Codex thread param | Effect |
| --- | --- | --- |
| baseInstructions | baseInstructions | Replaces Codex's built-in base system prompt for the thread. |
| developerInstructions | developerInstructions | Injects developer-role instructions for the thread. |
Both are optional strings; omit a key to keep Codex's default, and a present non-string value is
rejected with an invalid-params error. Example session/new params:
{
"cwd": "/abs/path/to/workspace",
"mcpServers": [],
"_meta": {
"baseInstructions": "You are a release bot. Only touch CHANGELOG.md.",
"developerInstructions": "Prefer conventional-commit summaries."
}
}Structured output (turn-level outputSchema)
Clients can constrain a turn's final assistant message to a JSON Schema by setting the bare
outputSchema key on the session/prompt request's _meta. The schema is forwarded verbatim
into the Codex App Server's turn/start.outputSchema (OpenAI Responses API strict mode); when
the key is absent the turn is unconstrained. The key is per-turn — each session/prompt sets
(or omits) it independently. Example session/prompt params:
{
"sessionId": "sess-123",
"prompt": [{ "type": "text", "text": "List the three largest files." }],
"_meta": {
"outputSchema": {
"type": "object",
"properties": { "files": { "type": "array", "items": { "type": "string" } } },
"required": ["files"],
"additionalProperties": false
}
}
}Fork capability advertisement
So clients can feature-detect the fork's non-standard _meta inputs instead of sending them
blind, the initialize response advertises them under the fork's package name (per the ACP
extensibility convention):
"agentCapabilities": {
"_meta": {
"@automatalabs/codex-acp": {
"outputSchema": true, // session/prompt _meta.outputSchema (see above)
"baseInstructions": true, // session-scoped instruction overrides (see above)
"developerInstructions": true
}
}
}Each flag is named exactly like the bare _meta wire key it gates. A client that sees the
namespace object should send a gated key only when its flag is true; clients that predate the
advertisement can continue sending the keys blind (the adapter accepts them regardless).
Development
npm install
npm run start
npm run typecheck
npm testBuild standalone binaries in dist/bin with:
npm run bundle:allSee readme-dev.md for local client configuration, binary packaging, and Codex type regeneration.
Subagent sessions
Subagent sessions follow the draft ACP subagent RFD and are enabled only after bilateral capability negotiation during initialize. Without native negotiation, the subagent lifecycle stays an ordinary ACP tool call.
See docs/subagent-sessions.md for the negotiation, lifecycle events, session/load reconstruction, and legacy fallback details.
Background terminal tasks
Codex can keep a shell command running after a turn continues. AIR clients can show this work in the Async Tasks panel and stop one command.
See docs/async-tasks.md for the capability, lifecycle events, and stop request.
License
By contributing, you agree that your contributions will be licensed under the Apache 2.0 License.
