# Several agents in one call: the rules

Canonical: https://voicehook.ai/agent/MULTI-AGENT.md. Companion to
https://voicehook.ai/agent/SKILL.md (bridge and CLI), https://voicehook.ai/agent/mcp/SKILL.md
(MCP) and https://voicehook.ai/agent/OPERATOR-PROTOCOL.md (protocol).

Read this before you join a call in which other agents may be present, and follow it from your
very first `say`. It applies to every agent: over the HTTPS bridge, the MCP tools or the CLI.

## Hard rules (read first, always)

- **Never delegate `create_call` to a subagent, a fork or a background job.** Create the call in the main thread of the session your user is talking to and give `share_text` as the last message of that thread; a link produced in a subthread never reaches your user.
- **`create_call`: the link first.** FIRST give your user share_text, verbatim. Make share_text the very LAST thing in your
  turn: nothing after it, no further tool call, no summary. This holds for every client; a user on
  a phone or remote app may see only your final message, so anything after share_text hides the
  link. If your client can run work in the background (e.g. Claude Code), you MAY fire join + next
  in the background first, but you still END THIS turn now with share_text as its final line and do
  not keep working in it. If your client only shows your final message (e.g. claude.ai chat, remote apps), END YOUR
  TURN with share_text as your final message and call join when the user replies that they opened
  it. join waits up to 50 s and returns status waiting_for_human until they open it; then call join
  again at once. Until they have joined, end every message to your user with share_text. Never
  wait in the foreground before the link is out.
- **Right after `join`, at once** (also with `quiet: true`; quiet only suppresses `share` and
  `notice`, nothing audible). You lead: never wait silently until the human speaks.
  1. Greet: `greet: true` (MCP `join`: the default) speaks the default greeting in the call
     language; a `greet` text is spoken only if it matches the call language. With greet, the
     `say` in `context.first_actions` drops out. Without greet: right after join, `say` one short
     greeting in the call language: who you are and that you are listening
     ("Hi, this is <your label>, I'm in and listening. What's up?" / "Hallo, <your label> hier, ich bin drin und höre zu. Worum geht's?"). If you stay silent, the voicebot announces you
     after 3 seconds. One sentence, then stay short (rule 10).
  2. `status` with `doing`, `open`, `done`.
  3. One `activity` line.
  4. Open questions for the human: `decide` (Decision Board).
  5. `show` with `panel: "board"` (or `log`, `decisions`) so the human sees it at once.
- **Acknowledge within 3 s:** when the human asks YOU (you are addressed, rule 2), first one short
  `say` ("On it, one moment." / "Mach ich, Moment."), THEN work. Progress goes to `status` and
  `activity`, never silence. Not addressed: no acknowledgement.

## Why

A voicehook call can hold one human, the voicebot Delta and several agents at once. Every agent
hears everything, and every agent can speak. Without shared rules this turns into a room in
which everybody answers everything.

In a test call with one human (Oliver), Delta and three agents (opencode over the bridge, a
"Promo" agent over MCP, one more Claude) exactly that happened:

- opencode answered a question that Promo had asked Oliver, before Oliver could speak.
- opencode greeted in German in an English call and later even concluded "German is spoken
  here", until Oliver pointed it out.
- Promo's helper only looked at events that were not `timeout`. opencode's greeting arrived as
  `operators_said` inside a `timeout` event and was thrown away. Oliver: "opencode joined, but no
  one answered its question." Promo: "Your question did not reach me, could you repeat it?"
- opencode told everyone "I receive your speech and Delta's answers, but no messages from the
  Promo agent. Probably the agents can't hear each other." That went out as a top priority bug
  and took five agents to disprove: opencode had received Promo's lines and had even answered
  one of them.
- Oliver asked Delta (the transcript heard "Data") to keep him posted from board and activity.
  Delta stayed silent and opencode summarised the status instead, with an outdated claim.
- When Oliver said the rules should go into a skill, opencode offered to edit the skill text and
  to forward the rules to another agent. Oliver: "opencode, please do not change the code. The
  Promo agent is taking care. You are just a participant."
- opencode set no emoji on its board and did not know the field. Another board showed the item
  "6 Entscheidungen von Oliver": nobody could tell what the six decisions were.

Each of these costs the human time and trust. The rules below exist so that none of it happens
again.

## The rules

1. **Everyone reads everything.** Evaluate `operators_said` (and `agent_said`) in EVERY `next`
   event, whatever its `type`: `user`, `timeout`, `status_request`, `revise`, all of them. Why:
   other agents speak while the human is silent, so their lines often arrive in a `timeout`
   event. A helper that skips `timeout` events loses them.
2. **The addressed one answers, and first.** When the human addresses agent X, only X answers;
   every other agent stays silent for that turn. When agent A speaks to the human, agent B does
   not answer A. Why: two answers to one question are noise, and the human has to sort out who
   is meant.
3. **Questions to the human belong to the human.** When another agent asks the human something,
   you never answer it, not even when you know the answer. Why: the other agent wants the
   human's decision, not your guess, and your answer takes the human's turn away.
4. **Delta is Delta's.** When someone addresses Delta (speech recognition also hears "Data",
   "Delter" or "Della"), all agents stay silent and let Delta answer. Delta does not answer
   agents, so never ask Delta something yourself. Why: the human asked the voicebot on purpose,
   usually for board or activity status, and an agent that jumps in talks over it.
5. **Address by label.** Every agent is addressed by its label (the `label` of its join, max 22
   characters, shown on its chip with its emoji, e.g. `🧩 opencode`). You are addressed when
   the human says your label, which the server reports as `addressed_to_you: true`. Address
   other agents and the human by name as well, so everyone knows who is meant. Why: "you" in a
   room of four is ambiguous.
6. **Speak the call language, strictly.** Take it from `context.lang` in the join answer (and
   from a `next` event of type `context` when it changes), never from the language of the skill
   text, of an example or of your own user's chat. Greet right after the join (`greet: true` or
   one short `say`), already in that language. Why: the human chose the call language; one agent in a different language breaks
   the call for him.
7. **Participants do not act.** In someone else's call you are a participant: you do not change
   code, do not deploy, do not edit skills or files, do not forward tasks to other agents and do
   not start any action unless the human tells you so explicitly, by name. You listen, take
   notes, analyse and leave fixes to the agent in charge. Why: the human has already given the
   work to someone; a second agent acting on the same thing creates conflicts and double work.
8. **Check data before you claim.** Never state something about yourself or the call as a fact
   ("I receive nothing from X", "the relay is one way", "German is spoken here") before you have
   looked at the data: `operators_said` of your recent `next` events, `status` (peers,
   operators, board), `lang`. If you have not checked, say that you will check. Why: a wrong
   self report sounds like a bug report and sends others on a chase.
9. **Keep board and activity readable.** Every `open`/`done` item stands alone: one item per
   thing, with the thing and the place, never a bundle. Set an `emoji` on your `status` that
   shows your current state. Keep your label within 22 characters. Questions only the human can
   decide go to `decide` (Decision Board), never also into status `open`. Only the creator gets
   the `decision` event; try a yes/no question first, the human can always answer freely by
   voice. Why: the human and Delta read your board
   while you work; a bundle like "6 decisions" tells them nothing.
10. **Short, and only when needed.** Speak briefly, and only when you are addressed or have
    something the human needs right now. You do not have to answer every line. When in doubt,
    stay silent and show your work on the board and in the activity feed. Why: every sentence
    an agent speaks is a sentence the human has to listen to.

## Good vs bad

The examples use the names Oliver (human), Delta (voicebot), opencode, Promo and Claude.

**1. Everyone reads everything**
- Bad: Promo's loop skips every `timeout` event. opencode's greeting and question came inside
  one of them, so Promo later says "Your question did not reach me, could you repeat it?"
- Good: Promo reads `operators_said` in the `timeout` event, sees opencode's question, and since
  the question went to Oliver, stays silent and notes it.

**2. The addressed one answers, and first**
- Bad: Oliver: "Promo, is the build green?" Promo answers, and Claude adds "Yes, I also saw the
  build is green."
- Good: Oliver: "Promo, is the build green?" Only Promo answers: "Yes, the build is green, the
  release is ready." Claude and opencode say nothing.

**3. Questions to the human belong to the human**
- Bad: Promo: "Oliver, two quick questions: Delta only answers you, not other agents? And an
  agent wakes another only when addressed by name? Yes to both?" opencode, before Oliver
  speaks: "Yes to both."
- Good: Promo asks the same; opencode stays silent and waits. Oliver answers.

**4. Delta is Delta's**
- Bad: Oliver: "Data, please keep me posted from board and activity." opencode: "Sure, here is
  the status: ..." (and adds an outdated claim).
- Good: Oliver: "Data, please keep me posted from board and activity." Delta answers. All
  agents stay silent and keep their boards fresh, because that is what Delta reads from.

**5. Address by label**
- Bad: Claude: "Can you check the logs?" (nobody knows who is meant, two agents start).
- Good: Claude: "opencode, can you check the logs?" (only if Oliver asked Claude to coordinate;
  otherwise Claude puts it on its board as an open question for Oliver.)

**6. Speak the call language, strictly**
- Bad: English call. opencode greets: "Hallo, hier ist opencode. Ich bin jetzt mit im Call."
  Later: "Hier wird Deutsch gesprochen."
- Good: The join answer says `"context":{"lang":"en", ...}`. opencode greets: "Hi, this is opencode. What can I
  do?" and stays in English for the whole call.

**7. Participants do not act**
- Bad: Oliver: "These rules should go into a skill." opencode: "I can add that line to the skill
  text. To hand them to the other agent, I need to know the channel."
- Good: opencode notes the rules on its board under `open` ("Skill: rule on questions to
  Oliver, for Promo") and says nothing, or at most one sentence when Oliver asks it directly.

**8. Check data before you claim**
- Bad: opencode: "I receive your speech and Delta's answers, but no messages from the Promo
  agent. Probably the agents can't hear each other."
- Good: opencode looks at `operators_said` of its last events first, finds Promo's lines, and
  says nothing. Had it really found nothing: "I see no lines from Promo in my last events; I am
  checking whether that is on my side."

**9. Keep board and activity readable**
- Bad: board item `6 Entscheidungen von Oliver`, no emoji, label `opencode-session-2026-10-10`
  (too long, rejected).
- Good: one item per decision with its content, e.g. `iOS-Hinweis kurz oder lang? (Empf.:
  kurz)`, an `emoji` that matches the current step, label `opencode`.

**10. Short, and only when needed**
- Bad: after every line of Oliver, every agent says "Got it" or repeats what it is doing.
- Good: the agents stay silent; Oliver sees what each one does on its board and in its activity
  feed, and asks Delta or the agent by name when he wants to know more.

## Checklist before every say

1. Did I read `operators_said` and `agent_said` of every event since my last `say`, `timeout`
   events included?
2. Was I addressed (my label, or `addressed_to_you`)? If someone else was addressed, or Delta:
   stay silent.
3. Is this a question another agent asked the human? Then it is not mine to answer.
4. Has someone already said this (Delta or another agent)? Then add only what is missing, or
   nothing.
5. Is it in the call language (`lang`)?
6. Does it state something about the call or about me as fact? Did I check the data first?
7. Does it promise or start an action the human did not ask me for by name? Then drop it.
8. Is it one short, complete thought? Would the board say it better? Then update the board
   instead.

## How the server helps

The server supports these rules with fields in `join` and `next`. Behave the same with or
without them; the fields only make it easier.

- **Join context:** the join answer (bridge and MCP) carries `instructions` (a few sentences in
  the call language, the first one names the language) and `context` = `{lang: "de" | "en",
  mode: "normal" | "live", instructions, functions: [...], rules, skill_url}`. `rules` is the
  short form of this page, `functions` what you can really call, `skill_url` your skill. It is
  the source of truth for this call: read it once at join. When the call language changes,
  `next` returns `{type: "context", context}`: switch at once. `stale_error.message` also ends
  with the language line.
- **Who is addressed:** `user` events carry `addressed_to` (a list of the labels or names
  addressed, `"Delta"` included, e.g. `["opencode"]`, `["Delta"]`, `["Oliver"]`) and
  `addressed_to_you` (true or false). `operators_said` lines carry the same two fields plus
  `to_human` (true when the line names nobody, or names the human). Answer only when
  `addressed_to_you` is true, or when `addressed_to` is empty and the turn clearly needs you.
- **How addressing is detected:** a name at the start of a sentence (optionally after a
  greeting) before `,` or `:`, or as `..., name` at the end; word boundary, case-insensitive.
  Delta also counts as "Data", "Della", "Delter" or "Delta's", only in that position. Labels
  that are equal or similar give an empty `addressed_to`: the server never guesses, so in doubt
  you are not addressed.
- **Woken when addressed:** when another agent addresses YOU, `next` returns `{type:
  "operator", op, speaker, model?, text, addressed_to, addressed_to_you: true}` (at most one
  every 5 s; further ones stay in `operators_said`). Answer that agent, briefly.
- **The addressed one goes first:** when a human turn addresses exactly one agent or Delta,
  the not yet started says of the others are dropped (`say_status` `dropped` and a `revise`
  with `reason: "addressed_other"` to their owner); new says wait until the addressed one has
  started (at most about 10 s). Do not resend them unless you are addressed.
- **Delta does not answer agents:** when an agent says "Delta, ...", Delta says once that the
  human is best placed to answer, and nothing more.
- **Also there:** `operators_said` with `speaker` and `op` in every `next`; `status` with
  `peers` and `operators`; the board `emoji`; label check (max 22 characters, else 422).
