|
15 | 15 | //! ``` |
16 | 16 |
|
17 | 17 | use crate::agent::{AgentConfig, AgentEvent, AgentLoop}; |
18 | | -use crate::llm::LlmClient; |
| 18 | +use crate::llm::{LlmClient, ToolDefinition}; |
19 | 19 | use crate::mcp::manager::McpManager; |
20 | | -use crate::subagent::AgentRegistry; |
| 20 | +use crate::permissions::PermissionChecker; |
| 21 | +use crate::subagent::{AgentDefinition, AgentRegistry}; |
21 | 22 | use crate::tools::types::{Tool, ToolContext, ToolOutput}; |
22 | 23 | use anyhow::{Context, Result}; |
23 | 24 | use async_trait::async_trait; |
@@ -128,6 +129,39 @@ fn format_task_result_for_context(result: &TaskResult) -> (String, bool) { |
128 | 129 | (formatted, truncated) |
129 | 130 | } |
130 | 131 |
|
| 132 | +fn has_defined_permissions(agent: &AgentDefinition) -> bool { |
| 133 | + !agent.permissions.is_default_policy() |
| 134 | +} |
| 135 | + |
| 136 | +fn agent_permission_checker(agent: &AgentDefinition) -> Option<Arc<dyn PermissionChecker>> { |
| 137 | + if has_defined_permissions(agent) { |
| 138 | + Some(Arc::new(agent.permissions.clone()) as Arc<dyn PermissionChecker>) |
| 139 | + } else { |
| 140 | + None |
| 141 | + } |
| 142 | +} |
| 143 | + |
| 144 | +fn build_child_config( |
| 145 | + agent: &AgentDefinition, |
| 146 | + params: &TaskParams, |
| 147 | + tools: Vec<ToolDefinition>, |
| 148 | +) -> AgentConfig { |
| 149 | + let mut prompt_slots = crate::prompts::SystemPromptSlots::default(); |
| 150 | + if let Some(ref p) = agent.prompt { |
| 151 | + prompt_slots.extra = Some(p.clone()); |
| 152 | + } |
| 153 | + |
| 154 | + AgentConfig { |
| 155 | + prompt_slots, |
| 156 | + tools, |
| 157 | + max_tool_rounds: params |
| 158 | + .max_steps |
| 159 | + .unwrap_or_else(|| agent.max_steps.unwrap_or(20)), |
| 160 | + permission_checker: agent_permission_checker(agent), |
| 161 | + ..AgentConfig::default() |
| 162 | + } |
| 163 | +} |
| 164 | + |
131 | 165 | /// Task executor for delegated child runs. |
132 | 166 | pub struct TaskExecutor { |
133 | 167 | /// Agent registry for looking up agent definitions |
@@ -217,26 +251,13 @@ impl TaskExecutor { |
217 | 251 | } |
218 | 252 | } |
219 | 253 |
|
220 | | - if !agent.permissions.allow.is_empty() || !agent.permissions.deny.is_empty() { |
| 254 | + if has_defined_permissions(&agent) { |
221 | 255 | child_executor.set_guard_policy(Arc::new(agent.permissions.clone()) |
222 | 256 | as Arc<dyn crate::permissions::PermissionChecker>); |
223 | 257 | } |
224 | 258 | let child_executor = Arc::new(child_executor); |
225 | 259 |
|
226 | | - // Inject the agent system prompt via the extra slot. |
227 | | - let mut prompt_slots = crate::prompts::SystemPromptSlots::default(); |
228 | | - if let Some(ref p) = agent.prompt { |
229 | | - prompt_slots.extra = Some(p.clone()); |
230 | | - } |
231 | | - |
232 | | - let child_config = AgentConfig { |
233 | | - prompt_slots, |
234 | | - tools: child_executor.definitions(), |
235 | | - max_tool_rounds: params |
236 | | - .max_steps |
237 | | - .unwrap_or_else(|| agent.max_steps.unwrap_or(20)), |
238 | | - ..AgentConfig::default() |
239 | | - }; |
| 260 | + let child_config = build_child_config(&agent, ¶ms, child_executor.definitions()); |
240 | 261 |
|
241 | 262 | let tool_context = |
242 | 263 | ToolContext::new(PathBuf::from(&self.workspace)).with_session_id(session_id.clone()); |
@@ -1256,4 +1277,57 @@ mod tests { |
1256 | 1277 |
|
1257 | 1278 | assert!(props.get("permissive").is_none()); |
1258 | 1279 | } |
| 1280 | + |
| 1281 | + #[test] |
| 1282 | + fn test_child_config_inherits_agent_permissions() { |
| 1283 | + use crate::permissions::{PermissionDecision, PermissionPolicy}; |
| 1284 | + |
| 1285 | + let agent = AgentDefinition::new("worker", "Worker") |
| 1286 | + .with_permissions(PermissionPolicy::new().allow("bash(echo:*)")); |
| 1287 | + let params = TaskParams { |
| 1288 | + agent: "worker".to_string(), |
| 1289 | + description: "Run allowed command".to_string(), |
| 1290 | + prompt: "Use bash".to_string(), |
| 1291 | + background: false, |
| 1292 | + max_steps: None, |
| 1293 | + }; |
| 1294 | + |
| 1295 | + let config = build_child_config(&agent, ¶ms, Vec::new()); |
| 1296 | + |
| 1297 | + let checker = config |
| 1298 | + .permission_checker |
| 1299 | + .expect("agent permission policy should be installed"); |
| 1300 | + assert_eq!( |
| 1301 | + checker.check("bash", &serde_json::json!({"command": "echo ok"})), |
| 1302 | + PermissionDecision::Allow |
| 1303 | + ); |
| 1304 | + } |
| 1305 | + |
| 1306 | + #[test] |
| 1307 | + fn test_child_config_inherits_default_allow_permissions() { |
| 1308 | + use crate::permissions::{PermissionDecision, PermissionPolicy}; |
| 1309 | + |
| 1310 | + let permissions = PermissionPolicy { |
| 1311 | + default_decision: PermissionDecision::Allow, |
| 1312 | + ..PermissionPolicy::new() |
| 1313 | + }; |
| 1314 | + let agent = AgentDefinition::new("worker", "Worker").with_permissions(permissions); |
| 1315 | + let params = TaskParams { |
| 1316 | + agent: "worker".to_string(), |
| 1317 | + description: "Run any command".to_string(), |
| 1318 | + prompt: "Use bash".to_string(), |
| 1319 | + background: false, |
| 1320 | + max_steps: None, |
| 1321 | + }; |
| 1322 | + |
| 1323 | + let config = build_child_config(&agent, ¶ms, Vec::new()); |
| 1324 | + |
| 1325 | + let checker = config |
| 1326 | + .permission_checker |
| 1327 | + .expect("non-default permission policy should be installed"); |
| 1328 | + assert_eq!( |
| 1329 | + checker.check("bash", &serde_json::json!({"command": "echo ok"})), |
| 1330 | + PermissionDecision::Allow |
| 1331 | + ); |
| 1332 | + } |
1259 | 1333 | } |
0 commit comments