Draw a peer message above the turn it started, not below it
The live Claude Code path only learns a turn was another agent's when the turn ends -- the whole of the message arrives as an `origin` object on the `result` -- so the note was appended after everything it caused, and the transcript showed the answer above the question. It cannot be recorded in place: by the time anyone knows, the reply is already written, and the transcript is append-only. So the event carries where it belongs instead. `PeerMessage` gains `turnStart`, the seq of the status that opened its turn, stamped by the pump -- the only thing that knows a seq and the only thing that sees every driver's turns. The phone gives the note that seq, so it sorts into place rather than being drawn out of order at the end. A status draws no row, so there is nothing for it to collide with and the list stays sorted, which the scroll anchor and paging both depend on. Absent where there is nothing to correct: a message replayed out of a session file by `import` is already in the right place, and one that opened no turn has no turn to sit above. Both stay where they arrive. The echo driver gets `/peer-turn` for the live shape, beside `/peer` for the in-place one. Verified on the emulator both ways, live and on replay, plus an ordinary `/tools` turn to confirm the run grouping the insertion cuts through is unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7525fc925a
commit
465645cefb
9 files changed
+246
-13
No files matched your search
@@ -99,7 +99,19 @@ sealed class SessionEvent {
|
||||
* claim they had. It is also the explanation for a session that starts working on something
|
||||
* this device never asked for.
|
||||
*/
|
||||
data class PeerMessage(val from: String, val text: String) : SessionEvent()
|
||||
data class PeerMessage(
|
||||
val from: String,
|
||||
val text: String,
|
||||
/**
|
||||
* Where the turn this started begins, when the server could say.
|
||||
*
|
||||
* The live Claude Code path only learns a turn was somebody else's when the turn ends, so
|
||||
* the event arrives below everything it caused; this is what puts it back above it. Null
|
||||
* for a message read out of a session file, which is already in the right place, and for
|
||||
* one that started no turn. See the server's `Event::PeerMessage`.
|
||||
*/
|
||||
val turnStart: Long? = null,
|
||||
) : SessionEvent()
|
||||
|
||||
/**
|
||||
* A command the session was asked to run on itself and cannot run yet.
|
||||
@@ -237,7 +249,11 @@ fun parseSeqEvent(json: String): SeqEvent {
|
||||
},
|
||||
)
|
||||
"peerMessage" ->
|
||||
SessionEvent.PeerMessage(body.getString("from"), body.getString("text"))
|
||||
SessionEvent.PeerMessage(
|
||||
body.getString("from"),
|
||||
body.getString("text"),
|
||||
if (body.has("turnStart")) body.getLong("turnStart") else null,
|
||||
)
|
||||
"commandQueued" ->
|
||||
SessionEvent.CommandQueued(body.getString("id"), body.getString("text"))
|
||||
"commandSent" -> SessionEvent.CommandSent(body.getString("id"), body.getString("text"))
|
||||
|
||||
Reference in new issue
Block a user