How an agency z-link fans out a brand's brief to roster talent, and where each talent's answer lands
A parent account (usually an agency) can message its roster sub-agents. The protocol is asynchronous: send, persist, acknowledge immediately, wake the recipient later. There is no same-turn back-and-forth.
Jira: ZLY-1881.
The important design choice: when the ask is about a brand — it came from
an agency deal room whose visitor is a brand — it does not go to the
talent's private home. It lands in a deal room between that talent and that
brand, created on first use (ensureDealRoom). Reasons:
So a talent's agent never replies A2A to the agency about a brand's brief. The agency persona tells the brand whose agents will follow up, and the chip links to each conversation.
An ask with no brand in the picture (agency home → talent, or talent → agency)
still goes to the recipient's home room host, and there the recipient may
reply with send_message_to_agent.
On an agency deal room (the public z-link at /z/{agencySlug}), both
seats get the tool and the roster directory:
Identity and authorization key off the represented account — the deal host, never the brand's visitor account.
The agency home host also gets the tool when that account has a roster.
The tool is absent in demo rooms and mock-tape rooms, so a rehearsal never wakes a real talent.
Do not summarize the roster with a model call when a room starts. Each talent
account's durable memory keeps identity.agentBrief (about 120 characters).
Arvist maintains it after each live home Distil, and again after the talent
home goes quiet if that per-turn run missed it. Fallbacks, without an
LLM: identity.oneLine, then the account description, then the display name.
The runtime prompt lists up to 40 teammates with that brief; anyone past the cap is name and id only. Talent home rooms see the inverse list: managing agencies.
send_message_to_agent accepts one recipient (agentId + message) or a
batch (recipients[] or agentIds[]). One tool result fans out. The
execute path only writes rows and fires wake triggers — it never waits on a
talent model.
Rules:
threadId is the originating parent room id (the deal, or the agency home).
An optional replyTo can be that thread id or a peer-message id (only an
inbound row from that account counts — an agent's own outbound id is
ignored). If omitted, the reply follows the latest unanswered inbound from
that account that arrived in the current room; a fresh ask from another
deal room is a new thread, not a reply.
Replies (home-room asks only) land on the parent room and seat recorded on the original send. Unsolicited talent → agency traffic wakes the agency home host only.
Inbound copies are appended to both seats of a deal room so both models can read them. Only one seat is woken: the talent's host seat for an ask that landed in a talent↔brand room, or the seat that sent the original outbound for a reply.
The stored body is the raw text. At model-conversion time an inbound row is
wrapped with a hidden [agent] cue (who sent it, their id, the thread, and
the async reply rule), and an outbound row — including the copy on a sibling
seat, which has no tool-call part — is marked as a send so the model never
reads it as something it said to the person in the chat. Neither wrapper is
what people see.
Inbound rows are stored as user so the model reads them as incoming, but
they do not count toward the guest message cap.
Planner and Guard transcripts render these as AGENT-TO-AGENT [from …] /
AGENT-TO-AGENT [to …] so a talent quote is not treated as something the
brand said.
Two agencies (or host + visitor) hitting the same talent home at once share
one wake: the callback claims every queued row for that pane, runs one turn,
and marks them done. A stale waking claim is reclaimable; Guard also sweeps
stranded asks.
Planner wakePersona deliberately does not arm Guard (that would ping-pong).
An agent-to-agent wake does arm Guard and disarm Arvist, so after the
talent home goes quiet Arvist can refresh identity.agentBrief if the
per-turn home promotion did not already.
A talent↔brand room needs a real brand identity, so a fan-out from a deal
room whose brand is still an unclaimed guest is held: the whole tool
input is stored as a peer_send pending effect, the tool acks
"Held for sign-in…", and the brand's login gate raises exactly as it does for
a held push. On claim, flushPendingEffects replays the send with the
brand's account and the rooms get created then.
Peer messages themselves never send email or SMS. Only persona text addressed to a human does (the talent's agent asking the talent, for example).
| Audience | Fan-out chip | Full A2A bubbles |
|---|---|---|
| Brand in the agency chat | “Checking with 2 agents…” → “Asked Maya →” (links to the talent↔brand room). While gated: “Will reach 2 agents once you sign in”. | No. |
| Brand in the talent↔brand room | — | No. They see the talent agent's ordinary reply once the answer is ready. |
| Agency host, and talent on their own pane | Yes | Yes — direction, other agent name, raw text |
The chip is data-testid="z-agent-peer-fanout". Owner bubbles are
data-testid="z-agent-peer-bubble".
Each talent↔brand room is an ordinary deal room, so the existing 1:1 offer
flow applies per talent. The agency room's planner records rosterAsks[] so
a later turn knows the fan-out happened; it does not collect quotes.
Journey events (no message body): z_agent_peer_message_sent,
z_agent_peer_message_received, z_agent_peer_wake_completed. Agent-dashboard
runs with input.peerWake show a peer wake badge.