Repository navigation
Fix assistant losing answers when the chat panel is closed or reconnects - #332
Open
zwanski-start wants to merge 2 commits into
Open
zwanski-start wants to merge 2 commits into
zwanski-start wants to merge 2 commits into
Conversation
…ForgeAI#133) - Keep consuming Claude's stream after the client goes away so the full answer is stored in the conversation history - Persist partial responses if the stream is aborted - Reuse the live session when reconnecting to the same conversation and wait for any in-flight response before signalling the client Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ct (AutoForgeAI#133) - Keep AssistantChat mounted after the panel is first opened (per project) so closing it no longer drops the WebSocket mid-response; mark the hidden panel inert and scroll the message list directly so it can't steal focus or scroll the page - On resume/reconnect, wait for the server to finish any in-flight reply, then reload the stored history instead of showing stale cached messages - Stop reconnecting after the chat unmounts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
🔍 QA report: ✅ PassedTester: Guido (cubefarm agent, in a fresh session) · Round: 1 · Author: Barbara The PR fixes #133: an assistant answer that is streaming when the panel is closed, the page is reloaded or the socket drops now finishes on the server, is saved to history, and shows once (no duplicates) when the chat reconnects. Tested end-to-end with a stub Claude client (no API key) on both the WebSocket protocol and in a real browser, including reusing the session for a follow-up. ruff, the security tests, the 4 new tests, the UI build and the UI lint pass. mypy and pytest show the same failures as master (environment/SDK related, not this PR).
🧪 Commands run
No browser screenshots were taken in this round. Posted by cubefarm · merges automatically once GitHub's checks pass |
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.
Closes #133
Problem
If you closed the assistant panel (or switched views, reloaded the page, or lost the connection) while an answer was still streaming, the answer disappeared. It was still missing after reopening the conversation from history. There were three causes:
AssistantPanelrendered<AssistantChat>only whileisOpen, so closing it closed the WebSocket.send_jsonraised partway through the stream. That abandoned the generator before_query_claudereachedadd_message, so the reply was never saved. On reconnect,create_sessionalso closed the live session and started a new one.useConversationcaches for 30s, so reopening showed the old message list even when the database had newer messages.Changes
Server (
server/services/assistant_chat_session.py,server/routers/assistant_chat.py)_forward_chunks(): if the client disconnects mid-reply, the server keeps reading Claude's stream so the full reply finishes and gets saved._query_claudesaves the reply in afinallyblock, so a partial reply is kept if the stream is aborted.aclosing()passes an early close through the nested generators.create_sessionreuses the live session when the client reconnects to the same conversation. Queries are serialized with anasyncio.Lock. The newresume()waits for any reply still in progress before sendingresponse_done.UI
AssistantPanel: after the chat is first opened for a project, it stays mounted (keyed by project). While closed, the panel isinert, so it can't take focus or capture keyboard shortcuts.AssistantChat: auto-scroll now scrolls the message container directly instead of usingscrollIntoView, which could scroll the page while the panel was hidden off-screen.useAssistantChat: after an unexpected reconnect it sendsstartagain for the current conversation. When a resume finishes, it reloads the conversation from the database and replaces the live messages, so nothing shows twice. It also no longer reconnects after unmount (before, the close in the cleanup function triggeredonclose, which reconnected).Verification
test_assistant_chat_session.py(4 tests): the full reply is saved when the socket drops mid-stream, a partial reply is saved when the stream is aborted, the session is reused for the same conversation, andresume()waits for the in-flight reply.ruff check .passes.python test_security.pypasses.test_ws_security.pypasses.npm run lintshows 0 errors (the warnings were already there).npm run buildpasses.inert, focus stays on<body>, and the page doesn't scroll.Notes / assumptions
/api/*(only/wshasws: true), so the assistant can't connect undernpm run dev. This is already the case on master. I tested against the built UI served by the backend and leftvite.config.tsunchanged.ask_userquestion, you won't see the question buttons after reconnecting. Only the text is stored, as before.🤖 Generated with Claude Code