Skip to main content

llama_cpp_v3_agent_sdk/
permission.rs

1use serde::{Deserialize, Serialize};
2
3/// Permission decision for a tool call.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5pub enum PermissionDecision {
6    /// Allow this single call.
7    AllowOnce,
8    /// Allow all future calls to this tool.
9    AllowAlways,
10    /// Deny this single call.
11    DenyOnce,
12    /// Deny all future calls to this tool.
13    DenyAlways,
14}
15
16impl PermissionDecision {
17    pub fn is_allowed(self) -> bool {
18        matches!(self, Self::AllowOnce | Self::AllowAlways)
19    }
20}
21
22/// Permission request sent to the user/callback.
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct PermissionRequest {
25    /// Name of the tool being invoked.
26    pub tool_name: String,
27    /// Human-readable description of what the tool will do.
28    pub description: String,
29    /// Whether this action is considered dangerous.
30    pub dangerous: bool,
31    /// The raw arguments (for inspection).
32    pub arguments: serde_json::Value,
33}
34
35/// Configures how the agent handles permissions.
36pub enum PermissionMode {
37    /// Auto-approve everything (dangerous!).
38    AutoApprove,
39    /// Use a callback to ask for permission.
40    Callback(Box<dyn Fn(&PermissionRequest) -> PermissionDecision + Send + Sync>),
41}
42
43impl std::fmt::Debug for PermissionMode {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        match self {
46            PermissionMode::AutoApprove => write!(f, "AutoApprove"),
47            PermissionMode::Callback(_) => write!(f, "Callback(...)"),
48        }
49    }
50}
51
52/// Tracks per-tool permission state across the session.
53pub struct PermissionTracker {
54    mode: PermissionMode,
55    /// Tools that have been granted "always allow".
56    always_allowed: std::collections::HashSet<String>,
57    /// Tools that have been denied "always deny".
58    always_denied: std::collections::HashSet<String>,
59}
60
61impl PermissionTracker {
62    pub fn new(mode: PermissionMode) -> Self {
63        Self {
64            mode,
65            always_allowed: std::collections::HashSet::new(),
66            always_denied: std::collections::HashSet::new(),
67        }
68    }
69
70    /// Check if a tool call is permitted.
71    ///
72    /// Returns `true` if the tool may execute, `false` otherwise.
73    pub fn check(&mut self, request: &PermissionRequest) -> bool {
74        // Already permanently decided?
75        if self.always_allowed.contains(&request.tool_name) {
76            return true;
77        }
78        if self.always_denied.contains(&request.tool_name) {
79            return false;
80        }
81
82        match &self.mode {
83            PermissionMode::AutoApprove => true,
84            PermissionMode::Callback(cb) => {
85                let decision = cb(request);
86                match decision {
87                    PermissionDecision::AllowAlways => {
88                        self.always_allowed.insert(request.tool_name.clone());
89                        true
90                    }
91                    PermissionDecision::DenyAlways => {
92                        self.always_denied.insert(request.tool_name.clone());
93                        false
94                    }
95                    PermissionDecision::AllowOnce => true,
96                    PermissionDecision::DenyOnce => false,
97                }
98            }
99        }
100    }
101}