Hooks
Run custom logic before and after tool executions with the hook system.
Hooks
Hooks let you run custom shell commands in response to agent lifecycle events. Use them for logging, validation, notifications, or to enforce custom policies.
Hook Events
| Event | When | Use Case |
| --- | --- | --- |
| pre_tool | Before a tool executes | Validate args, log intent |
| post_tool | After a tool completes | Audit output, trigger CI |
| pre_prompt | Before LLM call | Inject context, rate limit |
| post_response | After LLM responds | Log tokens, filter output |
Configuration
Define hooks in your ACL config:
hooks "pre_tool" {command = "node scripts/hooks/validate-tool.js"timeout_ms = 5000}hooks "post_tool" {command = "node scripts/hooks/audit-log.js"timeout_ms = 3000}
Hook Scripts
Hook scripts receive event data via stdin (JSON) and can:
- Exit 0 to allow the operation
- Exit non-zero to block it (pre_tool only)
- Write to stdout to modify the event (advanced)
// scripts/hooks/validate-tool.jsimport { readFileSync } from 'fs';const event = JSON.parse(readFileSync('/dev/stdin', 'utf-8'));if (event.tool_name === 'bash') {const cmd = event.args?.command || '';// Block dangerous commandsif (cmd.includes('rm -rf') || cmd.includes('DROP TABLE')) {console.error(`Blocked dangerous command: ${cmd}`);process.exit(1);}}process.exit(0); // allow
// scripts/hooks/audit-log.jsimport { readFileSync, appendFileSync } from 'fs';const event = JSON.parse(readFileSync('/dev/stdin', 'utf-8'));const entry = {timestamp: new Date().toISOString(),tool: event.tool_name,exit_code: event.exit_code,duration_ms: event.duration_ms,};appendFileSync('audit.jsonl', JSON.stringify(entry) + '\n');
SDK-Level Hooks
Register hooks programmatically:
const session = agent.session('.', {hooks: {preTool: async (event) => {console.log(`[hook] About to run: ${event.toolName}`);// Return false to blockif (event.toolName === 'bash' && event.args.command.includes('sudo')) {return false;}return true;},postTool: async (event) => {if (event.exitCode !== 0) {await notifySlack(`Tool ${event.toolName} failed`);}},},});
Combining Hooks with Structured Output
Use post_tool hooks to validate generate_object output against business rules:
// scripts/hooks/validate-output.jsconst event = JSON.parse(readFileSync('/dev/stdin', 'utf-8'));if (event.tool_name === 'generate_object' && event.exit_code === 0) {const output = JSON.parse(event.output);const obj = output.object;// Business rule: risk_score > 8 requires manager approvalif (obj.risk_score > 8) {appendFileSync('alerts.jsonl', JSON.stringify({type: 'high_risk',score: obj.risk_score,timestamp: new Date().toISOString(),}) + '\n');}}