2.1.141: Notification:permission_prompt hook event stops firing during active thinking (regression from 2.1.139)
Summary
In 2.1.141, the Notification hook event with notification_type: permission_prompt does not fire when a permission prompt appears while the assistant is in an active thinking phase. The prompt renders correctly in the TUI; downstream hooks subscribed to Notification never run.
Confirmed working in 2.1.139 with the same project, settings, and hook scripts. 2.1.140 not tested.
Repro
- Configure a
Notificationhook with matcherpermission_prompt:
``jsonc`
"Notification": [{
"matcher": "permission_prompt",
"hooks": [{"type": "command", "command": "/tmp/log-prompt.sh"}]
}]
log-prompt.sh
where is echo "$(date '+%H:%M:%S') fired" >> /tmp/prompts.log`.
- Run any task in extended-thinking mode that triggers a permission prompt mid-reasoning. The unsandboxed-Bash override prompt is reliable because it always appears mid-thinking.
- Observe: prompt renders, user sees it.
/tmp/prompts.loggets no new line. - Downgrade to 2.1.139, repeat.
/tmp/prompts.loggets a new line for every prompt.
PreToolUse and AskUserQuestion hooks fire correctly on 2.1.141 — only Notification:permission_prompt is affected.
Suspected mechanism (from the 2.1.141 binary)
The permission_prompt Notification trigger is gated by a 6-second user-idle check:
const NIK = 6000;
function AR() { return m_.lastInteractionTime; }
function hF3() { return Date.now() - AR(); }
function EF3(H){ return hF3() < H; }
function SF3(H){ return !EF3(H); } // idle ≥ H ms
function KG_(message, notificationType) { // permission_prompt path
...
P1(() => {
if (SF3(NIK)) {
setFired(true);
MHH({ message, notificationType }, q); // ← actual hook fire
}
}, fired ? null : NIK);
}
m_.lastInteractionTime is reset by bt8() (called via u6H(true) → Ci8()). Ci8() is invoked from the SDK control-request handlers:
if (plK(K.request)) Ci8(...) // adjacent to can_use_tool / pendingRequests
if (mlK(_)) Ci8(...) // adjacent to onControlRequestResolved
The release note for 2.1.141 mentions the spinner now warms to amber after 10 seconds during long thinking periods. That feature appears to emit periodic progress/heartbeat control messages on the same channel — each tick matches plK/mlK, calls Ci8 → bt8 → lastInteractionTime = Date.now(). The 6-second idle window never accumulates during thinking, so SF3(6000) stays false and MHH(...) is never reached.
Symbol names may roll in subsequent versions; the structural pattern is KG_(..., "permission_prompt"), gated by SF3(NIK) with NIK = 6000, with lastInteractionTime reset via Ci8() from control-request predicates.
Impact
Any hook subscribed to Notification:permission_prompt silently misses prompts that arrive mid-thinking. The terminalSequence hook output field added in this same release is also effectively unreachable for permission_prompt, since it depends on the parent event firing.
Suggested fix
Exclude progress/spinner heartbeats from the user-interaction reset. Options:
- Narrow
plK/mlKpredicates so spinner heartbeats don't match. - Tag heartbeat control messages and skip
Ci8()for them. - Decouple the
permission_promptNotification gate fromlastInteractionTime— user-input idle is the wrong proxy when the assistant is what's keeping the system "active".
Environment
- claude-code 2.1.141 (Mach-O arm64, macOS 25.5.0)
- Verified working: 2.1.139
- Not tested: 2.1.140
- Terminal: VS Code Insiders integrated terminal + tmux; also reproduces in Ghostty
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗