Lifecycle Hooks
8 lifecycle hooks for intercepting tool calls, LLM generations, sessions, and skills
Lifecycle Hooks
A3S Code provides 8 lifecycle hooks for intercepting and reacting to events during agent execution. Hooks enable logging, security enforcement, telemetry, and custom behavior without modifying the core agent loop.
Hook Events
Prop
Type
How Hooks Work
Hooks are registered on the HookEngine and fire at specific points in the agent loop. Multiple hooks can listen to the same event — they execute in priority order (lower number = higher priority).
Agent Loop
├── GenerateStart hooks fire
├── LLM call
├── GenerateEnd hooks fire
└── For each tool_use:
├── PreToolUse hooks fire
├── Permission check
├── HITL check
├── Tool execution
└── PostToolUse hooks fireBuilt-in SecurityGuard
The SecurityGuard registers hooks at priority 1 (highest) automatically:
Prop
Type
Hook Registration (Rust)
Register hooks and handlers via the HookEngine:
use a3s_code_core::hooks::{
Hook, HookConfig, HookEngine, HookEvent, HookEventType,
HookHandler, HookResult, HookResponse, HookMatcher,
};
use std::sync::Arc;
let engine = HookEngine::new();
// 1. Register a hook definition (what to listen for)
engine.register(
Hook::new("log-tools", HookEventType::PreToolUse)
.with_config(HookConfig { priority: 10, ..Default::default() })
);
// 2. Register a handler (what to do when fired)
struct LogHandler;
impl HookHandler for LogHandler {
fn handle(&self, event: &HookEvent) -> HookResponse {
if let HookEvent::PreToolUse(e) = event {
println!("Tool: {} Args: {:?}", e.tool, e.args);
}
HookResponse::continue_()
}
}
engine.register_handler("log-tools", Arc::new(LogHandler));
// Block dangerous patterns
engine.register(
Hook::new("block-dangerous", HookEventType::PreToolUse)
.with_matcher(HookMatcher::tool("bash"))
.with_config(HookConfig { priority: 5, ..Default::default() })
);
struct BlockHandler;
impl HookHandler for BlockHandler {
fn handle(&self, event: &HookEvent) -> HookResponse {
if let HookEvent::PreToolUse(e) = event {
if e.args.to_string().contains("rm -rf") {
return HookResponse::block("Dangerous command blocked");
}
}
HookResponse::continue_()
}
}
engine.register_handler("block-dangerous", Arc::new(BlockHandler));
// Fire a hook (done automatically by the agent loop)
let result = engine.fire(&event).await;
match result {
HookResult::Continue(None) => { /* proceed */ }
HookResult::Continue(Some(modified)) => { /* proceed with modified data */ }
HookResult::Block(reason) => { /* stop execution */ }
_ => {}
}Hook Priority
Hooks execute in priority order. Lower numbers run first.
Prop
Type
If any hook returns HookResult::Block, the operation is cancelled and subsequent hooks for that event are skipped.
Hook Event Payloads
Each hook event carries a typed payload with event-specific data:
API Reference
Hook registration
Prop
Type
HookHandler trait (Rust)
impl HookHandler for MyHandler {
fn handle(&self, event: &HookEvent) -> HookResponse {
match event {
HookEvent::PreToolUse(e) => { /* ... */ }
_ => {}
}
HookResponse::continue_()
}
}HookResponse values
Prop
Type
Hook priority
Lower number = higher priority. SecurityGuard uses priority 1. Default hooks use priority 100.
Hook::new("my-hook", HookEventType::PreToolUse).with_priority(50)