Skip to main content

car_engine/
capabilities.rs

1//! Per-agent capability permissions for the Common Agent Runtime.
2//!
3//! A [`CapabilitySet`] defines what an agent is allowed to do: which tools
4//! it may invoke, which state keys it may access, and how many actions it
5//! may include in a single proposal.
6
7use serde::{Deserialize, Serialize};
8use std::collections::HashSet;
9
10/// Capability set defining what an agent is allowed to do.
11#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12pub struct CapabilitySet {
13    /// If set, only these tools are allowed. Empty = all tools allowed.
14    pub allowed_tools: HashSet<String>,
15    /// These tools are always denied (overrides allowed_tools).
16    pub denied_tools: HashSet<String>,
17    /// If set, only these state keys can be read/written. Empty = all keys allowed.
18    pub allowed_state_keys: HashSet<String>,
19    /// Maximum actions per proposal. None = unlimited.
20    pub max_actions: Option<u32>,
21}
22
23impl CapabilitySet {
24    pub fn new() -> Self {
25        Self::default()
26    }
27
28    pub fn allow_tool(mut self, tool: &str) -> Self {
29        self.allowed_tools.insert(tool.to_string());
30        self
31    }
32
33    pub fn deny_tool(mut self, tool: &str) -> Self {
34        self.denied_tools.insert(tool.to_string());
35        self
36    }
37
38    pub fn allow_state_key(mut self, key: &str) -> Self {
39        self.allowed_state_keys.insert(key.to_string());
40        self
41    }
42
43    pub fn with_max_actions(mut self, max: u32) -> Self {
44        self.max_actions = Some(max);
45        self
46    }
47
48    /// Check if a tool is permitted.
49    pub fn tool_allowed(&self, tool: &str) -> bool {
50        if self.denied_tools.contains(tool) {
51            return false;
52        }
53        if self.allowed_tools.is_empty() {
54            return true;
55        }
56        self.allowed_tools.contains(tool)
57    }
58
59    /// Check if a state key is permitted.
60    pub fn state_key_allowed(&self, key: &str) -> bool {
61        if self.allowed_state_keys.is_empty() {
62            return true;
63        }
64        self.allowed_state_keys.contains(key)
65    }
66
67    /// Check if action count is within budget.
68    pub fn actions_within_budget(&self, count: u32) -> bool {
69        match self.max_actions {
70            None => true,
71            Some(max) => count <= max,
72        }
73    }
74}