fix: recover pending permission requests

This commit is contained in:
Haileyesus
2026-06-16 17:20:40 +03:00
parent 39b0473e38
commit 56b2e14059
2 changed files with 40 additions and 28 deletions

View File

@@ -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;

View File

@@ -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 => {