The agent loop of D1.1 runs the model’s tool calls automatically. Hooks are how the architect gets between the model and those calls — to block a dangerous one, rewrite its input, or clean up its output — without editing the model’s prompt. The exam tests three things: which events exist, the two modes of intervention, and who wins when hooks disagree.
Hooks intercept the loop at named events
A hook is a callback that runs your code in response to an agent event: “Hooks are callback functions that run your code in response to agent events, like a tool being called, a session starting, or execution stopping.”
[Official]
Intercept and control agent behavior with hooks · AnthropicT1-official original They arrive through two channels that share one lifecycle — programmatic hooks (callbacks in your query() options) and filesystem hooks (shell commands in settings.json, loaded via settingSources).
[Official]
Intercept and control agent behavior with hooks · AnthropicT1-official original
The events you must recognize
Hooks fire at named lifecycle points. The Python SDK exposes ten; the TypeScript SDK extends the same set to twenty. [Official] Intercept and control agent behavior with hooks · AnthropicT1-official original The ones an architect must recognize:
| Event | Fires when | Typical use |
|---|---|---|
PreToolUse | a tool call is requested | block or rewrite the call before it runs |
PostToolUse | a tool returns a result | normalize or replace the result before the model sees it |
PostToolUseFailure | a tool execution fails | log or handle the error |
UserPromptSubmit | a prompt is submitted | inject extra context |
Stop | the agent stops | persist state before exit |
SubagentStart / SubagentStop | a subagent begins / ends | track spawned parallel work |
PreCompact | compaction is about to run | archive the full transcript first |
PermissionRequest | a permission dialog would appear | custom permission handling |
Notification | an agent status message | forward to Slack / PagerDuty |
The TypeScript-only additions (PostToolBatch, SessionStart, SessionEnd, Setup, and others) are why SessionStart / SessionEnd are not available as Python SDK callbacks — Python apps needing them load filesystem hooks from settings instead.
[Official]
Intercept and control agent behavior with hooks · AnthropicT1-official original
PreToolUse gates; PostToolUse normalizes
The two most-tested events define the two interception modes, and they sit on opposite sides of tool execution. [Official] Intercept and control agent behavior with hooks · AnthropicT1-official original
PreToolUsegates — it runs before the tool and returns apermissionDecisionofallow,deny,ask, ordefer, optionally withupdatedInputto rewrite the call. Blocking a write to a.envfile is aPreToolUsehook matchingWrite|Editthat returnsdenywhen the target path ends in.env.PostToolUsenormalizes — it runs after the tool and returns eitheradditionalContext(appended to the result) orupdatedToolOutput(which replaces the output before Claude sees it). Stripping noise, redacting secrets, or reshaping a tool’s response into a clean form isPostToolUsework.
Matchers select which calls a hook sees: they are regex strings tested against the tool name — "Write|Edit", "^mcp__" for all MCP tools, or omitted to match everything.
[Official]
Intercept and control agent behavior with hooks · AnthropicT1-official original Matchers do not filter by argument; filter on the file path or command inside the callback.
Precedence: deny beats defer beats ask beats allow
When several hooks (or permission rules) act on one event, the outcome is decided by a fixed precedence, not by who ran first: “When multiple hooks or permission rules apply, deny takes priority over defer, which takes priority over ask, which takes priority over allow. If any hook returns deny, the operation is blocked regardless of other hooks.”
[Official]
Intercept and control agent behavior with hooks · AnthropicT1-official original The four decisions are not symmetric, and defer is the one candidates miss:
Hooks and subagents
Two subagent-aware events — SubagentStart and SubagentStop — let you track spawned work, but the operational gotcha is about permissions. As D1.3 established, subagents do not inherit the parent’s permissions; each runs its own evaluation chain.
[Official]
Configure permissions · AnthropicT1-official original The clean way to pre-approve a subagent’s tools is a PreToolUse hook rather than re-prompting inside every child.
[Official]
Intercept and control agent behavior with hooks · AnthropicT1-official original
Practice
Exercise solutions
(a) is a gate, (b) is a normalization — opposite sides of tool execution, so they need different events. (a) Use a PreToolUse hook with a matcher of "Write|Edit"; in the callback, inspect the target path and return hookSpecificOutput.permissionDecision: "deny" when it ends in .env. The decision must come before the write runs. (b) Use a PostToolUse hook with a matcher of "Bash"; return hookSpecificOutput.updatedToolOutput containing the result with ANSI codes stripped — updatedToolOutput replaces the output before Claude sees it. (b) can’t reuse PreToolUse because the output does not exist yet when the call is requested; you can only normalize a result after the tool has produced it.
The call is blocked. All matching hooks run in parallel and the most restrictive result wins, so the deny overrides the allow and the ask — the precedence is deny > defer > ask > allow. The one-word rule is restrictive (equivalently, “deny wins”): one hook saying deny is enough to block; permitting requires every hook to agree.
Both expectations are wrong. defer does not run the call — it ends the query so the host can resume it later from the persisted session (a pause-and-hand-back, not an allow). And updatedInput is ignored with defer — that field applies only to allow (or ask). To run a rewritten command, the hook must return allow with updatedInput, not defer.
Exam essentials
- A hook is a callback at a named lifecycle event, delivered programmatically (query options) or via filesystem settings. It runs in your process and does not consume agent context.
- Two interception modes:
PreToolUsegates (returnsallow/deny/ask/defer+ optionalupdatedInput);PostToolUsenormalizes (updatedToolOutputreplaces the result,additionalContextappends). - The four decisions:
allow/ask/deny, anddefer— ends the query so the host can resume it later from the persisted session (andupdatedInputis ignored withdefer). - Precedence is
deny > defer > ask > allow. Matching hooks run in parallel; the most restrictive wins; onedenyblocks. - Don’t rely on hook order (non-deterministic) — write each hook independently.
- Subagents don’t inherit permissions; pre-approve their tools with a
PreToolUsehook. Watch the silent-failure traps: case-sensitive names,max_turnscut-offs, recursive subagent loops.