Hooks

Struct Hooks 

Source
pub struct Hooks {
    pub pre_tool_use: Vec<Arc<dyn Fn(PreToolUseEvent) -> Pin<Box<dyn Future<Output = Option<HookDecision>> + Send>> + Send + Sync>>,
    pub post_tool_use: Vec<Arc<dyn Fn(PostToolUseEvent) -> Pin<Box<dyn Future<Output = Option<HookDecision>> + Send>> + Send + Sync>>,
    pub user_prompt_submit: Vec<Arc<dyn Fn(UserPromptSubmitEvent) -> Pin<Box<dyn Future<Output = Option<HookDecision>> + Send>> + Send + Sync>>,
}
Expand description

Container for registering and managing lifecycle hooks.

The Hooks struct stores collections of hook handlers for different lifecycle events. It provides a builder pattern for registering hooks and executor methods for running them.

§Design Principles

  • Builder Pattern: Hooks can be chained during construction using .add_*() methods
  • Multiple Hooks: You can register multiple hooks for the same event type
  • Execution Order: Hooks execute in the order they were registered (FIFO)
  • First Wins: The first hook returning Some(HookDecision) determines the outcome
  • Thread Safe: The struct is Clone and all handlers are Arc-wrapped for sharing

§Example: Building a Hooks Collection

use open_agent::{Hooks, PreToolUseEvent, PostToolUseEvent, HookDecision};

let hooks = Hooks::new()
    // First: Security gate (highest priority)
    .add_pre_tool_use(|event| async move {
        if event.tool_name == "dangerous" {
            return Some(HookDecision::block("Security violation"));
        }
        None
    })
    // Second: Rate limiting
    .add_pre_tool_use(|event| async move {
        // Check rate limits...
        None
    })
    // Audit logging (happens after execution)
    .add_post_tool_use(|event| async move {
        println!("Tool '{}' executed", event.tool_name);
        None
    });

§Fields

  • pre_tool_use: Handlers invoked before tool execution
  • post_tool_use: Handlers invoked after tool execution
  • user_prompt_submit: Handlers invoked before processing user prompts

All fields are public, allowing direct manipulation if needed, though the builder methods are the recommended approach.

Fields§

§pre_tool_use: Vec<Arc<dyn Fn(PreToolUseEvent) -> Pin<Box<dyn Future<Output = Option<HookDecision>> + Send>> + Send + Sync>>

Collection of PreToolUse hook handlers, executed in registration order

§post_tool_use: Vec<Arc<dyn Fn(PostToolUseEvent) -> Pin<Box<dyn Future<Output = Option<HookDecision>> + Send>> + Send + Sync>>

Collection of PostToolUse hook handlers, executed in registration order

§user_prompt_submit: Vec<Arc<dyn Fn(UserPromptSubmitEvent) -> Pin<Box<dyn Future<Output = Option<HookDecision>> + Send>> + Send + Sync>>

Collection of UserPromptSubmit hook handlers, executed in registration order

Implementations§

Source§

impl Hooks

Source

pub fn new() -> Self

Creates a new, empty Hooks container.

Use this as the starting point for building a hooks collection using the builder pattern.

§Example
use open_agent::Hooks;

let hooks = Hooks::new()
    .add_pre_tool_use(|event| async move { None });
Source

pub fn add_pre_tool_use<F, Fut>(self, handler: F) -> Self
where F: Fn(PreToolUseEvent) -> Fut + Send + Sync + 'static, Fut: Future<Output = Option<HookDecision>> + Send + 'static,

Registers a PreToolUse hook handler using the builder pattern.

This method takes ownership of self and returns it back, allowing method chaining. The handler is wrapped in Arc and added to the collection of PreToolUse hooks.

§Parameters
  • handler: An async function or closure that takes PreToolUseEvent and returns Option<HookDecision>. Must be Send + Sync + 'static for thread safety.
§Type Parameters
  • F: The function/closure type
  • Fut: The future type returned by the function
§Example
use open_agent::{Hooks, HookDecision};

let hooks = Hooks::new()
    .add_pre_tool_use(|event| async move {
        println!("About to execute: {}", event.tool_name);
        None
    })
    .add_pre_tool_use(|event| async move {
        // This runs second (if first returns None)
        if event.tool_name == "blocked" {
            Some(HookDecision::block("Not allowed"))
        } else {
            None
        }
    });
Source

pub fn add_post_tool_use<F, Fut>(self, handler: F) -> Self
where F: Fn(PostToolUseEvent) -> Fut + Send + Sync + 'static, Fut: Future<Output = Option<HookDecision>> + Send + 'static,

Registers a PostToolUse hook handler using the builder pattern.

Identical to add_pre_tool_use but for PostToolUse events. See Self::add_pre_tool_use for detailed documentation.

§Example
use open_agent::Hooks;

let hooks = Hooks::new()
    .add_post_tool_use(|event| async move {
        // Audit log all tool executions
        println!("Tool '{}' completed: {:?}",
                 event.tool_name, event.tool_result);
        None // Don't interfere with execution
    });
Source

pub fn add_user_prompt_submit<F, Fut>(self, handler: F) -> Self
where F: Fn(UserPromptSubmitEvent) -> Fut + Send + Sync + 'static, Fut: Future<Output = Option<HookDecision>> + Send + 'static,

Registers a UserPromptSubmit hook handler using the builder pattern.

Identical to add_pre_tool_use but for UserPromptSubmit events. See Self::add_pre_tool_use for detailed documentation.

§Example
use open_agent::{Hooks, HookDecision};

let hooks = Hooks::new()
    .add_user_prompt_submit(|event| async move {
        // Content moderation
        if event.prompt.contains("forbidden") {
            Some(HookDecision::block("Content violation"))
        } else {
            None
        }
    });
Source

pub async fn execute_pre_tool_use( &self, event: PreToolUseEvent, ) -> Option<HookDecision>

Executes all registered PreToolUse hooks in order and returns the first decision.

This method implements the “first non-None wins” execution model:

  1. Iterates through hooks in registration order (FIFO)
  2. Calls each hook with a clone of the event
  3. If a hook returns Some(decision), immediately returns that decision
  4. Remaining hooks are not executed
  5. If all hooks return None, returns None
§Parameters
  • event: The PreToolUseEvent to pass to each hook
§Returns
  • Some(HookDecision): A hook made a decision (block, modify, or continue)
  • None: All hooks returned None (continue normally)
§Example
use open_agent::{Hooks, PreToolUseEvent, HookDecision};
use serde_json::json;

let hooks = Hooks::new()
    .add_pre_tool_use(|e| async move { None }) // Runs first
    .add_pre_tool_use(|e| async move {
        Some(HookDecision::block("Blocked")) // Runs second, blocks
    })
    .add_pre_tool_use(|e| async move {
        None // NEVER runs because previous hook returned Some
    });

let event = PreToolUseEvent::new(
    "test".to_string(),
    json!({}),
    "id".to_string(),
    vec![]
);

let decision = hooks.execute_pre_tool_use(event).await;
assert!(decision.is_some());
assert!(!decision.unwrap().continue_execution());
Source

pub async fn execute_post_tool_use( &self, event: PostToolUseEvent, ) -> Option<HookDecision>

Executes all registered PostToolUse hooks in order and returns the first decision.

Identical in behavior to Self::execute_pre_tool_use but for PostToolUse events. See that method for detailed documentation of the execution model.

§Note

PostToolUse hooks rarely return decisions in practice. They’re primarily used for observation (logging, metrics) and typically always return None.

Source

pub async fn execute_user_prompt_submit( &self, event: UserPromptSubmitEvent, ) -> Option<HookDecision>

Executes all registered UserPromptSubmit hooks in order and returns the first decision.

Identical in behavior to Self::execute_pre_tool_use but for UserPromptSubmit events. See that method for detailed documentation of the execution model.

Trait Implementations§

Source§

impl Clone for Hooks

Source§

fn clone(&self) -> Hooks

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Hooks

Custom Debug implementation for Hooks.

Since hook handlers are closures (which don’t implement Debug), we provide a custom implementation that shows the number of registered handlers instead of trying to debug-print the closures themselves.

§Example Output

Hooks {
    pre_tool_use: 3 handlers,
    post_tool_use: 1 handlers,
    user_prompt_submit: 2 handlers
}
Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Hooks

Source§

fn default() -> Hooks

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for Hooks

§

impl !RefUnwindSafe for Hooks

§

impl Send for Hooks

§

impl Sync for Hooks

§

impl Unpin for Hooks

§

impl !UnwindSafe for Hooks

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more