1#[derive(Debug, Clone, Serialize, Deserialize)]
2pub struct A2aConfig {
3 #[serde(default = "default_true")]
4 pub enabled: bool,
5 #[serde(default = "default_a2a_max_msg_size")]
6 pub max_message_size: usize,
7 #[serde(default = "default_a2a_rate_limit")]
8 pub rate_limit_per_peer: u32,
9 #[serde(default = "default_a2a_session_timeout")]
10 pub session_timeout_seconds: u64,
11 #[serde(default = "default_true")]
12 pub require_on_chain_identity: bool,
13 #[serde(default = "default_a2a_nonce_ttl")]
14 pub nonce_ttl_seconds: u64,
15}
16
17impl Default for A2aConfig {
18 fn default() -> Self {
19 Self {
20 enabled: true,
21 max_message_size: default_a2a_max_msg_size(),
22 rate_limit_per_peer: default_a2a_rate_limit(),
23 session_timeout_seconds: default_a2a_session_timeout(),
24 require_on_chain_identity: true,
25 nonce_ttl_seconds: default_a2a_nonce_ttl(),
26 }
27 }
28}
29
30fn default_a2a_max_msg_size() -> usize {
31 65536
32}
33fn default_a2a_rate_limit() -> u32 {
34 10
35}
36fn default_a2a_session_timeout() -> u64 {
37 3600
38}
39fn default_a2a_nonce_ttl() -> u64 {
40 7200
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct SkillsConfig {
45 #[serde(default = "default_skills_dir")]
46 pub skills_dir: PathBuf,
47 #[serde(default = "default_script_timeout")]
48 pub script_timeout_seconds: u64,
49 #[serde(default = "default_script_max_output")]
50 pub script_max_output_bytes: usize,
51 #[serde(default = "default_interpreters")]
52 pub allowed_interpreters: Vec<String>,
53 #[serde(default = "default_true")]
54 pub sandbox_env: bool,
55 #[serde(default = "default_true")]
56 pub hot_reload: bool,
57 #[serde(default = "default_script_max_memory")]
60 pub script_max_memory_bytes: Option<u64>,
61 #[serde(default)]
65 pub network_allowed: bool,
66 #[serde(default)]
70 pub workspace_dir: Option<PathBuf>,
71}
72
73impl Default for SkillsConfig {
74 fn default() -> Self {
75 Self {
76 skills_dir: default_skills_dir(),
77 script_timeout_seconds: default_script_timeout(),
78 script_max_output_bytes: default_script_max_output(),
79 allowed_interpreters: default_interpreters(),
80 sandbox_env: true,
81 hot_reload: true,
82 script_max_memory_bytes: default_script_max_memory(),
83 network_allowed: false,
84 workspace_dir: None,
85 }
86 }
87}
88
89fn default_skills_dir() -> PathBuf {
90 dirs_next().join("skills")
91}
92fn default_script_timeout() -> u64 {
93 30
94}
95fn default_script_max_output() -> usize {
96 1_048_576
97}
98fn default_script_max_memory() -> Option<u64> {
99 Some(256 * 1024 * 1024) }
101fn default_interpreters() -> Vec<String> {
102 #[cfg(windows)]
103 {
104 vec![
105 "bash".into(),
106 "python".into(),
107 "python3".into(),
108 "node".into(),
109 ]
110 }
111 #[cfg(not(windows))]
112 {
113 vec!["bash".into(), "python3".into(), "node".into()]
114 }
115}
116
117#[derive(Debug, Clone, Default, Serialize, Deserialize)]
118pub struct VoiceChannelConfig {
119 #[serde(default)]
120 pub enabled: bool,
121 #[serde(default)]
122 pub stt_model: Option<String>,
123 #[serde(default)]
124 pub tts_model: Option<String>,
125 #[serde(default)]
126 pub tts_voice: Option<String>,
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct ChannelsConfig {
131 #[serde(default)]
132 pub telegram: Option<TelegramConfig>,
133 #[serde(default)]
134 pub whatsapp: Option<WhatsAppConfig>,
135 #[serde(default)]
136 pub discord: Option<DiscordConfig>,
137 #[serde(default)]
138 pub signal: Option<SignalConfig>,
139 #[serde(default)]
140 pub matrix: Option<MatrixConfig>,
141 #[serde(default)]
142 pub email: EmailConfig,
143 #[serde(default)]
144 pub voice: VoiceChannelConfig,
145 #[serde(default)]
149 pub trusted_sender_ids: Vec<String>,
150 #[serde(default = "default_thinking_threshold")]
154 pub thinking_threshold_seconds: u64,
155 #[serde(default)]
159 pub startup_announcements: Option<StartupAnnouncementsConfig>,
160}
161
162impl Default for ChannelsConfig {
163 fn default() -> Self {
164 Self {
165 telegram: None,
166 whatsapp: None,
167 discord: None,
168 signal: None,
169 matrix: None,
170 email: EmailConfig::default(),
171 voice: VoiceChannelConfig::default(),
172 trusted_sender_ids: Vec::new(),
173 thinking_threshold_seconds: default_thinking_threshold(),
174 startup_announcements: None,
175 }
176 }
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
180#[serde(untagged)]
181pub enum StartupAnnouncementsConfig {
182 Flag(bool),
183 Text(String),
184 Channels(Vec<String>),
185}
186
187impl ChannelsConfig {
188 pub fn startup_announcement_channels(&self) -> Vec<String> {
189 fn normalize_channel(s: &str) -> Option<String> {
190 let v = s.trim().to_ascii_lowercase();
191 if v.is_empty() || v == "none" || v == "null" || v == "false" {
192 None
193 } else {
194 Some(v)
195 }
196 }
197
198 let mut out = match &self.startup_announcements {
199 None => Vec::new(),
200 Some(StartupAnnouncementsConfig::Flag(_)) => Vec::new(),
201 Some(StartupAnnouncementsConfig::Text(v)) => {
202 normalize_channel(v).map(|s| vec![s]).unwrap_or_default()
203 }
204 Some(StartupAnnouncementsConfig::Channels(v)) => {
205 v.iter().filter_map(|s| normalize_channel(s)).collect()
206 }
207 };
208 out.sort();
209 out.dedup();
210 out
211 }
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct TelegramConfig {
216 #[serde(default = "default_true")]
217 pub enabled: bool,
218 #[serde(default)]
219 pub token_env: String,
220 #[serde(default)]
221 pub token_ref: Option<String>,
222 #[serde(default)]
223 pub allowed_chat_ids: Vec<i64>,
224 #[serde(default = "default_poll_timeout")]
225 pub poll_timeout_seconds: u64,
226 #[serde(default)]
227 pub webhook_mode: bool,
228 #[serde(default)]
229 pub webhook_path: Option<String>,
230 #[serde(default)]
231 pub webhook_secret: Option<String>,
232}
233
234fn default_thinking_threshold() -> u64 {
235 30
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct WhatsAppConfig {
240 #[serde(default)]
241 pub enabled: bool,
242 #[serde(default)]
243 pub token_env: String,
244 #[serde(default)]
245 pub token_ref: Option<String>,
246 #[serde(default)]
247 pub phone_number_id: String,
248 #[serde(default)]
249 pub verify_token: String,
250 #[serde(default)]
251 pub allowed_numbers: Vec<String>,
252 #[serde(default)]
254 pub app_secret: Option<String>,
255}
256
257#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct DiscordConfig {
259 #[serde(default = "default_true")]
260 pub enabled: bool,
261 #[serde(default)]
262 pub token_env: String,
263 #[serde(default)]
264 pub token_ref: Option<String>,
265 #[serde(default)]
266 pub application_id: String,
267 #[serde(default)]
268 pub allowed_guild_ids: Vec<String>,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
274pub struct SignalConfig {
275 #[serde(default)]
276 pub enabled: bool,
277 #[serde(default)]
279 pub phone_number: String,
280 #[serde(default = "default_signal_daemon_url")]
282 pub daemon_url: String,
283 #[serde(default)]
285 pub allowed_numbers: Vec<String>,
286}
287
288fn default_signal_daemon_url() -> String {
289 "http://localhost:8080".into()
290}
291
292#[derive(Debug, Clone, Serialize, Deserialize)]
293pub struct MatrixConfig {
294 #[serde(default)]
295 pub enabled: bool,
296 #[serde(default)]
298 pub homeserver_url: String,
299 #[serde(default)]
301 pub access_token_env: String,
302 #[serde(default)]
304 pub allowed_rooms: Vec<String>,
305 #[serde(default)]
307 pub auto_join: bool,
308 #[serde(default = "default_matrix_sync_timeout")]
310 pub sync_timeout_seconds: u64,
311 #[serde(default)]
313 pub encryption_enabled: bool,
314 #[serde(default)]
317 pub device_store_path: Option<PathBuf>,
318 #[serde(default = "default_matrix_device_name")]
320 pub device_display_name: String,
321}
322
323fn default_matrix_sync_timeout() -> u64 {
324 30
325}
326
327fn default_matrix_device_name() -> String {
328 "Roboticus".into()
329}
330
331#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct EmailConfig {
333 #[serde(default)]
334 pub enabled: bool,
335 #[serde(default)]
336 pub imap_host: String,
337 #[serde(default = "default_imap_port")]
338 pub imap_port: u16,
339 #[serde(default)]
340 pub smtp_host: String,
341 #[serde(default = "default_smtp_port")]
342 pub smtp_port: u16,
343 #[serde(default)]
344 pub username: String,
345 #[serde(default)]
346 pub password_env: String,
347 #[serde(default)]
348 pub from_address: String,
349 #[serde(default)]
350 pub allowed_senders: Vec<String>,
351 #[serde(default = "default_poll_interval")]
352 pub poll_interval_seconds: u64,
353 #[serde(default)]
355 pub oauth2_token_env: String,
356 #[serde(default)]
358 pub use_oauth2: bool,
359 #[serde(default = "default_imap_idle_enabled")]
361 pub imap_idle_enabled: bool,
362}
363
364fn default_imap_idle_enabled() -> bool {
365 true
366}
367
368impl Default for EmailConfig {
369 fn default() -> Self {
370 Self {
371 enabled: false,
372 imap_host: String::new(),
373 imap_port: default_imap_port(),
374 smtp_host: String::new(),
375 smtp_port: default_smtp_port(),
376 username: String::new(),
377 password_env: String::new(),
378 from_address: String::new(),
379 allowed_senders: Vec::new(),
380 poll_interval_seconds: default_poll_interval(),
381 oauth2_token_env: String::new(),
382 use_oauth2: false,
383 imap_idle_enabled: default_imap_idle_enabled(),
384 }
385 }
386}
387
388fn default_imap_port() -> u16 {
389 993
390}
391fn default_smtp_port() -> u16 {
392 587
393}
394fn default_poll_interval() -> u64 {
395 30
396}
397
398fn default_poll_timeout() -> u64 {
399 30
400}
401