fix(events): reconcile open session transcript after SSE reconnect - #183
Closed
omgoshjosh wants to merge 1 commit into
Closed
fix(events): reconcile open session transcript after SSE reconnect#183omgoshjosh wants to merge 1 commit into
omgoshjosh wants to merge 1 commit into
Conversation
A prompt submitted from another client (CLI, TUI, another device) while this client's stream was down never appeared in an already-open session until the user navigated away and back. resyncBusySessions() was the only reconnect-time reconciliation, and it only considers sessions this client has marked "busy". A client only learns a session is busy from an SSE `session.status` event -- so if the stream was down when the other client prompted, this client never saw that event, the session is still "idle" in its store, and resyncBusySessions() finds nothing to do and returns immediately. Reconnect resumes the stream from "now" without replaying missed events, so the messages from the gap are never fetched by anything. Add reconcileOpenSession(), invoked alongside resyncBusySessions() in the same once-per-reconnect block: refetch the currently-open session's transcript unconditionally. refreshMessages() replaces messages/parts without touching isLoading, so this is a silent background reconcile rather than a spinner over content the user is already reading -- which only holds because dzianisv#150's fix stopped same-session refreshes from forcing the loading state. Distinct from dzianisv#150: that fixed a spinner hiding content that was arriving. This fixes content that never arrives at all. Same symptom, different layer. Server-side contract was verified to already pass (an already-connected /global/event subscriber does receive another client's prompt immediately), so this closes the remaining client-side gap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Author
|
Folding this into #182 to keep the tracker tidy — same commit, same verification, just consolidated with the other two Android fixes rather than three separate PRs. Sorry for the noise. |
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.
A prompt submitted from another client (CLI, TUI, a second device) while this client's SSE stream was down never appears in an already-open session until the user navigates away and back.
Root cause
resyncBusySessions()is the only reconnect-time reconciliation, and it returns immediately when no session is marked busy:But a client only learns a session is busy from an SSE
session.statusevent. If the stream was down when the other client submitted the prompt, this client never saw that event — so the session is stillidlein its store,resyncBusySessions()finds nothing to do, and since reconnect resumes the stream from "now" without replaying missed events (as the comment above that function already notes), the messages from the gap are never fetched by anything.The session screen does subscribe to
reconnectAttempts, but only to render the reconnect banner. Nothing refetches on reconnect.The change
reconcileOpenSession(), invoked alongsideresyncBusySessions()in the same once-per-reconnect block: refetch the currently-open session's transcript unconditionally.refreshMessages()replaces messages/parts without touchingisLoading, so this lands as a silent background reconcile rather than a spinner over content the user is already reading — which only holds because #151 stopped same-session refreshes from forcing the loading state. The two changes are complementary:Same user-visible symptom, different layer.
It can overlap with
resyncBusySessions()for a session that was busy and has since gone idle — both would refresh. That costs one redundant GET on an infrequent event, which seemed cheaper than the coupling needed to dedupe; happy to change that if you'd prefer.Verification
Reproduced and verified on an Android 12 emulator against a live server:
The last step is the regression check: before this change the transcript stayed stale until the user left the session and re-entered.
noReply: truewas used throughout so the test exercises prompt acceptance, persistence and realtime delivery without model timing.tsc --noEmitclean; full suite passes (286 tests) on this branch.