fix: reset hook state on abort so session recovers after user cancel

When a user cancels a generation (ESC x2), all three idle hooks could
enter a permanently broken state:

1. **todo-continuation-enforcer**: consecutiveFailures accumulated from
   abort-caused promptAsync failures, eventually hitting MAX_CONSECUTIVE_FAILURES
   and permanently stopping continuation injection.

2. **unstable-agent-babysitter**: no abort awareness at all — would keep
   firing reminders after user cancelled the session.

3. **runtime-fallback**: retry dedupe keys and pending fallback state
   persisted across cancellation, blocking legitimate error recovery.

Fix:
- Add shared `isAbortError()` utility for consistent abort detection
- Reset consecutiveFailures and clear stale state on AbortError in all hooks
- Track `lastCancelledAt` in todo-continuation-enforcer for abort window
- Add abort-awareness to unstable-agent-babysitter (skip if recently cancelled)
- Clear runtime-fallback retry state on abort errors

Tests: 61 pass, 0 fail across all 3 affected hook test suites.

Closes #2984
This commit is contained in:
YeonGyu-Kim
2026-04-03 18:40:02 +09:00
parent ed06428ba3
commit 1631509989
11 changed files with 344 additions and 18 deletions
@@ -42,6 +42,11 @@ export async function handleSessionIdle(args: {
return
}
if (state.wasCancelled) {
log(`[${HOOK_NAME}] Skipped: session was cancelled`, { sessionID })
return
}
if (state.abortDetectedAt) {
const timeSinceAbort = Date.now() - state.abortDetectedAt
if (timeSinceAbort < ABORT_WINDOW_MS) {