A3S Docs
A3S Code

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 fire

Built-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)

On this page