Skip to content

Inbox / Outbox Messaging

OACP agents communicate through point-to-point async messaging. Messages are YAML files written to filesystem directories — no broker, no daemon, no network calls. Each agent has an inbox/ for incoming messages and an outbox/ for sent copies.

Every agent registered in a project gets its own directory pair:

$OACP_HOME/projects/<project>/agents/
claude/
inbox/ # messages TO claude
archive/ # processed messages (moved here at terminal processing)
outbox/ # copies of messages FROM claude
codex/
inbox/
archive/
outbox/
cursor/
inbox/
archive/
outbox/

Messages are plain YAML files. Any tool that can read and write files can participate in the protocol.

Every message is a single YAML file with the following schema:

Field Required Description
id yes Unique identifier (UUID recommended)
from yes Sender agent name
to yes Recipient agent name (or list for broadcast)
type yes One of the 12 message types below
priority yes P0 through P3
created_at_utc yes ISO 8601 timestamp
subject yes Short summary (one line)
body yes Message content (free-form text or structured YAML)
expires_at no ISO 8601 expiration timestamp
channel no Logical channel or project context
related_packet no Reference to a related work packet
related_pr no Pull request URL or number
conversation_id no Thread identifier for multi-message conversations
parent_message_id no ID of the message this replies to
context_keys no List of context references for the recipient
id: "a1b2c3d4-5678-9abc-def0-1234567890ab"
from: claude
to: codex
type: review_request
priority: P1
created_at_utc: "2025-01-15T14:30:00Z"
subject: "Review auth middleware PR"
body: |
PR #42 adds JWT validation middleware.
Focus areas: token expiry handling, error responses.
related_pr: "https://github.com/kiloloop/oacp/pull/42"
conversation_id: "review-pr-42"

Since v0.4.0, a message may carry an optional Ed25519 signature as its final physical line — a detached-JWS auth trailer signed over the exact raw bytes of everything before it. Receivers verify against their own local key pins before parsing, with no network at verify time. Verification runs in the mode each receiver configures: warn annotates and never rejects; enforce (since v0.4.2) verifies at message intake and quarantines anything unverified to dead_letter/ before it is processed. The wire format, trust root, and key management live on the Message Signing page.

OACP defines 12 message types across three categories:

Type Purpose Expected response
task_request Ask an agent to perform work Completion notification or follow_up
question Ask for information or clarification Reply with answer via follow_up
notification One-way status update None required
follow_up Continue an existing conversation Depends on context
handoff Transfer ownership of a task handoff_complete
handoff_complete Confirm receipt of handoff None
Type Purpose Expected response
review_request Ask for code review review_feedback or review_lgtm
review_feedback Return review findings review_addressed
review_addressed Respond to feedback review_request (next round)
review_lgtm Approve the change None
Type Purpose Expected response
brainstorm_request Solicit ideas or analysis brainstorm_followup
brainstorm_followup Respond with ideas Further brainstorm_followup or notification

See Review Loop for the complete code review workflow.

A message follows six steps from creation to completion:

  1. Create — Sender constructs the YAML message with a unique id.
  2. Deliver — Sender writes the file to the recipient’s inbox/ directory.
  3. Archive — Sender writes a copy to their own outbox/ directory.
  4. Read — Recipient reads the message from their inbox/.
  5. Archive — At terminal processing, the recipient atomically moves the exact processed file — without overwriting — to inbox/archive/ under its original filename, bytes preserved. If archival fails or the live bytes changed since verification, the message stays pending in inbox/. (Before v0.4.3, recipients deleted processed messages; the single cross-runtime archive destination replaces that.)
  6. Reply — Recipient creates a new message (linking via parent_message_id) and delivers it to the original sender’s inbox/.

Messages are immutable once written. Replies are always new messages — never edits to existing files.

History is bounded by oacp retention (since v0.4.3): outbox copies, dead-letter quarantine, and processed archives are pruned on age or count bounds. Quarantine evidence in dead_letter/ is excluded by its durable filename format, and an adjacent .retain marker protects a manually managed dead-letter fixture.

OACP is poll-based. There is no real-time push mechanism.

Agents should check their inbox:

  • At session start
  • After completing each major task
  • On a regular interval if running continuously

This keeps the protocol simple and compatible with any agent runtime, including those that run as batch jobs.

When multiple messages are waiting in an inbox, process them in this order:

  1. Priority first — P0 before P1 before P2 before P3.
  2. Type tiebreak — At the same priority, task_request and review_request take precedence over other types.
  3. Age tiebreak — Oldest messages first (by created_at_utc).

Multi-message exchanges use three fields to maintain context:

  • conversation_id — A shared identifier for all messages in a thread. Set by the initiator; subsequent messages reuse the same value.
  • parent_message_id — The id of the message being replied to. Creates a direct reply chain.
  • context_keys — A list of references (file paths, URLs, prior message IDs) that the recipient may need.

Set conversation_id on the first message. Omit parent_message_id (there is no parent yet).

Copy conversation_id from the message you are replying to. Set parent_message_id to that message’s id. Add any relevant context_keys.

conversation_id: "review-pr-42"
parent_message_id: "a1b2c3d4-5678-9abc-def0-1234567890ab"
context_keys:
- "pr:42"
- "file:src/middleware/auth.ts"

The to field accepts a list to send the same message to multiple agents:

to:
- codex
- gemini
- claude

When broadcasting:

  • One copy is written to each recipient’s inbox/.
  • One copy is written to the sender’s outbox/.
  • Maximum 10 recipients per broadcast.
  • handoff and handoff_complete are point-to-point only and cannot be broadcast.