Summary
The user_input event carries no turn/task identity, so a consumer cannot attribute it to a specific turn. This breaks two things for any multi-client consumer: cancel-guard attribution, and cross-client turn reconciliation.
Where the identity is lost
src/serve/http_types.rs:222 — AgentEventPayload::UserInput { event_id, sequence, timestamp_ms, text }. Those four fields are the entire payload. One hop earlier, src/runtime_boundary.rs lowers LegacyAgentEvent::UserInput { thread_id, content } into this — the thread_id (the SESSION id, not a turn id) is dropped, and there was never a turn/task id to begin with.
src/grpc/agent.rs:6303 — user_input is explicitly NOT forwarded on the live gRPC stream (if let AgentEventPayload::UserInput { .. } = payload { return None; }). It reaches a reconnecting/reloading client only through GetSessionEvents as opaque JSON with those same four fields.
Consumer damage (observed in bipa-app/bip)
The desktop renderer runs a cancel guard: after a user stops a turn, late frames from the stopped turn are dropped until a NEW turn starts. A turn can start on ANOTHER frontend (multi-client), so the renderer must clear the guard when a genuinely-new user_input arrives — but it can only compare SEQUENCE to the guard, because there is no turn id. That works in every state except one: a user cancel that landed before the turn's first sequenced frame (guard armed at 0), where the canceled turn's OWN delayed user_input (sequence >= 1) is indistinguishable from a new turn's. The renderer resolves this conservatively (keeps the guard, so it never un-cancels the turn the user stopped), at the cost of a genuine remote turn on that narrow state staying filtered until the next local send. See bipa-app/bip#603 and ENG-9215.
Ask
Stamp a turn identifier — the root task id / turn number that already exists internally — onto the user_input wire event, threaded http_types.rs:222 -> grpc/agent.rs -> the JSON a client reads back. Then a consumer can compare turn ids instead of sequences and the ambiguity disappears, for cancel attribution and for any cross-client turn reconciliation.
Same family as #377 (the wire dropping identity a consumer needs). Either issue closing would help; this one is independent of the re-mint fix.
Summary
The
user_inputevent carries no turn/task identity, so a consumer cannot attribute it to a specific turn. This breaks two things for any multi-client consumer: cancel-guard attribution, and cross-client turn reconciliation.Where the identity is lost
src/serve/http_types.rs:222—AgentEventPayload::UserInput { event_id, sequence, timestamp_ms, text }. Those four fields are the entire payload. One hop earlier,src/runtime_boundary.rslowersLegacyAgentEvent::UserInput { thread_id, content }into this — thethread_id(the SESSION id, not a turn id) is dropped, and there was never a turn/task id to begin with.src/grpc/agent.rs:6303—user_inputis explicitly NOT forwarded on the live gRPC stream (if let AgentEventPayload::UserInput { .. } = payload { return None; }). It reaches a reconnecting/reloading client only throughGetSessionEventsas opaque JSON with those same four fields.Consumer damage (observed in bipa-app/bip)
The desktop renderer runs a cancel guard: after a user stops a turn, late frames from the stopped turn are dropped until a NEW turn starts. A turn can start on ANOTHER frontend (multi-client), so the renderer must clear the guard when a genuinely-new
user_inputarrives — but it can only compare SEQUENCE to the guard, because there is no turn id. That works in every state except one: a user cancel that landed before the turn's first sequenced frame (guard armed at 0), where the canceled turn's OWN delayeduser_input(sequence >= 1) is indistinguishable from a new turn's. The renderer resolves this conservatively (keeps the guard, so it never un-cancels the turn the user stopped), at the cost of a genuine remote turn on that narrow state staying filtered until the next local send. See bipa-app/bip#603 and ENG-9215.Ask
Stamp a turn identifier — the root task id / turn number that already exists internally — onto the
user_inputwire event, threadedhttp_types.rs:222->grpc/agent.rs-> the JSON a client reads back. Then a consumer can compare turn ids instead of sequences and the ambiguity disappears, for cancel attribution and for any cross-client turn reconciliation.Same family as #377 (the wire dropping identity a consumer needs). Either issue closing would help; this one is independent of the re-mint fix.