This page is the task-first guide. For the low-level type and payload inventory, see Hooks reference.
What this guide is for
Use this guide when you want to:- add approval gates
- log or audit agent behavior
- block unsafe tool inputs or outputs
- choose between in-process, command, and HTTP hook runtimes
Hook points
Eight extension points are available:
Classification is determined by the
is_pre() and is_post() methods on HookPoint. Foreground hooks block loop progression; background hooks run asynchronously for observation.
Capabilities
The former
Rewrite capability is deleted entirely; semantic patch authority is removed.
Execution modes
Semantic patch publication is retired.
RuntimeHookResponse rejects unknown fields, so a runtime response carrying any legacy patches field fails closed during deserialization.
How to add a hook
1
Choose hook point and capability
Decide which
HookPoint to fire at (e.g., PreToolExecution for tool guardrails) and whether the hook needs Observe or Guardrail.2
Choose a runtime
Pick one of the three runtimes: in-process (Rust closure), command (subprocess), or HTTP (remote endpoint).
3
Add configuration
Add a
[[hooks.entries]] block to the active realm config.toml (or register programmatically via HooksConfig).4
Implement the handler
Write the handler that receives a
HookInvocation and returns a RuntimeHookResponse with an optional decision.5
Test with overrides
Use
HookRunOverrides to test your hook in isolation through RPC, REST, MCP, or embedded builders before committing the config.The docs below show run-scoped hook overrides conceptually, but the current CLI does not expose run-scoped hook override flags in normal builds. For now, use RPC, REST, MCP, or the SDK/embedded surfaces when you want per-run hook overrides.
Runtimes
- In-process
- Command (subprocess)
- HTTP
Calls a registered Rust closure. Config:(
name is accepted as a legacy alias for handler.)The handler is registered at engine construction time via DefaultHookEngine::with_in_process_handler() or register_in_process_handler(). The handler type is:HookEngineError values; they are never converted into warning-only success or a hook-local Deny decision. Pre-point background hooks are still required to be Observe, because they cannot safely block a pre-execution boundary after the loop has moved on.
Decisions and patches
HookDecision
HookDecision
Retired patch payloads
Retired patch payloads
The
HookPatch type is deleted entirely. Older payloads such as llm_request, assistant_text, tool_args, tool_result, and run_result fail closed during deserialization instead of being ignored or applied.Hooks can observe typed projections and return Allow or Deny. Canonical provider parameters, assistant text, tool arguments, tool results, and final run text are owned by the runtime and tool execution path.Runtime hook response
All three runtimes return the sameRuntimeHookResponse structure:
decision is optional and is the only field. The struct is declared with deny_unknown_fields, so any legacy patches field — even an empty one — is rejected at deserialization.
Invocation payload
Hooks receive aHookInvocation struct containing contextual data. Fields are populated based on the hook point:
For command and HTTP hooks, the serialized wire payload additionally carries
prompt and error strings. These are serialize-only projections of prompt_input and error_report; they are never stored fields and are never deserialized back as authority.
Supporting types
Supporting types
HookLlmRequest:max_tokens,temperature,provider_params,message_countHookLlmResponse:assistant_text,tool_call_names,stop_reason,usage,server_tool_content(typedServerToolKindprojection of provider-native server-tool blocks in the response, e.g. web search — empty when none; lets a foreground hook classify provider-native content ingestion synchronously)HookToolCall:tool_use_id,name,args,provenance(typedToolProvenanceof the called tool — kind + source id, e.g. an MCP server name — projected from the tool’sToolDef; absent for tools without declared provenance)HookToolResult:tool_use_id,name,content_blocks,is_error,provenance(as onHookToolCall; the wire payload adds a serialize-onlycontenttext projection)
HookToolResult.content_blocks carries the typed tool-result blocks, including image blocks, in original order. The wire payload also serializes a content string — a text projection derived from content_blocks for external command/HTTP hooks; in Rust, use HookToolResult::text_projection(). Both are observational projections and cannot be returned as a rewrite.
Priority ordering and deny short-circuiting
Foreground hooks are sorted bypriority (ascending), then by registration_index (ascending, for determinism when priorities are equal). Lower numeric priority values run first.
When a foreground hook returns Deny:
1
Record denial
The denial is recorded as the merged decision.
2
Skip remaining foreground hooks
All remaining foreground hooks are skipped (short-circuit).
3
Skip background hooks
All background hooks are skipped (they only fire when no foreground
Deny occurred).A priority-1 guardrail that denies will prevent a priority-100 observer from running.
Configuration
Config file example (realm config.toml)
Config file example (realm config.toml)
Hook configuration lives under the
[hooks] table:HookEntryConfig fields
HookEntryConfig fields
Layered config loading
Layered config loading
Hook loading is runtime-root aware. In workspace-derived realms, global and project hook entries are layered (
~/.rkat/config.toml then .rkat/config.toml) and merged with active realm config hooks. In non-workspace realms, realm config is primary and no project config may be discovered.Per-run overrides
TheHookRunOverrides struct allows per-request hook customization:
- CLI
- RPC / REST / MCP
Run-scoped hook override flags are not currently exposed on the normal
rkat run surface. Use JSON-RPC, REST, MCP, or SDK/embedded surfaces for per-run hook override testing.Agent events
The hook engine emitsAgentEvent variants during execution:
SDK usage
Registering an in-process hook
Registering an in-process hook
Using hook overrides in AgentBuildConfig
Using hook overrides in AgentBuildConfig
