iron-core 0.1.36

Core AgentIron loop, session state, and tool registry
Documentation
use parking_lot::Mutex;
use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Duration;

use super::policy::{BuiltinToolPolicy, ShellAvailability};

#[derive(Debug, Clone)]
pub struct BuiltinToolConfig {
    pub allowed_roots: Vec<PathBuf>,
    pub policy: BuiltinToolPolicy,
    pub shell_availability: ShellAvailability,
    pub max_output_bytes: usize,
    pub max_read_bytes: usize,
    pub default_timeout: Duration,
    pub shell_timeout: Duration,
    pub max_glob_results: usize,
    pub max_grep_results: usize,
    pub max_fetch_bytes: usize,
    pub disabled_tools: Vec<String>,
    pub read_tracking: Arc<Mutex<HashSet<PathBuf>>>,
}

impl Default for BuiltinToolConfig {
    fn default() -> Self {
        Self {
            allowed_roots: vec![std::env::current_dir().unwrap_or_default()],
            policy: BuiltinToolPolicy::default(),
            shell_availability: ShellAvailability::detect(),
            max_output_bytes: 256 * 1024,
            max_read_bytes: 256 * 1024,
            default_timeout: Duration::from_secs(120),
            shell_timeout: Duration::from_secs(120),
            max_glob_results: 1000,
            max_grep_results: 500,
            max_fetch_bytes: 512 * 1024,
            disabled_tools: Vec::new(),
            read_tracking: Arc::new(Mutex::new(HashSet::new())),
        }
    }
}

impl BuiltinToolConfig {
    pub fn new(allowed_roots: Vec<PathBuf>) -> Self {
        Self {
            allowed_roots,
            ..Self::default()
        }
    }

    pub fn with_shell_availability(mut self, avail: ShellAvailability) -> Self {
        self.shell_availability = avail;
        self
    }

    pub fn with_disabled_tools(mut self, tools: Vec<String>) -> Self {
        self.disabled_tools = tools;
        self
    }

    pub fn with_policy(mut self, policy: BuiltinToolPolicy) -> Self {
        self.policy = policy;
        self
    }

    pub fn with_max_output_bytes(mut self, bytes: usize) -> Self {
        self.max_output_bytes = bytes;
        self
    }

    pub fn with_max_read_bytes(mut self, bytes: usize) -> Self {
        self.max_read_bytes = bytes;
        self
    }

    pub fn with_shell_timeout(mut self, timeout: Duration) -> Self {
        self.shell_timeout = timeout;
        self
    }

    pub fn with_default_timeout(mut self, timeout: Duration) -> Self {
        self.default_timeout = timeout;
        self
    }

    pub fn is_tool_enabled(&self, tool_name: &str) -> bool {
        !self.disabled_tools.iter().any(|d| d == tool_name)
    }

    pub fn validate(&self) -> Result<(), super::error::BuiltinToolError> {
        if self.allowed_roots.is_empty() {
            return Err(super::error::BuiltinToolError::config(
                "at least one allowed root is required",
            ));
        }
        for root in &self.allowed_roots {
            if !root.is_absolute() {
                return Err(super::error::BuiltinToolError::config(format!(
                    "allowed root must be an absolute path: {}",
                    root.display()
                )));
            }
        }
        Ok(())
    }

    pub fn record_read(&self, path: &Path) {
        self.read_tracking.lock().insert(path.to_path_buf());
    }

    pub fn has_read(&self, path: &Path) -> bool {
        self.read_tracking.lock().contains(path)
    }
}