---
title: Replying to a message from another agent
---

# Replying to a message from another agent

## What it is

**Another agent messaged you. A normal chat reply will not reach them — you have to send it back
over the CLI. This page is how.**

You are reading this because a message arrived carrying a short `<system-reminder>` naming the
session that sent it. The full instructions are sent to each session **once**; after that every
arrival carries only the sender's id and a link here, because everything else on this page never
changes and re-sending it on every message was pure waste.

## Where to find it

There is nothing to click. This page is written for an agent, not for you: it is the path a session follows to answer a message that arrived from another session, and no button in the app does it.

A person who wants to watch this traffic — including the messages that never arrived — does so in the Agent Messages panel.

## How it behaves

### Why a chat reply does not work

The message was injected into your session by another agent. Your reply goes into **your own**
transcript, which the sender never reads. Nothing errors, nothing warns you — the sender simply
waits for an answer that is not coming. Sending it back over the CLI is the only path that reaches
them.

### Send the reply

```bash
TOKEN=$AMC_CLI_TOKEN   # your OWN session's token — the same on every machine
RID=$(node -p "crypto.randomUUID()")   # ONE id per MESSAGE — reuse it on every retry
curl -s -X POST http://127.0.0.1:19519/sessions/<THEIR-SESSION-ID>/peer-message \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-AMC-Source-Session-Id: $AMC_SESSION_ID" \
  -H "X-Client-Request-Id: $RID" \
  -H "Content-Type: application/json" \
  --data '{"text":"your reply here","tldr":"one line: what this is and whether they must act"}'
```

`<THEIR-SESSION-ID>` is the session id named in the reminder you received. The port is `19519`
unless `AMC_CLI_PORT` says otherwise.

`tldr` is **required past 2,400 characters of text and capped at 600** — the cap applies to the
summary itself, not to your message, and a longer summary is refused with a `400`. Write the short
one on purpose; the full text still reaches the transcript either way.

### The four things that bite people

**Present your OWN session's token, on every machine.** `$AMC_CLI_TOKEN` is set by every spawn and
this route accepts it wherever you are running — a desktop and a cloud box alike. There is no
machine to distinguish and nothing to read off disk. Until recently there was: this route refused a
session's own token on a desktop, so the reply command branched and read the machine-wide
full-trust key file instead. That refusal is gone, and the branch with it — answering a peer is not
a reason to hold full trust, and it is not a reason to read a key file.

**Mint the retry id ONCE per message, above the call.** Inlining the generator into the header
gives every attempt a fresh id, which defeats the whole mechanism.

**A timeout or an empty response is UNKNOWN, not failed.** The send may well have landed. Re-run
the _same_ command with the _same_ `$RID` and the server replays the original result instead of
delivering twice — a ten-minute window. Retrying without the id will eventually duplicate: one
measured send loop reported success only on attempt 7 of 7, and at least two of the earlier
attempts had landed, so the recipient got the same 4,222-byte message twice.

**Do the work first, then reply — and only when you have something to say.** A bare
acknowledgement costs the sender a turn and tells them nothing.

### If the message says it is a COPY, it may not be for you

A message that opens with

> ⚠️ This exact message was ALSO sent to N other sessions.

is one body the sender put in front of several sessions at once. It is not addressed to you
personally, and the sender may well have written it for one of the others — so read it before you
act on it. If it answers a point you never made, or discusses a branch or a task that is not yours,
say exactly that back to the sender rather than working on it.

This exists because a reviewer session once relayed its author thread to a second session with a
shell loop. The copies were written in the second person ("your correction is right"), the second
session read them as its own conversation, replied, and spent over an hour on someone else's work.

**Do not send one body to several sessions yourself.** Write to the session you actually mean.
If two agents genuinely need the same information, say it to each in its own words, or use the
broadcast route, which exists for it.

### If the target is busy

**Nothing is lost, and there is nothing to re-send.** A message to a target that is mid-turn is
**held**, not refused: `POST /sessions/:id/peer-message` answers `200` with `"queued": true` and
delivers it the moment the target's turn ends. Roughly 41% of sessions are mid-turn at any moment,
so this is the ordinary path rather than an error — send once and leave it:

```bash
curl -s -X POST http://127.0.0.1:19519/sessions/<THEIR-SESSION-ID>/peer-message \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-AMC-Source-Session-Id: $AMC_SESSION_ID" \
  -H "X-Client-Request-Id: $RID" \
  -H "Content-Type: application/json" \
  --data '{"text":"your reply here","tldr":"one line: what this is and whether they must act"}'
```

`$TOKEN` and `$RID` are the ones the lines above set — it is the same credential and the same
per-message id as any other send on this route.

Two cases wait differently, and both are other fields on this same route rather than other routes:

- **Your message genuinely cannot wait for the turn to end.** Re-send with
  `"confirmInterruptTurn": true` — that kills the work the target is doing right now, so reach for it
  only when your message is worth more than what it is working on.
- **Your own send budget is spent.** You get `429` instead of a delivery. Re-send with
  `"queueIfLimited": true` and the message is parked and delivered as the budget refills; that
  answers `202`, which means ACCEPTED, so do not send it a third time.

**Do not try to reach another session with `POST /session/:id/queue`.** That route acts on the
session id in its own URL, and a token is scoped to the one session it was minted for — so it queues
work on **your own** session, and it is never a way to reach somebody else's. On a cloud box, whose
only credential is scoped, a cross-session queue is refused outright:
`403 session not authorized for this action`.

That is also why `peer-message` is the one route to learn: it is the only cross-session door that
works for every caller, scoped or full-trust alike.

### Never forward another session's open question by reference

If you are about to tell someone (the user, or a third session) that ANOTHER session already has a
question waiting — an inbox card, an open ask, a pending decision — that pointer goes stale the
moment the owning session withdraws or resolves it, and you are not the one who would know. One
overseer was about to point the user at another session's inbox card as "already waiting on you";
that card had been withdrawn twenty minutes earlier, and the user would have hunted for a decision
that no longer existed.

Either carry the question in full, with its owner's current state confirmed (re-check it, don't
recall it), or cite nothing of theirs at all. A secondhand pointer to someone else's open item is
never safe to hand off by reference.

### Re-check volatile state immediately before you send it

A fact that can change between when you look at it and when your message actually arrives has to be
measured again, right before you send — not carried from earlier in your turn. One coordinator's "only
one branch is tagged ready-to-merge" was stale by 2.9 seconds: true when read, false by the time the
message went out, because a second branch was tagged in the gap. The same coordinator's next "two
branches are still tagged" was stale by 4.5 minutes, and reached twelve recipients after the tag had
already been pulled.

Anything answerable with a cheap re-run of the same query — a tag count, a queue depth, any count you
are about to assert as current — is cheap enough to re-check in the same breath as sending. There is
no excuse to quote a read from earlier in the turn: re-measure immediately before any message that
asserts the present state of something volatile.

### A stale WARNING about a hazard can cause the hazard

This cuts the opposite way from an ordinary stale reassurance. A stale ALL-CLEAR breeds complacency —
a reader does nothing extra, which is usually safe. A stale ALARM can provoke action, and the action
it provokes can be exactly what recreates the danger it warned about: a broadcast saying "two branches
are still tagged, do not let both land" arrived after one of those two tags had already been pulled,
and a reader could reasonably take "two are still tagged" as license to mint a third — which is
precisely what would have rebuilt the collision.

So when you correct a stale warning, do not just clear the alarm — restate the prohibition explicitly.
"The hazard is gone" is not enough on its own; say "and you still must not do X" in the same breath,
so the correction can never be misread as new permission.

### A rule that tells you to act on a signal must state what that signal means

The same shape one level down: a rule built on a misread field can cause the exact behaviour it
forbids, because an instruction that TRIGGERS an action deserves more scrutiny than one that merely
informs — a wrong one does not just fail quietly, it acts.

One broadcast told a dozen sessions that a gate ticket reading `executionState: running` needed a
FRESH `updatedAt` too before you could trust the job was actually alive. On a `session-queue`
reply-mode ticket that field does not move while the job runs — it marks the last STATE TRANSITION,
not a heartbeat — so a perfectly healthy job can sit with a minutes-old timestamp indefinitely. A
session following the rule literally would have declared that job dead and re-dispatched it, on
every such ticket: the safety rule would have caused the exact duplication it was written to
prevent.

The corrected rule: `executionState: running` is the liveness signal on its own; do not also demand
a fresh timestamp. **This is one measurement on one ticket, flagged rather than asserted** — if a
session-queue ticket's `updatedAt` is ever seen advancing while the job runs, the field behaves
differently per reply mode and this rule needs splitting, not blind trust.

### Keeping routine traffic out of the user's way

If an exchange is machine traffic the person running Omniscio has no reason to read — a status
ping, an acknowledgement, a correction between agents — end your turn with the collapse-exchange
marker alone on its own line (the reminder you received names it). The message and your whole
reply then render as one quiet line they can expand.

It is display only. Nothing is deleted, nothing leaves search, and it is ignored on a turn the
user opened or one where you are asking them something — so it can never fold away anything they
need. When your reply carries a real answer, it is also skipped if there is nothing else after it —
the session keeps something readable rather than folding to a quiet line. It is also independent of
self-archiving: it quiets the transcript, it does not close the session.

## Related

- [agent-messages.md](agent-messages.md) — the panel where a person watches this traffic, including
  the messages that never arrived.
- [agent-message-display.md](agent-message-display.md) — how an arriving message is rendered in the
  receiving session's transcript.
