deepstrike_sdk/runtime/
os_profile.rs1pub use deepstrike_core::runtime::kernel::wire::{
2 ParamConstraint as ConstraintSpec, PolicyAction, PolicyRule, RateLimitSpec,
3};
4use deepstrike_core::runtime::kernel::wire::{SignalPolicy as WireSignalPolicy, WireU64};
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) -> WireSignalPolicy {
18 WireSignalPolicy {
19 queue_max: self.queue_max,
20 ttl_ms: self.ttl_ms.map(WireU64::new),
21 deadline_escalation: self.deadline_escalation,
22 }
23 }
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct MemoryWriteRateLimit {
28 pub max_writes: u32,
29 pub window_ms: u64,
30}
31
32impl From<MemoryWriteRateLimit> for (u32, u64) {
33 fn from(limit: MemoryWriteRateLimit) -> Self {
34 (limit.max_writes, limit.window_ms)
35 }
36}
37
38#[derive(Debug, Clone)]
39pub struct GovernancePolicy {
40 pub default_action: Option<PolicyAction>,
41 pub rules: Vec<PolicyRule>,
42 pub vetoed_tools: Vec<String>,
43 pub rate_limits: Vec<RateLimitSpec>,
44 pub constraints: Vec<ConstraintSpec>,
45 pub surface_denied_in_system: bool,
47}
48
49impl GovernancePolicy {
50 pub fn allow_all() -> Self {
51 Self {
52 default_action: None,
53 rules: vec![PolicyRule {
54 tool_pattern: "*".to_string(),
55 action: PolicyAction::Allow,
56 }],
57 vetoed_tools: vec![],
58 rate_limits: vec![],
59 constraints: vec![],
60 surface_denied_in_system: true,
61 }
62 }
63
64 pub(crate) fn into_host_fact(self) -> serde_json::Value {
65 serde_json::json!({
66 "kind": "load_governance_policy",
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}