feat: support parallel chat sessions streaming concurrently#55
Merged
Conversation
Move stream lifecycle out of ChatThreadContainer into a session-keyed chatStreamStore (useSyncExternalStore, no new deps) so streams survive session/view switches and multiple sessions can stream at once (capped at 3). Background completions are folded in on return via refetch; background errors surface with the question restored to the composer. AgentRail rows show a pulse dot for streaming sessions.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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
Lets a user run multiple chat sessions in parallel: start a reply streaming in one session, switch to another and stream there too, and come back without losing anything. The backend was already parallel-safe (per-request SSE generators, UUID-keyed sessions), so this is a UI-only change — no API or schema changes.
Root cause fixed: all stream state (
streamingText,chatting, abort controller) lived inside a singleChatThreadContainerinstance and was aborted/reset on every session or view switch.Changes
ui/src/components/chat/chatStreamStore.ts— module-level, session-keyed stream store built onuseSyncExternalStore(no new dependencies). Owns each stream's lifecycle (streaming → done/error). Streams started before a session id exists live under a pending key and are rekeyed via an alias when the server assigns one. One stream per session;MAX_CONCURRENT_STREAMS = 3global cap; errored never-resolved entries are swept on the next start.ChatThreadContainer.tsx— derives streaming UI from the store. Removed the unconditional abort-on-send; composer disabled only while this session streams; the in-flight exchange renders from stream state. Returning to a session whose stream finished in the background refetches persisted messages; a background failure surfaces the error banner and restores the question to the composer. Stop restores the question; edit deletes the last persisted exchange before resending.AgentRail.tsx— workspace/agent session rows show a pulsing dot while streaming (same pattern as research runs).chatThreadState.ts— simplified to a singleremoveLastExchangehelper;ui/DESIGN.mdupdated.Reviewed by the TypeScript reviewer agent: initial BLOCK (background errors swallowed; orphaned errored pending entries; double-send pending race; suggestions/webUsed cross-session bleed) — all fixed with regression tests, re-review verdict APPROVE.
Deliberately out of scope: resumable streams across page reload (server-side; can reuse the research-run polling pattern later), multi-pane chat UI.
Test plan
removeLastExchangeunit teststsc -b,eslint,vite buildclean; fullnpm test(26 tests) green