1use crate::audit::AuditLogger;
7use crate::error::Result;
8use crate::permission::{PermissionChecker, PermissionConfig, PermissionDecision};
9use crate::prompt::{PermissionPrompt, PromptResult};
10use std::sync::Arc;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum AgentExecutionResult {
15 Allowed,
17 Denied,
19 Approved,
21 UserDenied,
23}
24
25impl std::fmt::Display for AgentExecutionResult {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 match self {
28 AgentExecutionResult::Allowed => write!(f, "allowed"),
29 AgentExecutionResult::Denied => write!(f, "denied"),
30 AgentExecutionResult::Approved => write!(f, "approved"),
31 AgentExecutionResult::UserDenied => write!(f, "user_denied"),
32 }
33 }
34}
35
36pub struct AgentExecutor {
38 config: Arc<PermissionConfig>,
40 audit_logger: Arc<AuditLogger>,
42 agent_name: Option<String>,
44}
45
46impl AgentExecutor {
47 pub fn new(config: Arc<PermissionConfig>, audit_logger: Arc<AuditLogger>) -> Self {
49 Self {
50 config,
51 audit_logger,
52 agent_name: None,
53 }
54 }
55
56 pub fn with_agent(
58 config: Arc<PermissionConfig>,
59 audit_logger: Arc<AuditLogger>,
60 agent_name: String,
61 ) -> Self {
62 Self {
63 config,
64 audit_logger,
65 agent_name: Some(agent_name),
66 }
67 }
68
69 pub fn execute_with_permission<F, T>(
80 &self,
81 tool_name: &str,
82 tool_description: Option<&str>,
83 action_description: Option<&str>,
84 execute_fn: F,
85 ) -> Result<(AgentExecutionResult, Option<T>)>
86 where
87 F: FnOnce() -> Result<T>,
88 {
89 let decision = self.check_permission(tool_name)?;
91
92 match decision {
93 PermissionDecision::Allow => {
94 self.audit_logger
96 .log_execution(tool_name.to_string(), self.agent_name.clone(), None)
97 .map_err(crate::error::Error::Internal)?;
98
99 let result = execute_fn()?;
101 Ok((AgentExecutionResult::Allowed, Some(result)))
102 }
103 PermissionDecision::Ask => {
104 let prompt = PermissionPrompt::new(tool_name.to_string());
106 let prompt = if let Some(desc) = tool_description {
107 prompt.with_description(desc.to_string())
108 } else {
109 prompt
110 };
111 let prompt = if let Some(action) = action_description {
112 prompt.with_action(action.to_string())
113 } else {
114 prompt
115 };
116
117 self.audit_logger
119 .log_prompt(tool_name.to_string(), self.agent_name.clone(), None)
120 .map_err(crate::error::Error::Internal)?;
121
122 let prompt_result = prompt
124 .execute()
125 .map_err(|e| crate::error::Error::PromptError(e.to_string()))?;
126
127 match prompt_result {
128 PromptResult::Approved => {
129 self.audit_logger
131 .log_execution(
132 tool_name.to_string(),
133 self.agent_name.clone(),
134 Some("User approved via prompt".to_string()),
135 )
136 .map_err(crate::error::Error::Internal)?;
137
138 let result = execute_fn()?;
140 Ok((AgentExecutionResult::Approved, Some(result)))
141 }
142 PromptResult::Denied => {
143 self.audit_logger
145 .log_denial(
146 tool_name.to_string(),
147 self.agent_name.clone(),
148 Some("User denied via prompt".to_string()),
149 )
150 .map_err(crate::error::Error::Internal)?;
151
152 Ok((AgentExecutionResult::UserDenied, None))
153 }
154 }
155 }
156 PermissionDecision::Deny => {
157 self.audit_logger
159 .log_denial(
160 tool_name.to_string(),
161 self.agent_name.clone(),
162 Some("Permission denied".to_string()),
163 )
164 .map_err(crate::error::Error::Internal)?;
165
166 Ok((AgentExecutionResult::Denied, None))
167 }
168 }
169 }
170
171 pub fn check_permission(&self, tool_name: &str) -> Result<PermissionDecision> {
173 let permissions = self.config.get_permissions_for_tool(tool_name)?;
174 let default_level = self.config.default_permission_level();
175
176 let decision = PermissionChecker::check_permission(
177 &permissions,
178 self.agent_name.as_deref(),
179 default_level,
180 )?;
181
182 Ok(decision)
183 }
184
185 pub fn agent_name(&self) -> Option<&str> {
187 self.agent_name.as_deref()
188 }
189
190 pub fn audit_logger(&self) -> Arc<AuditLogger> {
192 Arc::clone(&self.audit_logger)
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199 use crate::permission::{PermissionLevel, ToolPermission};
200
201 #[test]
202 fn test_agent_executor_creation() {
203 let config = Arc::new(PermissionConfig::new());
204 let logger = Arc::new(AuditLogger::new());
205 let executor = AgentExecutor::new(config, logger);
206
207 assert_eq!(executor.agent_name(), None);
208 }
209
210 #[test]
211 fn test_agent_executor_with_agent_name() {
212 let config = Arc::new(PermissionConfig::new());
213 let logger = Arc::new(AuditLogger::new());
214 let executor = AgentExecutor::with_agent(config, logger, "agent1".to_string());
215
216 assert_eq!(executor.agent_name(), Some("agent1"));
217 }
218
219 #[test]
220 fn test_check_permission_allow() {
221 let mut config = PermissionConfig::new();
222 config.add_permission(ToolPermission::new(
223 "test_tool".to_string(),
224 PermissionLevel::Allow,
225 ));
226
227 let config = Arc::new(config);
228 let logger = Arc::new(AuditLogger::new());
229 let executor = AgentExecutor::new(config, logger);
230
231 let decision = executor.check_permission("test_tool").unwrap();
232 assert_eq!(decision, PermissionDecision::Allow);
233 }
234
235 #[test]
236 fn test_check_permission_deny() {
237 let mut config = PermissionConfig::new();
238 config.add_permission(ToolPermission::new(
239 "test_tool".to_string(),
240 PermissionLevel::Deny,
241 ));
242
243 let config = Arc::new(config);
244 let logger = Arc::new(AuditLogger::new());
245 let executor = AgentExecutor::new(config, logger);
246
247 let decision = executor.check_permission("test_tool").unwrap();
248 assert_eq!(decision, PermissionDecision::Deny);
249 }
250
251 #[test]
252 fn test_execute_with_permission_allowed() {
253 let mut config = PermissionConfig::new();
254 config.add_permission(ToolPermission::new(
255 "test_tool".to_string(),
256 PermissionLevel::Allow,
257 ));
258
259 let config = Arc::new(config);
260 let logger = Arc::new(AuditLogger::new());
261 let executor = AgentExecutor::new(config, logger.clone());
262
263 let (result, output) = executor
264 .execute_with_permission("test_tool", None, None, || Ok(42))
265 .unwrap();
266
267 assert_eq!(result, AgentExecutionResult::Allowed);
268 assert_eq!(output, Some(42));
269
270 let entries = logger.entries().unwrap();
272 assert_eq!(entries.len(), 1);
273 }
274
275 #[test]
276 fn test_execute_with_permission_denied() {
277 let mut config = PermissionConfig::new();
278 config.add_permission(ToolPermission::new(
279 "test_tool".to_string(),
280 PermissionLevel::Deny,
281 ));
282
283 let config = Arc::new(config);
284 let logger = Arc::new(AuditLogger::new());
285 let executor = AgentExecutor::new(config, logger.clone());
286
287 let (result, output) = executor
288 .execute_with_permission("test_tool", None, None, || Ok(42))
289 .unwrap();
290
291 assert_eq!(result, AgentExecutionResult::Denied);
292 assert_eq!(output, None);
293
294 let entries = logger.entries().unwrap();
296 assert_eq!(entries.len(), 1);
297 }
298
299 #[test]
300 fn test_execute_with_permission_per_agent_override() {
301 let mut config = PermissionConfig::new();
302 config.add_permission(ToolPermission::new(
303 "test_tool".to_string(),
304 PermissionLevel::Allow,
305 ));
306 config.add_permission(ToolPermission::with_agent(
307 "test_tool".to_string(),
308 PermissionLevel::Deny,
309 "agent1".to_string(),
310 ));
311
312 let config = Arc::new(config);
313 let logger = Arc::new(AuditLogger::new());
314
315 let executor =
317 AgentExecutor::with_agent(config.clone(), logger.clone(), "agent1".to_string());
318 let (result, _) = executor
319 .execute_with_permission("test_tool", None, None, || Ok(42))
320 .unwrap();
321 assert_eq!(result, AgentExecutionResult::Denied);
322
323 let executor = AgentExecutor::with_agent(config, logger, "agent2".to_string());
325 let (result, _) = executor
326 .execute_with_permission("test_tool", None, None, || Ok(42))
327 .unwrap();
328 assert_eq!(result, AgentExecutionResult::Allowed);
329 }
330}