Skip to main content

deepstrike_sdk/
governance.rs

1use compact_str::CompactString;
2use deepstrike_core::governance::constraint::{ConstraintRule, ParamConstraint};
3use deepstrike_core::governance::permission::{PermissionAction, PermissionRule};
4use deepstrike_core::governance::pipeline::GovernancePipeline;
5use deepstrike_core::governance::rate_limit::RateLimit;
6use deepstrike_core::types::agent::AgentIdentity;
7use deepstrike_core::types::message::ToolCall;
8use deepstrike_core::types::policy::GovernanceVerdict as CoreVerdict;
9
10/// SDK-facing governance verdict (aligned with Node/Python `kind` field).
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct GovernanceVerdict {
13    pub kind: String,
14    pub reason: Option<String>,
15    pub retry_after_ms: Option<u64>,
16}
17
18/// Facade over the kernel `GovernancePipeline`.
19pub struct Governance {
20    inner: GovernancePipeline,
21    agent_id: String,
22    session_id: String,
23}
24
25impl Governance {
26    pub fn new(default_action: PermissionAction) -> Self {
27        Self {
28            inner: GovernancePipeline::new(default_action),
29            agent_id: "anonymous".into(),
30            session_id: String::new(),
31        }
32    }
33
34    pub fn allow() -> Self {
35        Self::new(PermissionAction::Allow)
36    }
37
38    pub fn set_identity(&mut self, agent_id: impl Into<String>, session_id: impl Into<String>) {
39        self.agent_id = agent_id.into();
40        self.session_id = session_id.into();
41    }
42
43    pub fn add_permission_rule(&mut self, pattern: impl Into<String>, action: PermissionAction) {
44        self.inner.permission.add_rule(PermissionRule {
45            tool_pattern: CompactString::new(pattern.into()),
46            action,
47        });
48    }
49
50    pub fn block_tool(&mut self, name: impl Into<String>) {
51        self.inner.veto.block_tool(name.into());
52    }
53
54    pub fn set_rate_limit(&mut self, tool_name: impl Into<String>, max_calls: u32, window_ms: u64) {
55        self.inner.rate_limiter.set_limit(
56            tool_name.into(),
57            RateLimit {
58                max_calls,
59                window_ms,
60            },
61        );
62    }
63
64    pub fn require_param(&mut self, tool_name: impl Into<String>, param_path: impl Into<String>) {
65        self.inner.constraints.add(ParamConstraint {
66            tool_name: tool_name.into(),
67            param_path: param_path.into(),
68            rule: ConstraintRule::Required,
69        });
70    }
71
72    pub fn allow_param_values(
73        &mut self,
74        tool_name: impl Into<String>,
75        param_path: impl Into<String>,
76        allowed_values: Vec<String>,
77    ) {
78        self.inner.constraints.add(ParamConstraint {
79            tool_name: tool_name.into(),
80            param_path: param_path.into(),
81            rule: ConstraintRule::Enum(allowed_values),
82        });
83    }
84
85    pub fn limit_param_range(
86        &mut self,
87        tool_name: impl Into<String>,
88        param_path: impl Into<String>,
89        min: Option<f64>,
90        max: Option<f64>,
91    ) {
92        self.inner.constraints.add(ParamConstraint {
93            tool_name: tool_name.into(),
94            param_path: param_path.into(),
95            rule: ConstraintRule::Range { min, max },
96        });
97    }
98
99    pub fn set_time(&mut self, now_ms: u64) {
100        self.inner.set_time(now_ms);
101    }
102
103    pub fn evaluate(&mut self, tool_name: &str, args_json: &str) -> GovernanceVerdict {
104        let args: serde_json::Value =
105            serde_json::from_str(args_json).unwrap_or(serde_json::Value::Null);
106        let call = ToolCall {
107            id: CompactString::new("gov"),
108            name: CompactString::new(tool_name),
109            arguments: args,
110        };
111        let caller = AgentIdentity::new(&self.agent_id, &self.session_id);
112        from_core(self.inner.evaluate(&call, &caller))
113    }
114}
115
116fn from_core(v: CoreVerdict) -> GovernanceVerdict {
117    match v {
118        CoreVerdict::Allow => GovernanceVerdict {
119            kind: "allow".into(),
120            reason: None,
121            retry_after_ms: None,
122        },
123        CoreVerdict::Deny { reason, .. } => GovernanceVerdict {
124            kind: "deny".into(),
125            reason: Some(reason),
126            retry_after_ms: None,
127        },
128        CoreVerdict::RateLimited { retry_after_ms } => GovernanceVerdict {
129            kind: "rate_limited".into(),
130            reason: None,
131            retry_after_ms: Some(retry_after_ms),
132        },
133        CoreVerdict::AskUser { reason } => GovernanceVerdict {
134            kind: "ask_user".into(),
135            reason: Some(reason),
136            retry_after_ms: None,
137        },
138    }
139}