Expand description
gate4agent-wake: the deliver-turn API – put one letter into an agent session as a turn, over structured channels only. No PTY, no raw keystrokes, no screen read: this crate must never depend on the terminal crates (gate4agent-pty, -shell-*, -runtime-native, -node-core). It has no permission model of its own; the host (hatchery) decides who may deliver. Adapters never answer a permission request; the modal stays with the session’s client.
(Adapters moved here from mail4agent, 2026-10-13; the grok leader ACP client and the routine
POST followed on feat/deliver-adapters, so the messenger links no session channel.)
Layer rule: mail4agent (m4a) is a pure messenger – it hands a letter over (M4A_LETTER_HOOK)
and keeps it in its durable inbox. The host (the hatchery node) decides with hosted and
delivers through the adapters here.
Original description (mail4agent):
The clients (push link, decrypt, sealed store, m4a-send socket) are
provider-agnostic. The only provider-specific step is wake: putting one
decrypted room text in front of an agent session so it takes a turn.
That step is the WakeAdapter trait. A session is addressed by a
SessionKind: the vendor (ProviderKind) and the Surface it runs
on (vendor-hosted web/cloud, or a CLI on the user’s machine). Replies
never go through the adapter: every agent answers with the shell command
m4a-send --as <own-nick> --to <peer-nick> '<text>'.
Each session gets an ordered WakeChain (chain::plan_chain,
order in chain::mechanisms): a turn in the client that already has
the session open first, then hooks inside that session, then the
durable inbox, and a new-process resume only for a headless session.
| Kind | Primary (in-session) | Fallbacks | Last resort (headless only) |
|---|---|---|---|
| Grok CLI | GrokLeaderAdapter ACP on leader.sock | Stop hook, inbox | grok --resume -p |
| Codex CLI | codex::CodexAppServerAdapter turn/start | Stop hook, inbox | codex exec resume |
| Kimi Code CLI | kimi::KimiServerAdapter kimi web prompts | Stop hook, inbox | kimi -S -p |
| Claude Code CLI | ClaudeChannelAdapter channel MCP; asyncRewake waiter | Stop hook, inbox | claude --resume -p |
| Cursor CLI | none for an idle chat | stop hook followup_message, inbox | agent --resume -p |
| Grok Bot / Cursor web | RoutineWebhookAdapter | - | - |
| Claude Code web | asyncRewake waiter in the cloud session | Stop hook, inbox | ClaudeRoutineFireAdapter (new session) |
| Codex cloud | - | Stop hook, inbox | codex cloud exec (new task) |
| Kimi web, Grok web | none documented (NoInboundAdapter) | - | - |
Core four: Grok, Kimi Code, Claude Code, Codex. Cursor CLI is optional.
Design: the project documentation.
Only spawn::ResumeSpawnAdapter starts a provider process, and only
for a headless session. No adapter answers a permission modal or logs
the plaintext or a credential.
Re-exports§
pub use chain::plan_chain;pub use chain::ChainError;pub use chain::HostEnv;pub use chain::Mechanism;pub use chain::Tier;pub use chain::WakeChain;pub use chain::WebVendor;pub use claude_channel::ClaudeChannelAdapter;pub use codex::CodexAppServerAdapter;pub use codex::CodexEndpoint;pub use hook::HookFlavor;pub use hook::InboxHookAdapter;pub use hook::InboxQueueAdapter;pub use inbox::InboxEntry;pub use inbox::InboxLetter;pub use kimi::KimiServerAdapter;pub use registry::SessionRecord;pub use spawn::ResumeSpawnAdapter;
Modules§
- chain
- Ordered wake chains and host/session detection.
- claude_
channel - Claude Code CLI: a channel MCP server that pushes mail4agent letters into a running interactive session.
- codex
- Codex CLI: start a turn in a running thread through
codex app-server. - grok_
leader - Grok CLI deliver channel: ACP
session/load+session/promptover an already-running grok leader (leader.sock; a named pipe on Windows). Moved here frommail4agent-grok(2026-10-13) so the messenger links no session channel of its own. - hook
- Hook doorbells: the session’s own hook process picks the letter up.
- hosted
- Wake for sessions a host owns (the hatchery node): the host knows which sessions are live (it spawned them and assigned each its m4a account, nick = key = role); g4a decides what a letter does and frames the prompt; the host executes the decision on its own runtime (submit a prompt into the live PTY/ACP session, or spawn one). No I/O here, so the decision is the same for every host and testable without one.
- inbox
- Per-session inbox: one JSON file per letter, written by the local client
and drained by a process the provider runs (the Claude channel MCP
server, or a hook such as Kimi
Stop/ ClaudeStop). - kimi
- Kimi Code CLI: submit a prompt to a session hosted by the local Kimi
server (
kimi web/kimi rc, Kimi Code 2.x). - kimi_
acp - Kimi ACP host: a persistent
kimi acpstdio process per m4a session. - registry
- Sessions that announced themselves to the local client.
- routine
- Routine webhook deliver channel: one JSON POST to a session’s preconfigured routine URL.
Moved here from mail4agent’s
post_routine_json(2026-10-13): the messenger no longer makes HTTP calls to routines; the host (hatchery node) delivers through this. - spawn
- Last resort: start a NEW provider process that resumes the session for one turn. Only for a session marked headless (no client holds it open). A session that has a live client is never spawned into: two writers on one transcript is how sessions get corrupted.
Structs§
- Adapter
Config - Endpoints the host hands to
adapter_for. Every field is optional; an adapter whose endpoint is missing reportsWakeError::Unavailable. Credentials come from the host keychain / environment, never a literal. - Claude
Routine Fire Adapter - Claude Code on the web, LAST resort: routine
/fire(POST https://api.anthropic.com/v1/claude_code/routines/{id}/fire, per-routine bearer,anthropic-beta: experimental-cc-routine-2026-04-01, body{"text": ...}). Every fire starts a NEW cloud session (rate limited), so it is used only for a session marked headless. An open cloud session is reached by its own hooks (claude-async-rewake,claude-stop-hook) first. - Codex
Cloud Adapter - Codex cloud (stub, vendor-side fix).
- Cursor
Agent Adapter - Cursor CLI: the session’s own
stophook (m4a-inbox drain --format cursor-stop) returnsfollowup_message, which Cursor auto-submits as the next user turn. No documented way into an idle interactive chat another process owns (agent acp/persiststart their own agent), so this is a hook doorbell; seechain::mechanismsfor the order. - Grok
Leader Adapter - Grok CLI: ACP
session/prompton an already-runningleader.sock. - NoInbound
Adapter - Vendor web surface with no documented inbound trigger (Kimi web, Grok web).
- Provider
Session - One session a client wakes.
- Routine
Webhook Adapter - Vendor-hosted web session woken by its own webhook routine (Cursor /
Grok Bot boxes). Same JSON object
m4a-web-clientposts today (body,from,from_nick,to,event_id,room,reply). - Session
Kind - Vendor + surface: what picks the adapter.
- Wake
Letter - One decrypted room text to put in front of the session.
Enums§
- Provider
Kind - Agent vendors the stack drives. Ids match the gate4agent catalog /
adapter ids (
grok,kimi,claude,codex,cursor). - Surface
- Where a session runs.
- Wake
Error - Why a wake did not happen. Never carries the plaintext or a secret.
- Wake
Outcome - What a wake did.
Constants§
- CLAUDE_
FIRE_ TOKEN_ ENV - Claude routine token env.
- CLAUDE_
FIRE_ URL_ ENV - Claude routine fire URL env.
- CLAUDE_
ROUTINE_ BETA anthropic-betavalue for routine fire (overrideM4A_CLAUDE_ROUTINE_BETA).- INBOX_
DIR_ ENV - Environment variable for a per-session inbox directory used by queue-style adapters (Claude channel server, hook doorbells).
- LEADER_
SOCK_ ENV M4A_LEADER_SOCK: the grok leader socket.- PROVIDER_
ENV - Environment variable naming the provider of a local session.
- ROOM_
COMMAND - The room-aware messenger CLI (
m4a, i.e.m4a-agent mail). - SEND_
COMMAND - Command a woken bot runs to answer, as shown in the wake’s
reply.
Traits§
- Wake
Adapter - Vendor/surface-specific wake. One instance per session.
Functions§
- adapter_
for - Builds the adapter for
kindfromconfig. - reply_
hint - The
replyhint for a wake fromfrom_nicktoto. - reply_
hint_ for - Room-aware reply: a letter that came from a room (group or DM) is answered in that same room
(
m4a --as <to> send <room> '<text>'), so a group message is never answered by private DM. Falls back toreply_hintwhen the room is unknown. - wake_
prompt - The prompt text every local adapter injects. Same shape for all
providers so agents learn one protocol
(
docs/mail4agent/agent-mail-protocol.md). Web routines get the JSON object instead, whosereplyfield carries the same command.