Chapter 7
The Hook System
Lifecycle events — inject custom logic at key moments
🎯
Like a Building's Security System
Imagine a building's entrance has security: everyone swipes a badge on the way in (PreToolUse) and signs a log on the way out (PostToolUse). The security system isn't the building's core function, but it can check, record, and even block people at key moments. The Hook system is the Agent's security system — at every tool "entry and exit," it automatically runs your custom logic.
What Is a Hook?
A Hook is a programming pattern: when a program reaches a specific moment, it automatically triggers a preset action.
In OpenHarness, the Hook system provides 4 timing points:
• SESSION_START — Triggered when a session begins
• SESSION_END — Triggered when a session ends
• PRE_TOOL_USE — Triggered before tool execution
• POST_TOOL_USE — Triggered after tool execution
PRE_TOOL_USE and POST_TOOL_USE are the most commonly used — they let you insert custom logic before and after each tool execution.
What Can Hooks Do?
Common use cases for hooks:
📝 Logging
Record every tool call's parameters and results for auditing and debugging.
🛡️ Extra Security Checks
For example, check if a Bash command contains dangerous operations — an extra layer of defense on top of the permission system.
🔄 Auto-Formatting
Automatically run a code formatter after file writes.
📊 Analytics
Track tool usage frequency, execution time, etc.
🚫 Blocking Operations
PreToolUse hooks can prevent tool execution — for example, blocking deploys outside work hours.
Hook Execution Flow
openharness/hooks/executor.py
1async def execute_tool_with_hooks(tool_call, context):2 # Step 1: Run PreToolUse hooks3 pre_result = await run_hooks("pre_tool_use", {4 "tool_name": tool_call.name,5 "tool_params": tool_call.params,6 })78 # If a hook requests blocking, return immediately9 if pre_result.should_block:10 return ToolResult("Blocked by Hook", is_error=True)1112 # Step 2: Permission check (after hooks)13 permission = await check_permission(tool_call)14 if not permission.allowed:15 return ToolResult("Permission denied", is_error=True)1617 # Step 3: Actually execute the tool18 result = await tool.execute(tool_call.params)1920 # Step 4: Run PostToolUse hooks21 await run_hooks("post_tool_use", {22 "tool_name": tool_call.name,23 "result": result,24 })2526 return result
Every time a tool is called, hooks execute in this order:
Four Types of Hooks
OpenHarness supports four different hook implementations:
1. Command Hook
Executes a shell command. The simplest and most direct approach.
2. HTTP Hook
Sends an HTTP request to a specified URL. Ideal for integrating with external services.
3. Prompt Hook
Lets the Agent process a prompt itself. Suitable for complex judgment logic.
4. Agent Hook
Launches a full sub-agent to handle it. For scenarios requiring multi-step reasoning.
In most cases, a Command Hook is sufficient.
How to Configure Hooks
settings.json
1{2 "hooks": {3 "pre_tool_use": [4 {5 "type": "command",6 "command": "echo 'About to execute tool: $TOOL_NAME'",7 "match_tools": ["Bash"]8 }9 ],10 "post_tool_use": [11 {12 "type": "command",13 "command": "echo 'Tool execution complete: $TOOL_NAME'"14 }15 ]16 }17}
Hooks are configured in settings.json:
📌 Key Takeaway
Hooks Make the Harness Observable and Extensible
The core value of the Hook system: inject custom logic at key moments without modifying engine code. This makes OpenHarness not just a tool, but an observable, extensible platform.
🧠 Check Your Understanding
What is the most important special ability of PreToolUse hooks?