Files
oh-my-opencode/src/shared/is-abort-error.ts
T
YeonGyu-Kim 1631509989 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
2026-04-03 18:51:10 +09:00

21 lines
765 B
TypeScript

export function isAbortError(error: unknown): boolean {
if (!error) return false
if (typeof error === "object") {
const errObj = error as Record<string, unknown>
const name = errObj.name as string | undefined
const message = (errObj.message as string | undefined)?.toLowerCase() ?? ""
if (name === "MessageAbortedError" || name === "AbortError") return true
if (name === "DOMException" && message.includes("abort")) return true
if (message.includes("aborted") || message.includes("cancelled") || message.includes("interrupted")) return true
}
if (typeof error === "string") {
const lower = error.toLowerCase()
return lower.includes("abort") || lower.includes("cancel") || lower.includes("interrupt")
}
return false
}