# ACP adapter for Codex CLI (`@automatalabs/codex-acp`)

[![npm version](https://img.shields.io/npm/v/%40automatalabs%2Fcodex-acp)](https://www.npmjs.com/package/@automatalabs/codex-acp)

Use [OpenAI Codex](https://github.com/openai/codex) from [Agent Client Protocol](https://agentclientprotocol.com/) 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`](https://github.com/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](docs/recommended-config-values-extension.md) capability.
- Text prompts, embedded context, images, resource links, and additional workspace directories.
- Turn-level structured output: a JSON Schema on the prompt's `_meta.outputSchema` constrains 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/steering` while 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 — `running` while a turn executes in-process (the `_session/loaded_turn/ended` notification then fires when that turn completes, with its stop reason or its error), `completed` when the loaded thread's last turn completed (the replayed final message is authoritative), and `interrupted` when 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/fork` subscribes the connection to the new thread exactly like `thread/resume` does, 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 no `session/resume` / `session/load` round trip (upstream unsubscribes the forked thread until it is reopened).
- Shell command, file change, [permission request](docs/permission-extension.md), MCP tool call, terminal output, reasoning, plan, web search, image generation, image view, token usage, and review events.
- Client `fs.readTextFile` capability: when the client advertises it, file-change diff content is read through `fs/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](docs/subagent-sessions.md) (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.subagent` metadata.
- [Background terminal tasks](docs/async-tasks.md) in AIR, with task status and targeted stop support after capability negotiation.
- Session-scoped long-running goals through the provider-neutral [goal extension](docs/goal-extension.md).
- A per-turn [agent file-change report](docs/agent-file-change-report.md) 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:

```bash
npx -y @automatalabs/codex-acp
```

Or install it globally:

```bash
npm install -g @automatalabs/codex-acp
codex-acp --version
```

The npm package includes a compatible `@openai/codex` dependency. Set `CODEX_PATH` only when you want the adapter to run a different Codex binary:

```bash
CODEX_PATH=/path/to/codex npx -y @automatalabs/codex-acp
```

To 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](docs/RELEASES.md#preview-releases).

```bash
npx -y @agentclientprotocol/codex-acp@preview
```

## Authentication

The adapter advertises ACP auth methods during initialization. Clients can authenticate with:

- ChatGPT login. Set `NO_BROWSER=1` to hide this method in remote or browserless environments.
- API key via `CODEX_API_KEY` or `OPENAI_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 over `OPENAI_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`, or `agent-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:

```jsonc
{
  "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:

```jsonc
{
  "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):

```jsonc
"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

```bash
npm install
npm run start
npm run typecheck
npm test
```

Build standalone binaries in `dist/bin` with:

```bash
npm run bundle:all
```

See [readme-dev.md](readme-dev.md) for local client configuration, binary packaging, and Codex type regeneration.

### Subagent sessions

Subagent sessions follow the draft [ACP subagent RFD](https://github.com/agentclientprotocol/agent-client-protocol/pull/1992) 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](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](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.
