agent_base/engine/approval.rs
1use async_trait::async_trait;
2
3use crate::types::{AgentResult, ApprovalDecision, ApprovalRequest};
4
5/// Trait for handling tool approval requests.
6///
7/// # Cancellation Contract
8///
9/// Implementors MUST handle cancellation via the provided `cancel_token`.
10/// The token is cancelled when the user interrupts the agent (e.g., Ctrl+C).
11///
12/// - **Async handlers** (channels, timers): race against `cancel_token.cancelled()` using `tokio::select!`
13/// - **Blocking handlers** (stdin, file IO): use `tokio::task::spawn_blocking` + `tokio::select!` to avoid blocking the tokio runtime
14///
15/// # Timeout
16///
17/// The caller (`process_approval`) wraps this method with a 300-second timeout.
18/// If the handler does not return within 300s, the approval is denied automatically.
19///
20/// # Example (blocking stdin)
21///
22/// ```ignore
23/// async fn approve(&self, request: ApprovalRequest, cancel_token: CancellationToken) -> AgentResult<ApprovalDecision> {
24/// tokio::select! {
25/// _ = cancel_token.cancelled() => Err(AgentError::Cancelled),
26/// result = tokio::task::spawn_blocking(|| read_stdin()) => result?,
27/// }
28/// }
29/// ```
30#[async_trait]
31pub trait ApprovalHandler: Send + Sync {
32 async fn approve(
33 &self,
34 request: ApprovalRequest,
35 cancel_token: tokio_util::sync::CancellationToken,
36 ) -> AgentResult<ApprovalDecision>;
37}
38
39#[derive(Clone, Debug, Default)]
40pub struct DenyAllApprovalHandler;
41
42#[async_trait]
43impl ApprovalHandler for DenyAllApprovalHandler {
44 async fn approve(
45 &self,
46 _request: ApprovalRequest,
47 _cancel_token: tokio_util::sync::CancellationToken,
48 ) -> AgentResult<ApprovalDecision> {
49 Ok(ApprovalDecision::Deny)
50 }
51}
52
53#[derive(Clone, Debug, Default)]
54pub struct AllowAllApprovalHandler;
55
56#[async_trait]
57impl ApprovalHandler for AllowAllApprovalHandler {
58 async fn approve(
59 &self,
60 _request: ApprovalRequest,
61 _cancel_token: tokio_util::sync::CancellationToken,
62 ) -> AgentResult<ApprovalDecision> {
63 Ok(ApprovalDecision::AllowAlways)
64 }
65}