Skip to content

Hooks allow you to intercept and customize agent behavior at key lifecycle points. They can add context, block operations, modify inputs, or trigger side effects.

Overview

Event Trigger Can Block Can Modify
pre_run Before agent.run() processes a prompt Yes No
post_run After agent.run() completes No No
pre_tool_use Before a tool is called Yes Yes (tool input)
post_tool_use After a tool completes No No

Configuration Reference

HooksConfig

Configuration for agent lifecycle hooks.

Hooks allow intercepting and customizing agent behavior at key points in the execution lifecycle. They can add context, block operations, modify inputs, or trigger side effects.

Currently supported events: - pre_run / post_run: Before/after agent.run() processes a prompt - pre_tool_use / post_tool_use: Before/after a tool is called

HooksConfig (YAML)
1
2
3
4
- pre_run: []
  post_run: []  # Hooks executed after agent.run() completes.
  pre_tool_use: []  # Hooks executed before a tool is called. Can block or modify the call.
  post_tool_use: []  # Hooks executed after a tool completes.

Hook Types

Command Hook

Hook that executes a shell command.

The command receives hook input as JSON via stdin and should return JSON output via stdout. Exit code 0 = success, exit code 2 = block.

Command Hook (YAML)
1
2
3
4
5
6
- type: command
  command: /path/to/script.sh  # Shell command to execute. Supports $PROJECT_DIR variable.
  env: null  # Additional environment variables for the command.
  matcher: null  # Regex pattern to match tool names. None or '' matches all.
  timeout: 60.0  # Maximum execution time in seconds.
  enabled: true  # Whether this hook is active.

Callable Hook

Hook that executes a Python callable.

The callable receives hook input as a dictionary and should return a HookResult dictionary or None.

Callable Hook (YAML)
1
2
3
4
5
6
- type: callable
  import_path: myproject.hooks.validate_tool  # Dotted import path to the callable.
  arguments: {}  # Additional keyword arguments passed to the callable.
  matcher: null  # Regex pattern to match tool names. None or '' matches all.
  timeout: 60.0  # Maximum execution time in seconds.
  enabled: true  # Whether this hook is active.

Prompt Hook

Hook that uses an LLM to evaluate the action.

The prompt is sent to a fast LLM which returns a structured decision. Use $TOOL_NAME, $TOOL_INPUT, \(INPUT placeholders in the prompt.</p> <div class="language-yaml highlight"><table class="highlighttable"><tr><th colspan="2" class="filename"><span class="filename">Prompt Hook (YAML)</span></th></tr><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-2-1">1</a></span> <span class="normal"><a href="#__codelineno-2-2">2</a></span> <span class="normal"><a href="#__codelineno-2-3">3</a></span> <span class="normal"><a href="#__codelineno-2-4">4</a></span> <span class="normal"><a href="#__codelineno-2-5">5</a></span> <span class="normal"><a href="#__codelineno-2-6">6</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1"></a><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">prompt</span> </span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2"></a><span class="w"> </span><span class="nt">prompt</span><span class="p">:</span><span class="w"> </span><span class="s">'Evaluate</span><span class="nv"> </span><span class="s">if</span><span class="nv"> </span><span class="s">this</span><span class="nv"> </span><span class="s">tool</span><span class="nv"> </span><span class="s">call</span><span class="nv"> </span><span class="s">is</span><span class="nv"> </span><span class="s">safe:</span><span class="nv"> </span><span class="s">\)INPUT' # Prompt template for LLM evaluation. Supports placeholders. model: null # Model to use for evaluation. Defaults to a fast model if not specified. matcher: null # Regex pattern to match tool names. None or '*' matches all. timeout: 60.0 # Maximum execution time in seconds. enabled: true # Whether this hook is active.

Command Hook

Executes a shell command. Receives JSON via stdin, returns JSON via stdout.

agents:
  my_agent:
    model: openai:gpt-4o
    hooks:
      pre_tool_use:
        - type: command
          command: python /path/to/validate.py
          matcher: "Bash|Write"
          timeout: 30.0
          env:
            LOG_LEVEL: debug

Exit codes:

Example script:

#!/usr/bin/env python3
import json
import sys

data = json.load(sys.stdin)
tool_input = data.get("tool_input", {})

if "rm -rf" in tool_input.get("command", ""):
    print("Dangerous command blocked", file=sys.stderr)
    sys.exit(2)  # Deny

print(json.dumps({"decision": "allow"}))
sys.exit(0)

Callable Hook

Executes a Python function by import path.

agents:
  my_agent:
    model: openai:gpt-4o
    hooks:
      post_tool_use:
        - type: callable
          import_path: myproject.hooks.log_tool_usage
          arguments:
            log_file: /var/log/tools.log

Function signature:

# myproject/hooks.py
from typing import Any

def log_tool_usage(
    tool_name: str,
    tool_output: Any,
    duration_ms: float,
    log_file: str,
    **kwargs
) -> dict | None:
    with open(log_file, "a") as f:
        f.write(f"{tool_name}: {duration_ms:.1f}ms\n")
    return {"decision": "allow"}

Prompt Hook

Uses an LLM to evaluate the action with structured output.

agents:
  my_agent:
    model: openai:gpt-4o
    hooks:
      pre_tool_use:
        - type: prompt
          prompt: |
            Should this command be allowed?
            Tool: $TOOL_NAME
            Input: $TOOL_INPUT
          model: openai:gpt-4o-mini
          matcher: "Bash"

Placeholders:

Placeholder Description
$INPUT Full hook input as JSON
$TOOL_NAME Name of the tool
$TOOL_INPUT Tool input arguments as JSON
$TOOL_OUTPUT Tool output (post_tool_use only)
$AGENT_NAME Name of the agent
$PROMPT The prompt being processed (pre_run/post_run)
$EVENT The hook event name

Matcher Patterns

The matcher field uses regex to filter which tools trigger the hook:

matcher: "Bash"           # Only Bash tool
matcher: "Write|Edit"     # Write or Edit tools
matcher: "mcp__.*"        # All MCP tools
matcher: ".*"             # All tools (same as null)

Full Example

agents:
  secure_agent:
    model: openai:gpt-4o
    hooks:
      pre_run:
        - type: callable
          import_path: myproject.auth.check_allowed

      pre_tool_use:
        - type: prompt
          prompt: "Is this Bash command safe? $TOOL_INPUT"
          matcher: "Bash"
          model: openai:gpt-4o-mini

        - type: command
          command: python -m myproject.hooks.validate_paths
          matcher: "Write|Edit|Read"

      post_tool_use:
        - type: callable
          import_path: myproject.metrics.record_usage

      post_run:
        - type: command
          command: /scripts/notify-complete.sh

Programmatic Usage

from agentpool import Agent
from agentpool.hooks import AgentHooks, CallableHook

def my_pre_tool_hook(tool_name: str, tool_input: dict, **kwargs):
    if tool_name == "Bash" and "rm" in tool_input.get("command", ""):
        return {"decision": "deny", "reason": "rm commands not allowed"}
    return {"decision": "allow"}

hooks = AgentHooks(
    pre_tool_use=[
        CallableHook(
            event="pre_tool_use",
            fn=my_pre_tool_hook,
            matcher="Bash",
        )
    ]
)

agent = Agent(model="openai:gpt-4o", hooks=hooks)