Repository navigation
Scope live chat to watched threads with overlay-based streaming - #23
Conversation
Replace global peer broadcast with ThreadEventBus, unary chat RPC, and watchThread/unwatchThread so subscribe only fans out to interested peers. Clients merge durable snapshots with a Zustand overlay and await turn completion via waitForTurnEnd instead of mutating the query cache. Co-authored-by: Cursor <cursoragent@cursor.com>
Point the queue bus at @cyrus/connections/rtc/bus, remove unused stdin helper, align OAuth client id with cyrusd, and tighten small style nits across CLI and web. Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThe PR replaces peer-wide chat streaming with thread-scoped event delivery and replay, changes ChangesThread-scoped live chat
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (5)
apps/web/src/components/connection-error.tsx (1)
4-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove or track the dummy TODO before merging.
This leaves the connection-error component explicitly marked as unfinished. Either implement the intended change or replace this with a concrete issue reference.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/connection-error.tsx` at line 4, Remove the placeholder TODO in the connection-error component by implementing the intended behavior, or replace it with a concrete tracked issue reference describing the remaining work.openspec/changes/archive/2026-07-10-scoped-thread-live-stream/design.md (1)
39-39: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSpecify a language for the fenced code block
The code block starting on line 39 has no language specifier, triggering markdownlint MD040. Add a language tag (e.g.,
textorplaintext) for proper rendering and lint compliance.📝 Proposed fix
-``` +```text🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@openspec/changes/archive/2026-07-10-scoped-thread-live-stream/design.md` at line 39, Specify a language identifier, such as text or plaintext, on the fenced code block beginning near the referenced section of the design document to satisfy markdownlint MD040.shared/utils/src/merge-conversation.ts (1)
13-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider replacing
localeComparewith simple string comparison forcreatedAtsorting.
createdAtvalues are ISO 8601 strings (new Date().toISOString()). UsinglocaleComparemay introduce locale-specific behavior. A simple</>comparison is more predictable for ISO 8601 lexicographic ordering.♻️ Proposed refactor
return merged.sort((left, right) => { if (left.seq !== right.seq) { if (left.seq === 0) return 1; if (right.seq === 0) return -1; return left.seq - right.seq; } - return left.createdAt.localeCompare(right.createdAt); + return left.createdAt < right.createdAt ? -1 : left.createdAt > right.createdAt ? 1 : 0; });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shared/utils/src/merge-conversation.ts` around lines 13 - 20, In the merge sorting logic, replace locale-dependent createdAt ordering in the comparator with direct string comparison using < and >, preserving the existing equality behavior and seq ordering in the surrounding merge/sort function.shared/hooks/src/connection/use-worker-conversation-sync.ts (1)
31-33: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
onSyncErrorusesconsole.errorinstead of the structuredlog.errorfromevlogused elsewhere in the codebase.Other hooks in this layer (e.g.,
use-thread-conversation.ts) uselog.error({ kind: "watch_thread", error, threadId })fromevlogfor structured logging. Usingconsole.errorhere is inconsistent and bypasses the structured logging pipeline.♻️ Proposed refactor
+import { log } from "evlog"; + // ... const onSyncError = useEffectEvent((error: unknown) => { - console.error("worker conversation sync failed", error); + log.error({ kind: "worker_conversation_sync", error }); });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shared/hooks/src/connection/use-worker-conversation-sync.ts` around lines 31 - 33, Replace the console.error call in onSyncError with the structured evlog log.error API, matching the pattern used by use-thread-conversation.ts; include an appropriate event kind and the received error in the structured payload.shared/hooks/src/stores/conversation-overlay.ts (1)
278-282: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
getLiveEntriescreates newConversationEntryobjects on every call, causing theliveEntriesselector inuseThreadConversationto always return a new reference and trigger re-renders on every store change.Each call to
getLiveEntriesinvokeschunkToEntrywhich generates new IDs (++overlayEntrySeq) and timestamps (new Date().toISOString()). Since Zustand's defaultObject.isequality check sees a new array reference each time, the selector triggers re-renders even when the relevant thread's overlay data hasn't changed. Consider selecting the rawlivearray from the store and converting toConversationEntry[]viauseMemoin the consumer, so re-renders only occur when the underlyinglivearray reference actually changes.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shared/hooks/src/stores/conversation-overlay.ts` around lines 278 - 282, Refactor getLiveEntries and its use in useThreadConversation so selectors return the stable raw live array from the thread overlay, while chunkToEntry conversion occurs in a useMemo keyed by that array reference. Preserve ConversationEntry typing and ensure conversion only reruns when the underlying live data changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/cli/src/handlers/controller/chat.ts`:
- Around line 24-49: Update runTurn so terminal notifications are always
delivered independently of persistence failures: ensure turn_completed emission
failures do not convert a successfully completed turn into turn_interrupted, and
when turn_interrupted persistence fails, publish a fallback terminal event
directly to the bus (for example with seq: 0), catching and logging persistence
errors. Use the existing emit flow and bus/logging abstractions available around
runTurn, and verify activeTurnLogs cleanup still occurs.
- Around line 91-102: Add a rejection handler to the promise chain returned by
runTurn in the chat handler, alongside the existing tapError handling. Ensure
errors from initial emit calls or the turn_interrupted emit are logged and that
the handler prevents an unhandled promise rejection; preserve the existing
result.tapError behavior for handled turn errors.
In `@apps/web/src/hooks/use-copy-to-clipboard.ts`:
- Around line 27-31: copy() in use-copy-to-clipboard.ts swallows clipboard
failures by only calling tapError on the Result. Update copy to re-throw the
captured error after setting copied to false, so its Promise rejects on failure
and callers such as message-copy-button.tsx and use-desktop.ts trigger their
error paths instead of reporting success.
In
`@openspec/changes/archive/2026-07-10-scoped-thread-live-stream/specs/thread-live-stream/spec.md`:
- Line 5: Update the live ChatChunk routing specification to reference
ThreadEventBus in apps/cli/src/queue/bus.ts instead of
apps/cli/src/queue/index.ts, and update all matching design and task
documentation references to the same path.
In `@openspec/changes/archive/2026-07-10-scoped-thread-live-stream/tasks.md`:
- Line 11: Update checklist item 2.1 to reference the actual ThreadEventBus
implementation in apps/cli/src/queue/bus.ts, or add a deliberate re-export from
apps/cli/src/queue/index.ts if that is intended as the public entrypoint; keep
the path consistent with the worker import.
In `@shared/hooks/src/stores/conversation-overlay.ts`:
- Around line 141-157: settleTurnWaiter invokes waiter.onAbort() before settling
the waiter, causing every turn to reject as aborted and preventing the intended
outcome. Remove the unconditional onAbort call from settleTurnWaiter; rely on
the resolve/reject wrappers’ cleanup behavior to remove the abort listener, then
preserve the existing completed resolve and interrupted reject logic.
- Around line 125-139: Update getOrCreateOverlay to return a cloned
ThreadOverlay when an entry already exists, including copies of live and
activeTurnIds, so applyChunkToOverlay, applyWatermark, and clearTurn never
mutate prior Zustand state. In commitLiveChunk, when shouldSkipPersistedChunk
returns true, return the previous state unchanged instead of constructing a new
byThread Map.
---
Nitpick comments:
In `@apps/web/src/components/connection-error.tsx`:
- Line 4: Remove the placeholder TODO in the connection-error component by
implementing the intended behavior, or replace it with a concrete tracked issue
reference describing the remaining work.
In `@openspec/changes/archive/2026-07-10-scoped-thread-live-stream/design.md`:
- Line 39: Specify a language identifier, such as text or plaintext, on the
fenced code block beginning near the referenced section of the design document
to satisfy markdownlint MD040.
In `@shared/hooks/src/connection/use-worker-conversation-sync.ts`:
- Around line 31-33: Replace the console.error call in onSyncError with the
structured evlog log.error API, matching the pattern used by
use-thread-conversation.ts; include an appropriate event kind and the received
error in the structured payload.
In `@shared/hooks/src/stores/conversation-overlay.ts`:
- Around line 278-282: Refactor getLiveEntries and its use in
useThreadConversation so selectors return the stable raw live array from the
thread overlay, while chunkToEntry conversion occurs in a useMemo keyed by that
array reference. Preserve ConversationEntry typing and ensure conversion only
reruns when the underlying live data changes.
In `@shared/utils/src/merge-conversation.ts`:
- Around line 13-20: In the merge sorting logic, replace locale-dependent
createdAt ordering in the comparator with direct string comparison using < and
>, preserving the existing equality behavior and seq ordering in the surrounding
merge/sort function.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ff28240a-f896-4434-84f4-20cd835da217
📒 Files selected for processing (47)
apps/cli/src/commands/agents/doctor.tsapps/cli/src/commands/agents/rm.tsapps/cli/src/commands/auth/login.tsapps/cli/src/commands/auth/whoami.tsapps/cli/src/commands/service/start.tsapps/cli/src/commands/service/stop.tsapps/cli/src/commands/service/worker.tsapps/cli/src/handlers/controller/chat.tsapps/cli/src/handlers/controller/threads.tsapps/cli/src/lib/env.tsapps/cli/src/queue/bus.tsapps/cli/src/utils/io.tsapps/web/src/components/auth/provider-button.tsxapps/web/src/components/chat/main/thread-workspace.tsxapps/web/src/components/connection-error.tsxapps/web/src/components/sidebar/chat-sidebar-footer-actions.tsxapps/web/src/components/sidebar/projects/thread-search-field.tsxapps/web/src/hooks/use-auto-animate-ref.tsapps/web/src/hooks/use-copy-to-clipboard.tsapps/web/src/hooks/use-media-query.tsapps/web/src/utils/dir.tsopenspec/changes/archive/2026-07-10-scoped-thread-live-stream/.openspec.yamlopenspec/changes/archive/2026-07-10-scoped-thread-live-stream/design.mdopenspec/changes/archive/2026-07-10-scoped-thread-live-stream/proposal.mdopenspec/changes/archive/2026-07-10-scoped-thread-live-stream/specs/conversation-overlay/spec.mdopenspec/changes/archive/2026-07-10-scoped-thread-live-stream/specs/conversation-view/spec.mdopenspec/changes/archive/2026-07-10-scoped-thread-live-stream/specs/thread-live-stream/spec.mdopenspec/changes/archive/2026-07-10-scoped-thread-live-stream/specs/wire-schemas/spec.mdopenspec/changes/archive/2026-07-10-scoped-thread-live-stream/tasks.mdopenspec/specs/conversation-overlay/spec.mdopenspec/specs/conversation-view/spec.mdopenspec/specs/thread-live-stream/spec.mdopenspec/specs/wire-schemas/spec.mdshared/connections/src/contracts/controller.tsshared/connections/src/rtc/bus.tsshared/connections/src/rtc/peer.tsshared/connections/src/rtc/worker/index.tsshared/constants/src/operation-keys.tsshared/database/src/repositories/conversations.tsshared/hooks/src/connection/use-controller-threads.tsshared/hooks/src/connection/use-thread-conversation.tsshared/hooks/src/connection/use-worker-conversation-sync.tsshared/hooks/src/stores/conversation-overlay.tsshared/schemas/src/rtc/chat.tsshared/schemas/src/rtc/threads.tsshared/utils/src/conversation-cache.tsshared/utils/src/merge-conversation.ts
💤 Files with no reviewable changes (5)
- apps/web/src/components/sidebar/projects/thread-search-field.tsx
- apps/cli/src/utils/io.ts
- shared/utils/src/conversation-cache.ts
- apps/web/src/components/auth/provider-button.tsx
- apps/cli/src/lib/env.ts
Ensure terminal chat events always reach the bus, fix waitForTurnEnd settlement, clone overlay state immutably, rethrow clipboard copy failures, and align OpenSpec paths with queue/bus.ts. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@openspec/changes/archive/2026-07-10-scoped-thread-live-stream/specs/thread-live-stream/spec.md`:
- Line 26: Update the ThreadEventBus activeTurnLogs requirement to impose
explicit size and/or age limits, evict buffers when turns are cancelled or fail
without publishing turn_completed or turn_interrupted, and clear all remaining
buffers during worker shutdown. Specify the behavior when replay data is
truncated, including whether clients receive a fresh snapshot afterward.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d28c5257-8ffc-48b5-9bb8-962ae8062cf6
📒 Files selected for processing (8)
apps/cli/src/handlers/controller/chat.tsapps/web/src/hooks/use-copy-to-clipboard.tsopenspec/changes/archive/2026-07-10-scoped-thread-live-stream/design.mdopenspec/changes/archive/2026-07-10-scoped-thread-live-stream/proposal.mdopenspec/changes/archive/2026-07-10-scoped-thread-live-stream/specs/thread-live-stream/spec.mdopenspec/changes/archive/2026-07-10-scoped-thread-live-stream/tasks.mdopenspec/specs/thread-live-stream/spec.mdshared/hooks/src/stores/conversation-overlay.ts
✅ Files skipped from review due to trivial changes (2)
- openspec/changes/archive/2026-07-10-scoped-thread-live-stream/proposal.md
- openspec/changes/archive/2026-07-10-scoped-thread-live-stream/tasks.md
🚧 Files skipped from review as they are similar to previous changes (4)
- apps/web/src/hooks/use-copy-to-clipboard.ts
- openspec/changes/archive/2026-07-10-scoped-thread-live-stream/design.md
- shared/hooks/src/stores/conversation-overlay.ts
- apps/cli/src/handlers/controller/chat.ts
Specify per-turn chunk limits, shutdown cleanup, and durable snapshot reconciliation in thread-live-stream specs; replace console.error with structured evlog in chat and conversation sync paths. Co-authored-by: Cursor <cursoragent@cursor.com>
…ow-up UX. Replace the overlay store with shared conversation cache utilities, turn waiters, and subscribe-driven sync so live chunks and refetches stay consistent. Fix cancel races, message ordering after interrupt, and render collapsible thinking in the feed. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
openspec/specs/thread-live-stream/spec.md (1)
30-36: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy liftPreserve emission order across concurrent turns.
watchThreadreplays buffered chunks perturnId, so interleaved events from concurrent turns on the same thread can come back out of emission order (A1, B1, A2→A1, A2, B1). Define a single per-thread replay order or prohibit concurrent turns on a thread.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@openspec/specs/thread-live-stream/spec.md` around lines 30 - 36, Preserve global emission order when replaying active turn logs from ThreadEventBus.watchThread: per-turn buffers cannot independently replay interleaved chunks without reordering them. Add a per-thread ordered replay structure (or explicitly enforce one active turn per thread), and update buffering, replay, cleanup, and closeAll logic so concurrent turns replay in original emission order.openspec/changes/archive/2026-07-10-scoped-thread-live-stream/specs/thread-live-stream/spec.md (1)
26-36: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy liftPreserve emission order across overlapping turns on the same thread.
watchThreadreplaysactiveTurnLogsturn-by-turn, so concurrent turns on one thread can come back reordered (A1, B1, A2→A1, A2, B1). Merge replay into a single per-thread order, or forbid overlapping turns per thread.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@openspec/changes/archive/2026-07-10-scoped-thread-live-stream/specs/thread-live-stream/spec.md` around lines 26 - 36, Preserve chronological emission order when replaying concurrent active turns on the same thread; the current turn-by-turn replay of activeTurnLogs can reorder chunks. Update ThreadEventBus.watchThread and its activeTurnLogs replay logic to maintain a per-thread ordered sequence of buffered ChatChunks, or explicitly prevent overlapping turns for a thread, ensuring replay produces A1, B1, A2 rather than grouping by turn.apps/web/src/components/chat/main/thread-workspace.tsx (1)
46-56: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win
threadin the effect dependency array causes the effect to run on every render.
threadis created via spread ({ ...baseThread, ...conversation }) on every render, producing a new object reference each time. This makes the effect fire on every render even when nothing relevant changed. The navigation guard prevents incorrect behavior, but the wasted execution is unnecessary.Depend on the primitive fields actually used inside the effect instead:
♻️ Proposed fix
useEffect(() => { - if (thread && thread.projectId !== projectId) + if (thread && thread.projectId !== projectId) navigate({ to: "/workers/$workerId/p/$projectId/t/$threadId", params: { workerId, projectId: thread.projectId, threadId: thread.id, }, }); - }, [navigate, projectId, thread, workerId]); + }, [navigate, projectId, thread?.projectId, thread?.id, workerId]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/chat/main/thread-workspace.tsx` around lines 46 - 56, Update the useEffect dependency array in the thread workspace component to replace the object dependency thread with the primitive values used by the effect, specifically thread?.projectId and thread?.id, while retaining navigate, projectId, and workerId.
🧹 Nitpick comments (1)
shared/utils/src/fold.ts (1)
387-427: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueOptional: de-duplicate the turn-order sort comparator.
The
leftTurn/rightTurnlookup viaturnOrder(Lines 390-396 and 408-413) is duplicated between thethoughtsandmessagessorts. Extracting a smallcompareByTurnOrder(turnOrder, left, right)helper would keep the two ordering rules from drifting.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shared/utils/src/fold.ts` around lines 387 - 427, Optionally deduplicate the repeated turn-order comparison logic in the thoughts and messages sorting within the fold result. Extract a compareByTurnOrder helper that accepts turnOrder and two items, performs the existing turnId lookup with the MAX_SAFE_INTEGER fallback, and returns the ordering difference; invoke it from both sort callbacks while preserving their existing tie-breakers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/cli/src/handlers/controller/chat.ts`:
- Around line 172-188: Filter the snapshotted turn IDs before publishing
interruption events in the cancel handler: after await
runtime.threadCoordinator.cancel, call
context.eventBus.getActiveTurnIdsForThread(input.threadId) again and publish
turn_interrupted only for IDs still present in the current active set,
preserving the existing cancellation flow.
In `@apps/web/src/components/chat/feed/chat-feed.tsx`:
- Around line 39-43: Update the auto-scroll useEffect in the chat feed component
to depend on conversation changes as well as feed length, so streaming content
updates trigger scrolling. Preserve the existing empty-feed guard and
requestAnimationFrame cleanup, and add the conversation reference to the effect
dependency array.
In
`@openspec/changes/archive/2026-07-10-scoped-thread-live-stream/specs/thread-live-stream/spec.md`:
- Line 26: Prevent post-shutdown event publication from recreating state after
ThreadEventBus.closeAll(). Add a shutdown fence in the worker lifecycle to stop
and await in-flight turns before calling closeAll(), or update
ThreadEventBus.publish() to reject once shutdown starts; ensure closeAll() and
the fence coordinate so activeTurnLogs and turnThreads cannot be repopulated.
In `@openspec/specs/thread-live-stream/spec.md`:
- Line 30: Make ThreadEventBus shutdown resistant to late publishes: update
closeAll() and publish() so shutdown sets a closed/stopping state before
clearing activeTurnLogs and turnThreads, and publish() rejects or ignores all
subsequent events; ensure closeAll() remains safe and idempotent.
- Line 30: Update the ThreadEventBus turn interruption flow and related
specification sections so non-delta events remain persist-before-broadcast,
while a failed durable terminal-event write still publishes turn_interrupted
through a non-durable or explicitly exempt path. Ensure the failure path always
evicts the turn’s activeTurnLogs buffer and documents this behavior consistently
with the worker’s cancellation and error handling.
In `@shared/utils/src/conversations/cache.ts`:
- Line 14: Prevent completedTurnKeys from growing indefinitely by removing each
turn’s key when its ephemeral cache entry is pruned. Update
pruneEphemeralTurnEntries to delete the corresponding key from
completedTurnKeys, while preserving removal in removeTurnFromCache and ensuring
terminal-event tracking remains correct.
---
Outside diff comments:
In `@apps/web/src/components/chat/main/thread-workspace.tsx`:
- Around line 46-56: Update the useEffect dependency array in the thread
workspace component to replace the object dependency thread with the primitive
values used by the effect, specifically thread?.projectId and thread?.id, while
retaining navigate, projectId, and workerId.
In
`@openspec/changes/archive/2026-07-10-scoped-thread-live-stream/specs/thread-live-stream/spec.md`:
- Around line 26-36: Preserve chronological emission order when replaying
concurrent active turns on the same thread; the current turn-by-turn replay of
activeTurnLogs can reorder chunks. Update ThreadEventBus.watchThread and its
activeTurnLogs replay logic to maintain a per-thread ordered sequence of
buffered ChatChunks, or explicitly prevent overlapping turns for a thread,
ensuring replay produces A1, B1, A2 rather than grouping by turn.
In `@openspec/specs/thread-live-stream/spec.md`:
- Around line 30-36: Preserve global emission order when replaying active turn
logs from ThreadEventBus.watchThread: per-turn buffers cannot independently
replay interleaved chunks without reordering them. Add a per-thread ordered
replay structure (or explicitly enforce one active turn per thread), and update
buffering, replay, cleanup, and closeAll logic so concurrent turns replay in
original emission order.
---
Nitpick comments:
In `@shared/utils/src/fold.ts`:
- Around line 387-427: Optionally deduplicate the repeated turn-order comparison
logic in the thoughts and messages sorting within the fold result. Extract a
compareByTurnOrder helper that accepts turnOrder and two items, performs the
existing turnId lookup with the MAX_SAFE_INTEGER fallback, and returns the
ordering difference; invoke it from both sort callbacks while preserving their
existing tie-breakers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 365f8e5c-a874-49b9-a455-18ebf8b3d590
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (23)
apps/cli/package.jsonapps/cli/src/handlers/controller/chat.tsapps/cli/src/queue/bus.tsapps/web/src/components/chat/composer/index.tsxapps/web/src/components/chat/composer/primary-action.tsxapps/web/src/components/chat/feed/chat-feed.tsxapps/web/src/components/chat/feed/feed-entry-view.tsxapps/web/src/components/chat/main/thread-workspace.tsxapps/web/src/components/chat/messages/assistant-loading.tsxapps/web/src/components/chat/messages/assistant-thinking.tsxopenspec/changes/archive/2026-07-10-scoped-thread-live-stream/specs/thread-live-stream/spec.mdopenspec/specs/thread-live-stream/spec.mdshared/connections/src/rtc/bus.tsshared/hooks/src/connection/use-controller-threads.tsshared/hooks/src/connection/use-thread-conversation.tsshared/hooks/src/connection/use-worker-conversation-sync.tsshared/hooks/src/use-thread-feed.tsshared/schemas/src/rtc/chat.tsshared/schemas/src/view/index.tsshared/utils/src/conversations/cache.tsshared/utils/src/conversations/merger.tsshared/utils/src/conversations/turn-waiters.tsshared/utils/src/fold.ts
✅ Files skipped from review due to trivial changes (2)
- apps/web/src/components/chat/messages/assistant-loading.tsx
- shared/connections/src/rtc/bus.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/cli/src/queue/bus.ts
Re-check active turns after cancel before publishing interrupts, scroll the feed on streaming conversation updates, ignore publishes after bus shutdown, prune completedTurnKeys with ephemeral entries, and clarify terminal-event specs. Co-authored-by: Cursor <cursoragent@cursor.com>
Use a derived streaming content key instead of the conversation object so scroll updates during token/thought streaming without tripping exhaustive-deps. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
ThreadEventBusthat fans outChatChunks only to peers watching a thread, with active-turn replay onwatchThreadchatto a unary{ threadId, turnId }RPC; all live chunks flow throughsubscribe()into a Zustand overlay merged with thegetConversationssnapshotsendMessageawaitswaitForTurnEnd, removeconversation-cache, add client-side token batching viarequestAnimationFrameTest plan
sendMessageresolves onturn_completedwatchThreadactive-turn logsendMessagerejects, overlay clears turnbun checkandbun check:typespassMade with Cursor
Summary by CodeRabbit
New Features
Bug Fixes