[FEATURE] Replace idle_prompt timer heuristic with deterministic state-aware idle detection
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
The idle_prompt notification hook uses a 60-second timer to guess when Claude is idle. This is fundamentally the wrong approach — Claude Code is a state machine that knows whether it's idle or actively working. The timer causes two problems:
- ~1 minute delay before notification: When Claude finishes its work and is genuinely waiting for input, the user doesn't find out for 60 seconds. This is especially painful when multitasking across multiple Claude sessions.
- False positives during "thinking" pauses: Claude sometimes pauses for extended periods while reasoning (e.g., between tool calls, during complex planning). The timer can't distinguish "Claude is thinking" from "Claude is done and waiting" — but Claude Code itself absolutely can, because it knows whether it's mid-turn or has yielded control back to the user.
Proposed Solution
Emit idle notifications immediately and deterministically based on Claude Code's internal state, not a timer:
- When Claude's turn ends and the input prompt is shown → fire notification immediately (0 delay, not 60s)
- When Claude is mid-turn (thinking, waiting for API response, between tool calls) → do not fire, regardless of elapsed time
- When Claude is waiting for permission approval → fire
permission_promptimmediately (this already works better)
Additionally, include background task metadata in the hook's stdin JSON, so the notification hook can make smarter decisions:
{
"notification_type": "idle_prompt",
"cwd": "/home/user/project",
"message": "Claude is waiting for your input",
"background_tasks": [
{"id": "task-123", "description": "Running tests", "status": "running"},
{"id": "task-456", "description": "Building project", "status": "running"}
],
"has_running_background_tasks": true
}
This lets hook scripts decide whether to notify differently when background work is still in progress (e.g., "Claude is idle but 2 background tasks are still running" vs "Claude is completely done").
Related Issues
- #13922 — Configurable timeout for idle_prompt (workaround for the same root cause)
- #21238 — Immediate notification when Claude awaits user input
- #12048 — idle_prompt fires after every response / false positives (closed)
- #19627 — High latency in hook invocation
- #23383 — Notification hook delay vs Stop hook
These issues all stem from the same root cause: using a timer heuristic instead of leveraging Claude Code's own state machine. A deterministic approach would resolve all of them.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗