Hooks
Hooks register lifecycle callbacks inside a session. The registration lifecycle
is registerHook(), hookCount(), and unregisterHook().
Events
The Node.js and Python registration helpers accept these stable names:
Rust Core also exposes permission_request, pre_compact, and post_compact
as first-class lifecycle points. The Go bridge can register the same serialized
enum names. Those three names are not yet accepted by the Node.js or Python
string parsers, so use a Rust or Go host when policy must intercept permission
or compaction directly.
Core also defines perception, memory, planning, reasoning, rate-limit, confirmation, success, and intent events for specialized harnesses. Treat an event as a policy boundary only when the protected runtime path consumes its decision.
Registration Example
A handler may return { action: 'continue' }, { action: 'skip' },
{ action: 'block', reason }, { action: 'retry', reason, delayMs }, or
null/undefined to continue. Validate the specific event path you depend on
before using a hook as a production gate.
Lifecycle Governance
The gating events are pre_tool_use, permission_request, pre_compact,
pre_prompt, and pre_planning. A handler failure or timeout at one of these
points fails closed. Other events are observational or advisory and continue
when their handler infrastructure fails.
pre_tool_use can replace arguments before the tool reaches confirmation or
side effects:
Core accepts updatedInput, updated_input, args, or a direct argument
object inside modified (and also accepts the Codex-style
hookSpecificOutput wrapper). The rewritten object is validated against the
tool's JSON Schema again. Invalid rewritten arguments are rejected before any
tool side effect.
pre_prompt can return modified.prompt and optional
modified.additionalContext (or additional_context). The resulting prompt,
including the bounded hook-context block, replaces the actual user message
sent to the model. permission_request can return decision: 'allow' or
decision: 'deny'; pre_compact can block compaction. session_start and
session_end provide matching lifecycle observations for host cleanup and
audit.
Denial Feedback
Use block when retrying the same invocation cannot succeed without changing
the request, arguments, or policy context. Use retry for a temporary
condition and include both a reason and suggested delay:
Python callbacks use delay_ms; Go callbacks return
&code.HookResponse{Action: "retry", Reason: "...", DelayMS: 1000}.
The current invocation is denied rather than automatically scheduled. The
model receives the explanation and explicit retry guidance, while direct SDK
callers receive a structured tool error:
A block response uses the same error type with retryable: false and
retry_after_ms: null. Rust callers that need the retry explanation can use
HookEngine::fire_outcome(); the existing fire() API retains its legacy
HookResult projection.
Propagation
Delegation and automatic subagent fan-out use the task tool. When a product
depends on hook behavior across delegated runs, cover that product path with an
integration test.