fix(atlas): stop stalled boulder continuations

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-05-18 12:43:06 +09:00
parent fff99aeb29
commit d3b4c022d2
4 changed files with 39 additions and 0 deletions
@@ -9,6 +9,7 @@ import { createInternalAgentContinuationTextPart, resolveInheritedPromptTools }
import { dispatchInternalPrompt } from "../shared/prompt-async-gate"
import { HOOK_NAME } from "./hook-name"
import { BOULDER_CONTINUATION_PROMPT } from "./system-reminder-templates"
import { markContinuationInjectedAwaitingToolProgress } from "./tool-progress"
import { resolveRecentPromptContextForSession } from "./recent-model-resolver"
import type { BackgroundTaskStatusProvider, SessionState } from "./types"
@@ -122,6 +123,7 @@ export async function injectBoulderContinuation(input: {
}
sessionState.promptFailureCount = 0
markContinuationInjectedAwaitingToolProgress(sessionState)
log(`[${HOOK_NAME}] Boulder continuation injected`, { sessionID })
return "injected"
} catch (err) {
+2
View File
@@ -96,6 +96,7 @@ export function createAtlasEventHandler(input: {
const deletedState = sessions.get(sessionID)
if (deletedState?.pendingRetryTimer) {
clearTimeout(deletedState.pendingRetryTimer)
deletedState.pendingRetryTimer = undefined
}
sessions.delete(sessionID)
log(`[${HOOK_NAME}] Session deleted: cleaned up`, { sessionID })
@@ -109,6 +110,7 @@ export function createAtlasEventHandler(input: {
const compactedState = sessions.get(sessionID)
if (compactedState?.pendingRetryTimer) {
clearTimeout(compactedState.pendingRetryTimer)
compactedState.pendingRetryTimer = undefined
}
sessions.delete(sessionID)
log(`[${HOOK_NAME}] Session compacted: cleaned up`, { sessionID })
+30
View File
@@ -25,6 +25,11 @@ import { injectBoulderContinuation } from "./boulder-continuation-injector"
import { HOOK_NAME } from "./hook-name"
import { resolveActiveBoulderSession } from "./resolve-active-boulder-session"
import { BOULDER_COMPLETE_PROMPT } from "./system-reminder-templates"
import {
markContinuationStalled,
shouldAbortForNoToolProgress,
updateNoToolProgressIterations,
} from "./tool-progress"
import type { AtlasHookOptions, SessionState } from "./types"
const CONTINUATION_COOLDOWN_MS = 5000
@@ -172,6 +177,7 @@ function scheduleRetry(input: {
sessionState.pendingRetryTimer = undefined
if (sessionState.promptFailureCount >= MAX_CONSECUTIVE_PROMPT_FAILURES) return
if (sessionState.stalledContinuationReason) return
if (sessionState.waitingForFinalWaveApproval) return
const now = Date.now()
@@ -352,6 +358,30 @@ export async function handleAtlasSessionIdle(input: {
return
}
if (sessionState.stalledContinuationReason) {
log(`[${HOOK_NAME}] Skipped: boulder continuation stalled`, {
sessionID,
reason: sessionState.stalledContinuationReason,
})
return
}
const noProgressIterations = updateNoToolProgressIterations(sessionState)
if (shouldAbortForNoToolProgress(sessionState)) {
markContinuationStalled(sessionState, boulderState.plan_name)
if (sessionState.pendingRetryTimer) {
clearTimeout(sessionState.pendingRetryTimer)
sessionState.pendingRetryTimer = undefined
}
log(`[${HOOK_NAME}] Aborting boulder continuation after repeated no-tool-progress iterations`, {
sessionID,
plan: boulderState.plan_name,
noProgressIterations,
reason: sessionState.stalledContinuationReason,
})
return
}
if (sessionState.lastEventWasAbortError) {
sessionState.lastEventWasAbortError = false
log(`[${HOOK_NAME}] Skipped: abort error immediately before idle`, { sessionID })
+5
View File
@@ -22,6 +22,7 @@ import { DIRECT_WORK_REMINDER } from "./system-reminder-templates"
import { isOmoPath } from "./omo-path"
import { resolvePreferredSessionId, resolveTaskContext } from "./task-context"
import { extractSessionIdFromMetadata, extractSessionIdFromOutput, validateSubagentSessionId } from "./subagent-session-id"
import { didToolMakeProgress, isTangibleProgressTool, recordToolProgress } from "./tool-progress"
import {
buildCompletionGate,
buildFinalWaveApprovalReminder,
@@ -142,6 +143,10 @@ export function createToolExecuteAfterHandler(input: {
return
}
if (toolInput.sessionID && isTangibleProgressTool(toolInput.tool) && didToolMakeProgress(toolOutput)) {
recordToolProgress(getState(toolInput.sessionID))
}
if (!(await resolveIsCallerOrchestrator(toolInput.sessionID))) {
return
}