fix(peer): route Tauri events once - #2384
Merged
Merged
Conversation
The regression was introduced by Bob Lee in commit 14c4b5c, which moved the stateful runtime-session cursor routing into every logical Tauri listener. The first subscriber advanced SessionStream, causing later subscribers to drop the same terminal event. Share each native Tauri listener across all adapter instances so surface and cursor routing runs once before logical subscriber fan-out. Keep subscription ownership isolated per adapter and preserve held-event delivery semantics. Add regressions for cross-adapter fan-out, adapter-specific disconnect, and attachment-held events. Refs: 14c4b5c
1688mengdie
pushed a commit
to 1688mengdie/BitFun
that referenced
this pull request
Aug 19, 2026
Sync upstream 0825520 (PR GCWing#2384 fix(peer): route Tauri events once) - tauri-adapter.ts + test, zero conflicts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the busy-hang where a conversation never settles after a turn completes, so every later message is stuck in the pending queue and only a new conversation recovers.
14c4b5c73(#2325) moved the stateful runtime-session cursor routing into every logical Tauri listener.SessionStreamis a per-(surface, session)ordering gate whose cursor dedup exists to suppress a re-delivered event (snapshot/live overlap), so it must decide about a delivery. With one native listener per logical subscriber, a single delivery was pushed through that gate once per subscriber: the first advanced the applied cursor, and every later subscriber was refused asnot-aheadand dropped.agentic://dialog-turn-completedhas several independent subscribers (turn settlement inAgenticEventListener,useAssistantBootstrap,useDialogCompletionNotify,useAcpPlan), so which one received it depended on registration order. Whenever the settlement subscriber was not the winner, the frontend state machine stayed inPROCESSINGforever.agentic://dialog-turn-startedandagentic://dialog-turn-interruptedhave a single subscriber each, which is why only the terminal event was affected.This cherry-picks the fix from
1.0.0-explore(5bb566c14, authored by @wsp) ontomainunchanged, so the two branches do not diverge. Each native Tauri listener is shared across all adapter instances, surface and cursor routing run once per delivery, and logical subscribers are then fanned out. Subscription ownership stays isolated per adapter and held-event delivery semantics are preserved — that also closes the cross-adapter variant that Peer Device Mode can hit while several transport adapters are alive.Closes #2381
Runtime evidence
Diagnosed on macOS Desktop with instrumentation at the emit site, the host delivery site, the surface gate, the
SessionStreamadmission point and the settlement handler.Before, for every completed turn: the backend emitted once (
emit_lifecycle_events: true,success: true) and the host delivered once, but the webview surface gate saw the event 3 times andSessionStreamreturned 1apply+ 2drop. On the reproducing run the settlement subscriber was adrop, sohandleDialogTurnCompletenever ran and the machine stayed inPROCESSINGwhile the backend was alreadyIdlewith the turn persisted.After, across 13 consecutive turns in 5 sessions: 1 arrival, 1
apply, 0dropper completed turn, settlement reachedfinalizeTurnCompletionState13/13, and 3 messages queued while busy all drained.Note for the issue reporter: the report attributed the regression to #2372 lock contention. Instrumentation rejected that — the completion path held the mutation lock for ~4 ms, the backend reset to
Idlecorrectly, and the second message was never submitted to the backend at all. The regression source is #2325.Test plan
pnpm --dir src/web-ui run test:run src/infrastructure/api/adapters src/infrastructure/peer-device src/flow_chat/session-stream— 14 files, 137 tests passpnpm run type-check:web— no errors in changed files (3 pre-existing@/generated/apierrors come from the gitignored codegen artifact,.gitignore:51)