A3S Docs
A3S CodeExamples

Hooks

Lifecycle event interception — PreToolUse, PostToolUse, PrePrompt, PostResponse, OnError

Hooks

Hooks let you intercept agent lifecycle events to audit, modify, or block operations. Register a Hook + HookHandler pair on a session.

Hook Event Types

Prop

Type

Audit Hook (Log All Tool Calls)

use a3s_code_core::hooks::{Hook, HookEvent, HookEventType, HookHandler, HookResponse};
use std::sync::{Arc, Mutex};

struct AuditHandler {
    log: Arc<Mutex<Vec<String>>>,
}

impl HookHandler for AuditHandler {
    fn handle(&self, event: &HookEvent) -> HookResponse {
        if let HookEvent::PreToolUse(e) = event {
            println!("→ tool: {}", e.tool);
            self.log.lock().unwrap().push(e.tool.clone());
        }
        HookResponse::continue_()
    }
}

// Register
let log = Arc::new(Mutex::new(Vec::new()));
let hook = Hook::new("audit", HookEventType::PreToolUse);
session.register_hook(hook);
session.register_hook_handler("audit", Arc::new(AuditHandler { log: Arc::clone(&log) }));

println!("Hooks: {}", session.hook_count());

// Run agent
let result = session.send("List files in the workspace", None).await?;

// Inspect log
println!("Tools called: {:?}", log.lock().unwrap());

// Cleanup
session.unregister_hook("audit");

Run: cargo run --example 08_hooks Source: core/examples/08_hooks.rs

tool_log = []

def on_pre_tool_use(event):
    tool = event.get("tool", "unknown")
    print(f"→ tool: {tool}")
    tool_log.append(tool)
    return None  # allow execution

session.register_hook("audit", "pre_tool_use", on_pre_tool_use)
print(f"Hooks: {session.hook_count()}")

result = await session.send("List files in the workspace")

print(f"Tools called: {tool_log}")
session.unregister_hook("audit")

Run: python examples/test_advanced_features.py Source: sdk/python/examples/test_advanced_features.py

const toolLog = [];

session.registerHook('audit', 'pre_tool_use', (event) => {
  const tool = event.tool || 'unknown';
  console.log(`→ tool: ${tool}`);
  toolLog.push(tool);
  return null; // allow execution
});

console.log(`Hooks: ${session.hookCount()}`);

const result = await session.send('List files in the workspace');

console.log('Tools called:', toolLog);
session.unregisterHook('audit');

Run: node examples/test_advanced_features.js Source: sdk/node/examples/test_advanced_features.js

Block Hook (Deny Specific Tools)

struct DenyBashHandler;

impl HookHandler for DenyBashHandler {
    fn handle(&self, event: &HookEvent) -> HookResponse {
        if let HookEvent::PreToolUse(e) = event {
            if e.tool == "bash" {
                return HookResponse::block("bash is not allowed in this session");
            }
        }
        HookResponse::continue_()
    }
}

let hook = Hook::new("no-bash", HookEventType::PreToolUse);
session.register_hook(hook);
session.register_hook_handler("no-bash", Arc::new(DenyBashHandler));
def deny_bash(event):
    if event.get("tool") == "bash":
        return {"action": "block", "reason": "bash is not allowed"}
    return None

session.register_hook("no-bash", "pre_tool_use", deny_bash)
session.registerHook('no-bash', 'pre_tool_use', (event) => {
  if (event.tool === 'bash') {
    return { action: 'block', reason: 'bash is not allowed' };
  }
  return null;
});

For persistent hook configuration across sessions, see Hooks.

API Reference

Hook Registration

Prop

Type

HookResponse (Rust)

Prop

Type

Hook Event Fields

Prop

Type

On this page