fix: prevent run completion race condition with consecutive stability checks

pollForCompletion exited immediately when session went idle before agent
created TODOs or registered children (0 todos + 0 children = vacuously
complete). Add consecutive stability checks (3x500ms debounce) and
currentTool guard to prevent premature exit.

Extract pollForCompletion to dedicated module for testability.
This commit is contained in:
YeonGyu-Kim
2026-02-09 10:41:51 +09:00
parent 2aa93aacfb
commit 81af4fbb13
3 changed files with 286 additions and 28 deletions
+1 -28
View File
@@ -1,6 +1,5 @@
import pc from "picocolors"
import type { RunOptions, RunContext } from "./types"
import { checkCompletionConditions } from "./completion"
import { createEventState, processEvents, serializeError } from "./events"
import { loadPluginConfig } from "../../plugin-config"
import { createServerConnection } from "./server-connection"
@@ -8,10 +7,10 @@ import { resolveSession } from "./session-resolver"
import { createJsonOutputManager } from "./json-output"
import { executeOnCompleteHook } from "./on-complete-hook"
import { resolveRunAgent } from "./agent-resolver"
import { pollForCompletion } from "./poll-for-completion"
export { resolveRunAgent }
const POLL_INTERVAL_MS = 500
const DEFAULT_TIMEOUT_MS = 0
export async function run(options: RunOptions): Promise<number> {
@@ -124,30 +123,4 @@ export async function run(options: RunOptions): Promise<number> {
}
}
async function pollForCompletion(
ctx: RunContext,
eventState: ReturnType<typeof createEventState>,
abortController: AbortController
): Promise<number> {
while (!abortController.signal.aborted) {
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS))
if (!eventState.mainSessionIdle) continue
if (eventState.mainSessionError) {
console.error(pc.red(`\n\nSession ended with error: ${eventState.lastError}`))
console.error(pc.yellow("Check if todos were completed before the error."))
return 1
}
if (!eventState.hasReceivedMeaningfulWork) continue
const shouldExit = await checkCompletionConditions(ctx)
if (shouldExit) {
console.log(pc.green("\n\nAll tasks completed."))
return 0
}
}
return 130
}