Events

The thirteen lifecycle events Claude Code fires. For the lifecycle narrative, see how hooks work.

A hook targets one event, or several combined with |:

from captain_hook import hook, Event

hook(Event.Stop | Event.SubagentStop, message="Review before finishing", block=True)

Event is a Flag enum, so | registers one hook for every named event at once.

Event reference

Each row lists the event’s typed class, when it fires, its key accessors, and whether it can block.

Event Event class Fires Key properties Can block?
SessionStart SessionStartEvent When a session starts, resumes, clears, or compacts source No — advisory
PreToolUse PreToolUseEvent Before a tool runs tool_name, cmd, file, content, old, agent_type Yes — deny the tool
PermissionRequest PermissionRequestEvent When a permission dialog would be shown permission_suggestions, agent_type, plus the tool accessors tool_name, cmd, file, … Yes — answer the dialog with allow, deny, or rewrite
PostToolUse PostToolUseEvent After a tool succeeds tool_name, command, file, content, tool_response No — advisory
PostToolUseFailure PostToolUseFailureEvent After a tool fails tool_name, error, is_interrupt No — advisory
UserPromptSubmit UserPromptSubmitEvent When the user submits a prompt user_prompt No — advisory
Stop StopEvent When the agent is about to stop stop_hook_active, background_tasks, session_crons Yes — force-continue
SubagentStop SubagentStopEvent When a subagent finishes agent_type, background_tasks, session_crons Yes — force-continue
SubagentStart SubagentStartEvent When a subagent launches agent_type No — advisory
PreCompact PreCompactEvent Before context compaction trigger, custom_instructions Yes — cancel the compaction; a warn’s message joins its custom instructions
Notification NotificationEvent On a system notification message, title, notification_type No — advisory
SessionEnd SessionEndEvent When the session ends reason No — output ignored
MessageDisplay MessageDisplayEvent For each streamed chunk of an assistant reply message_id, index, final, delta No — a rewrite replaces the chunk’s displayed text

Host events

One more event exists, and Claude Code never fires it. The capt-hookd host raises ResourcePressure, typed as ResourcePressureEvent, when a child of a session stays resource-heavy. It has no entry in hooks.json, so it adds nothing to the thirteen lifecycle events above. The performance pack’s resource monitor is its only handler. See Tune the subprocess resource monitor for the thresholds.

The event arrives in one of three stages, readable as evt.stage:

Stage Fires
warn On the first sustained sample of heavy CPU or disk use
judge When the grace period ends and the child is still heavy
escalate When the child outlives a SIGTERM
Property Meaning
stage "warn", "judge", or "escalate"
process The child’s identity and usage as a mapping
pid, ppid, pgid The child’s process ids
start_unix, start_usec The child’s start time
comm, argv The child’s command name and arguments
process_cwd The child’s working directory, or None
runtime_s How long the child has run, in seconds
cpu_fraction, disk_bps Measured usage, or None when the reading was unavailable
claude_pid, claude_start_unix The session’s claude process, which anchors ownership

The host refuses a ResourcePressure event that a client submits, so only the host can raise it.

Only the warn stage reads the answer. A warn or an allow renders {"decision": "proceed"}, which tells the host the stage ran. For a warn, the host reads anything else as a refusal and stops tracking that child, so the child is left running and is not warned about again. The host does not read the answers to judge and escalate. The worker records a notice for each refusal instead, and the next hook event delivers it to the session.

PreToolUse and PermissionRequest prevent an action. A PreToolUse block denies the tool call outright, and a PermissionRequest result answers the pending dialog. allow and rewrite approve it silently, block denies it with the message shown to the user, and None or a warn lets the dialog show normally. Stop and SubagentStop force the agent to keep going. A MessageDisplay rewrite replaces what the user sees of one reply chunk, and an empty message hides it. A PreCompact block cancels the compaction, and any other message is appended to the summarizer’s custom instructions. Every other event is advisory — a hook injects context but cannot block.

On Stop and SubagentStop, background_tasks and session_crons report the background work and scheduled prompts holding the session open — a non-empty tuple means it is paused for that work, not finished, which is what Waiting() reads.

Shared properties

Every event also exposes the base accessors: event, session_id, is_subagent, transcript_path, cwd, permission_mode, skip_permissions, disallowed_tools, parent_agent_type, stop_hook_active, tasks, and ctx. cwd is the session’s working directory when the event fired, for resolving relative tool paths. skip_permissions walks the process tree and reports whether the session’s claude process was launched with --dangerously-skip-permissions or --allow-dangerously-skip-permissions. disallowed_tools reads the same process and returns the tool names it was launched with under --disallowedTools, so a hook can stay silent when the tool its message names does not exist in the session. tasks is the live Claude Code task list; ctx is the hook context, with the transcript, settings, and the current turn.

Typed tool input

evt.input is the parsed tool call for the current event, typed as one of the *Call classes such as ReadCall and BashCall. evt.as_input(call_type) narrows it:

Method Returns
evt.input The parsed ToolCall; OtherCall for an unrecognized tool, FallbackCall for input outside the JSON contract
evt.as_input(ReadCall) evt.input typed as ReadCall, or None when the event is a different tool

For a non-matching tool as_input returns None instead of raising, so a hook reads typed fields without an isinstance check:

from captain_hook import ReadCall

call = evt.as_input(ReadCall)
if call and call.limit and call.limit > 1000:
    return evt.warn("Large read; narrow with offset/limit.")

ReadCall carries .file_path, .offset, and .limit; each *Call type exposes the fields of its tool. For a declarative form, subclass CustomInputTypeCondition[ReadCall].