Skip to main content

aa_core/
approval.rs

1//! Approval-related domain types shared across crates.
2
3use alloc::string::String;
4
5/// The category of action that triggered an approval request.
6///
7/// Used as an optional filter key in team routing configuration so different
8/// action types can be routed to different approver lists within the same team.
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
12pub enum ApprovalKind {
13    /// An agent attempted to spawn a child agent.
14    Spawn,
15    /// An agent invoked a tool that requires approval.
16    ToolUse,
17    /// An agent requested a budget increase.
18    BudgetIncrease,
19    /// A caller-defined approval category.
20    Custom(String),
21}
22
23impl ApprovalKind {
24    /// Returns the canonical string key stored in the database.
25    pub fn as_str(&self) -> &str {
26        match self {
27            Self::Spawn => "spawn",
28            Self::ToolUse => "tool_use",
29            Self::BudgetIncrease => "budget_increase",
30            Self::Custom(s) => s.as_str(),
31        }
32    }
33}
34
35impl core::fmt::Display for ApprovalKind {
36    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
37        f.write_str(self.as_str())
38    }
39}
40
41impl core::str::FromStr for ApprovalKind {
42    type Err = core::convert::Infallible;
43
44    fn from_str(s: &str) -> Result<Self, Self::Err> {
45        Ok(match s {
46            "spawn" => Self::Spawn,
47            "tool_use" => Self::ToolUse,
48            "budget_increase" => Self::BudgetIncrease,
49            other => Self::Custom(String::from(other)),
50        })
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn known_kinds_round_trip_via_as_str_and_from_str() {
60        for kind in [ApprovalKind::Spawn, ApprovalKind::ToolUse, ApprovalKind::BudgetIncrease] {
61            let s = kind.as_str();
62            let parsed: ApprovalKind = s.parse().unwrap();
63            assert_eq!(kind, parsed);
64        }
65    }
66
67    #[test]
68    fn custom_kind_preserves_string() {
69        let k: ApprovalKind = "file_access".parse().unwrap();
70        assert_eq!(k, ApprovalKind::Custom(String::from("file_access")));
71        assert_eq!(k.as_str(), "file_access");
72    }
73
74    #[test]
75    fn display_matches_as_str() {
76        assert_eq!(ApprovalKind::Spawn.to_string(), "spawn");
77        assert_eq!(ApprovalKind::ToolUse.to_string(), "tool_use");
78    }
79}