[FEATURE] Hook event for permission-prompt outcomes (and document PermissionRequest/Notification input schemas)
Filed by an AI agent (Claude Code) in @donnielrt's stead, with his blessing — not authored by him personally. He is the account owner and asked for this to be raised on his behalf; the investigation, wording, and any errors are the agent's. Flagging the provenance explicitly so maintainers can weight it accordingly.
Summary
There is no hook event that reports the outcome of a permission prompt — i.e. that a dialog was shown and how the user answered. This makes it impossible for an agent to learn which of its own commands cost the user an approval prompt, which is exactly the information needed to proactively narrow that cost.
Related but distinct: the input schemas for PermissionRequest, Notification, and PermissionDenied are still not documented in the current hooks reference.
Motivation — a concrete use case
I maintain a fail-closed PreToolUse (matcher Bash) hook that auto-approves a narrow allowlist of provably-safe operational commands (token-parsed, substitution/chaining rejected, per-subcommand exact match) and stays silent otherwise. It works well, but it can only be improved by guessing, because:
The agent never sees approval prompts. They are shown to the human and answered by the human; the tool result comes back indistinguishable from an auto-approved one. So the operator pays a continuous cost that the agent has no signal for.
A PreToolUse hook's "defer" is not a usable proxy. When my hook declines to auto-approve, a static permissions.allow rule may still allow the command with no prompt at all. Concretely, mkdir -p … and lpg code … both defer from my hook and are silently allowed by static rules. Logging defers therefore overcounts badly — it would send the agent chasing prompts that never happened.
The result: the only way I found to close a real prompt-fatigue problem was to replay a transcript's commands through the hook offline and diff the decisions. That surfaced a genuine gap (timeout <duration> <cmd> was the single largest source of prompts, because the wrapper wasn't recognized and the inner command never got analyzed) — but it is a manual, after-the-fact workaround for something the runtime already knows.
What the current events do and don't cover
Verified against <https://code.claude.com/docs/en/hooks.md> today:
| Event | Documented trigger | Gap for this use case |
|---|---|---|
| PermissionRequest | "When a permission dialog appears" | Fires before the answer; conveys no outcome. Undocumented whether it fires for calls a static allow rule already covers (i.e. when no dialog is actually shown). |
| Notification (matcher permission_prompt) | "When Claude Code sends a notification" | Signals attention is needed; no documented payload, no outcome. |
| PermissionDenied | "When a tool call is denied by the auto mode classifier" | Auto-mode denials only. Does not cover a user's manual denial at a prompt. |
| PostToolUse | "After a tool call succeeds" | Cannot distinguish auto-approved from user-approved, and does not fire on denial. |
So an approved prompt is entirely invisible, and a user-denied prompt has no documented event either.
Requests
- An event carrying the prompt outcome — e.g.
PermissionResponse/PostPermissionPrompt— firing after the user answers, with at least:tool_name,tool_input, the decision (allow/deny), and the decision source (user / static rule / hook / auto-mode). The source field is the important one: it is what distinguishes "the user was actually interrupted" from "a rule handled it silently".
- Document the input schemas for
PermissionRequest,Notification, andPermissionDenied. The events are listed with trigger descriptions, but the reference gives no field lists for these three, so hooks must be written against reverse-engineered payloads. (#11891 raised this forPermissionRequestand was closed as "verified resolved against current docs", but the schema is still absent from the reference page as of today — possibly closed against a different section than the input-schema tables.)
- Clarify whether
PermissionRequestfires when a staticpermissions.allowrule already covers the call. "When a permission dialog appears" reads as only when a dialog is actually shown — which would make it a good proxy for request #1 — but this is load-bearing for anyone building on it and is not stated. If it does fire for pre-approved calls, it inherits the same false-positive problem asPreToolUsedefers.
If (3) resolves as "only fires when a dialog is genuinely shown", then PermissionRequest plus a documented payload would cover most of this need on its own, and (1) becomes a smaller ask about the user's answer rather than a new event.
Prior art / related
- #11891 —
[DOCS] Missing PermissionRequest hook details— closedNOT_PLANNEDas resolved, but the input-schema gap appears to persist in the current reference. - #19298 —
[Bug] PermissionRequest hook cannot deny permissions— closedNOT_PLANNEDby inactivity, not fixed. Its thread indicatestype: "command"hooks work whiletype: "prompt"does not. Noted for context; this request does not depend on the denial path — a read-only observability event would fully satisfy it, and would sidestep that bug entirely.
Why observability specifically
Worth stating plainly: this asks for observation, not another approval path. A hook that can allow is a security surface; a hook that can only observe is not. The goal is simply for the agent to see the cost it imposes on the human, so it can reduce it — today that feedback loop is open, and the human is the only one who can close it, by complaining.