use std::time::Duration; use crate::config::helpers::{parse_bool_env, parse_option_env, parse_optional_env}; use crate::error::ConfigError; use crate::settings::Settings; /// Agent behavior configuration. #[derive(Debug, Clone)] pub struct AgentConfig { pub name: String, pub max_parallel_jobs: usize, pub job_timeout: Duration, pub stuck_threshold: Duration, pub repair_check_interval: Duration, pub max_repair_attempts: u32, /// Whether to use planning before tool execution. pub use_planning: bool, /// Session idle timeout. Sessions inactive longer than this are pruned. pub session_idle_timeout: Duration, /// Allow chat to use filesystem/shell tools directly (bypass sandbox). pub allow_local_tools: bool, /// Maximum daily LLM spend in cents (e.g. 10000 = $100). None = unlimited. pub max_cost_per_day_cents: Option, /// Maximum LLM/tool actions per hour. None = unlimited. pub max_actions_per_hour: Option, /// Maximum tool-call iterations per agentic loop invocation. Default 50. pub max_tool_iterations: usize, /// When true, skip tool approval checks entirely. For benchmarks/CI. pub auto_approve_tools: bool, } impl AgentConfig { /// Create a test-friendly config without reading env vars. #[cfg(feature = "libsql")] pub fn for_testing() -> Self { Self { name: "test-rig".to_string(), max_parallel_jobs: 1, job_timeout: Duration::from_secs(30), stuck_threshold: Duration::from_secs(300), repair_check_interval: Duration::from_secs(3600), max_repair_attempts: 0, use_planning: false, session_idle_timeout: Duration::from_secs(3600), allow_local_tools: true, max_cost_per_day_cents: None, max_actions_per_hour: None, max_tool_iterations: 10, auto_approve_tools: true, } } pub(crate) fn resolve(settings: &Settings) -> Result { Ok(Self { name: parse_optional_env("AGENT_NAME", settings.agent.name.clone())?, max_parallel_jobs: parse_optional_env( "AGENT_MAX_PARALLEL_JOBS", settings.agent.max_parallel_jobs as usize, )?, job_timeout: Duration::from_secs(parse_optional_env( "AGENT_JOB_TIMEOUT_SECS", settings.agent.job_timeout_secs, )?), stuck_threshold: Duration::from_secs(parse_optional_env( "AGENT_STUCK_THRESHOLD_SECS", settings.agent.stuck_threshold_secs, )?), repair_check_interval: Duration::from_secs(parse_optional_env( "SELF_REPAIR_CHECK_INTERVAL_SECS", settings.agent.repair_check_interval_secs, )?), max_repair_attempts: parse_optional_env( "SELF_REPAIR_MAX_ATTEMPTS", settings.agent.max_repair_attempts, )?, use_planning: parse_bool_env("AGENT_USE_PLANNING", settings.agent.use_planning)?, session_idle_timeout: Duration::from_secs(parse_optional_env( "SESSION_IDLE_TIMEOUT_SECS", settings.agent.session_idle_timeout_secs, )?), allow_local_tools: parse_bool_env("ALLOW_LOCAL_TOOLS", false)?, max_cost_per_day_cents: parse_option_env("MAX_COST_PER_DAY_CENTS")?, max_actions_per_hour: parse_option_env("MAX_ACTIONS_PER_HOUR")?, max_tool_iterations: parse_optional_env( "AGENT_MAX_TOOL_ITERATIONS", settings.agent.max_tool_iterations, )?, auto_approve_tools: parse_bool_env( "AGENT_AUTO_APPROVE_TOOLS", settings.agent.auto_approve_tools, )?, }) } }