recursive/
tool_set_provider.rs1use crate::tools::{policy_sandbox::PolicyConfig, ToolRegistry};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16pub enum SandboxMode {
17 #[default]
19 None,
20 Policy,
23 Container,
25 MicroVm,
28}
29
30pub trait ToolSetProvider: Send + Sync + 'static {
36 fn build_registry(&self) -> ToolRegistry;
38 fn sandbox_mode(&self) -> SandboxMode;
40}
41
42pub struct LocalToolSetProvider {
48 workspace: std::path::PathBuf,
49 shell_timeout_secs: u64,
50 skills: Vec<crate::skills::Skill>,
51}
52
53impl LocalToolSetProvider {
54 pub fn new(
56 workspace: std::path::PathBuf,
57 shell_timeout_secs: u64,
58 skills: Vec<crate::skills::Skill>,
59 ) -> Self {
60 Self {
61 workspace,
62 shell_timeout_secs,
63 skills,
64 }
65 }
66}
67
68impl ToolSetProvider for LocalToolSetProvider {
69 fn build_registry(&self) -> ToolRegistry {
70 crate::tools::build_standard_tools(&self.workspace, &self.skills, self.shell_timeout_secs)
71 }
72
73 fn sandbox_mode(&self) -> SandboxMode {
74 SandboxMode::None
75 }
76}
77
78pub struct PolicyToolSetProvider {
85 workspace: std::path::PathBuf,
86 shell_timeout_secs: u64,
87 skills: Vec<crate::skills::Skill>,
88 pub policy: PolicyConfig,
90}
91
92impl PolicyToolSetProvider {
93 pub fn new(
94 workspace: std::path::PathBuf,
95 shell_timeout_secs: u64,
96 skills: Vec<crate::skills::Skill>,
97 policy: PolicyConfig,
98 ) -> Self {
99 Self {
100 workspace,
101 shell_timeout_secs,
102 skills,
103 policy,
104 }
105 }
106
107 pub fn restrictive(
109 workspace: std::path::PathBuf,
110 shell_timeout_secs: u64,
111 skills: Vec<crate::skills::Skill>,
112 ) -> Self {
113 Self::new(
114 workspace,
115 shell_timeout_secs,
116 skills,
117 PolicyConfig::default_restrictive(),
118 )
119 }
120}
121
122impl ToolSetProvider for PolicyToolSetProvider {
123 fn build_registry(&self) -> ToolRegistry {
124 crate::tools::build_standard_tools(&self.workspace, &self.skills, self.shell_timeout_secs)
125 .with_policy(self.policy.clone())
126 }
127
128 fn sandbox_mode(&self) -> SandboxMode {
129 SandboxMode::Policy
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use tempfile::TempDir;
137
138 fn provider() -> (LocalToolSetProvider, TempDir) {
139 let dir = TempDir::new().unwrap();
140 let p = LocalToolSetProvider::new(dir.path().to_path_buf(), 30, vec![]);
141 (p, dir)
142 }
143
144 #[test]
145 fn local_provider_sandbox_mode_is_none() {
146 let (p, _dir) = provider();
147 assert_eq!(p.sandbox_mode(), SandboxMode::None);
148 }
149
150 #[test]
151 fn local_provider_registry_non_empty() {
152 let (p, _dir) = provider();
153 let reg = p.build_registry();
154 let names = reg.names();
155 assert!(
156 names.iter().any(|n| n == "run_shell"),
157 "expected run_shell in registry, got: {names:?}"
158 );
159 }
160
161 #[test]
162 fn policy_provider_sandbox_mode_is_policy() {
163 let dir = TempDir::new().unwrap();
164 let p = PolicyToolSetProvider::restrictive(dir.path().to_path_buf(), 30, vec![]);
165 assert_eq!(p.sandbox_mode(), SandboxMode::Policy);
166 }
167
168 #[test]
169 fn policy_provider_attaches_policy_to_registry() {
170 let dir = TempDir::new().unwrap();
171 let p = PolicyToolSetProvider::restrictive(dir.path().to_path_buf(), 30, vec![]);
172 let reg = p.build_registry();
173 let policy = reg.policy().expect("policy should be attached");
175 assert!(policy.check_shell("ls").is_ok());
176 assert!(policy.check_shell("rm -rf /").is_err());
177 }
178}