File size: 2,423 Bytes
fdc8b59 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | import { useEffect, useRef, useSyncExternalStore } from 'react'
import { useAppState, useSetAppState } from '../state/AppState.js'
import {
buildContinuationPrompt,
getGoalContinuationGoalId,
isGoalContinuationPrompt,
} from '../utils/goal.js'
import {
enqueue,
getCommandQueue,
removeByFilter,
} from '../utils/messageQueueManager.js'
import type { QueryGuard } from '../utils/QueryGuard.js'
/**
* Auto-continuation hook for /goal. Subscribes to the QueryGuard's
* isActive snapshot via useSyncExternalStore. When isActive transitions
* from true -> false (turn just ended), and a goal is pursuing, enqueue
* a meta continuation prompt.
*
* Bails out for: no goal, non-pursuing status, or plan mode.
*/
export function useGoalAutoContinue(queryGuard: QueryGuard): void {
const isActive = useSyncExternalStore(
queryGuard.subscribe,
queryGuard.getSnapshot,
)
const goal = useAppState(s => s.goal)
const mode = useAppState(s => s.toolPermissionContext.mode)
const setAppState = useSetAppState()
const wasActiveRef = useRef(isActive)
useEffect(() => {
const wasActive = wasActiveRef.current
wasActiveRef.current = isActive
// Only fire on running -> idle transition.
if (!(wasActive && !isActive)) return
if (!goal) return
if (goal.status !== 'pursuing') return
if (mode === 'plan') return
const now = Date.now()
// User/control input wins over autonomous continuation.
const queue = getCommandQueue()
const hasBlockingQueuedWork = queue.some(
cmd => !isGoalContinuationPrompt(cmd.value),
)
if (hasBlockingQueuedWork) return
const alreadyQueuedForThisGoal = queue.some(
cmd => getGoalContinuationGoalId(cmd.value) === goal.id,
)
if (alreadyQueuedForThisGoal) return
removeByFilter(cmd => {
if (!isGoalContinuationPrompt(cmd.value)) return false
return getGoalContinuationGoalId(cmd.value) !== goal.id
})
const prompt = buildContinuationPrompt(goal, now)
enqueue({
mode: 'prompt',
value: prompt,
priority: 'later',
isMeta: true,
})
setAppState(prev => ({
...prev,
goal: prev.goal && prev.goal.id === goal.id
? {
...prev.goal,
continuationCount: prev.goal.continuationCount + 1,
lastUpdatedAt: now,
}
: prev.goal,
}))
}, [isActive, goal, mode, setAppState])
} |