Draft
Conversation
Backend: - Add StreamBuffer class that buffers stream chunks and allows creating multiple readers for stream resumption - Modify AgentManager to buffer the stream output so reconnecting clients can replay accumulated data - Add GET /api/agent/:chatId/stream endpoint that returns a resume stream (204 if no active agent) - Add chat.activeChats tRPC query returning running chat IDs for the user Frontend: - Add useActiveAgents hook that polls for running backend agents and syncs chatActivityStore so the sidebar shows spinners after refresh - Configure prepareReconnectToStreamRequest on the transport so the AI SDK's built-in reconnectToStream sends GET to the correct URL - Pass resume flag to useChat when a backend agent is detected as running for the current chat (after DB messages are loaded)
- Add checkIsAgentRunning guard to shouldResume so active frontend streams don't trigger an unwanted reconnect - Track previously active chat IDs in useActiveAgents and clear running state for agents that have finished between polls
Contributor
🚀 Preview Deployment
Preview will be automatically removed when this PR is closed. |
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.
Problem
When a user refreshes the browser while an agent is running, the stream connection drops. The agent continues running on the backend, but the frontend loses its connection — the stream stops, the sidebar loader disappears, and the response appears frozen.
Solution
Leverage the Vercel AI SDK's built-in
reconnectToStream/resumeStreamsupport to resume the stream after refresh, and keep the sidebar showing a spinner for running agents.Backend
StreamBuffer(apps/backend/src/lib/stream-buffer.ts) — A new utility that consumes aReadableStream, buffers all chunks in memory, and allows creating multiple independent readers. Each reader replays buffered content from the start and then continues with live data. This enables stream resumption without losing any data.AgentManagerchanges — The agent'sstream()method now wraps the output through aStreamBuffer, and a newresumeStream()method returns a reader that filters out initial-setup events (data-newChat,data-newUserMessage) so they don't replay on reconnect.GET /api/agent/:chatId/stream— New resume endpoint that returns acreateUIMessageStreamResponsefrom the buffer, or 204 if no agent is running.chat.activeChatstRPC query — Returns the list of chat IDs with actively running agents for the current user.Frontend
useActiveAgentshook — Pollschat.activeChatson app load (and every 10s while agents are active). SetschatActivityStore.running = truefor active agents so the sidebar shows spinners immediately after refresh. Also clears stale running state when agents finish between polls.useAgenthook — ConfiguresprepareReconnectToStreamRequeston the transport so the AI SDK'sreconnectToStreamhits the correct URL. Computes ashouldResumeflag based on: chat exists, DB messages loaded, activity store shows running, and no frontend stream is already active. Passes this as theresumeoption touseChat.How it works
useActiveAgentsquerieschat.activeChats→ backend returns running chat IDschatActivityStoreis updated → sidebar shows spinners for running chatsuseAgentdetectsshouldResume = trueuseChat({ resume: true })triggers the AI SDK'sresumeStream()→GET /api/agent/:chatId/stream