diff --git a/apps/desktop/src/app/session/hooks/use-session-actions.ts b/apps/desktop/src/app/session/hooks/use-session-actions.ts index 6b4b08f945f4e..ca73467f681b3 100644 --- a/apps/desktop/src/app/session/hooks/use-session-actions.ts +++ b/apps/desktop/src/app/session/hooks/use-session-actions.ts @@ -20,6 +20,7 @@ import { getRememberedWorkspaceCwd, sessionPinId, setActiveSessionId, + setAttentionSessionIds, setAwaitingResponse, setBusy, setCurrentBranch, @@ -39,6 +40,7 @@ import { setSessionStartedAt, setSessionsTotal, setTurnStartedAt, + setWorkingSessionIds, setYoloActive } from '@/store/session' import { reportBackendContract } from '@/store/updates' @@ -293,6 +295,11 @@ export function useSessionActions({ setSelectedStoredSessionId(null) selectedStoredSessionIdRef.current = null setMessages([]) + // Clear stale session busy/attention indicators so background sessions + // that were still working when we navigated away don't visually persist + // as running rows or "needs input" dots in the sidebar of the new chat. + setWorkingSessionIds([]) + setAttentionSessionIds([]) setCurrentUsage({ calls: 0, input: 0, diff --git a/apps/desktop/src/app/session/hooks/use-session-state-cache.ts b/apps/desktop/src/app/session/hooks/use-session-state-cache.ts index 8683aa4a80c66..10116b389d2eb 100644 --- a/apps/desktop/src/app/session/hooks/use-session-state-cache.ts +++ b/apps/desktop/src/app/session/hooks/use-session-state-cache.ts @@ -157,22 +157,31 @@ export function useSessionStateCache({ setSessionAttention(previous.storedSessionId, false) } - setSessionWorking(next.storedSessionId, next.busy) - setSessionAttention(next.storedSessionId, next.needsInput) - - // Every state update is effectively a "still alive" heartbeat for - // streaming events. The session-store watchdog uses this to keep the - // working flag alive during long-running turns and to clear it once - // the stream goes silent. - if (next.busy) { - noteSessionActivity(next.storedSessionId) + // Only update the global working/attention atoms when this session is + // the one the user is currently viewing. Background sessions still + // update their own cache entry but must not re-add themselves to the + // global sidebar indicators after the user has started a new chat and + // startFreshSessionDraft cleared them — otherwise late gateway events + // from a still-busy old session make it appear as if the new chat is + // still working or waiting for input. + if (sessionId === activeSessionIdRef.current) { + setSessionWorking(next.storedSessionId, next.busy) + setSessionAttention(next.storedSessionId, next.needsInput) + + // Every state update is effectively a "still alive" heartbeat for + // streaming events. The session-store watchdog uses this to keep the + // working flag alive during long-running turns and to clear it once + // the stream goes silent. + if (next.busy) { + noteSessionActivity(next.storedSessionId) + } } syncSessionStateToView(sessionId, next) return next }, - [ensureSessionState, syncSessionStateToView] + [activeSessionIdRef, ensureSessionState, syncSessionStateToView] ) return {