🎯 Use case
This file lives under “hooks/”, which covers reusable UI or integration hooks. On the API surface it exposes useSessionBackgrounding — mainly functions, hooks, or classes. Dependencies touch React UI. It composes internal code from state and types (relative imports). What the file header says: Hook for managing session backgrounding (Ctrl+B to background/foreground sessions). Handles: - Calling onBackgroundQuery to spawn a background task for the current query - Re-backgrounding foregrounded tasks - Syncing foregrounded task messages/state to main view.
Generated from folder role, exports, dependency roots, and inline comments — not hand-reviewed for every path.
🧠 Inline summary
Hook for managing session backgrounding (Ctrl+B to background/foreground sessions). Handles: - Calling onBackgroundQuery to spawn a background task for the current query - Re-backgrounding foregrounded tasks - Syncing foregrounded task messages/state to main view
📤 Exports (heuristic)
useSessionBackgrounding
📚 External import roots
Package roots from from "…" (relative paths omitted).
react
🖥️ Source preview
/**
* Hook for managing session backgrounding (Ctrl+B to background/foreground sessions).
*
* Handles:
* - Calling onBackgroundQuery to spawn a background task for the current query
* - Re-backgrounding foregrounded tasks
* - Syncing foregrounded task messages/state to main view
*/
import { useCallback, useEffect, useRef } from 'react'
import { useAppState, useSetAppState } from '../state/AppState.js'
import type { Message } from '../types/message.js'
type UseSessionBackgroundingProps = {
setMessages: (messages: Message[] | ((prev: Message[]) => Message[])) => void
setIsLoading: (loading: boolean) => void
resetLoadingState: () => void
setAbortController: (controller: AbortController | null) => void
onBackgroundQuery: () => void
}
type UseSessionBackgroundingResult = {
/** Call when user wants to background (Ctrl+B) */
handleBackgroundSession: () => void
}
export function useSessionBackgrounding({
setMessages,
setIsLoading,
resetLoadingState,
setAbortController,
onBackgroundQuery,
}: UseSessionBackgroundingProps): UseSessionBackgroundingResult {
const foregroundedTaskId = useAppState(s => s.foregroundedTaskId)
const foregroundedTask = useAppState(s =>
s.foregroundedTaskId ? s.tasks[s.foregroundedTaskId] : undefined,
)
const setAppState = useSetAppState()
const lastSyncedMessagesLengthRef = useRef<number>(0)
const handleBackgroundSession = useCallback(() => {
if (foregroundedTaskId) {
// Re-background the foregrounded task
setAppState(prev => {
const taskId = prev.foregroundedTaskId
if (!taskId) return prev
const task = prev.tasks[taskId]
if (!task) {
return { ...prev, foregroundedTaskId: undefined }
}
return {
...prev,
foregroundedTaskId: undefined,
tasks: {
...prev.tasks,
[taskId]: { ...task, isBackgrounded: true },
},
}
})
setMessages([])
resetLoadingState()
setAbortController(null)
return
}
onBackgroundQuery()
}, [
foregroundedTaskId,
setAppState,
setMessages,
resetLoadingState,
setAbortController,
onBackgroundQuery,
])
// Sync foregrounded task's messages and loading state to the main view
useEffect(() => {
if (!foregroundedTaskId) {
// Reset when no foregrounded task
lastSyncedMessagesLengthRef.current = 0
return
}
if (!foregroundedTask || foregroundedTask.type !== 'local_agent') {
setAppState(prev => ({ ...prev, foregroundedTaskId: undefined }))
resetLoadingState()
lastSyncedMessagesLengthRef.current = 0
return
}
// Sync messages from background task to main view
// Only update if messages have actually changed to avoid redundant renders
const taskMessages = foregroundedTask.messages ?? []
if (taskMessages.length !== lastSyncedMessagesLengthRef.current) {
lastSyncedMessagesLengthRef.current = taskMessages.length
setMessages([...taskMessages])
}
if (foregroundedTask.status === 'running') {
// Check if the task was aborted (user pressed Escape)
const taskAbortController = foregroundedTask.abortController
if (taskAbortController?.signal.aborted) {
// Task was aborted - clear foregrounded state immediately
setAppState(prev => {
if (!prev.foregroundedTaskId) return prev
const task = prev.tasks[prev.foregroundedTaskId]
if (!task) return { ...prev, foregroundedTaskId: undefined }
return {
...prev,
foregroundedTaskId: undefined,
tasks: {
...prev.tasks,
[prev.foregroundedTaskId]: { ...task, isBackgrounded: true },
},
}
})
resetLoadingState()
setAbortController(null)
lastSyncedMessagesLengthRef.current = 0
return
}
setIsLoading(true)
// Set abort controller to the foregrounded task's controller for Escape handling
if (taskAbortController) {
setAbortController(taskAbortController)
}
} else {
// Task completed - restore to background and clear foregrounded view
setAppState(prev => {
const taskId = prev.foregroundedTaskId
if (!taskId) return prev
const task = prev.tasks[taskId]
if (!task) return { ...prev, foregroundedTaskId: undefined }
return {
...prev,
foregroundedTaskId: undefined,
tasks: { ...prev.tasks, [taskId]: { ...task, isBackgrounded: true } },
}
})
resetLoadingState()
setAbortController(null)
lastSyncedMessagesLengthRef.current = 0
}
}, [
foregroundedTaskId,
foregroundedTask,
setAppState,
setMessages,
setIsLoading,
resetLoadingState,
setAbortController,
])
return {
handleBackgroundSession,
}
}