Co-op mode enables multiple agents (e.g., Claude Code and Codex CLI) to work in parallel where safe, coordinate where risky, leveraging cognitive diversity (different models = different training = different blind spots) to catch errors that same-model review would miss.
AMQ is the communication layer in this setup, not the coordinator. The initiator, the pair, or an external orchestrator still owns the task plan; AMQ keeps the conversation, handoffs, and thread continuity intact.
- Co-op: lightweight, peer-to-peer messaging between agents via AMQ threads (
p2p/...). - Swarm: join Claude Code Agent Teams and coordinate via the shared task list (
amq swarm ...). - Messaging: swarm bridge delivers task notifications only. Claude Code teammates can
amq sendto external agents, but external agents cannot DM a specific Claude Code teammate directly. External -> team messages must go to the leader's AMQ inbox, then the leader drains and forwards via Claude Code internal messaging.
For swarm command reference, see CLAUDE.md.
-
Install amq CLI (releases):
curl -fsSL https://raw.githubusercontent.com/avivsinai/agent-message-queue/main/scripts/install.sh | bash -
Install amq-cli skill for your agents:
Via skills (recommended):
npx skills add avivsinai/agent-message-queue -g -y
Or via skild:
npx skild install @avivsinai/amq-cli -t claude -y
See INSTALL.md for manual installation or troubleshooting.
Terminal 1 - Claude Code:
amq coop exec claude -- --dangerously-skip-permissionsTerminal 2 - Codex CLI:
amq coop exec codex -- --dangerously-bypass-approvals-and-sandboxThat's it. coop exec auto-initializes the project if needed, sets AM_ROOT/AM_ME (and AM_BASE_ROOT for cross-session resolution), starts wake notifications, and execs into the agent. Without --session or --root, it defaults to --session collab (i.e., AM_ROOT=.agent-mail/collab).
To disable auto-wake (e.g., in CI or non-TTY environments):
amq coop exec --no-wake claudeRun multiple agent pairs on different features using --session:
# Pair A: auth feature
amq coop exec --session auth claude # Terminal 1
amq coop exec --session auth codex # Terminal 2
# Pair B: api refactor
amq coop exec --session api claude # Terminal 3
amq coop exec --session api codex # Terminal 4Each pair has isolated inboxes and threads. Messages stay within their root.
Equivalent explicit root form: --root .agent-mail/<session>.
When you can't use exec (non-interactive environments):
amq coop init
eval "$(amq env --me claude)"amq env resolves the root with the full precedence chain:
flags > AM_ROOT > project .amqrc > AMQ_GLOBAL_ROOT > ~/.amqrc > auto-detect
Auto-detect covers the default .agent-mail layout in the current tree, including .agent-mail/<session> session roots without .amqrc. Custom root names still need .amqrc, explicit flags, or env vars.
That matters when agents are launched by external orchestrators from outside the project root.
Co-op mode and orchestrator integrations use the same queue primitives. If you are wiring AMQ into Symphony or Cline Kanban, make the queue root discoverable globally so spawned agents land in the same mailbox tree:
export AMQ_GLOBAL_ROOT="$HOME/.agent-mail"
amq integration symphony init --me codex
amq integration kanban bridge --me codex
amq doctor --opsIntegration messages are self-delivered and carry context.orchestrator plus labels such as orchestrator:*, task-state:*, handoff, and blocking, so they fit naturally into the same amq drain / amq reply workflow used for co-op.
amq wake uses TIOCSTI which may be unavailable on:
- Hardened Linux (CONFIG_LEGACY_TIOCSTI=n)
- Windows (use WSL)
If wake fails, configure the notify hook for desktop notifications:
# ~/.codex/config.toml
notify = ["python3", "/path/to/repo/scripts/codex-amq-notify.py"]When Claude is in plan mode, it cannot run shell tools directly. Use the
UserPromptSubmit hook to inject AMQ context before prompt processing:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "AMQ_PROMPT_HOOK_MODE=plan AMQ_PROMPT_HOOK_ACTION=list python3 $CLAUDE_PROJECT_DIR/scripts/claude-amq-user-prompt-submit.py"
}
]
}
]
}
}Set AMQ_PROMPT_HOOK_ACTION=drain to auto-drain on submit (instead of list/peek).
- Initiator = whoever starts the task (agent or human). Owns decisions and receives all updates.
- Leader/Coordinator = coordinates phases, merges, and final decisions (often the initiator).
- Worker = executes assigned phases and reports back to the initiator.
Default pairing note: Claude is often faster and more decisive, while Codex tends to be deeper but slower. That commonly makes Claude a natural coordinator and Codex a strong worker. This is a default, not a rule — roles are set per task by the initiator.
| Phase | Mode | Description |
|---|---|---|
| Research | Parallel | Both explore codebase, read docs, search. No conflicts. |
| Design | Parallel -> Merge | Both propose approaches. Leader merges/decides. |
| Code | Split | Divide by file/module. Never edit same file. |
| Review | Parallel | Both review each other's code. Leader decides disputes. |
| Test | Parallel | Both run tests, report results to leader. |
- Parallel where safe - Research, design, review, and test phases run in parallel.
- Split where risky - Code phase divides files/modules to avoid conflicts.
- Never branch - Always work on same branch (joined work).
- Leader coordinates - The initiator or designated leader handles phase transitions and final decisions.
- The initiator is whoever started the task (agent or human).
- Always report progress and completion to the initiator.
- Ask questions only to the initiator. Do not ask a third party.
- Start: send
kind=statuswith an ETA to the initiator as soon as you begin. - Heartbeat: update on phase boundaries or every 10-15 minutes.
- Done: send Summary / Changes / Tests / Notes to the initiator.
- Blocked: send
kind=questionto the initiator with options and a recommendation.
Pick one mode per task; the initiator decides.
- Leader + Worker: leader decides, worker executes; best default.
- Co-workers: peers decide together; if no consensus, ask the initiator.
- Duplicate: independent solutions or reviews; initiator merges results.
- Driver + Navigator: driver codes, navigator reviews/tests and can interrupt.
- Spec + Implementer: one writes spec/tests, the other implements.
- Reviewer + Implementer: one codes, the other focuses on review and risk detection.
amq send --to codex --subject "Review: New parser" --kind review_request --body "..."
amq send --to codex --priority urgent --kind question --body "Blocked on API"amq drain --include-body # One-shot, silent when empty
amq watch --timeout 60s # Block until message arrives
amq list --new # Peek without side effectsamq reply --id "msg_123" --kind review_response --body "LGTM with minor suggestions..."Co-op works without wake.
coop execstarts it automatically.
amq wake uses TIOCSTI to inject notifications into your terminal by default.
For orchestrators or hardened environments without a controlling TTY, use an
explicit external transport:
amq wake --me orchestrator \
--inject-via ghostty-bridge \
--inject-arg exec \
--inject-arg "$TERMINAL_ID"--inject-via is an executable path, not a shell command line. Repeat
--inject-arg for fixed arguments; AMQ appends the sanitized notification text
as the final argv element. This executes a local process for each notification,
and the payload can include sanitized but message-derived header content such as
sender and subject.
Options:
--inject-mode auto|raw|paste- Injection strategy--inject-via <executable>- External transport executable; bypasses TIOCSTI and local TTY startup checks--inject-arg <arg>- Fixed argument before the payload; repeat for multiple arguments--inject-timeout 5s- Maximum runtime for one external injection command--bell- Ring terminal bell on new messages--debounce 250ms- Batch rapid messages--defer-while-input/--defer-while-input=false- Best-effort quiet-window gate before non-interrupt injection--input-quiet-for 1200ms- Required quiet window before deferred injection--input-poll-interval 200ms- Poll interval while waiting for terminal input to quiet--input-max-hold 15s- Maximum hold time for one deferred wake injection--interrupt/--interrupt=false- Enable/disable Ctrl+C for urgent messages
Input deferral is a heuristic, not a prompt-buffer guarantee. It only samples terminal state while a wake notification is already pending: unread bytes in the TTY input queue plus recent reads from the controlling terminal. If the foreground app has already consumed a partially typed prompt into its own editor buffer and the user pauses longer than --input-quiet-for, wake can still inject and submit. Explicit urgent interrupt messages bypass this deferral.
Platform support:
- macOS: Works
- Linux: May be disabled by kernel hardening (CONFIG_LEGACY_TIOCSTI)
- Windows: Not supported (use WSL)
{
"schema": 1,
"from": "codex",
"to": ["claude"],
"thread": "p2p/claude__codex",
"subject": "Code review needed",
"priority": "urgent",
"kind": "review_request",
"labels": ["parser"],
"context": {"paths": ["internal/cli/drain.go"], "focus": "sorting"}
}| Priority | Behavior |
|---|---|
urgent |
Interrupt current work |
normal |
Add to TODO list |
low |
Batch for later |
| Kind | Reply Kind | Default Priority |
|---|---|---|
review_request |
review_response |
normal |
question |
answer |
normal |
decision |
— | normal |
todo |
— | normal |
status |
— | normal |
brainstorm |
— | normal |
When --kind is set but --priority is omitted, the CLI defaults priority to
normal.
The spec workflow is a skill-managed protocol — agents follow the instructions in the amq-spec skill's spec-workflow.md using standard AMQ messaging primitives (amq send, amq drain, amq thread) with existing generic kinds and workflow:spec labels.
Phases: Research -> Discuss -> Draft -> Review -> Present -> Execute
All spec messages use thread spec/<topic> and labels workflow:spec,phase:<name>. See the amq-spec skill's spec-workflow.md for the full protocol.
{
"paths": ["internal/cli/send.go"],
"symbols": ["Header", "runSend"],
"focus": "error handling in validation",
"commands": ["go test ./internal/cli/..."]
}amq wake --me claude # Watch for warnings
amq drain --include-body # Manual fallbackamq list --me claude --new --json # Check inbox directly
amq watch --me claude --poll # Force poll mode