Skip to main content

selfware/config/
types.rs

1//! Miscellaneous configuration types: execution mode, UI, continuous work,
2//! retry settings, YOLO mode, and evolution daemon config.
3
4use serde::{Deserialize, Serialize};
5use std::path::PathBuf;
6
7/// Execution mode for tool approval
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, clap::ValueEnum)]
9#[serde(rename_all = "lowercase")]
10pub enum ExecutionMode {
11    /// Ask for confirmation before executing tools (default)
12    #[default]
13    Normal,
14    /// Auto-approve file edits, ask for other operations
15    AutoEdit,
16    /// Auto-approve all operations for this session
17    Yolo,
18    /// Run forever in autonomous loop
19    Daemon,
20}
21
22impl std::fmt::Display for ExecutionMode {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        match self {
25            ExecutionMode::Normal => write!(f, "normal"),
26            ExecutionMode::AutoEdit => write!(f, "auto-edit"),
27            ExecutionMode::Yolo => write!(f, "yolo"),
28            ExecutionMode::Daemon => write!(f, "daemon"),
29        }
30    }
31}
32
33/// UI configuration for themes, animations, and output
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct UiConfig {
36    /// Color theme: "amber", "ocean", "minimal", "high-contrast"
37    #[serde(default = "default_theme")]
38    pub theme: String,
39    /// Enable animations (spinners, progress bars)
40    #[serde(default = "default_true")]
41    pub animations: bool,
42    /// Default to compact mode
43    #[serde(default)]
44    pub compact_mode: bool,
45    /// Default to verbose mode
46    #[serde(default)]
47    pub verbose_mode: bool,
48    /// Allow the agent to use the `ask_user` clarification tool.
49    /// When `false`, the tool returns a null answer immediately so the
50    /// agent proceeds with its best assumption. Defaults to `true`.
51    #[serde(default = "default_true")]
52    pub allow_clarification: bool,
53    /// Always show token usage
54    #[serde(default)]
55    pub show_tokens: bool,
56    /// Animation speed multiplier (1.0 = normal, 2.0 = faster)
57    #[serde(default = "default_animation_speed")]
58    pub animation_speed: f64,
59}
60
61impl Default for UiConfig {
62    fn default() -> Self {
63        Self {
64            theme: default_theme(),
65            animations: true,
66            compact_mode: false,
67            verbose_mode: false,
68            allow_clarification: true,
69            show_tokens: false,
70            animation_speed: 1.0,
71        }
72    }
73}
74
75/// Continuous work configuration for long-running sessions.
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct ContinuousWorkConfig {
78    /// Enable periodic checkpointing policy.
79    #[serde(default = "default_true")]
80    pub enabled: bool,
81    /// Save checkpoint after this many tool calls.
82    #[serde(default = "default_checkpoint_interval_tools")]
83    pub checkpoint_interval_tools: usize,
84    /// Save checkpoint after this many seconds.
85    #[serde(default = "default_checkpoint_interval_secs")]
86    pub checkpoint_interval_secs: u64,
87    /// Enable automatic recovery attempts when available.
88    #[serde(default = "default_true")]
89    pub auto_recovery: bool,
90    /// Maximum recovery attempts per failure.
91    #[serde(default = "default_max_recovery_attempts")]
92    pub max_recovery_attempts: u32,
93}
94
95impl Default for ContinuousWorkConfig {
96    fn default() -> Self {
97        Self {
98            enabled: true,
99            checkpoint_interval_tools: default_checkpoint_interval_tools(),
100            checkpoint_interval_secs: default_checkpoint_interval_secs(),
101            auto_recovery: true,
102            max_recovery_attempts: default_max_recovery_attempts(),
103        }
104    }
105}
106
107/// Retry configuration for API/network operations.
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct RetrySettings {
110    /// Maximum retries before failing.
111    #[serde(default = "default_retry_max_retries")]
112    pub max_retries: u32,
113    /// Initial delay before first retry.
114    #[serde(default = "default_retry_base_delay_ms")]
115    pub base_delay_ms: u64,
116    /// Upper bound for retry delay.
117    #[serde(default = "default_retry_max_delay_ms")]
118    pub max_delay_ms: u64,
119}
120
121impl Default for RetrySettings {
122    fn default() -> Self {
123        Self {
124            max_retries: default_retry_max_retries(),
125            base_delay_ms: default_retry_base_delay_ms(),
126            max_delay_ms: default_retry_max_delay_ms(),
127        }
128    }
129}
130
131/// YOLO mode configuration (loaded from config file)
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct YoloFileConfig {
134    /// Whether YOLO mode is enabled
135    #[serde(default)]
136    pub enabled: bool,
137    /// Maximum operations before requiring check-in (0 = unlimited)
138    #[serde(default)]
139    pub max_operations: usize,
140    /// Maximum time in hours before requiring check-in (0 = unlimited)
141    #[serde(default)]
142    pub max_hours: f64,
143    /// Whether to allow git push operations
144    #[serde(default = "default_true")]
145    pub allow_git_push: bool,
146    /// Whether to allow destructive shell commands (rm -rf, etc.)
147    #[serde(default)]
148    pub allow_destructive_shell: bool,
149    /// Audit log file path
150    #[serde(default)]
151    pub audit_log_path: Option<PathBuf>,
152    /// Send periodic status updates (every N operations)
153    #[serde(default = "default_status_interval")]
154    pub status_interval: usize,
155}
156
157impl Default for YoloFileConfig {
158    fn default() -> Self {
159        Self {
160            enabled: false,
161            max_operations: 0,
162            max_hours: 0.0,
163            allow_git_push: true,
164            allow_destructive_shell: false,
165            audit_log_path: None,
166            status_interval: 100,
167        }
168    }
169}
170
171/// Concurrency governor configuration (loaded from `[concurrency]` in selfware.toml)
172#[derive(Debug, Clone, Serialize, Deserialize)]
173#[serde(deny_unknown_fields)]
174pub struct ConcurrencyConfig {
175    /// Maximum concurrent LLM streaming responses.
176    #[serde(default = "default_max_streams")]
177    pub max_streams: usize,
178    /// Maximum concurrent tool executions per agent.
179    #[serde(default = "default_max_tools")]
180    pub max_tools: usize,
181    /// Global limit on total inflight operations.
182    #[serde(default = "default_max_global")]
183    pub max_global: usize,
184}
185
186impl ConcurrencyConfig {
187    /// Validate concurrency limits: each must be >= 1 and <= 256.
188    pub fn validate(&self) -> anyhow::Result<()> {
189        for (name, value) in [
190            ("max_streams", self.max_streams),
191            ("max_tools", self.max_tools),
192            ("max_global", self.max_global),
193        ] {
194            if value < 1 {
195                anyhow::bail!(
196                    "Config error: concurrency.{} must be >= 1, got {}",
197                    name,
198                    value
199                );
200            }
201            if value > 256 {
202                anyhow::bail!(
203                    "Config error: concurrency.{} must be <= 256, got {}",
204                    name,
205                    value
206                );
207            }
208        }
209        Ok(())
210    }
211}
212
213impl Default for ConcurrencyConfig {
214    fn default() -> Self {
215        Self {
216            max_streams: default_max_streams(),
217            max_tools: default_max_tools(),
218            max_global: default_max_global(),
219        }
220    }
221}
222
223/// Evolution daemon configuration (loaded from `[evolution]` in selfware.toml)
224#[derive(Debug, Clone, Default, Serialize, Deserialize)]
225pub struct EvolutionTomlConfig {
226    /// Model profile name to use for hypothesis generation (e.g. "architect").
227    /// If set, looks up `[models.<name>]` for endpoint/model. Falls back to default.
228    #[serde(default)]
229    pub hypothesis_model: Option<String>,
230    /// Source files containing prompt construction logic
231    #[serde(default)]
232    pub prompt_logic: Vec<String>,
233    /// Source files containing tool implementations
234    #[serde(default)]
235    pub tool_code: Vec<String>,
236    /// Source files containing cognitive architecture
237    #[serde(default)]
238    pub cognitive: Vec<String>,
239    /// Config keys the agent can modify
240    #[serde(default)]
241    pub config_keys: Vec<String>,
242}
243
244// --- Default value functions ---
245
246pub(crate) fn default_true() -> bool {
247    true
248}
249pub(crate) fn default_status_interval() -> usize {
250    100
251}
252pub(crate) fn default_theme() -> String {
253    "amber".to_string()
254}
255pub(crate) fn default_animation_speed() -> f64 {
256    1.0
257}
258pub(crate) fn default_checkpoint_interval_tools() -> usize {
259    10
260}
261pub(crate) fn default_checkpoint_interval_secs() -> u64 {
262    300
263}
264pub(crate) fn default_max_recovery_attempts() -> u32 {
265    3
266}
267pub(crate) fn default_retry_max_retries() -> u32 {
268    5
269}
270pub(crate) fn default_retry_base_delay_ms() -> u64 {
271    1000
272}
273pub(crate) fn default_retry_max_delay_ms() -> u64 {
274    60000
275}
276pub(crate) fn default_max_streams() -> usize {
277    4
278}
279pub(crate) fn default_max_tools() -> usize {
280    8
281}
282pub(crate) fn default_max_global() -> usize {
283    12
284}
285
286#[cfg(test)]
287#[path = "../../tests/unit/config/types/types_test.rs"]
288mod tests;