mars-agents 0.4.8-rc.2

Agent package manager for .agents/ directories
Documentation
use std::collections::BTreeMap;
use std::path::Path;

use crate::build::bundle::ExecutionPolicy;
use crate::compiler::agents::AgentProfile;
use crate::error::MarsError;

mod config;
mod execution;
mod harness;
mod model;
mod runnable;

pub struct PolicyInput<'a> {
    pub project_root: &'a Path,
    pub profile: &'a AgentProfile,
    pub model_override: Option<&'a str>,
    pub harness_override: Option<&'a str>,
    pub effort_override: Option<&'a str>,
    pub approval_override: Option<&'a str>,
    pub sandbox_override: Option<&'a str>,
}

pub struct ResolvedPolicy {
    pub routing: crate::build::bundle::Routing,
    pub execution_policy: ExecutionPolicy,
    pub provenance: BTreeMap<String, String>,
    pub warnings: Vec<String>,
}

pub fn resolve_policy(input: PolicyInput<'_>) -> Result<ResolvedPolicy, MarsError> {
    let mut warnings = Vec::new();
    let mut provenance = BTreeMap::new();

    let resolution_config = config::load_policy_resolution_config(input.project_root)?;
    let cache = model::load_models_cache(input.project_root)?;
    let resolved_model = model::resolve_model(&input, &resolution_config.aliases, &cache)?;

    warnings.extend(resolved_model.warnings);
    provenance.insert(
        "model_source".to_string(),
        resolved_model.model_source.clone(),
    );

    let harness_resolution = harness::resolve_harness(
        &input,
        resolved_model.alias,
        resolved_model.provider.as_deref(),
        resolution_config.default_harness.as_deref(),
        resolution_config.harness_order.as_deref(),
    )?;

    warnings.extend(harness_resolution.warnings);
    provenance.insert(
        "harness_source".to_string(),
        harness_resolution.source.to_string(),
    );
    if harness_resolution.source == "config-order"
        && let Some(position) = harness_resolution.harness_order_position
    {
        provenance.insert("harness_order_position".to_string(), position.to_string());
    }
    if harness_resolution.is_experimental {
        warnings.push(
            "Cursor is an experimental launch-bundle target. The contract may change without notice.".to_string(),
        );
        provenance.insert("harness_stability".to_string(), "experimental".to_string());
    }

    let matched_harness_override = input
        .profile
        .harness_overrides
        .get(&harness_resolution.resolved_harness);
    let execution_resolution =
        execution::resolve_execution_policy(&input, resolved_model.alias, matched_harness_override);

    provenance.insert(
        "effort_source".to_string(),
        execution_resolution.effort_source,
    );
    provenance.insert(
        "approval_source".to_string(),
        execution_resolution.approval_source,
    );
    provenance.insert(
        "sandbox_source".to_string(),
        execution_resolution.sandbox_source,
    );
    provenance.insert(
        "autocompact_source".to_string(),
        execution_resolution.autocompact_source,
    );
    provenance.insert(
        "autocompact_pct_source".to_string(),
        execution_resolution.autocompact_pct_source,
    );
    if execution_resolution.native_config.is_some() {
        provenance.insert(
            "native_config_source".to_string(),
            "profile-harness-override".to_string(),
        );
    }

    let routing_resolution = runnable::resolve_routing(runnable::RoutingInput {
        model: resolved_model.model,
        model_token: resolved_model.model_token,
        harness: harness_resolution.harness,
        provider: resolved_model.provider.as_deref(),
        alias_resolution_failed: resolved_model.alias_resolution_failed,
        alias_exists: resolved_model.alias.is_some(),
        cache: &cache,
    });

    warnings.extend(routing_resolution.warnings);

    Ok(ResolvedPolicy {
        routing: routing_resolution.routing,
        execution_policy: ExecutionPolicy {
            effort: execution_resolution.effort,
            approval: execution_resolution.approval,
            sandbox: execution_resolution.sandbox,
            autocompact: execution_resolution.autocompact,
            autocompact_pct: execution_resolution.autocompact_pct,
            timeout: None,
            native_config: execution_resolution.native_config,
        },
        provenance,
        warnings,
    })
}