Short summary
Cross-session messages are delivered only at turn boundaries, one message per turn, with no queued-vs-read visibility and no interrupt path for stop/control messages.
Affected version or release
GitHub v1.1.2, Copilot CLI 1.0.73 (desktop app, multi-session workspace).
Installation context
Local desktop app on Windows, running several concurrent project sessions in one workspace that coordinate with each other through cross-session messaging, including a central relay session. Not specific to any one repository or workflow.
What happened?
When one session sends a message to another session that is mid-turn, the message is not surfaced until the receiving session's current turn ends. The send returns success immediately, so the sender cannot distinguish "delivered and read" from "sitting in a queue behind a long-running turn."
Worse, the queue does not drain in bulk. At the end of a turn the recipient receives one queued message. If handling that message starts another long turn, every remaining message waits a further full turn. Latency therefore compounds per message rather than being paid once. With long turns (builds, test suites, multi-step tool loops), a message sitting fifth in line can wait for five consecutive turns.
This is not just a latency annoyance. It changes correctness for control messages and produces cascading noise.
Current behavior, step by step:
- Sender calls the cross-session message tool; the call succeeds.
- If the recipient is idle, it wakes and reads promptly.
- If the recipient is mid-turn, the message waits until that turn completes.
- At turn end the recipient surfaces a single queued message, not the full backlog.
- Handling that message typically begins a new turn, during which the remaining backlog continues to wait.
- Neither side gets any signal that queuing occurred, how deep the backlog is, or how long anything has waited.
Why it matters
1. Stop and abort orders don't take effect when issued. The single most important message in a multi-session setup is "stop." Today a stop can sit in a queue while the recipient continues doing exactly the thing it was told to stop. Sessions keep acting on intent the operator has already revoked, and the operator has no way to see that their instruction hasn't landed. Illustrative example: an operator halts all work; some sessions honor it within seconds, others continue for many minutes because the halt is queued behind an in-flight turn. From the outside this is indistinguishable from a session ignoring the order.
2. One-per-turn delivery makes backlogs self-perpetuating. Because each turn releases only one message, a busy session can never catch up while it stays busy — and each released message is itself likely to start the next long turn. A stop message queued behind three routine notices doesn't wait for one turn; it waits for three, each potentially minutes long. The more responsive the session is to each individual message, the longer the rest of the queue waits. A queue can therefore grow faster than it drains under steady inbound traffic, with no backpressure signal to senders.
3. Delayed reads produce stale, contradictory reports. A session that finally reads a queued message answers using state it observed before its earlier turns began. Its reply is internally consistent and confidently worded, but describes a world that has since changed. Recipients cannot tell "this session is wrong" from "this session is late." Per-message compounding makes this sharper: two messages sent seconds apart can be answered many minutes apart, against different underlying states. Illustrative example: two sessions report opposite states of the same resource within minutes of each other; both read correctly, one just read earlier and replied later.
4. Silence looks like failure, so senders retry — creating a duplicate storm. Because there's no queued indicator, an unanswered message reads as a lost message. Senders re-send. In practice the same request can arrive several times in a short window, sometimes with drifted details or even flipped recommendations between copies, because each copy was composed at a different moment. Retries make the compounding worse: each duplicate occupies its own slot in a one-per-turn queue, pushing genuinely new messages further back. The receiving side then has to do provenance work to determine which copy, if any, reflects current intent.
5. You cannot cheaply check whether a message landed. Delivery itself wakes the recipient, so "did you get it?" is not a free observation — it's an intervention that starts work, and it also consumes a queue slot. This removes the obvious workaround and makes queue depth genuinely unobservable.
6. Human-facing latency. When a session's role is to relay questions and decisions to a person, queuing inserts the same delay into the human loop — multiplied by backlog depth. Time-sensitive approvals age through several turns, and by the time they surface the underlying state may have moved on.
Steps to reproduce
- Start session A and session B.
- Give B a task with a long single turn (for example, a multi-minute build or test run).
- While B is mid-turn, send B three distinguishable messages from A.
- Observe: all three sends succeed immediately; B surfaces none of them during the turn.
- Observe at turn end: B receives only the first message, not all three.
- Let B's handling of that message run as another long turn.
- Observe: the second message is not surfaced until that turn also ends, and the third waits for the one after. Total wait for the third message is the sum of all preceding turns.
- Throughout, nothing in A's view indicates that any message was queued, or how many were outstanding.
Expected behavior
- The sender can tell that a message is queued versus consumed, and how many are outstanding.
- Control-class messages (stop / abort / pause) can interrupt rather than wait behind unrelated work.
- Long-running turns can observe inbound messages at safe checkpoints, not only at turn end.
- A backlog can be surfaced together, so the recipient sees current intent as a set rather than one stale fragment at a time.
Additional context
Suggested directions, roughly in priority order; any one of these would help materially.
- Delivery vs. consumption signal, with queue depth. Distinguish "accepted for delivery" from "read by the recipient," and expose outstanding count and queued-since timestamps to the sender. This alone would eliminate most retry duplication.
- Priority / interrupt class for control messages. A stop, abort, or pause should reach a busy session without waiting for the current turn — and without waiting behind unrelated queued messages. Even a best-effort cancellation signal checked between tool calls would be a large improvement.
- Batch drain. Surface the full pending backlog at a turn boundary rather than one message per turn, so the recipient can reconcile the set and act on the newest intent instead of the oldest fragment.
- Mid-turn checkpoints. Allow a long turn to observe pending inbound messages at safe points (for example, between tool calls) so the recipient can fold in new information rather than finishing on stale intent.
- Automatic dedup or coalescing of repeated messages from the same sender while an earlier one is still queued.
- Staleness marking on delivery. Annotate how long each message waited, and how many remain, so the recipient can weigh it against work done in the meantime.
Short summary
Cross-session messages are delivered only at turn boundaries, one message per turn, with no queued-vs-read visibility and no interrupt path for stop/control messages.
Affected version or release
GitHub v1.1.2, Copilot CLI 1.0.73 (desktop app, multi-session workspace).
Installation context
Local desktop app on Windows, running several concurrent project sessions in one workspace that coordinate with each other through cross-session messaging, including a central relay session. Not specific to any one repository or workflow.
What happened?
When one session sends a message to another session that is mid-turn, the message is not surfaced until the receiving session's current turn ends. The send returns success immediately, so the sender cannot distinguish "delivered and read" from "sitting in a queue behind a long-running turn."
Worse, the queue does not drain in bulk. At the end of a turn the recipient receives one queued message. If handling that message starts another long turn, every remaining message waits a further full turn. Latency therefore compounds per message rather than being paid once. With long turns (builds, test suites, multi-step tool loops), a message sitting fifth in line can wait for five consecutive turns.
This is not just a latency annoyance. It changes correctness for control messages and produces cascading noise.
Current behavior, step by step:
Why it matters
1. Stop and abort orders don't take effect when issued. The single most important message in a multi-session setup is "stop." Today a stop can sit in a queue while the recipient continues doing exactly the thing it was told to stop. Sessions keep acting on intent the operator has already revoked, and the operator has no way to see that their instruction hasn't landed. Illustrative example: an operator halts all work; some sessions honor it within seconds, others continue for many minutes because the halt is queued behind an in-flight turn. From the outside this is indistinguishable from a session ignoring the order.
2. One-per-turn delivery makes backlogs self-perpetuating. Because each turn releases only one message, a busy session can never catch up while it stays busy — and each released message is itself likely to start the next long turn. A stop message queued behind three routine notices doesn't wait for one turn; it waits for three, each potentially minutes long. The more responsive the session is to each individual message, the longer the rest of the queue waits. A queue can therefore grow faster than it drains under steady inbound traffic, with no backpressure signal to senders.
3. Delayed reads produce stale, contradictory reports. A session that finally reads a queued message answers using state it observed before its earlier turns began. Its reply is internally consistent and confidently worded, but describes a world that has since changed. Recipients cannot tell "this session is wrong" from "this session is late." Per-message compounding makes this sharper: two messages sent seconds apart can be answered many minutes apart, against different underlying states. Illustrative example: two sessions report opposite states of the same resource within minutes of each other; both read correctly, one just read earlier and replied later.
4. Silence looks like failure, so senders retry — creating a duplicate storm. Because there's no queued indicator, an unanswered message reads as a lost message. Senders re-send. In practice the same request can arrive several times in a short window, sometimes with drifted details or even flipped recommendations between copies, because each copy was composed at a different moment. Retries make the compounding worse: each duplicate occupies its own slot in a one-per-turn queue, pushing genuinely new messages further back. The receiving side then has to do provenance work to determine which copy, if any, reflects current intent.
5. You cannot cheaply check whether a message landed. Delivery itself wakes the recipient, so "did you get it?" is not a free observation — it's an intervention that starts work, and it also consumes a queue slot. This removes the obvious workaround and makes queue depth genuinely unobservable.
6. Human-facing latency. When a session's role is to relay questions and decisions to a person, queuing inserts the same delay into the human loop — multiplied by backlog depth. Time-sensitive approvals age through several turns, and by the time they surface the underlying state may have moved on.
Steps to reproduce
Expected behavior
Additional context
Suggested directions, roughly in priority order; any one of these would help materially.