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
Cloneand all handlers areArc-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 executionpost_tool_use: Handlers invoked after tool executionuser_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
impl Hooks
Sourcepub fn new() -> Self
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 });Sourcepub fn add_pre_tool_use<F, Fut>(self, handler: F) -> Selfwhere
F: Fn(PreToolUseEvent) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Option<HookDecision>> + Send + 'static,
pub fn add_pre_tool_use<F, Fut>(self, handler: F) -> Selfwhere
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 takesPreToolUseEventand returnsOption<HookDecision>. Must beSend + Sync + 'staticfor thread safety.
§Type Parameters
F: The function/closure typeFut: 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
}
});Sourcepub fn add_post_tool_use<F, Fut>(self, handler: F) -> Selfwhere
F: Fn(PostToolUseEvent) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Option<HookDecision>> + Send + 'static,
pub fn add_post_tool_use<F, Fut>(self, handler: F) -> Selfwhere
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
});Sourcepub fn add_user_prompt_submit<F, Fut>(self, handler: F) -> Selfwhere
F: Fn(UserPromptSubmitEvent) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Option<HookDecision>> + Send + 'static,
pub fn add_user_prompt_submit<F, Fut>(self, handler: F) -> Selfwhere
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
}
});Sourcepub async fn execute_pre_tool_use(
&self,
event: PreToolUseEvent,
) -> Option<HookDecision>
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:
- Iterates through hooks in registration order (FIFO)
- Calls each hook with a clone of the event
- If a hook returns
Some(decision), immediately returns that decision - Remaining hooks are not executed
- If all hooks return
None, returnsNone
§Parameters
event: The PreToolUseEvent to pass to each hook
§Returns
Some(HookDecision): A hook made a decision (block, modify, or continue)None: All hooks returnedNone(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());Sourcepub async fn execute_post_tool_use(
&self,
event: PostToolUseEvent,
) -> Option<HookDecision>
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.
Sourcepub async fn execute_user_prompt_submit(
&self,
event: UserPromptSubmitEvent,
) -> Option<HookDecision>
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 Debug for Hooks
Custom Debug implementation for Hooks.
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
}