Skip to main content

convergio_types/
config.rs

1//! Configuration types — pure data structures, no I/O.
2//!
3//! Loading, validation, and hot-reload live elsewhere (server crate).
4//! These structs are shared across crates for type-safe config access.
5
6use serde::{Deserialize, Serialize};
7
8/// Roles a node can assume — controls which extensions load at boot.
9#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(rename_all = "lowercase")]
11pub enum NodeRole {
12    /// All extensions loaded (default, single-node).
13    #[default]
14    All,
15    /// Orchestrates workers, hosts plans DB, runs platform services.
16    Orchestrator,
17    /// Local AI kernel (Jarvis), Telegram, voice.
18    Kernel,
19    /// Voice I/O only.
20    Voice,
21    /// Receives delegated tasks, runs agents.
22    Worker,
23    /// Hosts night agent workloads (knowledge sync, nightly jobs).
24    NightAgent,
25}
26
27impl NodeRole {
28    pub fn as_str(&self) -> &'static str {
29        match self {
30            Self::All => "all",
31            Self::Orchestrator => "orchestrator",
32            Self::Kernel => "kernel",
33            Self::Voice => "voice",
34            Self::Worker => "worker",
35            Self::NightAgent => "nightagent",
36        }
37    }
38}
39
40impl std::fmt::Display for NodeRole {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        f.write_str(self.as_str())
43    }
44}
45
46#[derive(Debug, Clone, Deserialize)]
47#[serde(default)]
48pub struct NodeConfig {
49    pub name: String,
50    /// Node role — controls which extensions are loaded at boot.
51    pub role: NodeRole,
52}
53
54impl Default for NodeConfig {
55    fn default() -> Self {
56        Self {
57            name: String::new(),
58            role: NodeRole::All,
59        }
60    }
61}
62
63#[derive(Clone, Deserialize, Default)]
64#[serde(default)]
65pub struct TailscaleConfig {
66    pub enabled: bool,
67    pub auth_key: String,
68}
69
70impl std::fmt::Debug for TailscaleConfig {
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72        f.debug_struct("TailscaleConfig")
73            .field("enabled", &self.enabled)
74            .field("auth_key", &"[REDACTED]")
75            .finish()
76    }
77}
78
79#[derive(Debug, Clone, Deserialize)]
80#[serde(default)]
81pub struct MeshConfig {
82    pub transport: String,
83    pub discovery: String,
84    pub peers: Vec<String>,
85    pub tailscale: TailscaleConfig,
86}
87
88impl Default for MeshConfig {
89    fn default() -> Self {
90        Self {
91            transport: "lan".to_string(),
92            discovery: "mdns".to_string(),
93            peers: Vec::new(),
94            tailscale: TailscaleConfig::default(),
95        }
96    }
97}
98
99#[derive(Debug, Clone, Deserialize, PartialEq)]
100#[serde(default)]
101pub struct InferenceFallbackConfig {
102    pub max_attempts: usize,
103    pub t1: Vec<String>,
104    pub t2: Vec<String>,
105    pub t3: Vec<String>,
106    pub t4: Vec<String>,
107}
108
109impl Default for InferenceFallbackConfig {
110    fn default() -> Self {
111        Self {
112            max_attempts: 3,
113            t1: vec!["local".into(), "haiku".into(), "sonnet".into()],
114            t2: vec!["haiku".into(), "local".into(), "sonnet".into()],
115            t3: vec!["sonnet".into(), "opus".into()],
116            t4: vec!["opus".into(), "sonnet".into()],
117        }
118    }
119}
120
121#[derive(Debug, Clone, Deserialize)]
122#[serde(default)]
123pub struct InferenceConfig {
124    pub default_model: String,
125    pub api_key_env: String,
126    pub fallback: InferenceFallbackConfig,
127}
128
129impl Default for InferenceConfig {
130    fn default() -> Self {
131        Self {
132            default_model: "claude-sonnet-4-6".to_string(),
133            api_key_env: "ANTHROPIC_API_KEY".to_string(),
134            fallback: InferenceFallbackConfig::default(),
135        }
136    }
137}
138
139#[derive(Debug, Clone, Deserialize)]
140#[serde(default)]
141pub struct KernelConfig {
142    pub model: String,
143    pub model_path: String,
144    pub escalation_model: String,
145    pub max_tokens: u32,
146}
147
148impl Default for KernelConfig {
149    fn default() -> Self {
150        Self {
151            model: "none".to_string(),
152            model_path: String::new(),
153            escalation_model: String::new(),
154            max_tokens: 2048,
155        }
156    }
157}
158
159#[derive(Clone, Deserialize, Default)]
160#[serde(default)]
161pub struct TelegramConfig {
162    pub enabled: bool,
163    pub token_keychain: String,
164}
165
166impl std::fmt::Debug for TelegramConfig {
167    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168        f.debug_struct("TelegramConfig")
169            .field("enabled", &self.enabled)
170            .field("token_keychain", &"[REDACTED]")
171            .finish()
172    }
173}
174
175#[derive(Debug, Clone, Deserialize)]
176#[serde(default)]
177pub struct NightConfig {
178    pub night_mode: bool,
179    /// Format: "HH:MM-HH:MM" (e.g. "23:00-07:00")
180    pub night_hours: String,
181    pub night_model: String,
182}
183
184impl Default for NightConfig {
185    fn default() -> Self {
186        Self {
187            night_mode: false,
188            night_hours: "23:00-07:00".to_string(),
189            night_model: "claude-haiku-4-5".to_string(),
190        }
191    }
192}
193
194#[derive(Debug, Clone, Deserialize)]
195#[serde(default)]
196pub struct DaemonConfig {
197    pub port: u16,
198    pub quiet_hours: Option<String>,
199    pub timezone: Option<String>,
200    pub auto_update: bool,
201}
202
203impl Default for DaemonConfig {
204    fn default() -> Self {
205        Self {
206            port: 8420,
207            quiet_hours: None,
208            timezone: None,
209            auto_update: true,
210        }
211    }
212}
213
214/// Top-level config — deserialized from config.toml.
215#[derive(Debug, Clone, Deserialize, Default)]
216#[serde(default)]
217pub struct ConvergioConfig {
218    pub node: NodeConfig,
219    pub daemon: DaemonConfig,
220    pub night: NightConfig,
221    pub mesh: MeshConfig,
222    pub inference: InferenceConfig,
223    pub kernel: KernelConfig,
224    pub telegram: TelegramConfig,
225}