@@ -32,6 +32,7 @@ This proposal builds on and intersects with several existing draft RFDs:
3232- **[Agent Extensions via ACP Proxies](./proxy-chains)** — The proxy/multiplexer architecture pattern is central to the recommended implementation approach for multi-client sessions
3333- **[Session List](./session-list)** — Discovering existing sessions (prerequisite for attach)
3434- **[Resuming of existing sessions](./session-resume)** (`session/resume`) — Sequential handoff between clients
35+ - **[Message ID](./message-id)** — Stable message identifiers enabling `after_message` history replay and correlation of `turn_complete`/`prompt_received` notifications
3536- **Session Info Update** — Real-time metadata propagation (draft)
3637- **Session Fork** — Branching sessions (related but distinct — fork creates a copy, attach shares the original) (draft)
3738
@@ -91,8 +92,31 @@ The `historyPolicy` parameter controls what history is replayed on attach:
9192- `"full"` (default) — Replay the complete conversation history, matching the behaviour of `session/load`. Best for IDE and desktop clients.
9293- `"pending_only"` — Replay only events that require action (e.g., pending `request_permission` prompts). Best for notification clients.
9394- `"none"` — No history replay; receive only future `session/update` events. Best for lightweight observer or logging clients.
95+ - `"after_message"` — Replay history starting after the message identified by `afterMessageId`. Best for reconnecting clients that already have partial history and only need the delta. Requires the [Message ID RFD](./message-id) to be adopted.
9496
95- The response follows standard ACP patterns, returning session metadata. When `historyPolicy` is `"none"`, the `history` field is omitted. When `"pending_only"`, only items with `"status": " pending" ` are included:
97+ When using `"after_message"`, the client includes an `afterMessageId` field:
98+
99+ ```json
100+ {
101+ " jsonrpc" : " 2.0" ,
102+ " id" : 5 ,
103+ " method" : " session/attach" ,
104+ " params" : {
105+ " sessionId" : " sess_abc123def456" ,
106+ " role" : " controller" ,
107+ " historyPolicy" : " after_message" ,
108+ " afterMessageId" : " ea87d0e7-beb8-484a-a404-94a30b78a5a8" ,
109+ " clientInfo" : {
110+ " name" : " notification-dashboard" ,
111+ " version" : " 1.0.0"
112+ }
113+ }
114+ }
115+ ```
116+
117+ If the proxy does not recognise the provided `afterMessageId` (e.g., the message has been evicted from its buffer), it SHOULD fall back to `"full"` replay and indicate this in the response via the `historyPolicy` field.
118+
119+ The response follows standard ACP patterns, returning session metadata. When `historyPolicy` is `"none"`, the `history` field is omitted. When `"pending_only"`, only items with `"status": " pending" ` are included. When `"after_message"`, only events after the specified message are included:
96120
97121```json
98122{
@@ -188,6 +212,59 @@ When an agent emits `request_permission`, it is broadcast to **all connected con
188212}
189213```
190214
215+ ### Turn completion notification
216+
217+ When a controller sends a `session/prompt` and the agent finishes processing that turn, the proxy broadcasts a `turn_complete` notification to all connected clients. This is essential for multiplexed clients — without it, secondary clients have no reliable way to know when the agent has finished responding and is ready for the next prompt.
218+
219+ ```json
220+ {
221+ " jsonrpc" : " 2.0" ,
222+ " method" : " session/update" ,
223+ " params" : {
224+ " sessionId" : " sess_abc123def456" ,
225+ " update" : {
226+ " type" : " turn_complete" ,
227+ " stopReason" : " end_turn" ,
228+ " messageId" : " ea87d0e7-beb8-484a-a404-94a30b78a5a8"
229+ }
230+ }
231+ }
232+ ```
233+
234+ The `stopReason` field mirrors the `stopReason` from the `session/prompt` response (e.g., `"end_turn"`, `"max_tokens"`). The optional `messageId` field references the final agent message of the turn (requires the [Message ID RFD](./message-id)).
235+
236+ Note: The primary client (the one that sent the prompt) already receives this signal via the `session/prompt` response. The `turn_complete` notification is for all *other* connected clients that only see `session/update` events.
237+
238+ ### Prompt echoing
239+
240+ When a controller sends a `session/prompt`, the proxy echoes that prompt to all **other** connected clients via a `prompt_received` session update. Without this, secondary clients would see the agent's response stream without knowing what question or instruction triggered it.
241+
242+ ```json
243+ {
244+ " jsonrpc" : " 2.0" ,
245+ " method" : " session/update" ,
246+ " params" : {
247+ " sessionId" : " sess_abc123def456" ,
248+ " update" : {
249+ " type" : " prompt_received" ,
250+ " messageId" : " 4c12d49b-729c-4086-bfed-5b82e9a53400" ,
251+ " prompt" : [
252+ {
253+ " type" : " text" ,
254+ " text" : " Add authentication to the API"
255+ }
256+ ],
257+ " sentBy" : {
258+ " name" : " Claude Code" ,
259+ " version" : " 1.0.0"
260+ }
261+ }
262+ }
263+ }
264+ ```
265+
266+ The prompt is echoed *before* the agent begins streaming its response, so clients can display the user message in the correct position in the conversation. The `sentBy` field identifies which client sent the prompt, and the optional `messageId` allows clients to correlate the prompt with subsequent response chunks (requires the [Message ID RFD](./message-id)).
267+
191268### Transport considerations
192269
193270The current stdio transport is inherently single-client. Multi-client attach requires a **network-capable transport**. We propose this works over:
@@ -212,7 +289,10 @@ The proxy:
212289- Fans out `session/update` notifications to all connected clients
213290- Broadcasts `request_permission` to all controllers
214291- Routes the first response back to the agent
292+ - Echoes `session/prompt` from one controller to all other clients as `prompt_received`
293+ - Broadcasts `turn_complete` to all non-prompting clients when a turn finishes
215294- Tracks client roles and connection state
295+ - Buffers message history for `after_message` replay on reconnect
216296
217297This means existing agents work **unchanged** — the proxy handles all multi-client logic. This approach aligns perfectly with the proxy-chains architecture, where proxies sit between clients and agents to extend functionality without modifying the core agent implementation.
218298
@@ -285,7 +365,7 @@ With multi-client session attach:
2853651. Add `session/attach` and `session/detach` methods to schema.json
2863662. Define `attach` capability in `sessionCapabilities` with a `roles` field
2873673. Define client roles (controller/observer) and their semantics
288- 4. Specify `permission_resolved` and `client_disconnected ` session update variants
368+ 4. Specify `permission_resolved`, `client_disconnected`, `turn_complete`, and `prompt_received ` session update variants
2893695. Document interaction with existing `session/load` and `session/resume`
290370
291371### Phase 2: Reference proxy implementation
@@ -411,4 +491,5 @@ The typical flow for a dashboard client would be:
411491
412492## Revision history
413493
494+ - **2026-02-24**: Added `after_message` history policy with `afterMessageId` for delta sync on reconnect; added `turn_complete` notification for secondary clients; added `prompt_received` echoing so all clients see prompts sent by other controllers
414495- **2026-02-18**: Initial proposal
0 commit comments