deepstrike_sdk/runtime/
os_profile.rs1use deepstrike_core::runtime::kernel::{
2 ConstraintSpec, KernelInputEvent, PolicyAction, PolicyRule, RateLimitSpec, SignalPolicyConfig,
3 SIGNAL_POLICY_VERSION,
4};
5pub use deepstrike_core::scheduler::policy::SchedulerPolicyConfig;
6
7use crate::{Error, Result};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct SignalPolicy {
11 pub queue_max: u32,
12 pub ttl_ms: Option<u64>,
13 pub deadline_escalation: Option<bool>,
14}
15
16impl SignalPolicy {
17 pub(crate) fn into_kernel(self) -> SignalPolicyConfig {
18 SignalPolicyConfig {
19 version: SIGNAL_POLICY_VERSION,
20 queue_max: self.queue_max,
21 ttl_ms: self.ttl_ms,
22 deadline_escalation: self.deadline_escalation,
23 }
24 }
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct MemoryWriteRateLimit {
29 pub max_writes: u32,
30 pub window_ms: u64,
31}
32
33impl From<MemoryWriteRateLimit> for (u32, u64) {
34 fn from(limit: MemoryWriteRateLimit) -> Self {
35 (limit.max_writes, limit.window_ms)
36 }
37}
38
39#[derive(Debug, Clone)]
40pub struct GovernancePolicy {
41 pub default_action: Option<PolicyAction>,
42 pub rules: Vec<PolicyRule>,
43 pub vetoed_tools: Vec<String>,
44 pub rate_limits: Vec<RateLimitSpec>,
45 pub constraints: Vec<ConstraintSpec>,
46 pub surface_denied_in_system: bool,
48}
49
50impl GovernancePolicy {
51 pub fn allow_all() -> Self {
52 Self {
53 default_action: None,
54 rules: vec![PolicyRule {
55 tool_pattern: "*".to_string(),
56 action: PolicyAction::Allow,
57 }],
58 vetoed_tools: vec![],
59 rate_limits: vec![],
60 constraints: vec![],
61 surface_denied_in_system: true,
62 }
63 }
64
65 pub fn into_kernel_event(self) -> KernelInputEvent {
66 KernelInputEvent::LoadGovernancePolicy {
67 default_action: self.default_action,
68 rules: self.rules,
69 vetoed_tools: self.vetoed_tools,
70 rate_limits: self.rate_limits,
71 constraints: self.constraints,
72 }
73 }
74}
75
76#[derive(Debug, Clone)]
77pub struct NativeOsProfile {
78 pub id: &'static str,
79 pub signal_policy: SignalPolicy,
80 pub governance_policy: GovernancePolicy,
81}
82
83#[derive(Debug, Clone)]
84pub enum OsProfile {
85 Native,
86 Concrete(NativeOsProfile),
87}
88
89pub const DEFAULT_NATIVE_SIGNAL_POLICY: SignalPolicy = SignalPolicy {
90 queue_max: 64,
91 ttl_ms: None,
92 deadline_escalation: None,
93};
94
95pub fn default_native_governance_policy() -> GovernancePolicy {
96 GovernancePolicy::allow_all()
97}
98
99pub fn governance_filter_schema(
101 tools: &[deepstrike_core::types::message::ToolSchema],
102 policy: &GovernancePolicy,
103) -> (
104 Vec<deepstrike_core::types::message::ToolSchema>,
105 Vec<String>,
106) {
107 let mut allowed = Vec::with_capacity(tools.len());
108 let mut denied = Vec::new();
109 let matches = |pat: &str, name: &str| -> bool {
110 if pat == name {
111 return true;
112 }
113 if let Some(prefix) = pat.strip_suffix('*') {
114 return name.starts_with(prefix);
115 }
116 false
117 };
118 for tool in tools {
119 let name = tool.name.as_str();
120 if policy.vetoed_tools.iter().any(|v| v == name) {
121 denied.push(name.to_string());
122 continue;
123 }
124 let mut action = policy.default_action.clone().unwrap_or(PolicyAction::Allow);
125 for r in &policy.rules {
126 if matches(&r.tool_pattern, name) {
127 action = r.action.clone();
128 }
129 }
130 if matches!(action, PolicyAction::Deny) {
131 denied.push(name.to_string());
132 } else {
133 allowed.push(tool.clone());
134 }
135 }
136 (allowed, denied)
137}
138
139pub fn os_profile(profile: Option<OsProfile>) -> NativeOsProfile {
140 match profile.unwrap_or(OsProfile::Native) {
141 OsProfile::Native => NativeOsProfile {
142 id: "native",
143 signal_policy: DEFAULT_NATIVE_SIGNAL_POLICY,
144 governance_policy: default_native_governance_policy(),
145 },
146 OsProfile::Concrete(profile) => profile,
147 }
148}
149
150pub fn assert_native_profile(profile: Option<OsProfile>) -> Result<NativeOsProfile> {
151 let resolved = os_profile(profile);
152 if resolved.id != "native" {
153 return Err(Error::Other(format!(
154 "Unsupported OS profile: {}",
155 resolved.id
156 )));
157 }
158 if resolved.signal_policy.queue_max == 0 {
159 return Err(Error::Other(
160 "Invalid native OS profile: SignalPolicy queue_max must be positive".to_string(),
161 ));
162 }
163 if matches!(resolved.signal_policy.ttl_ms, Some(0)) {
164 return Err(Error::Other(
165 "Invalid native OS profile: SignalPolicy ttl_ms must be positive when present"
166 .to_string(),
167 ));
168 }
169 Ok(resolved)
170}