Skip to main content

roboticus_core/config/
runtime_ops.rs

1#[derive(Debug, Clone, Serialize, Deserialize)]
2pub struct ContextConfig {
3    #[serde(default = "default_max_context_tokens")]
4    pub max_tokens: usize,
5    #[serde(default = "default_soft_trim_ratio")]
6    pub soft_trim_ratio: f64,
7    #[serde(default = "default_hard_clear_ratio")]
8    pub hard_clear_ratio: f64,
9    #[serde(default = "default_preserve_recent")]
10    pub preserve_recent: usize,
11    #[serde(default)]
12    pub checkpoint_enabled: bool,
13    #[serde(default = "default_checkpoint_interval")]
14    pub checkpoint_interval_turns: u32,
15}
16
17impl Default for ContextConfig {
18    fn default() -> Self {
19        Self {
20            max_tokens: default_max_context_tokens(),
21            soft_trim_ratio: default_soft_trim_ratio(),
22            hard_clear_ratio: default_hard_clear_ratio(),
23            preserve_recent: default_preserve_recent(),
24            checkpoint_enabled: false,
25            checkpoint_interval_turns: default_checkpoint_interval(),
26        }
27    }
28}
29
30fn default_checkpoint_interval() -> u32 {
31    10
32}
33
34fn default_max_context_tokens() -> usize {
35    128_000
36}
37fn default_soft_trim_ratio() -> f64 {
38    0.8
39}
40fn default_hard_clear_ratio() -> f64 {
41    0.95
42}
43fn default_preserve_recent() -> usize {
44    10
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct ApprovalsConfig {
49    #[serde(default)]
50    pub enabled: bool,
51    #[serde(default)]
52    pub gated_tools: Vec<String>,
53    #[serde(default)]
54    pub blocked_tools: Vec<String>,
55    #[serde(default = "default_approval_timeout")]
56    pub timeout_seconds: u64,
57}
58
59impl Default for ApprovalsConfig {
60    fn default() -> Self {
61        Self {
62            enabled: false,
63            gated_tools: Vec::new(),
64            blocked_tools: Vec::new(),
65            timeout_seconds: default_approval_timeout(),
66        }
67    }
68}
69
70fn default_approval_timeout() -> u64 {
71    300
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct PluginsConfig {
76    #[serde(default = "default_plugins_dir")]
77    pub dir: PathBuf,
78    #[serde(default)]
79    pub allow: Vec<String>,
80    #[serde(default)]
81    pub deny: Vec<String>,
82    #[serde(default)]
83    pub strict_permissions: bool,
84    #[serde(default)]
85    pub allowed_permissions: Vec<String>,
86}
87
88impl Default for PluginsConfig {
89    fn default() -> Self {
90        Self {
91            dir: default_plugins_dir(),
92            allow: Vec::new(),
93            deny: Vec::new(),
94            strict_permissions: true,
95            allowed_permissions: Vec::new(),
96        }
97    }
98}
99
100fn default_plugins_dir() -> PathBuf {
101    dirs_next().join("plugins")
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct BrowserConfig {
106    #[serde(default)]
107    pub enabled: bool,
108    #[serde(default)]
109    pub executable_path: Option<String>,
110    #[serde(default = "default_true")]
111    pub headless: bool,
112    #[serde(default = "default_browser_profile_dir")]
113    pub profile_dir: PathBuf,
114    #[serde(default = "default_cdp_port")]
115    pub cdp_port: u16,
116}
117
118impl Default for BrowserConfig {
119    fn default() -> Self {
120        Self {
121            enabled: false,
122            executable_path: None,
123            headless: true,
124            profile_dir: default_browser_profile_dir(),
125            cdp_port: default_cdp_port(),
126        }
127    }
128}
129
130fn default_cdp_port() -> u16 {
131    9222
132}
133
134fn default_browser_profile_dir() -> PathBuf {
135    dirs_next().join("browser-profiles")
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct DaemonConfig {
140    #[serde(default)]
141    pub auto_restart: bool,
142    #[serde(default = "default_pid_file")]
143    pub pid_file: PathBuf,
144}
145
146impl Default for DaemonConfig {
147    fn default() -> Self {
148        Self {
149            auto_restart: false,
150            pid_file: default_pid_file(),
151        }
152    }
153}
154
155fn default_pid_file() -> PathBuf {
156    dirs_next().join("roboticus.pid")
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct UpdateConfig {
161    #[serde(default = "default_true")]
162    pub check_on_start: bool,
163    #[serde(default = "default_update_channel")]
164    pub channel: String,
165    /// Legacy single-registry URL. Kept for backward compatibility.
166    /// If `registries` is empty and this is set, `resolve_registries()` synthesizes
167    /// a single `RegistrySource` from it.
168    #[serde(default = "default_update_registry_url")]
169    pub registry_url: String,
170    /// Multi-registry support: ordered list of skill registries to sync from.
171    /// Higher-priority registries win on name collision.
172    #[serde(default)]
173    pub registries: Vec<RegistrySource>,
174}
175
176impl Default for UpdateConfig {
177    fn default() -> Self {
178        Self {
179            check_on_start: true,
180            channel: default_update_channel(),
181            registry_url: default_update_registry_url(),
182            registries: Vec::new(),
183        }
184    }
185}
186
187impl UpdateConfig {
188    /// Resolve the effective list of registries. If explicit `registries` is
189    /// non-empty, returns those. Otherwise, falls back to the legacy
190    /// `registry_url` field wrapped in a single `RegistrySource`.
191    pub fn resolve_registries(&self) -> Vec<RegistrySource> {
192        if !self.registries.is_empty() {
193            return self.registries.clone();
194        }
195        vec![RegistrySource {
196            name: "default".into(),
197            url: self.registry_url.clone(),
198            priority: 50,
199            enabled: true,
200        }]
201    }
202}
203
204/// A remote skill registry that the agent can sync from.
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct RegistrySource {
207    /// Human-readable name, used as the namespace prefix for remote skills
208    /// (e.g. `"community"` → skills namespaced as `community/skill_name`).
209    pub name: String,
210    /// URL of the registry manifest (JSON endpoint).
211    pub url: String,
212    /// Priority for conflict resolution: higher wins when two registries
213    /// publish a skill with the same name. Range: 0–100.
214    #[serde(default = "default_registry_priority")]
215    pub priority: u32,
216    /// Whether this registry is actively synced.
217    #[serde(default = "default_true")]
218    pub enabled: bool,
219}
220
221fn default_registry_priority() -> u32 {
222    50
223}
224
225fn default_update_channel() -> String {
226    "stable".into()
227}
228
229fn default_update_registry_url() -> String {
230    "https://roboticus.ai/registry/manifest.json".into()
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct PersonalityConfig {
235    #[serde(default = "default_os_file")]
236    pub os_file: String,
237    #[serde(default = "default_firmware_file")]
238    pub firmware_file: String,
239}
240
241impl Default for PersonalityConfig {
242    fn default() -> Self {
243        Self {
244            os_file: default_os_file(),
245            firmware_file: default_firmware_file(),
246        }
247    }
248}
249
250fn default_os_file() -> String {
251    "OS.toml".into()
252}
253fn default_firmware_file() -> String {
254    "FIRMWARE.toml".into()
255}
256
257#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct SessionConfig {
259    #[serde(default = "default_session_ttl")]
260    pub ttl_seconds: u64,
261    #[serde(default = "default_session_scope_mode")]
262    pub scope_mode: String,
263    #[serde(default)]
264    pub reset_schedule: Option<String>,
265}
266
267impl Default for SessionConfig {
268    fn default() -> Self {
269        Self {
270            ttl_seconds: default_session_ttl(),
271            scope_mode: default_session_scope_mode(),
272            reset_schedule: None,
273        }
274    }
275}
276
277fn default_session_ttl() -> u64 {
278    86400
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
282pub struct McpConfig {
283    #[serde(default)]
284    pub server_enabled: bool,
285    #[serde(default = "default_mcp_port")]
286    pub server_port: u16,
287    #[serde(default)]
288    pub clients: Vec<McpClientConfig>,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct McpClientConfig {
293    pub name: String,
294    pub url: String,
295    #[serde(default)]
296    pub transport: McpTransport,
297    #[serde(default)]
298    pub auth_token_env: Option<String>,
299}
300
301#[derive(Debug, Clone, Default, Serialize, Deserialize)]
302pub enum McpTransport {
303    #[default]
304    Sse,
305    Stdio,
306    Http,
307    WebSocket,
308}
309
310fn default_mcp_port() -> u16 {
311    3001
312}
313
314impl Default for McpConfig {
315    fn default() -> Self {
316        Self {
317            server_enabled: false,
318            server_port: default_mcp_port(),
319            clients: Vec::new(),
320        }
321    }
322}
323
324fn default_session_scope_mode() -> String {
325    "agent".into()
326}