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}