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;
5use std::num::NonZeroUsize;
6
7use crate::tools::ToolsConfig;
8use serde::{Deserialize, Serialize};
9use zeph_common::secret::Secret;
10
11use crate::agent::{AgentConfig, FocusConfig, GoalConfig, SubAgentConfig};
12use crate::channels::{
13    A2aClientConfig, A2aServerConfig, DiscordConfig, McpConfig, SlackConfig, TelegramConfig,
14};
15use crate::classifiers::ClassifiersConfig;
16use crate::cli::CliConfig;
17use crate::cocoon::CocoonConfig;
18use crate::defaults::{default_skill_paths, default_sqlite_path_field};
19use crate::execution::ExecutionConfig;
20use crate::experiment::{ExperimentConfig, OrchestrationConfig};
21use crate::features::{
22    CostConfig, DaemonConfig, DebugConfig, GatewayConfig, IndexConfig, RegistryConfig,
23    SchedulerConfig, SkillPromptMode, SkillsConfig, VaultConfig,
24};
25use crate::hooks::HooksConfig;
26use crate::learning::LearningConfig;
27use crate::logging::LoggingConfig;
28use crate::memory::{
29    CompressionConfig, DocumentConfig, GraphConfig, MagicDocsConfig, MemoryConfig, SemanticConfig,
30    SessionsConfig, SidequestConfig, TierConfig, VectorBackend,
31};
32use crate::metrics::MetricsConfig;
33use crate::notifications::NotificationsConfig;
34use crate::providers::{
35    LlmConfig, ProviderEntry, get_default_embedding_model, get_default_response_cache_ttl_secs,
36    get_default_router_ema_alpha, get_default_router_reorder_interval,
37};
38use crate::security::TrustConfig;
39use crate::security::{SecurityConfig, TimeoutConfig};
40use crate::telemetry::TelemetryConfig;
41use crate::ui::LspConfig;
42use crate::ui::{AcpConfig, TuiConfig};
43
44/// Top-level agent configuration.
45///
46/// Loaded from a TOML file via [`Config::load`]. Env-var overrides can be applied
47/// via `apply_env_overrides`. Secret resolution via `VaultProvider`
48/// is handled in `zeph-core` through the `SecretResolver` trait.
49#[derive(Debug, Deserialize, Serialize)]
50pub struct Config {
51    pub agent: AgentConfig,
52    pub llm: LlmConfig,
53    pub skills: SkillsConfig,
54    pub memory: MemoryConfig,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub telegram: Option<TelegramConfig>,
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub discord: Option<DiscordConfig>,
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub slack: Option<SlackConfig>,
61    #[serde(default)]
62    pub tools: ToolsConfig,
63    /// Named execution environments for per-turn CWD and env-var selection.
64    ///
65    /// Opt-in — existing configs without `[execution]` deserialise with all defaults.
66    #[serde(default)]
67    pub execution: ExecutionConfig,
68    #[serde(default)]
69    pub a2a: A2aServerConfig,
70    /// Client-side security policy for `zeph --connect <URL>` (remote-TUI A2A attach).
71    /// Separate from `[a2a]`, which governs this process's own A2A server (#5878).
72    #[serde(default)]
73    pub a2a_client: A2aClientConfig,
74    #[serde(default)]
75    pub mcp: McpConfig,
76    #[serde(default)]
77    pub index: IndexConfig,
78    #[serde(default)]
79    pub vault: VaultConfig,
80    #[serde(default)]
81    pub security: SecurityConfig,
82    #[serde(default)]
83    pub timeouts: TimeoutConfig,
84    #[serde(default)]
85    pub cost: CostConfig,
86    #[serde(default)]
87    pub gateway: GatewayConfig,
88    #[serde(default)]
89    pub daemon: DaemonConfig,
90    #[serde(default)]
91    pub scheduler: SchedulerConfig,
92    #[serde(default)]
93    pub tui: TuiConfig,
94    #[serde(default)]
95    pub acp: AcpConfig,
96    #[serde(default)]
97    pub agents: SubAgentConfig,
98    #[serde(default)]
99    pub orchestration: OrchestrationConfig,
100    #[serde(default)]
101    pub classifiers: ClassifiersConfig,
102    #[serde(default)]
103    pub experiments: ExperimentConfig,
104    #[serde(default)]
105    pub debug: DebugConfig,
106    #[serde(default)]
107    pub logging: LoggingConfig,
108    #[serde(default)]
109    pub hooks: HooksConfig,
110    #[serde(default)]
111    pub lsp: LspConfig,
112    /// `MagicDocs` auto-maintained markdown (#2702).
113    #[serde(default)]
114    pub magic_docs: MagicDocsConfig,
115    /// Profiling and distributed tracing configuration.
116    #[serde(default)]
117    pub telemetry: TelemetryConfig,
118    /// Prometheus metrics export configuration.
119    #[serde(default)]
120    pub metrics: MetricsConfig,
121    /// Session UX settings (recap-on-resume, etc.).
122    #[serde(default)]
123    pub session: crate::session::SessionConfig,
124    /// `zeph serve` persistent agent service settings (spec-068 §9).
125    #[serde(default)]
126    pub serve: crate::serve::ServeConfig,
127    /// Session-scoped CLI overrides (bare mode, JSON output, auto-approve).
128    #[serde(default)]
129    pub cli: CliConfig,
130    /// MARCH self-check quality pipeline configuration.
131    #[serde(default)]
132    pub quality: crate::quality::QualityConfig,
133    /// Per-turn completion notification settings (macOS banners, ntfy webhook).
134    #[serde(default)]
135    pub notifications: NotificationsConfig,
136    /// Long-horizon goal lifecycle configuration.
137    #[serde(default)]
138    pub goals: GoalConfig,
139    /// Cocoon display and behaviour settings (independent of `[[llm.providers]]` Cocoon entry).
140    #[serde(default)]
141    pub cocoon: CocoonConfig,
142    /// Resolved secrets from vault. Never serialized — populated at runtime.
143    #[serde(skip)]
144    pub secrets: ResolvedSecrets,
145    /// Git worktree isolation configuration.
146    #[serde(default)]
147    pub worktree: crate::worktree::WorktreeConfig,
148    /// Durable execution layer configuration (`[durable]`).
149    #[serde(default)]
150    pub durable: crate::durable::DurableConfig,
151    /// Vault-anchor downgrade-resistance configuration (`[integrity]`, issue #6449).
152    #[serde(default)]
153    pub integrity: crate::integrity::IntegrityConfig,
154    /// Ultra-compressed telegraphic output mode configuration (`[caveman]`).
155    ///
156    /// Toggle at runtime with `/caveman [on|off]` or via the bundled `caveman` skill.
157    #[serde(default)]
158    pub caveman: crate::features::CavemanConfig,
159    /// Knowledge-ingest subsystem configuration (`[knowledge]`).
160    ///
161    /// Controls `zeph knowledge ingest` behaviour: provider selection, concurrency cap,
162    /// document limit, and recall inclusion of imported rows (spec-067).
163    #[serde(default)]
164    pub knowledge: crate::knowledge::KnowledgeConfig,
165    /// Plugin subsystem configuration (`[plugins]`), currently the install-time
166    /// name-similarity/typosquat check (spec-043, #5864).
167    #[serde(default)]
168    pub plugins: crate::plugins::PluginsConfig,
169    /// Deep-link scheme configuration (`[deep_link]`). Gated by the `deep-link` feature.
170    #[cfg(feature = "deep-link")]
171    #[serde(default)]
172    pub deep_link: crate::deep_link::DeepLinkConfig,
173}
174
175/// Secrets resolved from the vault at runtime.
176///
177/// Populated by `SecretResolver::resolve_secrets()` in `zeph-core`.
178/// Never serialized to TOML.
179#[derive(Debug, Default)]
180pub struct ResolvedSecrets {
181    pub claude_api_key: Option<Secret>,
182    pub openai_api_key: Option<Secret>,
183    pub gemini_api_key: Option<Secret>,
184    pub compatible_api_keys: HashMap<String, Secret>,
185    pub discord_token: Option<Secret>,
186    pub slack_bot_token: Option<Secret>,
187    pub slack_signing_secret: Option<Secret>,
188    /// Gonka wallet private key for signing blockchain transactions.
189    pub gonka_private_key: Option<Secret>,
190    /// Gonka wallet address for node identity verification.
191    pub gonka_address: Option<Secret>,
192    /// Cocoon sidecar access hash resolved from the vault key `ZEPH_COCOON_ACCESS_HASH`.
193    pub cocoon_access_hash: Option<Secret>,
194    /// Skill/plugin registry bearer credential, resolved from the vault key named by
195    /// `skills.registry.auth_vault_key` when `skills.registry.enabled = true` (spec-045,
196    /// #5869). `None` when the registry is disabled or `auth_vault_key` is unset.
197    pub skill_registry_token: Option<Secret>,
198    /// `web_search` tool API key, resolved from the vault key named by
199    /// `tools.search.api_key_vault_key` when `tools.search.enabled = true` (spec
200    /// 006-1-web-search). `None` when the tool is disabled or the key is unset.
201    pub web_search_api_key: Option<Secret>,
202    /// Arbitrary named secrets resolved from `ZEPH_SECRET_*` vault keys.
203    /// Key is the lowercased name after stripping the prefix (e.g. `github_token`).
204    ///
205    /// Shared by two consumers: skill `requires_secrets` injection (see
206    /// `zeph-core::agent::tool_execution::inject_active_skill_env`) and sub-agent
207    /// `permissions.secrets` approval (see `zeph-core::agent::subagent_commands::resolve_subagent_secret`).
208    pub custom: HashMap<String, Secret>,
209}
210
211impl Default for Config {
212    #[allow(clippy::too_many_lines)] // flat struct literal with one field per config section — no meaningful split exists
213    fn default() -> Self {
214        Self {
215            agent: AgentConfig {
216                name: "Zeph".into(),
217                max_tool_iterations: 10,
218                auto_update_check: true,
219                instruction_files: Vec::new(),
220                instruction_auto_detect: true,
221                max_tool_retries: 2,
222                tool_repeat_threshold: 2,
223                max_retry_duration_secs: 30,
224                focus: FocusConfig::default(),
225                tool_filter: crate::agent::ToolFilterConfig::default(),
226                budget_hint_enabled: true,
227                supervisor: crate::agent::TaskSupervisorConfig::default(),
228                time_reminder_enabled: false,
229                time_reminder_interval_requests: 10,
230            },
231            llm: LlmConfig {
232                // #5932/#6018-followup (critic S1): `Config::default()` must itself satisfy
233                // `validate_pool` (non-empty pool), since it is what `--dump-config-defaults`
234                // serializes and what `Config::load()`/`run_tui_remote` fall back to when no
235                // config file exists — mirrors the single active `[[llm.providers]]` entry the
236                // shipped `config/default.toml` reference ships.
237                providers: vec![ProviderEntry::default()],
238                routing: crate::providers::LlmRoutingStrategy::None,
239                embedding_model: get_default_embedding_model(),
240                candle: None,
241                stt: None,
242                response_cache_enabled: false,
243                response_cache_ttl_secs: get_default_response_cache_ttl_secs(),
244                semantic_cache_enabled: false,
245                semantic_cache_threshold: 0.95,
246                semantic_cache_max_candidates: 10,
247                router_ema_enabled: false,
248                router_ema_alpha: get_default_router_ema_alpha(),
249                router_reorder_interval: get_default_router_reorder_interval(),
250                router: None,
251                instruction_file: None,
252                summary_model: None,
253                summary_provider: None,
254                complexity_routing: None,
255                coe: None,
256                stream_limits: crate::StreamLimits::default(),
257            },
258            skills: SkillsConfig {
259                paths: default_skill_paths(),
260                max_active_skills: NonZeroUsize::new(5).expect("5 is non-zero"),
261                disambiguation_threshold: 0.20,
262                min_injection_score: 0.20,
263                cosine_weight: 0.7,
264                hybrid_search: true,
265                bm25_alpha: 0.7,
266                learning: LearningConfig::default(),
267                trust: TrustConfig::default(),
268                registry: RegistryConfig::default(),
269                prompt_mode: SkillPromptMode::Auto,
270                two_stage_matching: false,
271                confusability_threshold: 0.0,
272                rl_routing_enabled: false,
273                rl_learning_rate: 0.01,
274                rl_weight: 0.3,
275                rl_persist_interval: 10,
276                rl_warmup_updates: 50,
277                rl_embed_dim: None,
278                query_rewrite_provider: crate::providers::ProviderName::default(),
279                generation_provider: crate::providers::ProviderName::default(),
280                generation_timeout_ms: 60_000,
281                generation_output_dir: None,
282                mining: crate::features::SkillMiningConfig::default(),
283                evaluation: crate::features::SkillEvaluationConfig::default(),
284                proactive_exploration: crate::features::ProactiveExplorationConfig::default(),
285                disambiguate_provider: crate::providers::ProviderName::default(),
286                semantic_scan: false,
287                semantic_scan_provider: crate::providers::ProviderName::default(),
288                group_structured: false,
289                support_similarity_threshold: 0.50,
290                subagent_skill_token_budget: NonZeroUsize::new(12_000).expect("12000 is non-zero"),
291            },
292            memory: MemoryConfig {
293                sqlite_path: default_sqlite_path_field(),
294                history_limit: 50,
295                qdrant_url: "http://localhost:6334".into(),
296                qdrant_api_key: None,
297                qdrant_timeout_secs: 10,
298                semantic: SemanticConfig::default(),
299                summarization_threshold: 50,
300                summarization_llm_timeout_secs: 60,
301                context_budget_tokens: 0,
302                soft_compaction_threshold: 0.60,
303                hard_compaction_threshold: 0.90,
304                compaction_preserve_tail: 6,
305                compaction_cooldown_turns: 2,
306                auto_budget: true,
307                prune_protect_tokens: 40_000,
308                cross_session_score_threshold: 0.35,
309                vector_backend: VectorBackend::default(),
310                token_safety_margin: 1.0,
311                redact_credentials: true,
312                autosave_assistant: true,
313                autosave_min_length: 20,
314                tool_call_cutoff: 6,
315                sqlite_pool_size: 5,
316                sessions: SessionsConfig::default(),
317                documents: DocumentConfig::default(),
318                eviction: crate::memory::EvictionConfig::default(),
319                compression: CompressionConfig::default(),
320                sidequest: SidequestConfig::default(),
321                graph: GraphConfig::default(),
322                compression_guidelines: crate::memory::CompressionGuidelinesConfig::default(),
323                shutdown_summary: true,
324                shutdown_summary_min_messages: 4,
325                shutdown_summary_max_messages: 20,
326                shutdown_summary_timeout_secs: 30,
327                shutdown_summary_provider: crate::providers::ProviderName::default(),
328                compaction_provider: crate::providers::ProviderName::default(),
329                structured_summaries: false,
330                tiers: TierConfig::default(),
331                admission: crate::memory::AdmissionConfig::default(),
332                digest: crate::memory::DigestConfig::default(),
333                context_strategy: crate::memory::ContextStrategy::default(),
334                crossover_turn_threshold: 20,
335                consolidation: crate::memory::ConsolidationConfig::default(),
336                forgetting: crate::memory::ForgettingConfig::default(),
337                database_url: None,
338                store_routing: crate::memory::StoreRoutingConfig::default(),
339                persona: crate::memory::PersonaConfig::default(),
340                trajectory: crate::memory::TrajectoryConfig::default(),
341                category: crate::memory::CategoryConfig::default(),
342                tree: crate::memory::TreeConfig::default(),
343                microcompact: crate::memory::MicrocompactConfig::default(),
344                autodream: crate::memory::AutoDreamConfig::default(),
345                key_facts_dedup_threshold: 0.95,
346                compression_spectrum: crate::features::CompressionSpectrumConfig::default(),
347                retrieval: crate::memory::RetrievalConfig::default(),
348                reasoning: crate::memory::ReasoningConfig::default(),
349                hebbian: crate::memory::HebbianConfig::default(),
350                memcot: crate::memory::MemCotConfig::default(),
351                retrieval_failures: crate::memory::RetrievalFailuresConfig::default(),
352                quality_gate: crate::memory::WriteQualityGateConfig::default(),
353                tiered_retrieval: crate::memory::TieredRetrievalConfig::default(),
354                type_aware_compose: crate::memory::TypeAwareComposeConfig::default(),
355                optical_forgetting: crate::memory::OpticalForgettingConfig::default(),
356                em_graph: crate::memory::EmGraphConfig::default(),
357                episodic_consolidation: crate::memory::EpisodicConsolidationConfig::default(),
358                shadow_memory: crate::memory::TrajectoryRiskAccumulatorConfig::default(),
359                five_signal: crate::memory::FiveSignalConfig::default(),
360                fidelity: None,
361                store: crate::memory::CrossThreadStoreConfig::default(),
362            },
363            telegram: None,
364            discord: None,
365            slack: None,
366            tools: ToolsConfig::default(),
367            execution: ExecutionConfig::default(),
368            a2a: A2aServerConfig::default(),
369            a2a_client: A2aClientConfig::default(),
370            mcp: McpConfig::default(),
371            index: IndexConfig::default(),
372            vault: VaultConfig::default(),
373            security: SecurityConfig::default(),
374            timeouts: TimeoutConfig::default(),
375            cost: CostConfig::default(),
376            gateway: GatewayConfig::default(),
377            daemon: DaemonConfig::default(),
378            scheduler: SchedulerConfig::default(),
379            tui: TuiConfig::default(),
380            acp: AcpConfig::default(),
381            agents: SubAgentConfig::default(),
382            orchestration: OrchestrationConfig::default(),
383            classifiers: ClassifiersConfig::default(),
384            experiments: ExperimentConfig::default(),
385            debug: DebugConfig::default(),
386            logging: LoggingConfig::default(),
387            lsp: LspConfig::default(),
388            hooks: HooksConfig::default(),
389            magic_docs: MagicDocsConfig::default(),
390            telemetry: TelemetryConfig::default(),
391            metrics: MetricsConfig::default(),
392            session: crate::session::SessionConfig::default(),
393            serve: crate::serve::ServeConfig::default(),
394            cli: CliConfig::default(),
395            quality: crate::quality::QualityConfig::default(),
396            notifications: NotificationsConfig::default(),
397            goals: GoalConfig::default(),
398            cocoon: CocoonConfig::default(),
399            secrets: ResolvedSecrets::default(),
400            worktree: crate::worktree::WorktreeConfig::default(),
401            durable: crate::durable::DurableConfig::default(),
402            integrity: crate::integrity::IntegrityConfig::default(),
403            caveman: crate::features::CavemanConfig::default(),
404            knowledge: crate::knowledge::KnowledgeConfig::default(),
405            plugins: crate::plugins::PluginsConfig::default(),
406            #[cfg(feature = "deep-link")]
407            deep_link: crate::deep_link::DeepLinkConfig::default(),
408        }
409    }
410}