magi-code 0.77.1

Repository-aware CLI coding agent for terminal work
Documentation
use super::profiles;
use crate::{
    agent::AgentSession,
    cancellation::AgentCancellation,
    config::{EffectiveConfig, McPaths, Settings},
    context::ContextBudget,
    hooks::HookRuntime,
    output::{ActivityId, ActivitySender},
    providers::{Provider, ProviderSelection},
    thinking::ThinkingCapabilityScope,
    tools::ToolRuntime,
};
use std::{
    collections::BTreeMap,
    path::{Path, PathBuf},
    sync::{Arc, Mutex},
    time::Duration,
};

#[derive(Clone)]
pub(crate) struct ResolvedProviderOverride {
    pub provider: Arc<dyn Provider>,
    pub scope: ThinkingCapabilityScope,
    pub active_config: EffectiveConfig,
    pub context_budget: ContextBudget,
    pub settings: Settings,
}

pub(crate) type SubagentProviderResolver = Arc<
    dyn Fn(&ProviderSelection, &Path) -> anyhow::Result<ResolvedProviderOverride> + Send + Sync,
>;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct ProviderOverrideCacheKey {
    provider: String,
    model: String,
    cwd: PathBuf,
}

#[derive(Clone)]
pub(crate) struct SubagentProviderOverride {
    pub(super) paths: McPaths,
    pub(super) resolve_provider: SubagentProviderResolver,
    cache: Arc<Mutex<BTreeMap<ProviderOverrideCacheKey, ProviderOverrideCacheSlot>>>,
}

type ProviderOverrideCacheSlot = Arc<Mutex<Option<ResolvedProviderOverride>>>;

impl SubagentProviderOverride {
    pub(crate) fn new(paths: McPaths, resolve_provider: SubagentProviderResolver) -> Self {
        Self {
            paths,
            resolve_provider,
            cache: Arc::new(Mutex::new(BTreeMap::new())),
        }
    }

    pub(crate) fn resolve(
        &self,
        selection: &ProviderSelection,
        cwd: &Path,
    ) -> anyhow::Result<ResolvedProviderOverride> {
        let key = ProviderOverrideCacheKey {
            provider: selection.provider.clone(),
            model: selection.model.clone(),
            cwd: cwd.to_path_buf(),
        };
        let slot = {
            let mut cache = self
                .cache
                .lock()
                .map_err(|_| anyhow::anyhow!("subagent provider override cache lock poisoned"))?;
            Arc::clone(
                cache
                    .entry(key)
                    .or_insert_with(|| Arc::new(Mutex::new(None))),
            )
        };
        let mut resolved = slot
            .lock()
            .map_err(|_| anyhow::anyhow!("subagent provider override cache slot lock poisoned"))?;
        if let Some(resolved) = resolved.as_ref() {
            return Ok(resolved.clone());
        }
        let value = (self.resolve_provider)(selection, cwd)?;
        *resolved = Some(value.clone());
        Ok(value)
    }
}

#[derive(Clone)]
pub(crate) struct SubagentCompactionConfig {
    pub(super) active_config: EffectiveConfig,
    pub(super) settings: Settings,
}

impl SubagentCompactionConfig {
    pub(crate) fn new(active_config: EffectiveConfig, settings: Settings) -> Self {
        Self {
            active_config,
            settings,
        }
    }
}

#[derive(Clone)]
pub struct SubagentRunConfig {
    pub parent_agent: AgentSession,
    pub provider: Arc<dyn Provider>,
    pub provider_override: Option<SubagentProviderOverride>,
    pub parent_tools: ToolRuntime,
    pub parent_cwd: PathBuf,
    pub cancellation: AgentCancellation,
    pub profiles: BTreeMap<String, profiles::SubagentProfile>,
    pub subagent_profiles_prompt: Option<String>,
    pub sessions_root: Option<PathBuf>,
    pub parent_session_id: Option<String>,
    pub depth: usize,
    pub parent_activity_id: Option<ActivityId>,
    pub activity_sender: Option<ActivitySender>,
    pub inherited_hooks: Option<HookRuntime>,
    pub semantic_progress_timeout: Duration,
    pub schema_validation_max_retries: u64,
    pub(crate) compaction: Option<SubagentCompactionConfig>,
}