Agent Message Queue (AMQ)

August 21, 2026 · View on GitHub

CI Release License: MIT

A local, file-based interoperability bus for agent sessions and adapters.

AMQ manages the conversation: agent-to-agent messaging, thread continuity, cross-session and cross-project routing, handoff state, and operational visibility. It does not try to own task decomposition, worktree management, dependency scheduling, or scheduler execution; Claude Code teams, Codex, Kanban, Symphony, and similar orchestrators stay one layer above it.

Start here: Getting started — install, start two agents, send one message.

Why AMQ?

Modern AI-assisted development often involves multiple agents working on the same codebase. But without coordination:

  • Agents duplicate work or create conflicts
  • Reviews require human intermediation
  • Context switching kills productivity

AMQ gives agents a local interoperability bus: they can send messages, reply in threads, share status, and optionally consume adapter-emitted events through the same queue primitives. The core product stays intentionally small: file-based messages first, lightweight adapters second.

Key Features

  • Zero infrastructure — Pure file-based. No server, no daemon, no database. Works anywhere files work.
  • Crash-safe — Atomic Maildir delivery (tmp→new→cur). Messages are never partially written or lost.
  • Human-readable — JSON frontmatter + Markdown body. Inspect with cat, debug with grep, version with git.
  • Real-time notificationsamq wake injects terminal notifications when messages arrive (experimental).
  • Built for agents — Priority levels, message kinds, threading, delivery receipts, and waitable handoffs.
  • Cross-project federation — Route messages across peer repos, preserve reply routing, and run decision threads that span projects.
  • Swarm mode — Join Claude Code Agent Teams, claim tasks, and bridge task notifications into AMQ.
  • Optional adapters — Lightweight Symphony hooks and an experimental Kanban bridge can emit normal AMQ messages with structured metadata.
  • Operational diagnosticsamq doctor --ops shows queue depth, sibling-session backlogs, DLQ state, presence freshness, and integration hints.
  • Two-host fleets — Companion amq-bridge hops signed envelopes between two local AMQ roots (apply-file today; HTTPS courier when an operator provisions a rendezvous).

v1 will not

AMQ Core stays a local CLI. These stay out of the amq binary and out of v1 product claims (see two-host fleets and bridge protocol):

  • sockets or listeners in amq
  • Maildir sync or remote drain of a foreign mailbox
  • git as the cross-host relay
  • OAuth MCP inside amq, ACP v2, or --always-approve in committed launch plans
  • prompt-selected --root / argv / env / executable
  • AMQ holding a Buzz nsec; Mac mailbox files on the Grok Bot VM
  • silent inject→notify or submit→prefill; Accessibility scraping of ChatGPT

Cross-host mail is companion amq-bridge (alias send / local apply / same-thread reply), not Core. The proven hop is amq-bridge apply-file on the destination host. The HTTPS courier stays implemented for an operator-provided rendezvous; AMQ does not ship a hosted relay. See amq-bridge.

AMQ Demo — Claude and Codex collaborating via split-pane terminal

Getting started

About five minutes from install to the first delivered message. You need two agent CLIs on PATH. This walkthrough uses Claude Code (claude) and Codex CLI (codex) on one machine. amq setup also detects Grok Build (grok) and Cursor when agent is on PATH (or legacy cursor-agent if agent is absent).

Native Windows can run the core queue from the Windows ZIP, but this walkthrough needs coop exec and wake, which are not supported natively. Use WSL with the Linux binary. See the platform capability matrix.

1. Install the binary

macOS (Homebrew):

brew install avivsinai/tap/amq

macOS / Linux (script):

curl -fsSL https://raw.githubusercontent.com/avivsinai/agent-message-queue/main/scripts/install.sh | bash

The script installs to ~/.local/bin or ~/go/bin (no sudo). Review it before running. It fails unless checksums.txt has exactly one valid entry for the selected asset and sha256sum or shasum verifies it before extraction.

Verify:

amq --version

Manual download, Windows ZIP, and build-from-source are in INSTALL.md.

2. Install Skill

So each agent knows the AMQ commands:

npx skills add avivsinai/agent-message-queue -g -y

If you used the install script instead of Homebrew, you can do binary and skill in one step:

curl -fsSL https://raw.githubusercontent.com/avivsinai/agent-message-queue/main/scripts/install.sh | bash -s -- --skill

Other skill methods (skild, marketplace, manual copy) are in INSTALL.md. Restart the agent after installing.

3. Set up the project

In the repository the two agents will share:

amq setup

Setup probes for Claude, Codex, and Grok (and Cursor when present), previews the roster and launcher preference, then writes .amqrc, .amq/launch.json, local preferences, the default session, and roster mailboxes. Confirm the preview.

If setup reports no supported agent CLI detected, install claude and codex, put them on PATH, and run amq setup again.

4. Start both agents

amq launch

The first launch asks you to trust the plan. That confirmation is stored outside the worktree.

  • If launch selects tmux, cmux, or ghostty, both agents start in that app.
  • If launch selects the commands backend, it prints one complete coop exec line per agent and exits 6. Paste each emitted line into its own terminal. Do not rewrite the lines: they bind the session, launch nonce, provider arguments, and execution ticket.

Start both agents before sending. A newly started wake baselines messages that were already waiting, so they stay unread but do not notify. If you sent first, run amq drain --include-body in the target agent.

5. Send one message and see it arrive

In the Claude terminal (AM_ME is already set):

amq send --to codex --subject "Hello" --body "Can you see this?"

In the Codex terminal:

amq list --new
amq drain --include-body

You should see Claude's message, then the drained body. That is the loop: send on one side, list / drain on the other. Reply with amq reply --id <msg_id> --body "..." when you have a message ID.

Roles, phases, and troubleshooting live in COOP.md. Daily commands after this first message are in Messaging below.

Installation

The default install is in Getting started. More methods (releases ZIP, checksums, source build, skill marketplace) are in INSTALL.md.

Updating

Homebrew:

brew upgrade amq

Retire live wakes started by the previous Cellar binary first. If a leftover lock's image directory is gone, wake check reports binary_dir_gone; remove it with amq doctor --ops --fix-wake-locks. See Wake operations.

GitHub Actions verify-brew-release confirms a published tag installs from avivsinai/tap/amq and that amq --version matches that tag. It does not replace brew upgrade on an operator machine.

Install-script or other manual binary installs:

amq upgrade

Keepalive companion

amq-keepalive is developed and released from this repository alongside AMQ. make build produces both binaries, and each AMQ release includes a separate amq-keepalive archive stamped with the same release version. Verify a build with any equivalent form:

amq-keepalive -v
amq-keepalive --version
amq-keepalive version

See COOP.md for the operational guide.

Setup and launch

amq setup is the one-time project configuration. amq launch reconciles the committed roster and starts or resumes that session.

amq setup
amq launch
amq session create feature-x   # once, before the first named-session launch
amq launch --session feature-x
amq session resume feature-x

launch reads the committed roster, selects the declared default session when --session is absent, and resumes exact provider-qualified conversation IDs. It never uses a provider's "last" or "continue" heuristic. The first semantic plan, and each semantic plan change, requires an interactive trust confirmation stored outside the worktree. Non-interactive or --json calls exit 6 until that digest is trusted. An unknown session resume name exits 3 and writes nothing. Managed backends use a fail-closed recovery journal; see Managed launch recovery.

Registered launchers are commands, tmux, cmux (envelope >=0.64.3 <1.0, protocol 2), and ghostty (AppleScript, envelope >=1.3.0 <2.0). --launcher auto is the default: it walks the local launcher preference and selects the first backend whose Detect reports Available. An explicit --launcher <name> wins. When CMUX_SURFACE_ID is set, auto prepends cmux ahead of ghostty; otherwise TERM_PROGRAM=ghostty prepends ghostty. Setup lists cmux and Ghostty in available_launchers only after their Detect ping succeeds, not from LookPath alone.

The commands backend prints complete coop exec commands and exits 6 because executing them is the remaining operator action. Paste those emitted lines exactly, one per terminal. Managed tmux, cmux, and ghostty backends run the declared plan in-app instead of printing those lines.

Grok Build is also supported by the managed launch adapter. It mints an exact --session-id from the AMQ launch nonce and resumes only with the stored --resume <UUID>; --continue, --always-approve, and --yolo are rejected from committed launch arguments. Grok uses --tools / --disallowed-tools with opaque provider names (not Claude --allowedTools).

Each launched agent gets a session environment and wake notifications. See COOP.md for co-op operations.

Provider arguments belong in the committed .amq/launch.json, so launch can validate and include them in its semantic trust digest. For example:

{
  "schema": 1,
  "default_session": "collab",
  "agents": [
    {
      "handle": "claude",
      "adapter": "claude",
      "command": ["claude", "--permission-mode", "acceptEdits"],
      "resume_policy": "resume"
    },
    {
      "handle": "codex",
      "adapter": "codex",
      "command": ["codex", "--sandbox", "workspace-write", "--ask-for-approval", "on-request"],
      "resume_policy": "resume"
    }
  ],
  "layout": {"type": "columns"}
}

Dangerous permission-bypass flags are not valid committed arguments. Keep them in an operator-controlled direct coop exec invocation when that low-level path is intentionally required.

Named sessions

For isolated pairs (multiple pairs on different features):

amq session create feature-a
amq launch --session feature-a

When launch uses the commands backend, paste the complete emitted commands into separate terminals.

Non-interactive setup

Automation uses a stateless preview and applies only that approved digest. The first non-interactive setup must name the roster, default session, and launcher preference explicitly:

setup_args=(--agents claude,codex --default-session collab --launcher-preference commands)
setup_preview="$(amq setup --preview --json "${setup_args[@]}")"
setup_digest="$(printf '%s\n' "$setup_preview" | jq -r '.preview.digest')"
amq setup --apply "$setup_digest" "${setup_args[@]}"

--preview performs zero writes. --apply recomputes the preview and exits 6 without writing if its sha256:<hex> digest differs. -y remains available for callers that already own an approval gate, but it cannot be combined with --preview or --apply.

Shell aliases

Optional aliases are a convenience, not part of Getting started. A bare eval "$(amq shell-setup)" affects only the current shell. To make aliases such as amc, amx, and amg available in future terminals, add the setup command to your shell startup file:

# zsh
amq shell-setup --shell zsh >> ~/.zshrc

# bash
amq shell-setup --shell bash >> ~/.bashrc

Run the appropriate append command once, then open a new terminal or source that startup file. Use the bare eval only when you intentionally want aliases in one already-open shell.

coop init and direct coop exec provisioning remain available as legacy low-level plumbing. See COOP.md for those paths, operator-only bypass examples, and advanced wake options; they are not a second project-onboarding flow.

Scripts and orchestrators that must plan and apply a session without parsing human output should use the public launch contract in docs/launch-api.md and schemas/launch-api-v1.schema.json.

Messaging

Inside a launched agent, identity and session are already set:

amq send --to codex --subject "Review needed" --kind review_request \
  --body "Please review internal/cli/send.go"

amq list --new
amq list --new --priority urgent
amq list --new --from codex --kind review_request

amq drain --include-body

amq send --to codex --body "Please pick this up" \
  --wait-for drained --wait-timeout 60s

amq receipts list --me codex --msg-id <msg_id>

amq reply --id <msg_id> --kind review_response --body "LGTM with comments"

To send between known sessions before entering coop exec:

amq send --root .agent-mail --from-session feature-a --me claude \
  --to codex --session feature-b --body "Please review the setup"

read, drain, and monitor apply the same strict message validation. Invalid messages move to DLQ and produce a dlq receipt. Participating shells also pin their exact session context and refuse mismatched mailbox operations. See Session routing and safety.

Health

amq doctor
amq doctor --ops
amq wake check --me <agent>

amq wake check is read-only. It reports whether this process can start or repair a wake, and a restart_capability of agent_safe, operator_only, or unavailable with an exact next action. Automated agents may act only on agent_safe; leave a live wake running otherwise.

Wake lock states, JSON schema 2, repair, owner recovery, retirement, and quarantine rules are in Wake operations, wake lifecycle, and wake state invariants. Consumption itself is drain / monitor, evidenced by receipts. Long-running wake / monitor under systemd or launchd is in Supervisor recipes.

Message Kinds & Priority

AMQ messages support kinds (review_request, question, todo, etc.) and priority levels (urgent, normal, low). See COOP.md for the full protocol.

Co-op Mode

For real-time Claude Code + Codex CLI collaboration patterns, roles, and phased workflows, see COOP.md.

Cross-Project Federation

AMQ can route messages across repositories, not just across agents in one checkout. Add a project name plus peer roots to .amqrc:

{
  "root": ".agent-mail",
  "project": "app",
  "peers": {
    "infra-lib": "/Users/me/src/infra-lib/.agent-mail"
  }
}

Then send directly to another project:

amq send --to codex --project infra-lib --body "Can you review the shared API change?"
amq send --to codex@infra-lib:collab --thread decision/release-v0.24 --kind decision \
  --labels "decision:proposal,project:app,project:infra-lib" \
  --body "Proposal: align both repos on v0.24"

Replies route back automatically with the stamped reply_project metadata. When from matches your own handle, inspect from_project before treating the message as an echo; the same handle in a different project is a legitimate cross-project sender. This shipped in v0.22.0 and is the recommended way to coordinate multi-repo agent work without adding a broker.

Swarm Mode (Claude Code Agent Teams)

External agents (Codex, etc.) can join Claude Code Agent Teams via amq swarm join, claim tasks, and receive notifications through amq swarm bridge. Note: the bridge delivers task notifications only; direct messages require relay through the team leader.

For the full command reference, see CLAUDE.md.

Global Root Fallback

Most AMQ commands resolve the queue root from the project .amqrc or the default .agent-mail layout in the current tree. For agents launched outside an AMQ-enabled repo by external orchestrators, you can configure a global root. Explicit AMQ_GLOBAL_ROOT does not shadow project .amqrc, but it does take precedence over repo-local auto-detection:

export AMQ_GLOBAL_ROOT="$HOME/.agent-mail"

Or create ~/.amqrc:

{"root": ".agent-mail"}

Root resolution precedence is:

explicit --root > AM_ROOT > project-local .amqrc > AMQ_GLOBAL_ROOT > implicit fallbacks

Inside a Git worktree or bare repository, the remaining eligible fallback is repo-local detected .agent-mail; implicit ~/.amqrc is refused. Outside Git, ~/.amqrc remains a convenience fallback and precedes detected .agent-mail. Set AMQ_GLOBAL_ROOT explicitly when shared routing is intentional.

coop exec honors that precedence before bootstrap. In a Git worktree with no eligible root, it bootstraps <git-top>/.agent-mail; --session X creates that named session afterward, while --no-init preserves the refusal. coop init is the explicit local-bootstrap command and also targets the Git top. Bare repositories do not auto-bootstrap.

If a project .amqrc exists but cannot be read or parsed, AMQ stops instead of silently delivering through a lower-precedence fallback. Use an explicit --root or AM_ROOT when you intentionally need to override that config.

For an external orchestrator or plain shell that should stay pinned to one session, opt in explicitly:

amq_context="$(amq env --session auth --me claude --export)" && eval "$amq_context"

Every shell-mode amq env output replaces the complete context: AM_ROOT, AM_ROOT_ID, AM_ME, AM_BASE_ROOT, AM_BASE_ROOT_ID, and AM_SESSION. The two _ID values are opaque physical-identity tokens emitted or unset by AMQ; do not set them manually. Sessionless output sets AM_BASE_ROOT to the exact root and writes an empty AM_SESSION, so changing to another sessionless root is detectable. --export additionally prints a stderr note that the terminal is pinned. Treat this as one terminal, one session.

Auto-detect covers the default .agent-mail layout, including .agent-mail/<session> session roots without .amqrc. Custom root names and peer config still require .amqrc or explicit flags/env. This same chain is used by amq env, amq doctor, and the integration commands, so Symphony and Kanban-launched agents can find the correct queue even when they are not started from the project directory.

Extension Metadata

Higher-level layers can store launch records, role metadata, restore state, and indexes without writing into AMQ-owned mailbox directories. AMQ reserves these extension namespaces:

<AM_ROOT>/extensions/<layer>/
<AM_ROOT>/agents/<handle>/extensions/<layer>/

Layer names use lowercase ASCII letters, digits, hyphen, underscore, and dot; reverse-DNS names are supported. For example, amq-squad — a role-aware agent team launcher built on AMQ — stores its launch records and role state under io.github.omriariav.amq-squad. AMQ does not create files inside layer-owned directories, and amq cleanup leaves extension directories alone unless a future command explicitly targets extension metadata.

Layers may publish a passive manifest at:

<AM_ROOT>/extensions/<layer>/manifest.json

amq doctor --json reports valid manifests under extension_manifests and malformed metadata under extension_diagnostics. Manifests are diagnostics-only: AMQ does not execute extension code, load callbacks, or invoke hooks from them. See docs/adr-layer-extensions.md for the full contract.

Integrations

AMQ transports messages, not remote task state. The integration layer is intentionally narrow: optional adapters convert external lifecycle or task events into normal AMQ messages. Integration messages are self-delivered (from=<me>, to=<me>) so an agent monitoring its own inbox can react without polling another tool directly.

Symphony

Symphony support is a lightweight hook recipe for Codex workspaces orchestrated through WORKFLOW.md:

amq integration symphony init --me codex
amq integration symphony init --me codex --check
amq integration symphony emit --event after_run --me codex

init patches an AMQ-managed fragment into WORKFLOW.md. emit is hook-friendly and supports after_create, before_run, after_run, and before_remove. This stays intentionally small: AMQ does not try to become a Symphony control plane. Current limitation: because WORKFLOW.md is parsed and rewritten as structured YAML/Markdown, comments and formatting inside the frontmatter may be normalized.

Cline Kanban Bridge

The Kanban bridge is experimental. Use it when you want runtime session transitions and review handoffs mirrored into AMQ, with the understanding that it depends on a fast-moving preview WebSocket surface:

amq integration kanban bridge --me codex
amq integration kanban bridge --me codex --workspace-id my-workspace

The bridge connects to ws://127.0.0.1:3484/api/runtime/ws by default, bootstraps from snapshot, refreshes from workspace_state_updated, and emits notifications only for task session transitions plus task_ready_for_review.

Integration Metadata

The built-in adapters share a versioned contract under context.orchestrator. See docs/adapter-contract.md for the formal v1 envelope and stability expectations.

Integration messages also carry standard labels such as:

  • orchestrator
  • orchestrator:symphony or orchestrator:kanban
  • task-state:<state>
  • handoff for review-ready transitions
  • blocking for failed or interrupted work

That makes integration traffic filterable with existing AMQ primitives such as amq list --label orchestrator --label handoff.

Command Reference

Common command groups:

AreaCommands
Core messaginginit, send, list, read, drain, reply, thread, trace, watch, monitor, receipts
Collaborationsetup, launch, coop init, coop exec, session create, session list, session resume, swarm list, swarm join, swarm tasks, swarm bridge
Integrationsintegration symphony init, integration symphony emit, integration kanban bridge
Operationspresence set, presence list, route explain, who, doctor, doctor --ops, wake check, wake repair, wake recover-owner, wake retire, cleanup, dlq *, upgrade, env, shell-setup

--json-schema requires --json. Diagnostic schema 2 and the public launch --plan / --prepare / --apply forms are in docs/wake-lifecycle.md and docs/launch-api.md. Use amq <command> --help for exact flags.

Exit codes

AMQ exposes stable process exit codes for scripts and agent consumers:

CodeMeaning
0Success. The command completed normally.
1General error. The failure has no more specific exit-code classification.
2Usage error. Arguments, flags, or command input are invalid.
3Not found. A requested resource such as a mailbox, message, session, agent, or configuration does not exist.
4Timeout. A watch, monitor, receipt wait, or delivery wait reached its deadline.
5Context mismatch. A syntactically valid route was refused, including a pin conflict or an ineligible implicit root inside Git.
6Action required. The command cannot proceed without an operator action (stale conversation token, unknown backend inspect, untrusted config, blocked rebind).

The numeric meaning is the machine contract; stderr is human-readable context and should not be parsed as a stable discriminator. --json does not change these process exit codes. A read-only list on a mismatched session pin warns and continues; commands that consume or mutate mailbox state fail with code 5.

When a command reports per-agent outcomes, whole-command failures that precede any per-agent work keep codes 2, 5, and 3 and preempt mixed results. Once per-agent work begins, the process exit code is the highest-precedence per-agent outcome: 6 over 4 over 1 over 0. Expected dispositions (disabled, unsupported, and policy-consistent fresh) contribute 0.

For the full CLI syntax, examples, and message schema, see CLAUDE.md. For the read-only trace contract and its evidence limits, see docs/trace.md.

How It Works

AMQ uses the battle-tested Maildir format:

  1. Write — Message written to a unique attempt file in tmp/
  2. Sync — File fsynced to disk
  3. Deliver — No-replace publication to new/ (never overwrites a retained same-name message; identical bytes are idempotent)
  4. Process — Reader moves to cur/ after reading

If a different same-name message is already in new/, AMQ preserves both copies and reports a collision. A claim never replaces a retained same-name message in cur/.

This guarantees crash-safety: if the process dies mid-write, no corrupt message appears in the inbox. See CLAUDE.md for the full directory layout.

Built on AMQ

AMQ is meant to be the messaging layer underneath higher-level orchestrators. Projects building on it:

  • amq-squad by @omriariav — a role-aware agent team launcher. AMQ owns messaging between agents; amq-squad owns the layer above: who is on the team, what role each agent plays, the shared norms they follow, and how to bring the whole squad up, down, back, or into a new workstream. It builds on AMQ's extension metadata surface for launch records and role state.

Building something on AMQ? Open an issue or PR to be listed here.

Documentation

Development

git clone https://github.com/avivsinai/agent-message-queue.git
cd agent-message-queue
make build   # Build amq plus companion binaries
make test    # Run tests
make ci      # Full CI: vet + lint + test + smoke

FAQ

Why not just use a database? Files are universal, debuggable, and work everywhere. No connection strings, no migrations, no ORM. Just files.

Why not Redis/RabbitMQ/etc? Those require infrastructure. AMQ is for local inter-process communication where agents share a filesystem. No server to configure or keep running.

What about Windows? Native Windows supports the core queue, but not coop exec or wake. Use WSL with the Linux binary for the complete co-op workflow. See the explicit platform capability matrix.

Is this production-ready? For local development workflows, yes. AMQ is intentionally simple—it's not trying to be a distributed message broker.

How does AMQ compare to other multi-agent tools? Tools like MCP Agent Mail (server-based coordination + SQLite), Gas Town (tmux-based orchestration), and others offer richer features. AMQ is intentionally minimal: single Core binary, no server, Maildir delivery. Best for a handful of local agents. Two machines use companion amq-bridge, not a shared filesystem.

License

MIT