From 06e9f90387ef8877735fe196675d4d9b9cf09884 Mon Sep 17 00:00:00 2001 From: Thomas Obermueller Date: Mon, 18 May 2026 15:36:59 +0000 Subject: [PATCH] fix(sessions): show inline banner on cloud stream disconnect When the cloud SSE stream gives up after exhausted reconnect attempts, the task UI rendered a fullscreen error overlay on top of the already populated ConversationView, hiding all cached events. The session data was still in memory (and logs on disk), so the overlay was the only thing blocking access to the conversation. Plumb the existing `retryable` flag from CloudTaskErrorUpdate through the session store onto AgentSession. When `hasError && errorRetryable && events.length > 0`, render a small red banner above the conversation with the error title and a Retry button instead of the fullscreen overlay. Hard errors (no events yet, non-retryable) keep the existing overlay path. Generated-By: PostHog Code Task-Id: 0be36564-39e9-475b-8d1c-c74911ebecde --- .../sessions/components/SessionView.tsx | 54 ++++++++++++++++++- .../sessions/hooks/useSessionViewState.ts | 1 + .../features/sessions/service/service.ts | 4 ++ .../features/sessions/stores/sessionStore.ts | 1 + .../task-detail/components/TaskLogsPanel.tsx | 2 + 5 files changed, 61 insertions(+), 1 deletion(-) diff --git a/apps/code/src/renderer/features/sessions/components/SessionView.tsx b/apps/code/src/renderer/features/sessions/components/SessionView.tsx index b95675e7d..bdd446685 100644 --- a/apps/code/src/renderer/features/sessions/components/SessionView.tsx +++ b/apps/code/src/renderer/features/sessions/components/SessionView.tsx @@ -63,6 +63,7 @@ interface SessionViewProps { hasError?: boolean; errorTitle?: string; errorMessage?: string; + errorRetryable?: boolean; onRetry?: () => void; onNewSession?: () => void; isInitializing?: boolean; @@ -78,6 +79,48 @@ interface SessionViewProps { const DEFAULT_ERROR_MESSAGE = "Failed to resume this session. The working directory may have been deleted. Please start a new session."; +interface CloudStreamDisconnectedBannerProps { + errorTitle?: string; + errorMessage?: string; + onRetry?: () => void; +} + +function CloudStreamDisconnectedBanner({ + errorTitle, + errorMessage, + onRetry, +}: CloudStreamDisconnectedBannerProps) { + return ( + + + + {errorTitle && ( + + {errorTitle} + + )} + {errorMessage && ( + + {errorMessage} + + )} + + {onRetry && ( + + )} + + ); +} + /** * When an allow_always permission is granted outside a mode-switch prompt, * ratchet the session to the closest "auto-accept edits" preset offered by @@ -116,6 +159,7 @@ export function SessionView({ hasError = false, errorTitle, errorMessage = DEFAULT_ERROR_MESSAGE, + errorRetryable = false, onRetry, onNewSession, isInitializing = false, @@ -137,6 +181,7 @@ export function SessionView({ const currentModeId = modeOption?.currentValue; const handoffInProgress = useSessionForTask(taskId)?.handoffInProgress ?? false; + const showInlineBanner = hasError && errorRetryable && events.length > 0; useEffect(() => { if (allowBypassPermissions) return; @@ -524,6 +569,13 @@ export function SessionView({ ) : ( <> + {showInlineBanner && ( + + )} - {hasError ? ( + {hasError && !showInlineBanner ? (