use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
#[derive(Default)]
pub struct Config {
pub api: ApiConfig,
pub permissions: PermissionsConfig,
pub ui: UiConfig,
#[serde(default)]
pub mcp_servers: std::collections::HashMap<String, McpServerEntry>,
#[serde(default)]
pub hooks: Vec<crate::hooks::HookDefinition>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpServerEntry {
pub command: Option<String>,
#[serde(default)]
pub args: Vec<String>,
pub url: Option<String>,
#[serde(default)]
pub env: std::collections::HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ApiConfig {
pub base_url: String,
pub model: String,
#[serde(skip_serializing)]
pub api_key: Option<String>,
pub max_output_tokens: Option<u32>,
pub thinking: Option<String>,
pub effort: Option<String>,
pub max_cost_usd: Option<f64>,
pub timeout_secs: u64,
pub max_retries: u32,
}
impl Default for ApiConfig {
fn default() -> Self {
let api_key = std::env::var("AGENT_CODE_API_KEY")
.or_else(|_| std::env::var("ANTHROPIC_API_KEY"))
.or_else(|_| std::env::var("OPENAI_API_KEY"))
.ok();
let base_url = if std::env::var("OPENAI_API_KEY").is_ok()
&& std::env::var("AGENT_CODE_API_KEY").is_err()
&& std::env::var("ANTHROPIC_API_KEY").is_err()
{
"https://api.openai.com/v1".to_string()
} else {
"https://api.anthropic.com/v1".to_string()
};
Self {
base_url,
model: "claude-sonnet-4-20250514".to_string(),
api_key,
max_output_tokens: Some(16384),
thinking: None,
effort: None,
max_cost_usd: None,
timeout_secs: 120,
max_retries: 3,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct PermissionsConfig {
pub default_mode: PermissionMode,
pub rules: Vec<PermissionRule>,
}
impl Default for PermissionsConfig {
fn default() -> Self {
Self {
default_mode: PermissionMode::Ask,
rules: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PermissionMode {
Allow,
Deny,
Ask,
AcceptEdits,
Plan,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PermissionRule {
pub tool: String,
pub pattern: Option<String>,
pub action: PermissionMode,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct UiConfig {
pub markdown: bool,
pub syntax_highlight: bool,
pub theme: String,
}
impl Default for UiConfig {
fn default() -> Self {
Self {
markdown: true,
syntax_highlight: true,
theme: "dark".to_string(),
}
}
}