open_agent/hooks/events.rs
1/// Event fired **before** a tool is executed, enabling validation, modification, or blocking.
2///
3/// This event provides complete visibility into the tool that's about to be executed,
4/// allowing you to implement security policies, modify inputs, or collect telemetry
5/// before any potentially dangerous or expensive operations occur.
6///
7/// # Use Cases
8///
9/// - **Security gates**: Block dangerous operations (file deletion, network access)
10/// - **Input validation**: Ensure tool inputs meet schema or business rules
11/// - **Parameter injection**: Add authentication tokens, user context, or default values
12/// - **Rate limiting**: Track and limit tool usage per user/session
13/// - **Audit logging**: Record who is calling what tools with what parameters
14///
15/// # Fields
16///
17/// - `tool_name`: The name of the tool about to execute (e.g., "Bash", "Read", "WebFetch")
18/// - `tool_input`: The parameters that will be passed to the tool (as JSON)
19/// - `tool_use_id`: Unique identifier for this specific tool invocation
20/// - `history`: Read-only snapshot of the conversation history up to this point
21///
22/// # Example: Security Gate
23///
24/// ```rust
25/// use open_agent::{PreToolUseEvent, HookDecision};
26/// use serde_json::json;
27///
28/// async fn security_gate(event: PreToolUseEvent) -> Option<HookDecision> {
29/// // Block all Bash commands containing 'rm -rf'
30/// if event.tool_name == "Bash" {
31/// if let Some(command) = event.tool_input.get("command") {
32/// if command.as_str()?.contains("rm -rf") {
33/// return Some(HookDecision::block(
34/// "Dangerous command blocked for safety"
35/// ));
36/// }
37/// }
38/// }
39/// None // Allow other tools
40/// }
41/// ```
42///
43/// # Example: Parameter Injection
44///
45/// ```rust
46/// use open_agent::{PreToolUseEvent, HookDecision};
47/// use serde_json::json;
48///
49/// async fn inject_auth(event: PreToolUseEvent) -> Option<HookDecision> {
50/// // Add authentication header to all API calls
51/// if event.tool_name == "WebFetch" {
52/// let mut modified = event.tool_input.clone();
53/// modified["headers"] = json!({
54/// "Authorization": "Bearer secret-token"
55/// });
56/// return Some(HookDecision::modify_input(
57/// modified,
58/// "Injected auth token"
59/// ));
60/// }
61/// None
62/// }
63/// ```
64#[derive(Debug, Clone)]
65pub struct PreToolUseEvent {
66 /// Name of the tool about to be executed (e.g., "Bash", "Read", "Edit")
67 pub tool_name: String,
68 /// Input parameters for the tool as a JSON value
69 pub tool_input: Value,
70 /// Unique identifier for this tool use (for correlation with PostToolUseEvent)
71 pub tool_use_id: String,
72 /// Structured JSON snapshot of conversation history before this tool call executes.
73 pub history: Vec<Value>,
74}
75
76impl PreToolUseEvent {
77 /// Creates a new PreToolUseEvent.
78 ///
79 /// This constructor is typically called by the agent runtime, not by user code.
80 /// Users receive instances of this struct in their hook handlers.
81 pub fn new(
82 tool_name: String,
83 tool_input: Value,
84 tool_use_id: String,
85 history: Vec<Value>,
86 ) -> Self {
87 Self {
88 tool_name,
89 tool_input,
90 tool_use_id,
91 history,
92 }
93 }
94}
95
96/// Event fired **after** a tool completes execution, enabling audit, filtering, or validation.
97///
98/// This event provides complete visibility into what a tool did, including both the input
99/// parameters and the output result. Use this for auditing, metrics collection, output
100/// filtering, or post-execution validation.
101///
102/// # Use Cases
103///
104/// - **Audit logging**: Record all tool executions with inputs and outputs for compliance
105/// - **Output filtering**: Redact sensitive information from tool results
106/// - **Metrics collection**: Track tool performance, success rates, error patterns
107/// - **Result validation**: Ensure tool outputs meet quality or safety standards
108/// - **Error handling**: Implement custom error recovery or alerting
109///
110/// # Fields
111///
112/// - `tool_name`: The name of the tool that was executed
113/// - `tool_input`: The parameters that were actually used (may have been modified by PreToolUse hooks)
114/// - `tool_use_id`: Unique identifier for this invocation (matches PreToolUseEvent.tool_use_id)
115/// - `tool_result`: The result returned by the tool (contains either success data or error info)
116/// - `history`: Read-only snapshot of conversation history including this tool's execution
117///
118/// # Example: Audit Logging
119///
120/// ```rust
121/// use open_agent::{PostToolUseEvent, HookDecision};
122///
123/// async fn audit_logger(event: PostToolUseEvent) -> Option<HookDecision> {
124/// // Log all tool executions to your audit system
125/// let is_error = event.tool_result.get("error").is_some();
126///
127/// println!(
128/// "[AUDIT] Tool: {}, ID: {}, Status: {}",
129/// event.tool_name,
130/// event.tool_use_id,
131/// if is_error { "ERROR" } else { "SUCCESS" }
132/// );
133///
134/// // Send to external logging service
135/// // log_to_service(&event).await;
136///
137/// None // Don't interfere with execution
138/// }
139/// ```
140///
141/// # Example: Sensitive Data Redaction
142///
143/// ```rust
144/// use open_agent::{PostToolUseEvent, HookDecision};
145/// use serde_json::json;
146///
147/// async fn redact_secrets(event: PostToolUseEvent) -> Option<HookDecision> {
148/// // Redact API keys from Read tool output
149/// if event.tool_name == "Read" {
150/// if let Some(content) = event.tool_result.get("content") {
151/// if let Some(text) = content.as_str() {
152/// if text.contains("API_KEY=") {
153/// let redacted = text.replace(
154/// |c: char| c.is_alphanumeric(),
155/// "*"
156/// );
157/// // Note: PostToolUse hooks typically don't modify results,
158/// // but you could log this for security review
159/// println!("Warning: Potential API key detected in output");
160/// }
161/// }
162/// }
163/// }
164/// None
165/// }
166/// ```
167///
168/// # Note on Modification
169///
170/// While `HookDecision` theoretically allows modification in PostToolUse hooks, this is
171/// rarely used in practice. The tool has already executed, and most agents don't support
172/// modifying historical results. PostToolUse hooks are primarily for observation and auditing.
173#[derive(Debug, Clone)]
174pub struct PostToolUseEvent {
175 /// Name of the tool that was executed
176 pub tool_name: String,
177 /// Input parameters that were actually used (may differ from original if modified by PreToolUse)
178 pub tool_input: Value,
179 /// Unique identifier for this tool use (correlates with PreToolUseEvent)
180 pub tool_use_id: String,
181 /// Result returned by the tool - may contain "content" on success or "error" on failure
182 pub tool_result: Value,
183 /// Structured JSON snapshot including this tool call and its unmodified result.
184 pub history: Vec<Value>,
185}
186
187impl PostToolUseEvent {
188 /// Creates a new PostToolUseEvent.
189 ///
190 /// This constructor is typically called by the agent runtime after tool execution,
191 /// not by user code. Users receive instances of this struct in their hook handlers.
192 pub fn new(
193 tool_name: String,
194 tool_input: Value,
195 tool_use_id: String,
196 tool_result: Value,
197 history: Vec<Value>,
198 ) -> Self {
199 Self {
200 tool_name,
201 tool_input,
202 tool_use_id,
203 tool_result,
204 history,
205 }
206 }
207}
208
209/// Event fired **before** processing user input, enabling content moderation and prompt enhancement.
210///
211/// This event is triggered whenever a user submits a prompt to the agent, before the agent
212/// begins processing it. Use this to implement content moderation, add context, inject
213/// instructions, or track user interactions.
214///
215/// # Use Cases
216///
217/// - **Content moderation**: Filter inappropriate or harmful user inputs
218/// - **Prompt enhancement**: Add system context, timestamps, or user information
219/// - **Input validation**: Ensure prompts meet format or length requirements
220/// - **Usage tracking**: Log user interactions for analytics or billing
221/// - **Context injection**: Add relevant background information to every prompt
222///
223/// # Fields
224///
225/// - `prompt`: The user's original input text
226/// - `history`: Read-only snapshot of the conversation history before this prompt
227///
228/// # Example: Content Moderation
229///
230/// ```rust
231/// use open_agent::{UserPromptSubmitEvent, HookDecision};
232///
233/// async fn content_moderator(event: UserPromptSubmitEvent) -> Option<HookDecision> {
234/// // Block prompts containing banned words
235/// let banned_words = ["spam", "malware", "hack"];
236///
237/// for word in banned_words {
238/// if event.prompt.to_lowercase().contains(word) {
239/// return Some(HookDecision::block(
240/// format!("Content policy violation: contains '{}'", word)
241/// ));
242/// }
243/// }
244/// None // Allow clean prompts
245/// }
246/// ```
247///
248/// # Example: Automatic Context Enhancement
249///
250/// ```rust
251/// use open_agent::{UserPromptSubmitEvent, HookDecision};
252///
253/// async fn add_context(event: UserPromptSubmitEvent) -> Option<HookDecision> {
254/// // Add helpful context to every user prompt
255/// let enhanced = format!(
256/// "{}\n\n---\nContext: User timezone is UTC, current session started at 2025-11-07",
257/// event.prompt
258/// );
259///
260/// Some(HookDecision::modify_prompt(
261/// enhanced,
262/// "Added session context"
263/// ))
264/// }
265/// ```
266///
267/// # Example: Usage Tracking
268///
269/// ```rust
270/// use open_agent::{UserPromptSubmitEvent, HookDecision};
271///
272/// async fn track_usage(event: UserPromptSubmitEvent) -> Option<HookDecision> {
273/// // Log every user interaction for analytics
274/// println!(
275/// "[ANALYTICS] User submitted prompt of {} characters at history depth {}",
276/// event.prompt.len(),
277/// event.history.len()
278/// );
279///
280/// // Could also:
281/// // - Update usage quotas
282/// // - Send to analytics service
283/// // - Check rate limits
284///
285/// None // Don't modify the prompt
286/// }
287/// ```
288///
289/// # Modification Behavior
290///
291/// If you return `HookDecision::modify_prompt()`, the modified prompt completely replaces
292/// the original user input before the agent processes it. This is powerful but should be
293/// used carefully to avoid confusing the user or the agent.
294#[derive(Debug, Clone)]
295pub struct UserPromptSubmitEvent {
296 /// The user's original input prompt text
297 pub prompt: String,
298 /// Structured JSON snapshot of history before this prompt is added.
299 pub history: Vec<Value>,
300}
301
302impl UserPromptSubmitEvent {
303 /// Creates a new UserPromptSubmitEvent.
304 ///
305 /// This constructor is typically called by the agent runtime when processing user input,
306 /// not by user code. Users receive instances of this struct in their hook handlers.
307 pub fn new(prompt: String, history: Vec<Value>) -> Self {
308 Self { prompt, history }
309 }
310}