3 Host Integrations
Ope Olatunji edited this page 2026-05-15 00:46:22 -04:00

Host Integrations

One AgenticMail dispatcher per LLM host. Currently shipping for Claude Code and OpenAI Codex CLI, with two more on the roadmap.

The @agenticmail/claudecode package was the reference implementation; @agenticmail/codex shipped on 2026-05-14 with ~90% architectural reuse. Grok Build and Hermes Agent follow the same shape, each adjusting only for the target host's plugin/hook conventions.

Co-installation: one machine, multiple hosts

As of 0.9.20, a single machine can run both agenticmail-claudecode and agenticmail-codex side-by-side. Each host:

  • Has its own bridge account (claudecode@localhost, codex@localhost) with role='bridge' and metadata.host matching its name
  • Has its own PM2 dispatcher daemon (agenticmail-claudecode-dispatcher, agenticmail-codex-dispatcher)
  • Has its own MCP server config block, lifecycle hooks, and subagent directory
  • Watches only the accounts it owns (per metadata.host), so a reply to a Codex-owned teammate fires exactly one worker (under @openai/codex-sdk), and a reply to a Claude-owned teammate fires exactly one worker (under @anthropic-ai/claude-agent-sdk).

The MCP server auto-stamps metadata.host on every new account from the AGENTICMAIL_MCP_HOST env var that each installer writes into its MCP server's env block. Legacy accounts that predate this can be retro-tagged with agenticmail-<host> claim --all.

What every integration needs

Every host package implements the same five primitives:

  1. MCP server registration — write into the host's config so the MCP toolbelt becomes available to the model.
  2. Sub-agent registration — surface every AgenticMail account as a host-native subagent the model can call directly.
  3. Lifecycle hooks — SessionStart (capabilities blurb), UserPromptSubmit (mail context), Stop (autonomous-mode awareness).
  4. Headless dispatcher — long-running daemon that wakes one fresh model turn per new-mail / new-task event.
  5. Installer + uninstaller — agenticmail-<host>-install / -uninstall that patches/cleans up the host's config files idempotently.

If a host doesn't support one of these as a first-class primitive, the package documents the workaround.

Status matrix

Host Package MCP Subagents Hooks Headless Status
Claude Code (Anthropic) @agenticmail/claudecode ✅ native ✅ native ✅ native ✅ SDK Shipping (0.2.13)
Codex CLI (OpenAI) @agenticmail/codex ✅ native (TOML) ✅ spawn_agent ✅ native (8 events, byte-compat with Claude Code) ✅ @openai/codex-sdk Shipping (0.1.5)
Grok Build (xAI) @agenticmail/grok-build (planned) ✅ announced ✅ native ✅ native (17 events) ✅ ACP / -p Researched
Hermes Agent (Nous Research) @agenticmail/hermes (planned) ✅ native ✅ delegate_task ✅ native (richer than CC) ✅ Python SDK Researched

How host ownership is enforced

The three layers that keep co-installed hosts from stepping on each other:

Layer Where Behavior
MCP auto-stamp packages/mcp/src/tools.ts's create_account reads process.env.AGENTICMAIL_MCP_HOST Every account created through MCP gets metadata.host pre-filled with the env var's value. Each host installer writes this var into its own MCP server's env block, so accounts created from a Claude Code session are stamped 'claudecode' and accounts created from a Codex session are stamped 'codex'.
Install-time filter selectExposableAgents() in install.ts (both host packages) A teammate appears in the host's subagent roster (~/.claude/agents/, ~/.codex/agents/) ONLY if metadata.host === ownHost. Legacy unclaimed accounts are skipped — the operator must claim them explicitly.
Dispatcher-time filter shouldWatch() in dispatcher.ts (both host packages) The dispatcher's per-account SSE channel opens only if metadata.host === ownHost, OR if metadata.host is unset (legacy back-compat). Bridge accounts (role='bridge' or metadata.bridge===true) are always skipped regardless of host.

The claim subcommand

agenticmail-<host> claim <name> [--unclaim] [--all] [--json] is the operator-facing tool for transferring ownership:

# Take ownership of every unclaimed teammate
agenticmail-claudecode claim --all

# Hand a specific teammate to the other host
agenticmail-claudecode claim vesper --unclaim
agenticmail-codex claim vesper

# Audit-friendly output
agenticmail-codex claim --all --json

--all deliberately refuses to steal accounts already owned by another host — you have to unclaim first, then re-claim, so cross-host transfers are intentional rather than accidental.

Architectural overlap

Across all four planned hosts, roughly 70% of the dispatcher code is identical: SSE channel management, per-agent serialization, wake-coalesce, wake-budget, catch-up scan, state persistence, activity reporting, host-ownership filter. The host-specific bits are:

  • Spawning a model turn. Claude Code uses @anthropic-ai/claude-agent-sdk's query(). Codex CLI's headless mode is TBD (likely codex -p). Grok Build uses ACP-over-stdio or grok -p. Hermes Agent uses from run_agent import AIAgent.
  • Config-file layout. Each host stores its settings + hooks + MCP servers in a different path/shape.
  • Subagent definition format. Claude Code: markdown with YAML frontmatter in ~/.claude/agents/. Grok: JSON entries in subAgents[]. Hermes: plugin entry-points. Codex: TBD.

To keep the four packages DRY, the plan is to factor out @agenticmail/host-toolkit — a shared library with:

  • Dispatcher base class (everything except the spawnWorker and installer slots).
  • DispatcherState (persistence, already isolated).
  • runCatchUp (mail + task backlog).
  • composeWakePrompt (thread cache + agent memory injection).
  • WakeBudget, WakeCoalesce (rate limiter + debounce).
  • The capabilities-blurb generator.

Each host package then becomes a thin layer (~500 lines) that supplies host-specific spawnWorker, Installer, and event-name mappings.