Lifecycle Hooks

Hooks let you observe and gate agent activity as it happens. You register a named callback against a lifecycle event, the runtime invokes it at that point, and the callback returns a decision such as { action: "continue" }. Use hooks for auditing, redaction, logging, or enforcing policy without changing the agent's prompt.

The lifecycle is symmetric: registerHook adds a callback by name, hookCount tells you how many are active, and unregisterHook removes one by its name.

Rust
Node.js
Python
Go

Notes:

  • A hook callback returns a decision. Return { action: "continue" } (Node) / {"action": "continue"} (Python) / &code.HookResponse{Action: "continue"} (Go) to let the agent proceed.
  • Permanent denials return block with a reason. Temporary denials return retry with a reason and delayMs (Node), delay_ms (Python), or DelayMS (Go). In a send or stream turn, a denied tool call emits a permission_denied event and the model receives Hook denied <tool>: <reason> instead of the tool output. For host-direct calls (session.tool(...) and the typed helpers), the denied result also carries error_kind.type = "hook_denied" with retryable and retry_after_ms, so hosts do not need to parse output text.
  • The path matcher reads the tool's file_path argument, falling back to path.
  • The matcher ({ pathPattern: '**/.env*' } in Node, {'path_pattern': ...} in Python, HookMatcher{PathPattern: ...} in Go) scopes the hook to events whose path matches the pattern, and { priority: 100 } orders hooks on the same event (lower values run first).
  • Callbacks receive the event as their only argument.
  • A Node callback that throws, returns an invalid value, or runs past its timeout (timeoutMs in the config, default 30,000 ms) counts as a failed hook. On gating events (pre_tool_use, permission_request, pre_compact, pre_prompt, pre_planning, pre_run_control) the action is blocked with Required hook '<name>' failed: ...; on other events the agent continues.
  • hookCount / hook_count / HookCount reflects the number of currently registered hooks, which is handy in tests to assert that registration and cleanup happened.
  • unregisterHook / unregister_hook / UnregisterHook takes the name you registered with. Always tear down hooks you no longer need so they do not leak across runs.
  • Validate the event path you depend on before using a hook as a production gate.