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>,
}