From 56b2e1405967c50301d0c773567349763edc8560 Mon Sep 17 00:00:00 2001 From: Haileyesus <118998054+blackmammoth@users.noreply.github.com> Date: Tue, 16 Jun 2026 17:20:40 +0300 Subject: [PATCH] fix: recover pending permission requests --- .../chat/hooks/useChatRealtimeHandlers.ts | 30 ++++++++------- .../chat/hooks/useChatSessionState.ts | 38 +++++++++++-------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/components/chat/hooks/useChatRealtimeHandlers.ts b/src/components/chat/hooks/useChatRealtimeHandlers.ts index be741c7e..5c294efa 100644 --- a/src/components/chat/hooks/useChatRealtimeHandlers.ts +++ b/src/components/chat/hooks/useChatRealtimeHandlers.ts @@ -202,7 +202,9 @@ export function useChatRealtimeHandlers({ // indicator derives from the processing map, so deleting the entry // hides it immediately and atomically. onSessionIdle?.(sid); - setPendingPermissionRequests([]); + if (sid === activeViewSessionId) { + setPendingPermissionRequests([]); + } if (msg.aborted) { // Abort was requested — the complete event confirms it. No @@ -232,17 +234,19 @@ export function useChatRealtimeHandlers({ case 'permission_request': { if (!msg.requestId) break; - setPendingPermissionRequests((prev) => { - if (prev.some((r: PendingPermissionRequest) => r.requestId === msg.requestId)) return prev; - return [...prev, { - requestId: msg.requestId as string, - toolName: (msg.toolName as string) || 'UnknownTool', - input: msg.input, - context: msg.context, - sessionId: sid || null, - receivedAt: new Date(), - }]; - }); + if (sid === activeViewSessionId) { + setPendingPermissionRequests((prev) => { + if (prev.some((r: PendingPermissionRequest) => r.requestId === msg.requestId)) return prev; + return [...prev, { + requestId: msg.requestId as string, + toolName: (msg.toolName as string) || 'UnknownTool', + input: msg.input, + context: msg.context, + sessionId: sid || null, + receivedAt: new Date(), + }]; + }); + } if (sid) { onSessionProcessing?.(sid); } @@ -250,7 +254,7 @@ export function useChatRealtimeHandlers({ } case 'permission_cancelled': { - if (msg.requestId) { + if (msg.requestId && sid === activeViewSessionId) { setPendingPermissionRequests((prev) => prev.filter((r: PendingPermissionRequest) => r.requestId !== msg.requestId)); } break; diff --git a/src/components/chat/hooks/useChatSessionState.ts b/src/components/chat/hooks/useChatSessionState.ts index 652edd87..780e5ec3 100644 --- a/src/components/chat/hooks/useChatSessionState.ts +++ b/src/components/chat/hooks/useChatSessionState.ts @@ -452,14 +452,31 @@ export function useChatSessionState({ return; } - const sessionKey = `${selectedSession.id}:${selectedProject.projectId}`; + const selectedSessionId = selectedSession.id; + const sessionKey = `${selectedSessionId}:${selectedProject.projectId}`; + + const subscribeToSelectedSession = () => { + if (!ws) { + return; + } + + statusCheckSentAtRef.current.set(selectedSessionId, Date.now()); + sendMessage({ + type: 'chat.subscribe', + sessions: [{ + sessionId: selectedSessionId, + lastSeq: lastSeqRef.current.get(selectedSessionId) ?? 0, + }], + }); + }; // Skip if already loaded and fresh - if (lastLoadedSessionKeyRef.current === sessionKey && sessionStore.has(selectedSession.id) && !sessionStore.isStale(selectedSession.id)) { + if (lastLoadedSessionKeyRef.current === sessionKey && sessionStore.has(selectedSessionId) && !sessionStore.isStale(selectedSessionId)) { + subscribeToSelectedSession(); return; } - const sessionChanged = currentSessionId !== null && currentSessionId !== selectedSession.id; + const sessionChanged = currentSessionId !== null && currentSessionId !== selectedSessionId; if (sessionChanged) { resetStreamingState(); } @@ -482,29 +499,20 @@ export function useChatSessionState({ setTokenBudget(null); } - setCurrentSessionId(selectedSession.id); + setCurrentSessionId(selectedSessionId); // Subscribe to the session's live run (if any): the ack reconciles the // processing indicator, re-attaches a mid-flight stream to this socket, // and replays any live events missed since `lastSeq`. Recording the send // time lets the ack handler discard idle acks that a newer request has // since outdated. - if (ws) { - statusCheckSentAtRef.current.set(selectedSession.id, Date.now()); - sendMessage({ - type: 'chat.subscribe', - sessions: [{ - sessionId: selectedSession.id, - lastSeq: lastSeqRef.current.get(selectedSession.id) ?? 0, - }], - }); - } + subscribeToSelectedSession(); lastLoadedSessionKeyRef.current = sessionKey; // Fetch from server → store updates → chatMessages re-derives automatically setIsLoadingSessionMessages(true); - sessionStore.fetchFromServer(selectedSession.id, { + sessionStore.fetchFromServer(selectedSessionId, { limit: MESSAGES_PER_PAGE, offset: 0, }).then(slot => {