vtcode 0.125.1

A Rust-based terminal coding agent with modular architecture supporting multiple LLM providers
use anyhow::{Context, Result};
use std::path::Path;
use vtcode_config::{SubagentDiscoveryInput, SubagentSpec, discover_subagents};
use vtcode_core::config::VTCodeConfig;
use vtcode_core::{ActivePrimaryAgent, build_primary_agent_runtime_config};

use crate::agent::runloop::unified::session_setup::active_primary_agent_from_specs_for_mode;

#[derive(Debug)]
pub(crate) struct FullAutoPrimaryAgentRuntime {
    pub(crate) vt_cfg: VTCodeConfig,
    pub(crate) active_primary_agent: ActivePrimaryAgent,
}

pub(crate) fn resolve_full_auto_primary_agent_runtime(
    workspace: &Path,
    vt_cfg: &VTCodeConfig,
    primary_agent_explicitly_configured: bool,
) -> Result<FullAutoPrimaryAgentRuntime> {
    let discovered = discover_subagents(&SubagentDiscoveryInput::new(workspace.to_path_buf()))
        .with_context(|| {
            format!(
                "Failed to discover primary agents in {}",
                workspace.display()
            )
        })?;

    resolve_full_auto_primary_agent_runtime_from_specs(
        &discovered.effective,
        vt_cfg,
        primary_agent_explicitly_configured,
    )
}

fn resolve_full_auto_primary_agent_runtime_from_specs(
    specs: &[SubagentSpec],
    vt_cfg: &VTCodeConfig,
    primary_agent_explicitly_configured: bool,
) -> Result<FullAutoPrimaryAgentRuntime> {
    let active = active_primary_agent_from_specs_for_mode(
        specs,
        Some(vt_cfg),
        true,
        primary_agent_explicitly_configured,
    )?;
    let active_primary_agent = active.active().clone();
    let mut runtime_vt_cfg = build_primary_agent_runtime_config(vt_cfg, &active_primary_agent);
    runtime_vt_cfg.runtime_agent_permissions = Some(active_primary_agent.permissions.clone());

    Ok(FullAutoPrimaryAgentRuntime {
        vt_cfg: runtime_vt_cfg,
        active_primary_agent,
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use vtcode_config::{
        builtin_primary_auto_agent, builtin_primary_build_agent,
        core::permissions::{AgentPermissionsConfig, PermissionDefault},
    };
    use vtcode_core::config::types::ReasoningEffortLevel;

    #[test]
    fn defaulted_full_auto_uses_effective_custom_auto() {
        let mut auto = builtin_primary_auto_agent();
        auto.prompt = "Custom auto instructions".to_string();
        auto.model = Some("gpt-5".to_string());
        auto.reasoning_effort = Some("high".to_string());
        auto.permissions = AgentPermissionsConfig::new(PermissionDefault::Deny);

        let resolved = resolve_full_auto_primary_agent_runtime_from_specs(
            &[auto],
            &VTCodeConfig::default(),
            false,
        )
        .expect("defaulted auto should resolve");

        assert_eq!(resolved.active_primary_agent.identity.name, "auto");
        assert_eq!(
            resolved.active_primary_agent.instructions,
            "Custom auto instructions"
        );
        assert_eq!(resolved.vt_cfg.agent.default_model, "gpt-5");
        assert_eq!(
            resolved.vt_cfg.agent.reasoning_effort,
            ReasoningEffortLevel::High
        );
        assert_eq!(
            resolved
                .vt_cfg
                .runtime_agent_permissions
                .as_ref()
                .expect("agent permissions")
                .default,
            PermissionDefault::Deny
        );
    }

    #[test]
    fn explicit_duck_is_honoured_for_full_auto() {
        let cfg = VTCodeConfig {
            default_primary_agent: "duck".to_string(),
            ..VTCodeConfig::default()
        };

        let resolved = resolve_full_auto_primary_agent_runtime_from_specs(
            &[builtin_primary_auto_agent()],
            &cfg,
            true,
        )
        .expect("explicit duck should resolve");

        assert_eq!(resolved.active_primary_agent.identity.name, "duck");
        assert_eq!(
            resolved
                .vt_cfg
                .runtime_agent_permissions
                .as_ref()
                .expect("agent permissions")
                .default,
            PermissionDefault::Deny
        );
    }

    #[test]
    fn missing_defaulted_auto_fails_fast() {
        let err = resolve_full_auto_primary_agent_runtime_from_specs(
            &[builtin_primary_build_agent()],
            &VTCodeConfig::default(),
            false,
        )
        .expect_err("missing auto should fail");

        assert!(
            err.to_string()
                .contains("no effective primary agent named 'auto' was discovered")
        );
    }
}