magi-code 0.96.1

Repository-aware CLI coding agent for terminal work
Documentation
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
}