diff --git a/src/hooks/useSessionProtection.ts b/src/hooks/useSessionProtection.ts index cbdcdda..cb5f5ca 100644 --- a/src/hooks/useSessionProtection.ts +++ b/src/hooks/useSessionProtection.ts @@ -3,6 +3,10 @@ import { useCallback, useState } from 'react'; export function useSessionProtection() { const [activeSessions, setActiveSessions] = useState>(new Set()); const [processingSessions, setProcessingSessions] = useState>(new Set()); + // sessionId -> epoch ms when processing began (for the live elapsed timer) + const [processingStartTimes, setProcessingStartTimes] = useState>(new Map()); + // sessions waiting on the user (permission / AskUserQuestion pending) + const [needsInputSessions, setNeedsInputSessions] = useState>(new Set()); const markSessionAsActive = useCallback((sessionId?: string | null) => { if (!sessionId) { @@ -30,6 +34,16 @@ export function useSessionProtection() { } setProcessingSessions((prev) => new Set([...prev, sessionId])); + setProcessingStartTimes((prev) => { + // Preserve an existing start time so the elapsed timer doesn't reset on + // repeated marks for the same run. + if (prev.has(sessionId)) { + return prev; + } + const next = new Map(prev); + next.set(sessionId, Date.now()); + return next; + }); }, []); const markSessionAsNotProcessing = useCallback((sessionId?: string | null) => { @@ -42,14 +56,49 @@ export function useSessionProtection() { next.delete(sessionId); return next; }); + setProcessingStartTimes((prev) => { + if (!prev.has(sessionId)) { + return prev; + } + const next = new Map(prev); + next.delete(sessionId); + return next; + }); + }, []); + + const markSessionAsNeedsInput = useCallback((sessionId?: string | null) => { + if (!sessionId) { + return; + } + + setNeedsInputSessions((prev) => new Set([...prev, sessionId])); + }, []); + + const clearSessionNeedsInput = useCallback((sessionId?: string | null) => { + if (!sessionId) { + return; + } + + setNeedsInputSessions((prev) => { + if (!prev.has(sessionId)) { + return prev; + } + const next = new Set(prev); + next.delete(sessionId); + return next; + }); }, []); return { activeSessions, processingSessions, + processingStartTimes, + needsInputSessions, markSessionAsActive, markSessionAsInactive, markSessionAsProcessing, markSessionAsNotProcessing, + markSessionAsNeedsInput, + clearSessionNeedsInput, }; }