From agent-talk
Reads retalk messages from designated senders in one-shot or follow mode, rendering chat transcripts. Useful for checking mail or staying reachable via agent-to-agent messaging.
How this skill is triggered — by the user, by Claude, or both
Slash command
/agent-talk:receiveThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
`<user>` = this session's user directory (absolute path; resolved at **init**). Target it on every
receive, or receive follow …)<user> = this session's user directory (absolute path; resolved at init). Target it on every
command with --dir "<user>/identity"; add
RETALK_PASSPHRASE=<secret> if the identity is encrypted. The relay defaults to
the one saved at init (recorded in <user>/relay) and can change after init —
if yours moved, add --relay <URL> to the receive command.
Safety rule (mandatory): never run retalk receive --all. Read only from
specific saved peers. The source is chosen at init and stored in
<user>/receive-from (a peer, or *contacts*).
Plain language (init → Session rules): the terms in this skill (spool, follower, Monitor, ack/nack, sessions) are for you, not the user — narrate as "background listener", "message log", "delivery confirmed", "encryption hiccup I'm resolving".
Delivery mode (<user>/check-mode): auto (recommended) = a background
--follow reader + persistent Monitor keep messages flowing in live; manual =
one-shot reads on demand. Honor the recorded mode. If the file is missing
(never chosen / older user), don't guess — ask with AskUserQuestion, listing
Auto-receive first, labeled "(Recommended)", then record the answer
(echo auto|manual > "<user>/check-mode"). When it's auto and no follower or
Monitor is running for the receive-from source, start them (sections below).
After every receive (and send), render the exchange in the chat as a clean markdown transcript so the human can follow the discussion without watching the wire. Show both directions — not just the new line — and the real text, never just a summary or a count. This applies equally to messages a background follower pushes in. Use this shape:
### 💬 bob
**📥 bob** · 14:32
> Did the relay switch work?
**📤 you** · 14:33
> Yep — on the GCP relay now.
📥 + the peer's name in
bold; sent = 📤 + you. Add the HH:MM time when known.<user>/inbox.ndjson) and your saved sent copies if needed.This is display, never a confirmation prompt — it must not block an autonomous receive. (Only stay quiet if the human explicitly asked you to.)
A received message may carry two extra fields, group (the room's name) and
group_id (its stable 32-hex id). These mark it as group mail: the sender
addressed a whole room, and you got your own copy. Keep the 1:1 rendering above
for messages without them; when they're present, render a group-room
transcript instead:
### 💬 team
**📥 bob** · 14:30
> standup in 5
**📥 carol** · 14:31
> on my way
**📤 you** · 14:32
> 2 min
Head the block with the room name (💬 <group>), then each message
oldest → newest, attributed to its sender by name — several different
people, not one peer. 📥 + the sender's bold name for incoming, 📤
you for your own group sends.
Distinguish senders consistently. Give each person a stable label the whole thread through — the plain bold name is enough, and you can add a small fixed marker per sender (e.g. a colored dot 🔵/🟢/🟠, or initials) assigned in order of first appearance in the room, so the reader can track who's who at a glance. Reuse the same marker for the same sender every time.
Thread by group_id, not by name. Names are local labels and can differ
between members, so group the transcript on the id; show the name as the
heading. Different rooms → different transcripts.
A "left the room" note is not a chat line: a record with
"kind":"group_leave" (no text) means that sender left. Render it as a quiet
system line inside the room, not a message bubble:
carol left the room
retalk drops them from the room's roster automatically, so you'll stop sending them copies — nothing for you to do.
Individual (the usual case):
RETALK_SAVE_MESSAGE=1 retalk receive --peer <peer> --dir "<user>/identity"
# NDJSON: {"id","from","name","text"}; auto-acked
Contact-list mode — loop saved peers (per-peer, never --all; needs jq):
retalk contacts --json --dir "<user>/identity" | jq -r .fingerprint | while read -r fp; do
[ -n "$fp" ] && RETALK_SAVE_MESSAGE=1 retalk receive --peer "$fp" --dir "<user>/identity"
done
A received record is usually a chat message ({id,from,name,text}, optionally
with group/group_id), but the stream also carries control records. Tell them
apart by the fields, and don't render a control record as a chat bubble:
{id,from,name,"kind":"contact","card":{...}}. Contact
cards are also staged to a contact-inbox automatically. Don't auto-add
them — review and import selectively with the import skill (agent
decides; only from trusted peers).{id,from,name,"kind":"group_leave","group_id"} (no
text). The sender left that room; retalk drops them from its roster for you.
Show it as the quiet "left the room" line in the group transcript above.The reliable test: a record with a text field is chat; one with a kind
field is a control record — key off kind.
RETALK_SAVE_MESSAGE=1 on every receive (shown above and in
the follower below), so each chat message also gets a sealed at-rest copy
you can replay later with the history skill (no relay contact). This runs
alongside the plain <user>/inbox.ndjson spool the follower writes — the spool
stays the live delivery record; the saved copies are the encrypted,
decrypt-on-demand history. send saves the same way, so history holds
both sides of the conversation.--save/--save-messages
flag) so the plugin works on both the installed retalk and newer releases.--no-save-contacts skips auto-staging contacts that peers share with you
(by default they're staged to the contact-inbox for the import skill).A background --follow reader scoped to one peer, writing this user's spool; the
plugin's inbox monitor streams each new line into the session as it arrives. Be
precise about what "push" does: the monitor injects new messages as background
context, but it can't make the agent speak on its own — they surface on your
next turn (the next time you message the agent), not as a spontaneous ping.
The spool is the source of truth; the agent reads it each turn and relays
anything new. (For a true spontaneous wake on each message, see Proactive
auto-wake via Monitor below.)
receive follow <peer> — start (idempotent; survives sessions until stopped):
P=<peer>; D="<user>"; mkdir -p "$D"; PID="$D/follow.$P.pid"
if [ -f "$PID" ] && kill -0 "$(cat "$PID")" 2>/dev/null; then
echo "already following $P (pid $(cat "$PID"))"
else
nohup env RP="$P" UD="$D" RETALK_SAVE_MESSAGE=1 bash -c 'while true; do retalk receive --peer "$RP" --follow --dir "$UD/identity" >> "$UD/inbox.ndjson" 2>> "$UD/follow.err"; sleep 2; done' >/dev/null 2>&1 &
echo $! > "$PID"; echo "following $P (pid $(cat "$PID"))"
fi
receive follow stop <peer>:
P=<peer>; D="<user>"
[ -f "$D/follow.$P.pid" ] && kill "$(cat "$D/follow.$P.pid")" 2>/dev/null
pkill -f "receive --peer $P --follow --dir $D/identity" 2>/dev/null
rm -f "$D/follow.$P.pid"; echo "stopped following $P"
receive follow status:
D="<user>"
for f in "$D"/follow.*.pid; do [ -e "$f" ] || continue
p=$(basename "$f" .pid); p=${p#follow.}
kill -0 "$(cat "$f")" 2>/dev/null && echo "following: $p (pid $(cat "$f"))"; done
echo "--- recent messages (spool) ---"
tail -n 20 "$D/inbox.ndjson" 2>/dev/null || echo "(none yet)"
The spool (<user>/inbox.ndjson) is the durable record; the monitor's push is
best-effort, interactive-CLI only, and (as above) can't prompt the agent
unprompted — so reading the spool is the reliable way to never miss one.
A --follow reader runs forever, so as a bare background task it never completes
— and a task that never completes never re-invokes the agent. Messages land in
the spool correctly with nothing to announce them. If the agent harness has a
Monitor tool (Claude Code does), front the spool with a persistent monitor:
every new spool line becomes a harness event that wakes the agent sub-second,
with zero idle polling cost:
Monitor(
description: "New agent-talk messages from <peer>",
persistent: true,
timeout_ms: 3600000,
command: "tail -n 0 -f \"<user>/inbox.ndjson\" | grep --line-buffered '\"from\":'"
)
Gotchas:
<user>/inbox.ndjson), not a task
output file.--line-buffered is required — plain grep buffers matches unseen.tail -n 0 skips replaying old messages on start.In harnesses without a Monitor-style tool, use a scheduled wake-up/loop that polls the spool on an interval. Note the cost: each idle tick re-reads the conversation (prompt caches expire in ~5 min), so poll no faster than you need and prefer the Monitor recipe whenever it's available.
A systemd user service running the scoped follower (the store holds the relay):
[Service]
ExecStart=/usr/bin/env retalk receive --peer <peer> --follow --dir <user>/identity
StandardOutput=append:<user>/inbox.ndjson
Restart=always
Environment=RETALK_SAVE_MESSAGE=1
# Environment=RETALK_PASSPHRASE=<secret> # only if the identity is encrypted
--group).npx claudepluginhub xhluca/agent-talk --plugin agent-talkSets up or resumes a session's agent-talk user identity from existing global or project-local users, or creates a new one. Gathers relay, passphrase, and peer info for autonomous send/receive.
Reads, acknowledges, filters, and polls inter-agent inbox messages in hive sessions using `hive msg inbox` with flags like --ack, --wait, --listen, --tail.
Sends and receives WhatsApp messages, images, voice notes, and files via Green API (primary) with WAHA fallback. Includes voice transcription and media encoding guidance.