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    /// Ultra-compressed telegraphic output mode configuration (`[caveman]`).
152    ///
153    /// Toggle at runtime with `/caveman [on|off]` or via the bundled `caveman` skill.
154    #[serde(default)]
155    pub caveman: crate::features::CavemanConfig,
156    /// Knowledge-ingest subsystem configuration (`[knowledge]`).
157    ///
158    /// Controls `zeph knowledge ingest` behaviour: provider selection, concurrency cap,
159    /// document limit, and recall inclusion of imported rows (spec-067).
160    #[serde(default)]
161    pub knowledge: crate::knowledge::KnowledgeConfig,
162    /// Deep-link scheme configuration (`[deep_link]`). Gated by the `deep-link` feature.
163    #[cfg(feature = "deep-link")]
164    #[serde(default)]
165    pub deep_link: crate::deep_link::DeepLinkConfig,
166}
167
168/// Secrets resolved from the vault at runtime.
169///
170/// Populated by `SecretResolver::resolve_secrets()` in `zeph-core`.
171/// Never serialized to TOML.
172#[derive(Debug, Default)]
173pub struct ResolvedSecrets {
174    pub claude_api_key: Option<Secret>,
175    pub openai_api_key: Option<Secret>,
176    pub gemini_api_key: Option<Secret>,
177    pub compatible_api_keys: HashMap<String, Secret>,
178    pub discord_token: Option<Secret>,
179    pub slack_bot_token: Option<Secret>,
180    pub slack_signing_secret: Option<Secret>,
181    /// Gonka wallet private key for signing blockchain transactions.
182    pub gonka_private_key: Option<Secret>,
183    /// Gonka wallet address for node identity verification.
184    pub gonka_address: Option<Secret>,
185    /// Cocoon sidecar access hash resolved from the vault key `ZEPH_COCOON_ACCESS_HASH`.
186    pub cocoon_access_hash: Option<Secret>,
187    /// Skill/plugin registry bearer credential, resolved from the vault key named by
188    /// `skills.registry.auth_vault_key` when `skills.registry.enabled = true` (spec-045,
189    /// #5869). `None` when the registry is disabled or `auth_vault_key` is unset.
190    pub skill_registry_token: Option<Secret>,
191    /// Arbitrary named secrets resolved from `ZEPH_SECRET_*` vault keys.
192    /// Key is the lowercased name after stripping the prefix (e.g. `github_token`).
193    ///
194    /// Shared by two consumers: skill `requires_secrets` injection (see
195    /// `zeph-core::agent::tool_execution::inject_active_skill_env`) and sub-agent
196    /// `permissions.secrets` approval (see `zeph-core::agent::subagent_commands::resolve_subagent_secret`).
197    pub custom: HashMap<String, Secret>,
198}
199
200impl Default for Config {
201    #[allow(clippy::too_many_lines)] // flat struct literal with one field per config section — no meaningful split exists
202    fn default() -> Self {
203        Self {
204            agent: AgentConfig {
205                name: "Zeph".into(),
206                max_tool_iterations: 10,
207                auto_update_check: true,
208                instruction_files: Vec::new(),
209                instruction_auto_detect: true,
210                max_tool_retries: 2,
211                tool_repeat_threshold: 2,
212                max_retry_duration_secs: 30,
213                focus: FocusConfig::default(),
214                tool_filter: crate::agent::ToolFilterConfig::default(),
215                budget_hint_enabled: true,
216                supervisor: crate::agent::TaskSupervisorConfig::default(),
217            },
218            llm: LlmConfig {
219                // #5932/#6018-followup (critic S1): `Config::default()` must itself satisfy
220                // `validate_pool` (non-empty pool), since it is what `--dump-config-defaults`
221                // serializes and what `Config::load()`/`run_tui_remote` fall back to when no
222                // config file exists — mirrors the single active `[[llm.providers]]` entry the
223                // shipped `config/default.toml` reference ships.
224                providers: vec![ProviderEntry::default()],
225                routing: crate::providers::LlmRoutingStrategy::None,
226                embedding_model: get_default_embedding_model(),
227                candle: None,
228                stt: None,
229                response_cache_enabled: false,
230                response_cache_ttl_secs: get_default_response_cache_ttl_secs(),
231                semantic_cache_enabled: false,
232                semantic_cache_threshold: 0.95,
233                semantic_cache_max_candidates: 10,
234                router_ema_enabled: false,
235                router_ema_alpha: get_default_router_ema_alpha(),
236                router_reorder_interval: get_default_router_reorder_interval(),
237                router: None,
238                instruction_file: None,
239                summary_model: None,
240                summary_provider: None,
241                complexity_routing: None,
242                coe: None,
243                stream_limits: crate::StreamLimits::default(),
244            },
245            skills: SkillsConfig {
246                paths: default_skill_paths(),
247                max_active_skills: NonZeroUsize::new(5).expect("5 is non-zero"),
248                disambiguation_threshold: 0.20,
249                min_injection_score: 0.20,
250                cosine_weight: 0.7,
251                hybrid_search: true,
252                bm25_alpha: 0.7,
253                learning: LearningConfig::default(),
254                trust: TrustConfig::default(),
255                registry: RegistryConfig::default(),
256                prompt_mode: SkillPromptMode::Auto,
257                two_stage_matching: false,
258                confusability_threshold: 0.0,
259                rl_routing_enabled: false,
260                rl_learning_rate: 0.01,
261                rl_weight: 0.3,
262                rl_persist_interval: 10,
263                rl_warmup_updates: 50,
264                rl_embed_dim: None,
265                query_rewrite_provider: crate::providers::ProviderName::default(),
266                generation_provider: crate::providers::ProviderName::default(),
267                generation_timeout_ms: 60_000,
268                generation_output_dir: None,
269                mining: crate::features::SkillMiningConfig::default(),
270                evaluation: crate::features::SkillEvaluationConfig::default(),
271                proactive_exploration: crate::features::ProactiveExplorationConfig::default(),
272                disambiguate_provider: crate::providers::ProviderName::default(),
273                semantic_scan: false,
274                semantic_scan_provider: crate::providers::ProviderName::default(),
275                group_structured: false,
276                support_similarity_threshold: 0.50,
277            },
278            memory: MemoryConfig {
279                sqlite_path: default_sqlite_path_field(),
280                history_limit: 50,
281                qdrant_url: "http://localhost:6334".into(),
282                qdrant_api_key: None,
283                qdrant_timeout_secs: 10,
284                semantic: SemanticConfig::default(),
285                summarization_threshold: 50,
286                summarization_llm_timeout_secs: 60,
287                context_budget_tokens: 0,
288                soft_compaction_threshold: 0.60,
289                hard_compaction_threshold: 0.90,
290                compaction_preserve_tail: 6,
291                compaction_cooldown_turns: 2,
292                auto_budget: true,
293                prune_protect_tokens: 40_000,
294                cross_session_score_threshold: 0.35,
295                vector_backend: VectorBackend::default(),
296                token_safety_margin: 1.0,
297                redact_credentials: true,
298                autosave_assistant: true,
299                autosave_min_length: 20,
300                tool_call_cutoff: 6,
301                sqlite_pool_size: 5,
302                sessions: SessionsConfig::default(),
303                documents: DocumentConfig::default(),
304                eviction: crate::memory::EvictionConfig::default(),
305                compression: CompressionConfig::default(),
306                sidequest: SidequestConfig::default(),
307                graph: GraphConfig::default(),
308                compression_guidelines: crate::memory::CompressionGuidelinesConfig::default(),
309                shutdown_summary: true,
310                shutdown_summary_min_messages: 4,
311                shutdown_summary_max_messages: 20,
312                shutdown_summary_timeout_secs: 30,
313                shutdown_summary_provider: crate::providers::ProviderName::default(),
314                compaction_provider: crate::providers::ProviderName::default(),
315                structured_summaries: false,
316                tiers: TierConfig::default(),
317                admission: crate::memory::AdmissionConfig::default(),
318                digest: crate::memory::DigestConfig::default(),
319                context_strategy: crate::memory::ContextStrategy::default(),
320                crossover_turn_threshold: 20,
321                consolidation: crate::memory::ConsolidationConfig::default(),
322                forgetting: crate::memory::ForgettingConfig::default(),
323                database_url: None,
324                store_routing: crate::memory::StoreRoutingConfig::default(),
325                persona: crate::memory::PersonaConfig::default(),
326                trajectory: crate::memory::TrajectoryConfig::default(),
327                category: crate::memory::CategoryConfig::default(),
328                tree: crate::memory::TreeConfig::default(),
329                microcompact: crate::memory::MicrocompactConfig::default(),
330                autodream: crate::memory::AutoDreamConfig::default(),
331                key_facts_dedup_threshold: 0.95,
332                compression_spectrum: crate::features::CompressionSpectrumConfig::default(),
333                retrieval: crate::memory::RetrievalConfig::default(),
334                reasoning: crate::memory::ReasoningConfig::default(),
335                hebbian: crate::memory::HebbianConfig::default(),
336                memcot: crate::memory::MemCotConfig::default(),
337                retrieval_failures: crate::memory::RetrievalFailuresConfig::default(),
338                quality_gate: crate::memory::WriteQualityGateConfig::default(),
339                tiered_retrieval: crate::memory::TieredRetrievalConfig::default(),
340                type_aware_compose: crate::memory::TypeAwareComposeConfig::default(),
341                optical_forgetting: crate::memory::OpticalForgettingConfig::default(),
342                em_graph: crate::memory::EmGraphConfig::default(),
343                episodic_consolidation: crate::memory::EpisodicConsolidationConfig::default(),
344                shadow_memory: crate::memory::TrajectoryRiskAccumulatorConfig::default(),
345                five_signal: crate::memory::FiveSignalConfig::default(),
346                fidelity: None,
347            },
348            telegram: None,
349            discord: None,
350            slack: None,
351            tools: ToolsConfig::default(),
352            execution: ExecutionConfig::default(),
353            a2a: A2aServerConfig::default(),
354            a2a_client: A2aClientConfig::default(),
355            mcp: McpConfig::default(),
356            index: IndexConfig::default(),
357            vault: VaultConfig::default(),
358            security: SecurityConfig::default(),
359            timeouts: TimeoutConfig::default(),
360            cost: CostConfig::default(),
361            gateway: GatewayConfig::default(),
362            daemon: DaemonConfig::default(),
363            scheduler: SchedulerConfig::default(),
364            tui: TuiConfig::default(),
365            acp: AcpConfig::default(),
366            agents: SubAgentConfig::default(),
367            orchestration: OrchestrationConfig::default(),
368            classifiers: ClassifiersConfig::default(),
369            experiments: ExperimentConfig::default(),
370            debug: DebugConfig::default(),
371            logging: LoggingConfig::default(),
372            lsp: LspConfig::default(),
373            hooks: HooksConfig::default(),
374            magic_docs: MagicDocsConfig::default(),
375            telemetry: TelemetryConfig::default(),
376            metrics: MetricsConfig::default(),
377            session: crate::session::SessionConfig::default(),
378            serve: crate::serve::ServeConfig::default(),
379            cli: CliConfig::default(),
380            quality: crate::quality::QualityConfig::default(),
381            notifications: NotificationsConfig::default(),
382            goals: GoalConfig::default(),
383            cocoon: CocoonConfig::default(),
384            secrets: ResolvedSecrets::default(),
385            worktree: crate::worktree::WorktreeConfig::default(),
386            durable: crate::durable::DurableConfig::default(),
387            caveman: crate::features::CavemanConfig::default(),
388            knowledge: crate::knowledge::KnowledgeConfig::default(),
389            #[cfg(feature = "deep-link")]
390            deep_link: crate::deep_link::DeepLinkConfig::default(),
391        }
392    }
393}