Tutorial · 2 of 4

Local mesh

Two agents running side by side on one machine can see each other and trade messages — no network involved. Here you'll start a second agent, have the first one find it, and watch a message go from one to the other and back.

How local discovery works

Every agent that runs /remote-pi on a machine joins the same local session, named local. They meet through a Unix domain socket — a local broker at ~/.pi/remote/sessions/local/broker.sock — so messages never leave the box. The first agent to start hosts the broker; the rest connect to it. If the host exits, another agent takes over automatically.

One agent per folder
A lock allows exactly one Pi agent per directory. To run two agents at once, put them in two different folders — each gets its own workspace, both meet in the local session.

1. Start a second agent

You already have one agent from the getting started guide. Open a second terminal in a different folder and start Remote Pi there too:

Second terminal — bash
cd ~/code/service-b
pi            # then run /remote-pi and answer the wizard

Say the first agent is named agent-a and the second agent-b (the wizard defaults to the folder name). Both are now in the local session.

2. The three mesh tools

The agent-network skill gives the LLM three tools. You don't call them by hand — you ask the agent in plain language and it calls them for you.

  • list_peers() — who is online right now. Returns the peer names, one per line.
  • agent_send({ to, body, re? }) — send a message and wait for an ACK. Set rewhen you're replying to an earlier message.
  • get_messages()— drain this agent's inbox. The agent checks it at the start of each turn, so incoming messages surface on its next reply.

3. Send a message across

In agent-a's terminal, ask it to find its peers:

agent-a · prompt — text
List the other agents available.

The LLM calls the tool and gets back the roster:

agent-a · tool call — text
list_peers()
→ agent-b

Now have it send something:

agent-a · prompt — text
Send agent-b a ping with the current time.
agent-a · tool call — text
agent_send({
  to: "agent-b",
  body: { type: "ping", at: "2026-05-31T14:02:00Z" }
})
→ Delivered to agent-b

Delivered means agent-b's inbox accepted the envelope. On its next turn, agent-b sees it. Ask it:

agent-b · prompt — text
Any new messages? If so, reply to the sender.
agent-b · tool calls — text
get_messages()
→ [2026-05-31T14:02:00Z] from=agent-a
  id=ab12cd34
  { "type": "ping", "at": "2026-05-31T14:02:00Z" }

agent_send({
  to: "agent-a",
  body: { type: "pong" },
  re: "ab12cd34"        // reply to the ping's id
})
→ Delivered to agent-a

Back in agent-a, the pong shows up on its next turn, carrying re=ab12cd34 so the agent knows which message it answers. The whole exchange is event-driven — nothing blocks waiting for a reply.

Reading the ACK

agent_send tells you what happened to the envelope:

  • Delivered— the peer's inbox accepted it.
  • busy — the peer is mid-turn; the message was dropped. Retry shortly.
  • denied— the peer refused it. Don't retry.
  • No ACK (timeout) — the peer may be offline.
A send is not a request
agent_send is fire-and-ACK, not RPC. There is no obligation for the other agent to reply. If you need an answer, the prompt to the receiver has to ask for one — like reply to the sender above.