Skip to main content

zeph_config/
root.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use std::collections::HashMap;
5
6use serde::{Deserialize, Serialize};
7use zeph_common::secret::Secret;
8use zeph_tools::ToolsConfig;
9
10use crate::agent::{AgentConfig, FocusConfig, SubAgentConfig};
11use crate::channels::{A2aServerConfig, DiscordConfig, McpConfig, SlackConfig, TelegramConfig};
12use crate::classifiers::ClassifiersConfig;
13use crate::defaults::{default_skill_paths, default_sqlite_path_field};
14use crate::experiment::{ExperimentConfig, OrchestrationConfig};
15use crate::features::{
16    CostConfig, DaemonConfig, DebugConfig, GatewayConfig, IndexConfig, ObservabilityConfig,
17    SchedulerConfig, SkillPromptMode, SkillsConfig, VaultConfig,
18};
19use crate::learning::LearningConfig;
20use crate::logging::LoggingConfig;
21use crate::memory::{
22    CompressionConfig, DocumentConfig, GraphConfig, MemoryConfig, RoutingConfig, SemanticConfig,
23    SessionsConfig, SidequestConfig, TierConfig, VectorBackend,
24};
25use crate::providers::{
26    LlmConfig, get_default_embedding_model, get_default_response_cache_ttl_secs,
27    get_default_router_ema_alpha, get_default_router_reorder_interval,
28};
29use crate::security::TrustConfig;
30use crate::security::{SecurityConfig, TimeoutConfig};
31use crate::ui::{AcpConfig, TuiConfig};
32
33#[cfg(feature = "lsp-context")]
34use crate::ui::LspConfig;
35
36/// Top-level agent configuration.
37///
38/// Loaded from a TOML file via [`Config::load`]. Env-var overrides can be applied
39/// via [`crate::env::apply_env_overrides`]. Secret resolution via `VaultProvider`
40/// is handled in `zeph-core` through the `SecretResolver` trait.
41#[derive(Debug, Deserialize, Serialize)]
42pub struct Config {
43    pub agent: AgentConfig,
44    pub llm: LlmConfig,
45    pub skills: SkillsConfig,
46    pub memory: MemoryConfig,
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub telegram: Option<TelegramConfig>,
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub discord: Option<DiscordConfig>,
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub slack: Option<SlackConfig>,
53    #[serde(default)]
54    pub tools: ToolsConfig,
55    #[serde(default)]
56    pub a2a: A2aServerConfig,
57    #[serde(default)]
58    pub mcp: McpConfig,
59    #[serde(default)]
60    pub index: IndexConfig,
61    #[serde(default)]
62    pub vault: VaultConfig,
63    #[serde(default)]
64    pub security: SecurityConfig,
65    #[serde(default)]
66    pub timeouts: TimeoutConfig,
67    #[serde(default)]
68    pub cost: CostConfig,
69    #[serde(default)]
70    pub observability: ObservabilityConfig,
71    #[serde(default)]
72    pub gateway: GatewayConfig,
73    #[serde(default)]
74    pub daemon: DaemonConfig,
75    #[serde(default)]
76    pub scheduler: SchedulerConfig,
77    #[serde(default)]
78    pub tui: TuiConfig,
79    #[serde(default)]
80    pub acp: AcpConfig,
81    #[serde(default)]
82    pub agents: SubAgentConfig,
83    #[serde(default)]
84    pub orchestration: OrchestrationConfig,
85    #[serde(default)]
86    pub classifiers: ClassifiersConfig,
87    #[serde(default)]
88    pub experiments: ExperimentConfig,
89    #[serde(default)]
90    pub debug: DebugConfig,
91    #[serde(default)]
92    pub logging: LoggingConfig,
93    #[cfg(feature = "lsp-context")]
94    #[serde(default)]
95    pub lsp: LspConfig,
96    /// Resolved secrets from vault. Never serialized — populated at runtime.
97    #[serde(skip)]
98    pub secrets: ResolvedSecrets,
99}
100
101/// Secrets resolved from the vault at runtime.
102///
103/// Populated by `SecretResolver::resolve_secrets()` in `zeph-core`.
104/// Never serialized to TOML.
105#[derive(Debug, Default)]
106pub struct ResolvedSecrets {
107    pub claude_api_key: Option<Secret>,
108    pub openai_api_key: Option<Secret>,
109    pub gemini_api_key: Option<Secret>,
110    pub compatible_api_keys: HashMap<String, Secret>,
111    pub discord_token: Option<Secret>,
112    pub slack_bot_token: Option<Secret>,
113    pub slack_signing_secret: Option<Secret>,
114    /// Arbitrary skill secrets resolved from `ZEPH_SECRET_*` vault keys.
115    /// Key is the lowercased name after stripping the prefix (e.g. `github_token`).
116    pub custom: HashMap<String, Secret>,
117}
118
119impl Default for Config {
120    #[allow(clippy::too_many_lines)] // flat struct literal with one field per config section — no meaningful split exists
121    fn default() -> Self {
122        Self {
123            agent: AgentConfig {
124                name: "Zeph".into(),
125                max_tool_iterations: 10,
126                auto_update_check: true,
127                instruction_files: Vec::new(),
128                instruction_auto_detect: true,
129                max_tool_retries: 2,
130                tool_repeat_threshold: 2,
131                max_retry_duration_secs: 30,
132                focus: FocusConfig::default(),
133                tool_filter: crate::agent::ToolFilterConfig::default(),
134            },
135            llm: LlmConfig {
136                providers: Vec::new(),
137                routing: crate::providers::LlmRoutingStrategy::None,
138                routes: std::collections::HashMap::new(),
139                embedding_model: get_default_embedding_model(),
140                candle: None,
141                stt: None,
142                response_cache_enabled: false,
143                response_cache_ttl_secs: get_default_response_cache_ttl_secs(),
144                semantic_cache_enabled: false,
145                semantic_cache_threshold: 0.95,
146                semantic_cache_max_candidates: 10,
147                router_ema_enabled: false,
148                router_ema_alpha: get_default_router_ema_alpha(),
149                router_reorder_interval: get_default_router_reorder_interval(),
150                router: None,
151                instruction_file: None,
152                summary_model: None,
153                summary_provider: None,
154                complexity_routing: None,
155            },
156            skills: SkillsConfig {
157                paths: default_skill_paths(),
158                max_active_skills: 5,
159                disambiguation_threshold: 0.05,
160                cosine_weight: 0.7,
161                hybrid_search: true,
162                learning: LearningConfig::default(),
163                trust: TrustConfig::default(),
164                prompt_mode: SkillPromptMode::Auto,
165            },
166            memory: MemoryConfig {
167                sqlite_path: default_sqlite_path_field(),
168                history_limit: 50,
169                qdrant_url: "http://localhost:6334".into(),
170                semantic: SemanticConfig::default(),
171                summarization_threshold: 50,
172                context_budget_tokens: 0,
173                soft_compaction_threshold: 0.60,
174                hard_compaction_threshold: 0.90,
175                compaction_preserve_tail: 6,
176                compaction_cooldown_turns: 2,
177                auto_budget: true,
178                prune_protect_tokens: 40_000,
179                cross_session_score_threshold: 0.35,
180                vector_backend: VectorBackend::default(),
181                token_safety_margin: 1.0,
182                redact_credentials: true,
183                autosave_assistant: true,
184                autosave_min_length: 20,
185                tool_call_cutoff: 6,
186                sqlite_pool_size: 5,
187                sessions: SessionsConfig::default(),
188                documents: DocumentConfig::default(),
189                eviction: zeph_memory::EvictionConfig::default(),
190                compression: CompressionConfig::default(),
191                sidequest: SidequestConfig::default(),
192                routing: RoutingConfig::default(),
193                graph: GraphConfig::default(),
194                compression_guidelines: zeph_memory::CompressionGuidelinesConfig::default(),
195                shutdown_summary: true,
196                shutdown_summary_min_messages: 4,
197                shutdown_summary_max_messages: 20,
198                shutdown_summary_timeout_secs: 10,
199                structured_summaries: false,
200                tiers: TierConfig::default(),
201            },
202            telegram: None,
203            discord: None,
204            slack: None,
205            tools: ToolsConfig::default(),
206            a2a: A2aServerConfig::default(),
207            mcp: McpConfig::default(),
208            index: IndexConfig::default(),
209            vault: VaultConfig::default(),
210            security: SecurityConfig::default(),
211            timeouts: TimeoutConfig::default(),
212            cost: CostConfig::default(),
213            observability: ObservabilityConfig::default(),
214            gateway: GatewayConfig::default(),
215            daemon: DaemonConfig::default(),
216            scheduler: SchedulerConfig::default(),
217            tui: TuiConfig::default(),
218            acp: AcpConfig::default(),
219            agents: SubAgentConfig::default(),
220            orchestration: OrchestrationConfig::default(),
221            classifiers: ClassifiersConfig::default(),
222            experiments: ExperimentConfig::default(),
223            debug: DebugConfig::default(),
224            logging: LoggingConfig::default(),
225            #[cfg(feature = "lsp-context")]
226            lsp: LspConfig::default(),
227            secrets: ResolvedSecrets::default(),
228        }
229    }
230}