Skip to main content

open_agent/hooks/
decision.rs

1/// A hook decision to continue, block, or replace input.
2///
3/// Returning `Some(decision)` stops the remaining hooks for that event, even when
4/// the decision is [`continue_`](Self::continue_). Return `None` to let the next
5/// hook decide. `Default` blocks execution with no reason or replacement.
6#[derive(Debug, Clone, Default)]
7pub struct HookDecision {
8    /// Whether to continue execution. If `false`, the operation is aborted.
9    /// Default: `false` (via Default trait), but builder methods set this appropriately.
10    continue_execution: bool,
11
12    /// For PreToolUse hooks: If set, replaces the original tool input with this value.
13    /// The tool will execute with this modified input instead of the original.
14    modified_input: Option<Value>,
15
16    /// For UserPromptSubmit hooks: If set, replaces the user's prompt with this value.
17    /// The agent will process this modified prompt instead of the original.
18    modified_prompt: Option<String>,
19
20    /// Optional human-readable explanation for why this decision was made.
21    /// Useful for logging, debugging, and audit trails.
22    reason: Option<String>,
23}
24
25impl HookDecision {
26    /// Continues the operation without modifications, skipping subsequent hooks.
27    ///
28    /// Return `None` from the hook instead when later hooks should also run.
29    pub fn continue_() -> Self {
30        Self {
31            continue_execution: true,
32            modified_input: None,
33            modified_prompt: None,
34            reason: None,
35        }
36    }
37
38    /// Blocks the prompt or tool call with the supplied reason.
39    ///
40    /// A blocked prompt becomes an error; a blocked automatic tool call becomes an
41    /// error result sent back to the model.
42    pub fn block(reason: impl Into<String>) -> Self {
43        Self {
44            continue_execution: false,
45            modified_input: None,
46            modified_prompt: None,
47            reason: Some(reason.into()),
48        }
49    }
50
51    /// Continues with replacement tool input.
52    ///
53    /// In a post-tool hook, the same field replaces the tool result instead.
54    pub fn modify_input(input: Value, reason: impl Into<String>) -> Self {
55        Self {
56            continue_execution: true,
57            modified_input: Some(input),
58            modified_prompt: None,
59            reason: Some(reason.into()),
60        }
61    }
62
63    /// Continues with replacement prompt text in a user-prompt-submit hook.
64    pub fn modify_prompt(prompt: impl Into<String>, reason: impl Into<String>) -> Self {
65        Self {
66            continue_execution: true,
67            modified_input: None,
68            modified_prompt: Some(prompt.into()),
69            reason: Some(reason.into()),
70        }
71    }
72
73    /// Returns whether execution should continue.
74    pub fn continue_execution(&self) -> bool {
75        self.continue_execution
76    }
77
78    /// Returns the modified input, if any.
79    pub fn modified_input(&self) -> Option<&Value> {
80        self.modified_input.as_ref()
81    }
82
83    /// Returns the modified prompt, if any.
84    pub fn modified_prompt(&self) -> Option<&str> {
85        self.modified_prompt.as_deref()
86    }
87
88    /// Returns the reason, if any.
89    pub fn reason(&self) -> Option<&str> {
90        self.reason.as_deref()
91    }
92}