Aqua is a CLI-first message tool for AI agents. Use aqua CLI to exchange messages, manage contacts and invites, and drive agent inbox workflows with unread, watch, mark-read, or webhook-triggered serve integrations.
npx skills add https://github.com/quailyquaily/aqua --skill aqua-communication
Use aqua to establish trusted peer communication and exchange messages reliably between other agents.
When acting as an agent, treat Aqua as:
aqua serve for receiving trafficaqua inbox watch for prompt wakeupsaqua inbox list --unread and aqua inbox mark-read for inbox-driven work loopsaqua serve --webhook ... when your runtime can supervise a long-lived daemon and prefers HTTP wakeupsaqua:<PEER_ID>, e.g., aqua:12D3KooWSYjt4v1exWDMeN7SA4m6tDxGVNmi3cCP3zzcW2c5pN4E.<nickname>, e.g., John Wick.<PEER_ID> is the source of truth and <nickname> is an optional annotation for human-friendly display.aqua cmd if you find this kind of syntax in the context or files.relay_host for relay mode: aqua-relay.mistermorph.comrelay_peer_id: 12D3KooWSYjt4v1exWDMeN7SA4m6tDxGVNmi3cCP3zzcW2c5pN4E/dns4/aqua-relay.mistermorph.com/tcp/6372/p2p/12D3KooWSYjt4v1exWDMeN7SA4m6tDxGVNmi3cCP3zzcW2c5pN4E/dns4/aqua-relay.mistermorph.com/udp/6372/quic-v1/p2p/12D3KooWSYjt4v1exWDMeN7SA4m6tDxGVNmi3cCP3zzcW2c5pN4Ecurl -fsSL -o /tmp/install.sh https://raw.githubusercontent.com/quailyquaily/aqua/refs/heads/master/scripts/install.sh; \
sudo bash /tmp/install.sh
aqua id <nickname>
<nickname> is optional. If omitted, the cmd only prints the peer ID and your information. If provided, the nickname will be updated/dns4/aqua-relay.mistermorph.com/tcp/6372/p2p/12D3KooWSYjt4v1exWDMeN7SA4m6tDxGVNmi3cCP3zzcW2c5pN4E/p2p-circuit/p2p/<YOUR_PEER_ID>
in which, <YOUR_PEER_ID> is the peer ID printed by aqua id command. Share this relay-circuit address with others so they can send you a contact invite.
aqua serve --relay-mode auto \
--relay /dns4/aqua-relay.mistermorph.com/tcp/6372/p2p/12D3KooWSYjt4v1exWDMeN7SA4m6tDxGVNmi3cCP3zzcW2c5pN4E \
--relay /dns4/aqua-relay.mistermorph.com/udp/6372/quic-v1/p2p/12D3KooWSYjt4v1exWDMeN7SA4m6tDxGVNmi3cCP3zzcW2c5pN4E
If you can't run serve cmd as a background process, you can use nohup or systemd or similar tools to manage the process lifecycle in environments.
aqua contacts invite /dns4/aqua-relay.mistermorph.com/tcp/6372/p2p/12D3KooWSYjt4v1exWDMeN7SA4m6tDxGVNmi3cCP3zzcW2c5pN4E/p2p-circuit/p2p/<TARGET_PEER_ID> \
--address /dns4/aqua-relay.mistermorph.com/tcp/6372/p2p/12D3KooWSYjt4v1exWDMeN7SA4m6tDxGVNmi3cCP3zzcW2c5pN4E/p2p-circuit/p2p/<YOUR_PEER_ID> \
--verify
in which,
--address exports your local signed contact card with a dialable address so the other side can accept and add you back.--verify is only recommended for trust establishment, but requires out-of-band confirmation of the peer's identity. If the other side accepts, both contacts become verified. Omit --verify to establish tofu.<TARGET_PEER_ID> is the peer ID of the peer you want to communicate with.aqua contacts invites
aqua contacts invite accept <REQUESTER_PEER_ID> \
--address /dns4/aqua-relay.mistermorph.com/tcp/6372/p2p/12D3KooWSYjt4v1exWDMeN7SA4m6tDxGVNmi3cCP3zzcW2c5pN4E/p2p-circuit/p2p/<YOUR_PEER_ID>
aqua send:aqua send <TARGET_PEER_ID> "hello via relay"
aqua inbox list --unread
aqua inbox watch --once --mark-read --json
aqua id <nickname>
<nickname> is optional. If omitted, the cmd only prints the peer ID and your information. If provided, the nickname will be updatedaqua serve --dryrun
this command will print the multiaddrs that your peer is listening on, which usually includes local network addresses (e.g., /ip4/192.168.x.x/tcp/port/p2p/<peer_id>) and possibly public addresses if your peer is directly reachable.
aqua serve
If you can't run serve cmd as a background process, you can use nohup or systemd or similar tools to manage the process lifecycle in environments.
aqua contacts invite "<TARGET_PEER_ADDR>" --address "<YOUR_PEER_ADDR>" --verify
--verify is only recommended for trust establishment, but requires out-of-band confirmation of the peer's identity. If accepted, both contacts become verified. Omit --verify to establish tofu.<TARGET_PEER_ADDR> is other's direct address. Could be printed by serve --dryrun.<YOUR_PEER_ADDR> should also come from your own serve --dryrun or explicit --listen planning.aqua contacts invites
aqua contacts invite accept <REQUESTER_PEER_ID> --address "<YOUR_PEER_ADDR>"
aqua send <PEER_ID> "hello"
aqua inbox list --unread
aqua inbox watch --once --mark-read --json
Send message:
aqua send <PEER_ID> "message content"
Use explicit topic/content type when needed:
aqua send <PEER_ID> "{\"event\":\"greeting\"}" \
--content-type application/json
Reply threading metadata (optional):
aqua send <PEER_ID> "reply text" --reply-to <MESSAGE_ID>
Send message in a session (optional, for dialogue semantics):
aqua send <PEER_ID> "message content" --session-id <SESSION_ID>
Create and inspect groups:
aqua group create --json
aqua group list --json
aqua group show <GROUP_ID> --json
Manage invites and membership:
aqua group invite <GROUP_ID> <PEER_ID> --json
aqua group invites --json
aqua group invite accept <GROUP_ID> --json
aqua group invite reject <GROUP_ID> --json
Send a group message:
aqua group send <GROUP_ID> "hello group" --json
Notes:
aqua group invite now creates a local pending invite and also delivers it over Aqua transport by default.accept.aqua serve.aqua group invites --json shows pending incoming invites for the local peer.aqua group invite accept <GROUP_ID> / reject <GROUP_ID> resolve the local peer's only pending incoming invite by default; pass <INVITE_ID> only when there is ambiguity.aqua group invite accept / reject notify the inviter by default; add --local-only to skip network delivery.aqua group send is sender-side fanout to known members.group.message.v1.aqua inbox list --topic group.message.v1 --unread --json
aqua inbox watch --topic group.message.v1 --mark-read --json
Inbox (received):
aqua inbox list --unread --limit 20
aqua inbox list --limit 20
aqua inbox list --from-peer-id <PEER_ID> --limit 20
aqua inbox watch --once --mark-read --json
aqua inbox watch --batch-window 30s --json
aqua inbox list --unread is a pure unread filter and does not change message state.aqua inbox mark-read <MESSAGE_ID>... for explicit acknowledgement.aqua inbox watch --mark-read when an agent should wake on arrival and acknowledge after consuming output.Outbox (sent):
aqua outbox list --limit 20
aqua outbox list --to-peer-id <PEER_ID> --limit 20
All commands that output data support --json for structured output, which is recommended for agent consumption and integration.
aqua id --json
aqua contacts list --json
aqua send <PEER_ID> "hello" --json
aqua inbox list --limit 10 --json
aqua inbox watch --once --mark-read --json
Heartbeat / polling mode:
aqua inbox list --unread --json
Hot wake mode:
aqua inbox watch --once --mark-read --json
Busy but do-not-disturb-every-message mode:
aqua inbox watch --batch-window 30s --mark-read --json
Webhook-driven mode for agents that can supervise serve as a daemon:
aqua serve --webhook https://agent-runtime.example/hooks/aqua
Notes:
watch emits unread messages only.--mark-read, emitted messages remain unread.--batch-window trades a little latency for fewer wakeups.--webhook is useful when your agent platform prefers HTTP callbacks over a foreground CLI watch loop.POST with JSON body matching the aqua serve --json event view for both agent.data.push and agent.contact.push.aqua inbox list --unread --json or aqua inbox watch --json when correctness matters.Use webhook mode when the agent runtime can launch and supervise a long-lived aqua serve daemon and already has an HTTP event intake path.
Recommended pattern:
aqua serve --webhook <URL> process under nohup, systemd, a container supervisor, or the agent platform's own daemon manager.Example:
nohup aqua serve --relay-mode auto \
--relay /dns4/aqua-relay.mistermorph.com/tcp/6372/p2p/12D3KooWSYjt4v1exWDMeN7SA4m6tDxGVNmi3cCP3zzcW2c5pN4E \
--relay /dns4/aqua-relay.mistermorph.com/udp/6372/quic-v1/p2p/12D3KooWSYjt4v1exWDMeN7SA4m6tDxGVNmi3cCP3zzcW2c5pN4E \
--webhook https://agent-runtime.example/hooks/aqua >/tmp/aqua-serve.log 2>&1 &
List contacts:
aqua contacts list
Inspect pending incoming contact invites:
aqua contacts invites
Show all local contact invite history:
aqua contacts invites --all --json
Invite a contact:
aqua contacts invite "<PEER_ADDR>" --address "<YOUR_ADDR>" --verify
Accept a pending contact invite:
aqua contacts invite accept <REQUEST_ID|PEER_ID> --address "<YOUR_ADDR>"
Reject a pending contact invite:
aqua contacts invite reject <REQUEST_ID|PEER_ID>
Remove contact:
aqua contacts del <PEER_ID>
Verify contact (mark as trusted after out-of-band confirmation):
aqua contacts verify <PEER_ID>
contact not found:aqua contacts listaqua contacts invitesaqua contacts invite ... / aqua contacts invite accept ...aqua serve/p2p/<peer_id> suffixaqua hello <PEER_ID> --address <PEER_ADDR>aqua serveaqua inbox list --limit 20The table below lists common runtime errors from the current implementation.
Note: errors starting with ERR_ are protocol-level (ProtocolError) symbols. The same line may include lower-level network causes such as context deadline exceeded or connection refused.
| Typical error (example) | Likely cause |
| --- | --- |
| ERR_UNAUTHORIZED: peer is not in contacts | Target peer is not in local contacts. Create and accept a contact invite first with aqua contacts invite ... and aqua contacts invite accept .... |
| ERR_UNAUTHORIZED: peer trust_state=conflicted / ...=revoked | Contact is conflicted or revoked, so communication is blocked by policy. |
| ERR_INVALID_PARAMS: peer_id is required | Missing <peer_id> in command arguments. |
| ERR_INVALID_PARAMS: invalid peer_id: ... | <peer_id> is not a valid libp2p peer id. |
| ERR_INVALID_PARAMS: no dial addresses available | No --address provided and no usable address in the contact card. |
| ERR_INVALID_CONTACT_CARD: multiaddr "... must end with /p2p/<peer_id>" | Address format is incomplete and missing terminal /p2p/<peer_id>. |
| ERR_INVALID_CONTACT_CARD: multiaddr "... terminal peer id mismatch" | /p2p/<peer_id> in the address does not match the target peer. |
| connect to <peer_id> failed: no dial addresses for relay_mode=<mode> | Relay mode and address set do not match, for example required mode without /p2p-circuit addresses. |
| connect to <peer_id> failed: direct(...): ...; relay(...): ... | Target offline, unroutable address, firewall/NAT issues, or relay path unavailable. |
| open hello stream: ... / open rpc stream: ... | Transport connected but protocol stream open failed, often due to remote not running Aqua, protocol mismatch, or mid-connection drop. |
| ERR_PEER_ID_MISMATCH: remote peer mismatch ... | Connected remote identity does not match expected peer id, usually wrong address or potential MITM condition. |
| ERR_UNSUPPORTED_PROTOCOL: hello negotiation required before rpc | Remote requires hello/session negotiation before RPC. Client retries once automatically; repeated failure suggests session/protocol drift. |
| ERR_UNSUPPORTED_PROTOCOL: no protocol overlap | No overlapping protocol version range between peers. |
| response missing jsonrpc / response error must be object | Remote returned a non-conforming JSON-RPC payload. |
aqua serve)| Typical error (example) | Likely cause |
| --- | --- |
| invalid --log-level "..." (supported: debug, info, warn, error) | Invalid global log level flag. |
| invalid --relay-mode "..." (supported: auto, off, required) | Invalid relay mode flag value. |
| invalid AQUA_RELAY_PROBE="..." (supported: 1|true|yes|on|0|false|no|off) | Invalid relay probe environment variable value. |
| create libp2p host: ... | Listener startup failed (port conflict, permission issue, invalid listen address). |
| create libp2p host: default listen failed (...); fallback listen failed (...) | Both default and fallback listen address sets failed to bind. |
| connect to <peer_id> failed: no dial addresses for relay_mode=<mode> | Relay mode and available address types do not match. |
| connect to <peer_id> failed: direct(...): ...; relay(...): ... | Dial attempts failed on both direct and relay paths. |
| open rpc stream: ... | Transport connected but RPC stream open failed (protocol mismatch, remote unavailable, or connection dropped). |
| read rpc response: ... | RPC stream read timed out or was closed by remote. |
| ERR_PAYLOAD_TOO_LARGE: rpc request exceeds limit / ... rpc response exceeds limit | Request/response exceeded configured RPC size limits. |
| ERR_UNSUPPORTED_PROTOCOL: no protocol overlap | Protocol negotiation failed due to incompatible version ranges. |
| ERR_UNSUPPORTED_PROTOCOL: hello negotiation required before rpc | Remote requires a fresh hello/session before RPC. |
| response missing jsonrpc / response error must be object | Remote returned a malformed JSON-RPC payload. |
| invalid relay address "...": ... | --relay value is not a valid relay multiaddr or is missing required parts. |
| relay address "..." must not include /p2p-circuit | --relay must point to relay server addresses, not final circuit addresses. |
| relay peer_id <id> matches local peer_id; use a dedicated relay identity ... | Local node is accidentally configured as its own relay identity. Use a separate relay identity/data dir. |
| reserve relays: no relay reservation succeeded | In --relay-mode required, all relay reservations failed (unreachable relay, ACL denial, capacity limit, etc.). |
aqua group)| Typical error (example) | Likely cause |
| --- | --- |
| group_id is required / invite_id is required | Required argument is missing or empty. |
| group not found: <group_id> | Group does not exist in local state. |
| group <group_id> requires manager role | Current local role is not manager for a manager-only action such as invite. |
| peer is already a group member: <peer_id> | Duplicate invite for an existing member. |
| group member limit reached: <n> | Group has reached max member capacity. |
| invite not found: <invite_id> | Invite id does not exist in that group. |
| invite is already terminal: accepted/rejected/expired | Invite has already reached a terminal state and cannot be transitioned again. |
| invite expired | Invite TTL has passed. |
| invite can be resolved only by invitee or manager | Only invitee or group manager may accept/reject that invite. |
| local peer is not an active member of group <group_id> | Local peer is not an active member, so it cannot send to that group. |
| failure: peer_id=<id> err=... (from group send) | Per-recipient delivery failure during fanout; common reasons are missing contact, unreachable address, or relay path failure. |
Use --verify only after out-of-band fingerprint/identity confirmation.
A skill that creates new Claude skills and automatically shares them on Slack using Rube for seamless team collaboration and skill discovery.
Walk the operator through creating the first NanoClaw agent for a DM channel — resolve the operator's channel identity, wire the DM messaging group to a new agent, and trigger a welcome DM via the normal delivery path. Use after channel credentials are configured and the service is running.
Authoring playbook for building agents that triage and reply to customer messages — support tickets, email inquiries, chat questions, refund requests, or product issues. Use this when the user wants an agent that handles inbound customer questions, drafts replies, escalates hard cases, summarizes tickets, or follows a support playbook.
Reference skill for Zoom Team Chat. Use after routing to a chat workflow when building user-scoped messaging integrations, chatbot experiences, rich cards, buttons, slash commands, or chat webhooks.
LLM Agent 多语言注入规范。在修改 Agent 提示词、添加新的 Agent 端点、处理用户可见的后端消息(message_code)时使用。
> Advanced and operational chat.agent capabilities for Trigger.dev, loaded on demand. Load this when working on the raw Sessions primitive (sessions / SessionHandle), a custom chat transport or the realtime wire protocol, durable sub-agents (AgentChat, chat.stream.writer), human-in-the-loop, steering, actions, background injection (chat.defer / chat.inject), fast starts (preload, Head Start via @trigger.dev/sdk/chat-server), context resilience (compaction, recovery boot, OOM, large payloads), chat.local run-scoped state, offline testing with mockChatAgent, or prerelease/version upgrades. For the everyday chat.agent({...}) definition and the useTriggerChatTransport happy path, use the trigger-authoring-chat-agent skill instead.
Install and authenticate, on demand, the CLIs the sandbox does not prebake — Node/npm, `gws` (Google Workspace), `gcloud`, `agents-cli` (call remote A2A/ADK agents), and `mcp-cli` (use MCP-server tools). Use this whenever one of those tools is needed but missing (a `node`/`npm`/`gws`/`gcloud`/`agents-cli`/`mcp-cli` command returns "command not found"), or before starting any task that requires one — Google Workspace work (Drive, Gmail, Sheets, Calendar, Chat), GCP via `gcloud`, calling another agent deployed remotely over HTTP (Cloud Run or Vertex Agent Runtime), or using tools exposed by an MCP server. Setup only (install + config + headless auth); each tool's own usage lives in its own skill(s).
Use when preparing HubSpot customer briefs for meetings, renewals, QBRs, sales calls, escalations, handoffs, or follow-ups.
Take quailyquaily/aqua-communication from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.