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
7 changes: 7 additions & 0 deletions apps/desktop/src/app/session/hooks/use-session-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
getRememberedWorkspaceCwd,
sessionPinId,
setActiveSessionId,
setAttentionSessionIds,
setAwaitingResponse,
setBusy,
setCurrentBranch,
Expand All @@ -39,6 +40,7 @@ import {
setSessionStartedAt,
setSessionsTotal,
setTurnStartedAt,
setWorkingSessionIds,
setYoloActive
} from '@/store/session'
import { reportBackendContract } from '@/store/updates'
Expand Down Expand Up @@ -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,
Expand Down
29 changes: 19 additions & 10 deletions apps/desktop/src/app/session/hooks/use-session-state-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down