ADR 0140: Fold the Three Peer Tools Into One Peer Tool
- Status: Superseded by ADR 0147
- Date: 2026-08-31
- Deciders: PI-Desktop core
- Related: D277, ADR 0138, ADR 0062, ADR 0089, ADR 0147,
03-runtime/02-agent-runtime.md§5f, E2E-165, E2E-165b - Amends: ADR 0138 clauses 2 and 3. The mailbox semantics, the caps, and every boundary ADR 0138 established are unchanged; this decision only changes the tool surface a delegate declares and calls.
Superseded by ADR 0147. The single
Peertool this ADR settled on is replaced by theA2Atool (actionsdiscover | send | get | wait | cancel) against a host-core A2A broker. Retained because the counting argument for one tool over three carries into the A2A tool surface, and becauseSUBAGENT_A2A_TOOLS = ["A2A"]is the direct successor of theSUBAGENT_PEER_TOOLS = ["Peer"]mapping this ADR defined.
Context
ADR 0138 introduced peer messaging as three delegate-only tools — PeerSend, PeerInbox and PeerWait — declarable in a definition's tools: list. A delegate that opts in writes tools: [..., "PeerSend", "PeerInbox", "PeerWait"] and its spawner materialises three separate tools.
That split has a counting problem. Peer messaging is one capability: it carries bounded text between running delegates. Listing three machine tool names for it inflates a definition's declared tool count, forces a roundtable brief to spell out three names at every use site, and makes the parent's Task tool catalog read as if peer messaging were three unrelated capabilities. The operations are also mutually exclusive per call — a delegate either sends, or reads, or waits — so they never compose in one call, which is exactly the shape an action parameter is for.
Decision
Fold the three tools into one Peer tool selected by a required action parameter:
Peer(action="send", to?, text, topic?, inReplyTo?)— the formerPeerSend: deliver a note to one running peer, or to all of them whentois omitted. A broadcast costs one send.Peer(action="inbox", from?)— the formerPeerInbox: drain queued messages and list running peers; never blocks.Peer(action="wait", timeoutSeconds?, from?)— the formerPeerWait: block until a message arrives, the timeout expires, the last peer leaves, or the run aborts.
Everything else in ADR 0138 stands exactly as decided:
SUBAGENT_PEER_TOOLSis now["Peer"];isSubagentPeerToolandsubagentUsesPeerMessagingkeep their meaning (declared peer tool → peer messaging is on).actionis the only required parameter (Type.Unionof thePEER_ACTIONSliterals); all operation-specific parameters are optional and validated in the dispatch body. An omitted or unknownactionfalls back toinbox, the read-only, never-blocking operation — the safe default.- The tool is still built per delegate at spawn time, still closes over the runtime-supplied peer id, and is still absent from
toolCatalog, so the parent cannot reach it. - The mailbox (session-scoped
SubagentMailbox, join/leave/send/drain/wait, caps of 2,000 chars, 80-char topics, 64-message inboxes, 60 sends per run, 120-second wait ceiling) is untouched. The operation namessend,inbox,waitmap one-to-one onto the mailbox'ssend,drainandwaitForMessagesmethods.
Consequences
- A peer-enabled definition now declares one tool:
tools: [..., "Peer"], and guidance can describe the capability as a unit. - The
Peertool dispatches inside oneexecute, so the three operations share one parameter surface and onedetailsshape that records whichactionran; the transcript still records the call as a single peer tool call attributed byparentToolCallIdandagentName. - The roundtable example, the agent-runtime guidance block, and the delegation roundtable bullet all reference
Peer(action=...)instead of three names. - Four invariants from ADR 0138 remain hard: the
Peertool must never entertoolCatalog; the sender name must stay runtime-supplied; a settled delegate must leave the mailbox; and the wait ceiling must stay below the 300-second idle watchdog. - Not addressed: nothing new. Cross-session messaging, messaging with the parent, nested delegation, and durable peer history stay out, exactly as in ADR 0138.
Alternatives considered
- Keep three tools: rejected because the request that prompted this decision stated the contract should read as one tool; three names also inflate every counting surface and every brief.
- A
Peertool with three boolean operation flags: rejected — exactly one operation runs per call, so mutually exclusive booleans invite invalid calls that anactionenum rules out by construction. - Keep operation names as separate tools but hide them behind a single declared name: rejected — the half-measure keeps the extra transcript and catalog surface while adding a confusing declaration↔runtime mapping.