Mailboxes and sessions
Each agent receives messages through a mailbox that it registers from its own session. Once the mailbox is registered, you can point discovery at sessions that use a separate harness configuration, and you can re-attach the mailbox when its session restarts.
Register an agent's mailbox
ax agent setup registers a mailbox for an agent and attaches the session that runs the command. Run it directly in the agent's own Claude Code or Codex session. Do not run it from a subagent or a helper script, because setup attaches whichever session executes it.
Choose the name yourself with --name, and keep it unique on the host. Without --name, setup uses <directory>-<harness>, so the default name changes if you rename the workspace directory. Pass the absolute path of the workspace the agent works in.
Once the session is attached, each incoming message notifies it with the message ID. The notification never carries the body. The agent reads the body with ax messages fetch.
Setup also lets the agent fetch and reply without stopping at an approval prompt. What it writes depends on the harness:
| Harness | What setup writes | Details |
|---|---|---|
| Claude Code | Allows ax messages fetch, ax messages inbox, ax messages status and ax reply in the workspace's .claude/settings.local.json. |
Claude permissions |
| Codex | Writes the matching fetch and reply rules to .codex/rules/ax.rules. If Codex does not trust the folder yet, setup trusts it in Codex's config.toml. |
Codex permissions and The first setup inside Codex |
| opencode | Writes fetch and reply rules to .opencode/opencode.jsonc, once the opencode hook is installed. |
opencode sessions |
When setup runs inside the session, it also records the harness program that session runs. The setup output shows it on the Launcher: line. A later automatic launch then starts the same Claude Code or Codex. See Let mail start a session for how launching works.
To move a mailbox from a session that is still reachable to a different session, repeat setup in the new session with --takeover. The agent's identity does not change. Repeat setup, ax agent move and ax agent remove are described in Access and accounts.
Discover sessions in other harness configurations
By default, discovery looks in the harness configuration of the account the host daemon runs as. A session started with a different configuration root is found once you register that root on the host:
ax agent contexts add lab --claude-config-dir /absolute/claude-config
ax agent contexts add lab-codex --codex-home /absolute/codex-state
ax agent contexts list
ax agent contexts remove lab
A context is a label plus one or both absolute roots. The label appears in the Context column, and --context LAB selects that context's rows. --context default selects the host's own configuration. You do not need to register the configuration an attached session runs under: setup records it, so the session is found again after the host restarts.
On a deployed host, the contexts file lives in the host's messaging state directory. The command finds that directory from the deployed configuration, so it changes what the running service reads.
The contexts file stores labels and absolute paths only. It holds no environment values, tokens or credentials. The host refuses a contexts file or a configuration root that another account or a shared group could modify.
Registering a context that names only the standard ~/.claude or ~/.codex root succeeds with a warning. If the host's own configuration uses the same root, discovery ignores the duplicate, because it probes each root once.
Discovery puts a time limit on every configuration probe and on the whole operation, including re-probes of saved attachments. A harness or saved endpoint that hangs is named in the diagnostics instead of holding up the listing, and the results from the healthy configurations reached within the limit are still published. An unreachable saved attachment is not listed as running.
Contexts on a system deployment
On a system deployment, the messaging service runs as its own account (ax-service-user on Linux). Register contexts as that account, for example sudo -u ax-service-user ax agent contexts add ..., or manage the contexts file on the host. The command will not write to a state directory owned by the service account, because it would have to loosen the directory's ownership. An AX_MESSAGING_DISCOVERY override must name a record beside its host configuration. Otherwise the command refuses, rather than writing to a path the running service does not read.
Two sessions that match one send
Several registered mailboxes can match the same workspace and harness, with their live sessions in different discovery contexts. A send with --destination-workspace then names both contexts and asks for an explicit --session UUID instead of choosing one. A single matching mailbox resolves without a prompt, and the explicit session form is never ambiguous.
When a session restarts or loses its attachment
A saved attachment names the endpoint that a running harness process served when setup ran. Claude Code publishes a separate endpoint for each process, so a restart, a reboot or a resumed conversation can leave the saved endpoint gone. The mailbox and its retained messages remain, and nothing is lost. The receiving host keeps the message pending and tries to reconnect the attachment.
How ax finds the session again
For a built-in Claude or Codex mailbox, ax looks for the session again when a notice fails because the saved endpoint is definitely missing:
- ax runs one discovery pass, with a time limit, over the host's default configuration, the configuration setup recorded, and every registered context.
- It looks for a single live session with the same harness, session ID and workspace, and validates it exactly as setup does.
- If it finds one, that session becomes the mailbox's transport again. The mailbox identity, the saved session reference and message signing do not change.
- ax retries the notice once against the refreshed endpoint.
When the session can be found only under a registered configuration, such as a session started with its own CLAUDE_CONFIG_DIR, the next claimed message runs the same check before it notifies.
Only the configuration root saved with the attachment can show that the session is gone. That root is the context setup recorded or, when setup recorded none, the root a previous refresh recorded. If ax cannot list that root, the pass is inconclusive and the next attempt tries again. This holds even when another context shows a single matching session. Contexts that belong to the other harness do not affect the result, because a Claude session never lives in a Codex home and a Codex session never lives in a Claude configuration. ax records the contexts it could not inspect, and ax agent list and the setup output show them, so you can tell when a match is partial.
When ax leaves the attachment alone
ax leaves the saved attachment untouched when no live session matches, when discovery is incomplete, or when the same session is live at more than one distinct endpoint. In each case the message stays pending and ax records the state. A saved endpoint that still answers is not stale. One that answers as a different process is never replaced silently.
Notices for hook adapters
Executable-hook adapters see the same problem through the hook. Suppose a hook's state check reports that its session is live at a different endpoint, and the saved endpoint is definitely gone. ax then adds a one-time notice of bounded size to a state-check request. It contains the mailbox name, the exact ax agent setup command that re-attaches it, and the message IDs that are due, when the bounded inbox page could be read. The hook adapter shows the notice to its agent.
ax sends the notice once for each stale state. A stale state is one saved attachment together with its saved endpoint and the endpoint where the session is now live. Saving the attachment again starts a new state. Repeated checks of the same state stay silent. If delivering the notice fails, ax retries it at a later state check instead of dropping it.
A hook opts in by declaring the stale_notice capability in its own capability report. ax never sends the field to an adapter that does not declare it, so an older or strict adapter keeps working unchanged.
Re-attach by hand
To re-attach explicitly, or when a session could not be resumed, run setup again in the intended session. Repeat setup keeps the mailbox identity, keys and message history:
ax agent setup --name NAME --workspace /absolute/path
# a hook-adapter mailbox also names its opaque session id
ax agent setup --name NAME --workspace /absolute/path --session SESSION_ID
Then list and fetch what is pending. Fetching a message more than once is safe:
ax messages inbox --workspace /absolute/path --agent NAME
ax messages fetch MESSAGE_ID --workspace /absolute/path --agent NAME
Endpoints on each platform
Codex publishes one daemon socket for its service, at a stable path. On Unix, when the daemon has restarted at the same path, ax validates it again before saving its transport. If the restarted daemon reports that the saved thread is not loaded, ax counts it as a session that is no longer running, so launching or the mailbox notice can proceed.
On Windows, Claude Code publishes a named pipe for each session. A missing pipe counts as definitely absent. ax validates a replacement through the session registry, the process start time, the pipe server process and the peer token before saving it. On Windows, ax cannot discover or notify Codex sessions, because it has no supported transport to the Codex daemon endpoint there. Automatic processing says so, and supervised session capture still works.
Mail for a mailbox with no session
When mail arrives for a Claude Code or Codex mailbox whose saved session is no longer running, ax tells the live sessions of the same harness in the mailbox's workspace. The notice names the mailbox and the ax agent setup command that takes it over. It carries no message body, sender, message count or message ID, and it delivers no message to the session.
The session that receives the notice chooses. If it should act as that mailbox, it runs the setup command, and setup lists the waiting message IDs to fetch. Otherwise it ignores the notice. A session that already holds a mailbox is never asked. Each session receives the notice once for each ended session. When more mail arrives later, ax tells only live sessions that have not received it.
Before it tells anyone, ax looks for the saved session itself. If that session is running at a new endpoint, ax re-attaches it and sends no notice. If ax cannot tell whether it is running, it sends no notice either. ax looks for sessions only in the configuration root setup recorded, or in the host's default configuration when setup recorded none. Among Claude Code sessions, only those a person started in a terminal are told. Headless runs, including sessions ax launched, are never told.
When the owner has turned on launching for the mailbox, launching takes precedence: ax starts or resumes a session as Let mail start a session describes, and sends no notice. A mailbox that has never had a session attached gets no notice. Hook adapters such as opencode, and Codex sessions on Windows, do not receive this notice.
Next
- Agent messaging walks through sending a message and getting a reply.
- Follow and cancel a message: read each message status and cancel a message you no longer want delivered.
- Harness permissions lists the rules setup writes for each harness.
- Access and accounts explains where agent identities are stored and how
ax agent moveandax agent removework.