From c064aff5683c5f68d2dd945071d8ce29861b83c1 Mon Sep 17 00:00:00 2001 From: Haileyesus <118998054+blackmammoth@users.noreply.github.com> Date: Tue, 5 May 2026 17:23:17 +0300 Subject: [PATCH] refactor(chat): trim non-rendered realtime state from session flow The frontend realtime pipeline was carrying control-plane events and unused state through message storage as if they were renderable chat content. That made the session path noisier than necessary and increased the chance of subtle drift between transport events and UI data. Why this change: - Control events like session_created, status, complete, and permission lifecycle updates drive UI side effects, not chat transcript rendering. - Persisting those events in the session store added avoidable churn, memory growth, and merge work while providing no user-visible value. - An unused streamBufferRef existed in the hot path, creating extra writes and cognitive overhead with no read consumer. - useChatRealtimeHandlers accepted selectedProject even though it was not used, which widened the hook surface and dependency noise without behavior impact. What this commit does: - Removes the write-only streamBufferRef from ChatInterface and realtime handler wiring. - Removes the unused selectedProject argument from useChatRealtimeHandlers. - Stops appending non-rendered control events to sessionStore realtime messages. These events still execute their side effects exactly as before. Net effect: The Codex/Claude/Cursor/Gemini realtime path stays behaviorally equivalent for users, but the data model now stores only message content that can actually be rendered, reducing unnecessary state traffic in the chat runtime. --- .../chat/hooks/useChatRealtimeHandlers.ts | 22 +++++++++---------- src/components/chat/view/ChatInterface.tsx | 4 ---- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/components/chat/hooks/useChatRealtimeHandlers.ts b/src/components/chat/hooks/useChatRealtimeHandlers.ts index 342ea117..4ae861db 100644 --- a/src/components/chat/hooks/useChatRealtimeHandlers.ts +++ b/src/components/chat/hooks/useChatRealtimeHandlers.ts @@ -3,7 +3,7 @@ import type { Dispatch, MutableRefObject, SetStateAction } from 'react'; import { usePaletteOps } from '../../../contexts/PaletteOpsContext'; import type { PendingPermissionRequest, SessionNavigationOptions } from '../types/types'; -import type { Project, ProjectSession, LLMProvider } from '../../../types/app'; +import type { ProjectSession, LLMProvider } from '../../../types/app'; import type { SessionStore, NormalizedMessage } from '../../../stores/useSessionStore'; type PendingViewSession = { @@ -51,7 +51,6 @@ type LatestChatMessage = { interface UseChatRealtimeHandlersArgs { latestMessage: LatestChatMessage | null; provider: LLMProvider; - selectedProject: Project | null; selectedSession: ProjectSession | null; currentSessionId: string | null; setCurrentSessionId: (sessionId: string | null) => void; @@ -61,7 +60,6 @@ interface UseChatRealtimeHandlersArgs { setTokenBudget: (budget: Record | null) => void; setPendingPermissionRequests: Dispatch>; pendingViewSessionRef: MutableRefObject; - streamBufferRef: MutableRefObject; streamTimerRef: MutableRefObject; accumulatedStreamRef: MutableRefObject; onSessionInactive?: (sessionId?: string | null) => void; @@ -80,7 +78,6 @@ interface UseChatRealtimeHandlersArgs { export function useChatRealtimeHandlers({ latestMessage, provider, - selectedProject, selectedSession, currentSessionId, setCurrentSessionId, @@ -90,7 +87,6 @@ export function useChatRealtimeHandlers({ setTokenBudget, setPendingPermissionRequests, pendingViewSessionRef, - streamBufferRef, streamTimerRef, accumulatedStreamRef, onSessionInactive, @@ -187,7 +183,6 @@ export function useChatRealtimeHandlers({ if (msg.kind === 'stream_delta') { const text = msg.content || ''; if (!text) return; - streamBufferRef.current += text; accumulatedStreamRef.current += text; if (!streamTimerRef.current) { streamTimerRef.current = window.setTimeout(() => { @@ -216,12 +211,18 @@ export function useChatRealtimeHandlers({ sessionStore.finalizeStreaming(sid); } accumulatedStreamRef.current = ''; - streamBufferRef.current = ''; return; } // --- All other messages: route to store --- - if (sid) { + const shouldPersist = + msg.kind !== 'session_created' + && msg.kind !== 'complete' + && msg.kind !== 'status' + && msg.kind !== 'permission_request' + && msg.kind !== 'permission_cancelled'; + + if (sid && shouldPersist) { sessionStore.appendRealtime(sid, msg as NormalizedMessage); } @@ -232,6 +233,8 @@ export function useChatRealtimeHandlers({ if (!newSessionId) break; if (!currentSessionId || currentSessionId.startsWith('new-session-')) { + console.log('Session created with ID:', newSessionId); + console.log('Existing session ID:', currentSessionId); sessionStorage.setItem('pendingSessionId', newSessionId); if (pendingViewSessionRef.current && !pendingViewSessionRef.current.sessionId) { pendingViewSessionRef.current.sessionId = newSessionId; @@ -257,7 +260,6 @@ export function useChatRealtimeHandlers({ sessionStore.finalizeStreaming(sid); } accumulatedStreamRef.current = ''; - streamBufferRef.current = ''; setIsLoading(false); setCanAbortSession(false); @@ -386,7 +388,6 @@ export function useChatRealtimeHandlers({ }, [ latestMessage, provider, - selectedProject, selectedSession, currentSessionId, setCurrentSessionId, @@ -396,7 +397,6 @@ export function useChatRealtimeHandlers({ setTokenBudget, setPendingPermissionRequests, pendingViewSessionRef, - streamBufferRef, streamTimerRef, accumulatedStreamRef, onSessionInactive, diff --git a/src/components/chat/view/ChatInterface.tsx b/src/components/chat/view/ChatInterface.tsx index 8589f29a..46e95abd 100644 --- a/src/components/chat/view/ChatInterface.tsx +++ b/src/components/chat/view/ChatInterface.tsx @@ -50,7 +50,6 @@ function ChatInterface({ const { t } = useTranslation('chat'); const sessionStore = useSessionStore(); - const streamBufferRef = useRef(''); const streamTimerRef = useRef(null); const accumulatedStreamRef = useRef(''); const pendingViewSessionRef = useRef(null); @@ -60,7 +59,6 @@ function ChatInterface({ clearTimeout(streamTimerRef.current); streamTimerRef.current = null; } - streamBufferRef.current = ''; accumulatedStreamRef.current = ''; }, []); @@ -225,7 +223,6 @@ function ChatInterface({ useChatRealtimeHandlers({ latestMessage, provider, - selectedProject, selectedSession, currentSessionId, setCurrentSessionId, @@ -235,7 +232,6 @@ function ChatInterface({ setTokenBudget, setPendingPermissionRequests, pendingViewSessionRef, - streamBufferRef, streamTimerRef, accumulatedStreamRef, onSessionInactive,