magi-code 0.102.0

Repository-aware CLI coding agent for terminal work
Documentation
//! Persistable capability ceiling. Applying it only removes current permissions.
use super::{MVP_TOOL_CAPABILITIES, ToolCapability, ToolRuntime};
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct ToolCeiling {
    pub(super) cwd: std::path::PathBuf,
    pub(super) inspection: bool,
    pub(super) enabled_builtins: BTreeSet<String>,
    pub(super) disabled_tools: BTreeSet<String>,
    #[serde(default)]
    pub(super) code_mode_disabled_tools: Option<BTreeSet<String>>,
    #[serde(default)]
    pub(super) client_tools: std::collections::BTreeMap<String, String>,
    pub(super) read_absolute_paths: bool,
    pub(super) view_image_absolute_paths: bool,
    pub(super) hashline_absolute_paths: bool,
    pub(super) write_absolute_paths: bool,
    pub(super) grep_absolute_paths: bool,
    pub(super) find_absolute_paths: bool,
    pub(super) list_files_absolute_paths: bool,
    pub(super) ast_grep_absolute_paths: bool,
    pub(super) bash_absolute_paths: bool,
    pub(super) bash_shell_expansion: bool,
    pub(super) subagents_absolute_paths: bool,
    pub(super) remaining_depth: usize,
}

pub(super) fn inspection_allows(name: &str) -> bool {
    matches!(
        ToolCapability::from_dispatch_name(name),
        Some(
            ToolCapability::Read
                | ToolCapability::Find
                | ToolCapability::ListFiles
                | ToolCapability::Grep
                | ToolCapability::AstGrep
        )
    )
}

impl ToolCeiling {
    pub(crate) fn capture(tools: &ToolRuntime) -> anyhow::Result<Self> {
        tools.capture_tool_ceiling()
    }

    pub(crate) fn validate(&self) -> anyhow::Result<()> {
        anyhow::ensure!(
            self.remaining_depth <= crate::subagents::MAX_SUBAGENT_MAX_DEPTH,
            "invalid task delegation ceiling"
        );
        anyhow::ensure!(
            self.disabled_tools.len() <= 1024
                && self.enabled_builtins.len()
                    <= MVP_TOOL_CAPABILITIES.len()
                        + usize::from(self.enabled_builtins.contains("jev")),
            "oversized task tool ceiling"
        );
        for name in self.enabled_builtins.iter().chain(&self.disabled_tools) {
            anyhow::ensure!(
                name.len() <= 512 && ToolRuntime::canonical_disabled_key(name)? == *name,
                "invalid task tool ceiling"
            );
        }
        if let Some(names) = &self.code_mode_disabled_tools {
            anyhow::ensure!(names.len() <= 1024, "oversized Code Mode tool ceiling");
            for name in names {
                anyhow::ensure!(
                    name.len() <= 512 && ToolRuntime::canonical_disabled_key(name)? == *name,
                    "invalid Code Mode tool ceiling"
                );
            }
        }
        anyhow::ensure!(
            self.client_tools.len() <= super::client::MAX_TOOLS,
            "oversized client tool ceiling"
        );
        for (name, fingerprint) in &self.client_tools {
            anyhow::ensure!(
                super::client::valid_name(name)
                    && fingerprint.len() == 64
                    && fingerprint.bytes().all(|c| c.is_ascii_hexdigit()),
                "invalid client tool ceiling"
            );
        }
        Ok(())
    }

    pub(crate) fn is_inspection(&self) -> bool {
        self.inspection
    }

    pub(crate) fn apply(&self, tools: &mut ToolRuntime) -> anyhow::Result<()> {
        tools.apply_tool_ceiling(self)
    }
}