# Codex Runtime

Duoduo supports Claude and Codex as **peer agent runtimes**. Either, both,
or neither can be installed; the daemon probes both at boot and adapts
to whichever are available.

This document describes how to use the Codex side. See
`claude-runtime.md` for the Claude side. The two have symmetric setup
shapes; the differences are listed under Caveats below.

## Prerequisites

- `codex` CLI installed and on `$PATH`.
- `codex login` has been run so `codex login status` reports
  "logged in".

Verify with:

```bash
codex --version
codex login status
```

## Selecting Codex as the default runtime

The daemon picks a runtime per actor (channel session, job, subconscious
partition) based on this fallback chain:

1. The actor's explicit declaration (e.g. `descriptor.runtime` or
   `job.frontmatter.runtime` or partition frontmatter `runtime: codex`).
2. Its kind defaults — for channels, `kernel/config/<kind>.md`
   `runtime: codex` covers every channel of that kind.
3. The global default: `ALADUO_DEFAULT_RUNTIME=codex` env var, or
   `defaultRuntime: "codex"` in `~/.config/duoduo/config.json`.
4. Otherwise: `"claude"`.

Setting `ALADUO_DEFAULT_RUNTIME=codex` (in `.env` or daemon
environment) routes every actor without a more-specific declaration
to Codex.

## Trusting the project (for partition-local subagents)

Codex loads partition-local `<partition>/.codex/agents/<name>.toml` only
when the **project root** (the directory containing `.git`) is
**trusted** in `~/.codex/config.toml`:

```toml
[projects."/Users/me/codebase/my-project"]
trust_level = "trusted"
```

Use the path that `realpath` resolves to — on macOS this matters because
`/tmp` is a symlink to `/private/tmp`. Aladuo's onboarding wizard
auto-writes this entry when you opt into Codex.

Aladuo seeds `.codex/agents/*.toml` from `.claude/agents/*.md` at
runtime init for every subconscious partition, so once trust is set
your partitions reach their named subagents via codex's `spawn_agent`.

## Environment Variables

| Variable                 | Required | Values           | Default           |
| ------------------------ | -------- | ---------------- | ----------------- |
| `ALADUO_DEFAULT_RUNTIME` | No       | `claude`,`codex` | `claude`          |
| `ALADUO_CODEX_SANDBOX`   | No       | see below        | `workspace-write` |

### Sandbox Modes

| Value                | Disk      | Network | Notes                                         |
| -------------------- | --------- | ------- | --------------------------------------------- |
| `workspace-write`    | cwd only  | No      | Default. Safe for most jobs.                  |
| `read-only`          | Read-only | No      | Analysis-only tasks.                          |
| `danger-full-access` | Full      | Yes     | Needed for localhost API or external network. |

## Refreshing availability after `codex login`

The daemon probes codex availability at boot. If you install codex
or run `codex login` while the daemon is already running, restart
the daemon to re-probe:

```bash
duoduo daemon restart
```

## Usage

When Codex is available, ManageJob, Feishu `/setup`, and channel.describe
all surface `runtime: "codex"` as a valid option alongside Claude. A
codex-only deployment (no Claude binary installed) sees only the codex
option — the daemon's `available_runtimes` reflects what actually
probed available.

## Caveats

These are the operational differences from Claude (none of them are
"Codex is a second-class engine" issues — they're protocol-level
trade-offs):

- Codex built-in tools (bash, file edit, web search) cannot be
  selectively disabled via the app-server protocol.
- `additionalDirectories` not honored by Codex (no equivalent
  Claude-SDK env flag). Memory board is injected inline into
  `developer_instructions`.
- `AGENTS.md` is auto-symlinked from `CLAUDE.md` so the same project
  CLAUDE conventions reach both runtimes.
- Memory board changes apply on the next thread, not mid-thread.
- Partition `.codex/agents/*.toml` files are derived from the matching
  `.claude/agents/*.md`; edit the markdown, daemon restart regenerates
  the toml on boot.
