ai-dispatch 10.36.0

Multi-AI CLI team orchestrator
// `aid run` argument assembly helpers for the primary dispatch handlers.
// Exports: build_run_args().
// Deps: crate::cli::RunExtrasArgs, crate::cmd::run::RunArgs, group resolution.

use crate::cli::RunExtrasArgs;
use crate::cmd;
use crate::cmd_dispatch::resolve_group;
use crate::agent::classifier::TaskCategory;
use crate::agent::model_validation::ModelSource;
use crate::types::{TaskBudget, TaskDifficulty, TaskEgress, TaskRigor, TaskUrgency};

#[allow(clippy::too_many_arguments)]
pub(super) fn build_run_args(
    agent_name: String,
    prompt: String,
    prompt_file: Option<String>,
    repo: Option<String>,
    repo_root: Option<String>,
    dir: Option<String>,
    output: Option<String>,
    result_file: Option<String>,
    model: Option<String>,
    auto_model: Option<String>,
    worktree: Option<String>,
    group: Option<String>,
    verify: Option<String>,
    iterate: Option<u32>,
    eval: Option<String>,
    eval_feedback_template: Option<String>,
    judge: Option<String>,
    peer_review: Option<String>,
    retry: u32,
    context: Vec<String>,
    checklist: Vec<String>,
    scope: Vec<String>,
    run_extras: Box<RunExtrasArgs>,
    no_skill: bool,
    bg: bool,
    dry_run: bool,
    read_only: bool,
    sandbox: bool,
    container: Option<String>,
    budget: bool,
    difficulty: Option<TaskDifficulty>,
    declared_budget: Option<TaskBudget>,
    urgency: Option<TaskUrgency>,
    rigor: Option<TaskRigor>,
    egress: TaskEgress,
    kind: Option<TaskCategory>,
    best_of: Option<usize>,
    metric: Option<String>,
    team_flag: Option<String>,
    parent: Option<String>,
    id: Option<String>,
    timeout: Option<u64>,
    idle_timeout: Option<u64>,
    audit: bool,
    no_audit: bool,
    no_link_deps: bool,
) -> cmd::run::RunArgs {
    let extras = *run_extras;
    let skills = if no_skill {
        vec![cmd::run::NO_SKILL_SENTINEL.to_string()]
    } else {
        extras.skill
    };
    let model_source = if model.is_some() {
        ModelSource::UserSupplied
    } else {
        ModelSource::AidResolved
    };
    cmd::run::RunArgs {
        agent_name,
        prompt,
        prompt_file,
        repo,
        repo_root,
        dir,
        output,
        result_file,
        model: model.or(auto_model),
        model_source,
        declared_difficulty: difficulty,
        declared_budget,
        declared_urgency: urgency,
        declared_rigor: rigor,
        declared_egress: egress,
        kind,
        worktree,
        base_branch: None,
        group: resolve_group(group),
        verify,
        iterate,
        eval,
        eval_feedback_template,
        judge,
        peer_review,
        max_duration_mins: None,
        retry,
        context,
        checklist,
        skills,
        hooks: extras.hook,
        template: extras.template,
        background: bg,
        dry_run,
        announce: true,
        on_done: extras.on_done,
        cascade: extras.cascade,
        read_only,
        sandbox,
        container,
        budget,
        best_of,
        metric,
        team: team_flag,
        context_from: extras.context_from,
        scope,
        parent_task_id: parent,
        idle_timeout_secs: idle_timeout,
        existing_task_id: id.map(crate::types::TaskId),
        timeout,
        audit,
        audit_explicit: audit,
        no_audit,
        link_deps: !no_link_deps,
        ..Default::default()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::cli::RunExtrasArgs;

    #[test]
    fn auto_model_is_marked_aid_resolved() {
        let args = build_run_args(
            "qwen".to_string(),
            "say hi".to_string(),
            None,
            None,
            None,
            None,
            None,
            None,
            None,
            Some("stale-aid-model".to_string()),
            None,
            None,
            None,
            None,
            None,
            None,
            None,
            None,
            0,
            Vec::new(),
            Vec::new(),
            Vec::new(),
            Box::new(RunExtrasArgs {
                context_from: Vec::new(),
                skill: Vec::new(),
                template: None,
                on_done: None,
                cascade: Vec::new(),
                hook: Vec::new(),
            }),
            false,
            false,
            true,
            false,
            false,
            None,
            false,
            None,
            None,
            None,
            None,
            TaskEgress::Any,
            None,
            None,
            None,
            None,
            None,
            None,
            None,
            None,
            false,
            false,
            false,
        );

        assert_eq!(args.model.as_deref(), Some("stale-aid-model"));
        assert_eq!(args.model_source, ModelSource::AidResolved);
    }
}