Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions ui/desktop/src/components/BaseChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export default function BaseChat({
submitElicitationResponse,
stopStreaming,
sessionLoadError,
retrySessionLoad,
setRecipeUserParams,
tokenState,
notifications: toolCallNotifications,
Expand Down Expand Up @@ -350,19 +351,30 @@ export default function BaseChat({
{renderHeader && renderHeader()}
<div className="flex flex-col flex-1 mb-0.5 min-h-0 relative">
<div className="flex-1 bg-background-primary rounded-b-2xl flex items-center justify-center">
<div className="flex flex-col items-center justify-center p-8">
<div className="text-red-700 dark:text-red-300 bg-red-400/50 p-4 rounded-lg mb-4 max-w-md">
<div className="flex flex-col items-center justify-center p-8 max-w-md w-full">
<div className="text-red-700 dark:text-red-300 bg-red-400/50 p-4 rounded-lg mb-6 w-full">
<h3 className="font-semibold mb-2">Failed to Load Session</h3>
<p className="text-sm">{sessionLoadError}</p>
</div>
<button
onClick={() => {
setView('chat');
}}
className="px-4 py-2 text-center cursor-pointer text-text-primary border border-border-primary hover:bg-background-secondary rounded-lg transition-all duration-150"
>
Go home
</button>
<div className="flex gap-3 mb-4">
<button
onClick={retrySessionLoad}
className="px-4 py-2 text-center cursor-pointer text-text-on-primary bg-blue-600 hover:bg-blue-700 rounded-lg transition-all duration-150 font-medium"
>
Retry connection
</button>
<button
onClick={() => {
setView('chat');
}}
className="px-4 py-2 text-center cursor-pointer text-text-primary border border-border-primary hover:bg-background-secondary rounded-lg transition-all duration-150"
>
New session
</button>
</div>
<p className="text-xs text-text-muted text-center">
Session: <code className="select-all bg-background-secondary px-1 py-0.5 rounded">{sessionId}</code>
</p>
</div>
</div>
</div>
Expand Down
13 changes: 11 additions & 2 deletions ui/desktop/src/hooks/useChatStream.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useReducer, useRef } from 'react';
import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';
import { v7 as uuidv7 } from 'uuid';
import { AppEvents } from '../constants/events';
import { ChatState } from '../types/chatState';
Expand Down Expand Up @@ -50,6 +50,7 @@ interface UseChatStreamReturn {
setRecipeUserParams: (values: Record<string, string>) => Promise<void>;
stopStreaming: () => void;
sessionLoadError?: string;
retrySessionLoad: () => void;
tokenState: TokenState;
notifications: Map<string, NotificationEvent[]>;
onMessageUpdate: (
Expand Down Expand Up @@ -349,6 +350,7 @@ export function useChatStream({
onSessionLoaded,
}: UseChatStreamProps): UseChatStreamReturn {
const [state, dispatch] = useReducer(streamReducer, initialState);
const [retryCount, setRetryCount] = useState(0);

// Long-lived SSE connection for this session
const { addListener, setActiveRequestsHandler } = useSessionEvents(sessionId);
Expand Down Expand Up @@ -776,7 +778,7 @@ export function useChatStream({
return () => {
cancelled = true;
};
}, [sessionId, onSessionLoaded]);
}, [sessionId, onSessionLoaded, retryCount]);

const handleSubmit = useCallback(
async (input: UserInput) => {
Expand Down Expand Up @@ -1058,8 +1060,15 @@ export function useChatStream({
}, new Map<string, NotificationEvent[]>());
}, [state.notifications]);

const retrySessionLoad = useCallback(() => {
dispatch({ type: 'SET_SESSION_LOAD_ERROR', payload: undefined });
dispatch({ type: 'SET_CHAT_STATE', payload: ChatState.LoadingConversation });
setRetryCount((c) => c + 1);
Comment on lines +1063 to +1066
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Invalidate cached session before retrying load

retrySessionLoad() only bumps retryCount, but it never clears resultsCache for this sessionId. In the common disconnect case (session already loaded once), the load effect immediately returns cached data (resultsCache.get(sessionId)) and skips resumeAgent, so "Retry connection" does not actually retry the backend connection and can falsely recover the UI while the server is still down.

Useful? React with 👍 / 👎.

}, []);

return {
sessionLoadError: state.sessionLoadError,
retrySessionLoad,
messages: maybe_cached_messages,
session: maybe_cached_session,
chatState: state.chatState,
Expand Down