agent_base/tool/policy.rs
1use async_trait::async_trait;
2use serde_json::Value;
3
4use super::{ToolContext, ToolOutput};
5use crate::types::{AgentResult, ApprovalRequest};
6
7/// Policy-based control over tool execution.
8///
9/// Implement this trait to customise how tools are approved, monitored, and
10/// validated during an agent run. The pipeline calls each hook at a specific
11/// point in the tool lifecycle:
12///
13/// ```text
14/// evaluate_approval → before_call → (tool executes) → after_call
15/// ```
16///
17/// # Example: auto-approve read-only tools
18///
19/// ```ignore
20/// struct ReadOnlyPolicy;
21///
22/// #[async_trait]
23/// impl ToolPolicy for ReadOnlyPolicy {
24/// async fn evaluate_approval(&self, tool_name: &str, _args: &Value) -> Option<ApprovalRequest> {
25/// if tool_name == "read_file" || tool_name == "search" {
26/// None // auto-approve — no prompt for user
27/// } else {
28/// Some(ApprovalRequest { message: format!("Allow {}?", tool_name) })
29/// }
30/// }
31/// }
32/// ```
33///
34/// All hooks have default no-op implementations, so you only need to override
35/// the ones you care about.
36#[async_trait]
37pub trait ToolPolicy: Send + Sync {
38 /// Called **before** every tool call.
39 ///
40 /// Return `None` to auto-approve (skip the approval handler entirely).
41 /// Return `Some(ApprovalRequest)` to defer to the configured
42 /// [`ApprovalHandler`](crate::ApprovalHandler).
43 ///
44 /// This is the primary hook for implementing permission guards — for
45 /// example, auto-approving read-only operations while prompting for
46 /// destructive ones.
47 async fn evaluate_approval(&self, tool_name: &str, args: &Value) -> Option<ApprovalRequest>;
48
49 /// Called immediately **before** a tool executes, after approval has been
50 /// granted (or auto-approved).
51 ///
52 /// Use this for:
53 /// - Input validation (reject malformed arguments before the tool runs).
54 /// - Auditing / logging the raw call.
55 /// - Rate-limiting or quota enforcement.
56 ///
57 /// Return an `Err` to **cancel** the tool call before execution. The error
58 /// message is surfaced to the LLM so it can correct its approach.
59 fn before_call(&self, tool_name: &str, args: &Value, ctx: &ToolContext) -> AgentResult<()> {
60 let _ = (tool_name, args, ctx);
61 Ok(())
62 }
63
64 /// Called immediately **after** a tool executes, before the result is
65 /// returned to the LLM.
66 ///
67 /// Use this for:
68 /// - Output scrubbing / redaction (strip secrets from tool results).
69 /// - Truncation or formatting of large outputs.
70 /// - Recording metrics or audit trails.
71 ///
72 /// The `result` is the raw [`ToolOutput`] produced by the tool. You can
73 /// inspect it but not modify it through this hook — if you need to
74 /// transform the output, use a middleware instead.
75 ///
76 /// Return an `Err` to **reject** the result. The error message is surfaced
77 /// to the LLM.
78 fn after_call(
79 &self,
80 tool_name: &str,
81 args: &Value,
82 result: &ToolOutput,
83 ctx: &ToolContext,
84 ) -> AgentResult<()> {
85 let _ = (tool_name, args, result, ctx);
86 Ok(())
87 }
88}