use super::*;
pub(in crate::subagents) struct ChildAgentRun {
pub(in crate::subagents) agent: AgentSession,
pub(in crate::subagents) provider: Arc<dyn Provider>,
pub(in crate::subagents) compaction: Option<super::config::SubagentCompactionConfig>,
}
pub(in crate::subagents) fn child_agent_for_task(
task: &SubagentTask,
cwd: &Path,
config: &SubagentRunConfig,
) -> anyhow::Result<ChildAgentRun> {
let Some(identity) = task.identity.as_deref() else {
return Ok(ChildAgentRun {
agent: config.parent_agent.clone(),
provider: Arc::clone(&config.provider),
compaction: config.compaction.clone(),
});
};
let identity = profiles::validate_subagent_identity_id(identity)?;
let profile = config.profiles.get(&identity).ok_or_else(|| {
anyhow::anyhow!(
"subagent identity '{identity}' is unavailable: no matching profile found under $MC_HOME/subagents"
)
})?;
let role_prompt =
crate::primary_agents::render_your_role_prompt(&profile.name, &profile.prompt);
let agent = config
.parent_agent
.with_appended_system_prompt(&role_prompt);
child_agent_with_profile_overrides(agent, profile, cwd, config)
}
pub(in crate::subagents) fn child_agent_with_profile_overrides(
mut agent: AgentSession,
profile: &profiles::SubagentProfile,
cwd: &Path,
config: &SubagentRunConfig,
) -> anyhow::Result<ChildAgentRun> {
let mut provider = Arc::clone(&config.provider);
let mut compaction = config.compaction.clone();
if let Some(model) = &profile.model {
let override_context = config.provider_override.as_ref().ok_or_else(|| {
anyhow::anyhow!(
"subagent provider/model overrides are unavailable in this execution context"
)
})?;
let selection = ProviderSelection {
provider: model.provider.clone(),
model: model.model.clone(),
};
let resolved = override_context.resolve(&selection, cwd)?;
provider = Arc::clone(&resolved.provider);
let settings = resolved.settings.clone();
let compaction_settings = config
.compaction
.as_ref()
.map(|compaction| compaction.settings.clone())
.unwrap_or_else(|| settings.clone());
let thinking_levels =
subagent_model_thinking_levels(&override_context.paths, &selection, resolved.scope);
agent = agent
.with_provider_model_reasoning(
selection.provider.clone(),
selection.model,
thinking_levels,
profile.reasoning,
)
.with_context_budget(resolved.context_budget)
.with_text_verbosity(settings.text_verbosity_for(&selection.provider));
compaction = Some(super::config::SubagentCompactionConfig::new(
resolved.active_config,
compaction_settings,
));
} else if let Some(reasoning) = profile.reasoning {
agent = agent.with_reasoning_override(reasoning);
}
Ok(ChildAgentRun {
agent,
provider,
compaction,
})
}
pub(in crate::subagents) fn subagent_model_thinking_levels(
paths: &crate::config::McPaths,
selection: &ProviderSelection,
scope: crate::thinking::ThinkingCapabilityScope,
) -> Vec<crate::thinking::ThinkingLevel> {
let cached = crate::model_catalog::cached_model_thinking_metadata(
paths,
&selection.provider,
&selection.model,
);
crate::thinking::available_thinking_levels(
&selection.provider,
&selection.model,
cached.as_ref(),
scope,
)
}
pub(in crate::subagents) fn subagent_prompt(
id: &str,
task: &SubagentTask,
output_schema: Option<&Value>,
) -> String {
let mut prompt = format!("Subagent {id} task intent:\n{}\n", task.intent);
if let Some(agent) = &task.agent {
prompt.push_str(&format!("\nAgent label/persona: {agent}\n"));
}
if let Some(context) = &task.context {
prompt.push_str("\nTask context:\n");
prompt.push_str(context);
prompt.push('\n');
}
if let Some(schema) = output_schema {
let schema_text = serde_json::to_string(schema).unwrap_or_else(|_| "{}".to_string());
prompt.push_str("\nOutput schema contract:\nReturn only a raw JSON object matching this JSON Schema. Do not include markdown, code fences, or prose before or after the JSON object.\n");
prompt.push_str(&schema_text);
prompt.push('\n');
}
prompt
}