use crate::cli::cli_request::require_cli_request;
use crate::cli::run_emit::{RunStartupEmitOpts, emit_run_logs_line, emit_run_startup_banner};
use crate::cli::{AgentRouteOpts, SharedOpts};
use malvin::agent_backend::{SdkClient, build_agent_backend};
use malvin::artifacts::{RunArtifacts, is_existing_md_file_path, resolve_user_md_request};
use malvin::prompts::PromptStore;
use std::path::PathBuf;
#[path = "router_flow_acp.rs"]
pub(crate) mod router_flow_acp;
#[path = "router_flow_loop.rs"]
pub(crate) mod router_flow_loop;
#[path = "router_flow_no_work.rs"]
pub(crate) mod router_flow_no_work;
pub(crate) mod router_flow_prompt;
pub use router_flow_prompt::prepare_router_prompt_store;
#[derive(Debug)]
pub struct RouterArgs {
pub request: Option<String>,
pub max_loops: usize,
pub max_hypotheses: usize,
}
struct RouterRunPrep {
client: SdkClient,
artifacts: RunArtifacts,
prompt_store: PromptStore,
watch_source: Option<PathBuf>,
}
fn new_router_client(shared: &SharedOpts) -> Result<SdkClient, String> {
build_agent_backend(
shared.model.clone(),
shared.max_acp_retries,
shared.acp_stdout_markdown_enabled(),
)
}
fn finish_router_run_artifacts(
artifacts: &RunArtifacts,
opts: AgentRouteOpts<'_>,
request: &str,
) -> Result<(), String> {
malvin::gate_loop_session::reset_for_independent_run();
if opts.router.gates {
malvin::artifacts::init_quality_gates_log_pending(artifacts).map_err(|e| e.to_string())?;
}
malvin::run_id::activate_run(artifacts.run_dir.clone());
emit_run_startup_banner(
artifacts,
RunStartupEmitOpts::from_route(opts, true),
request,
)?;
malvin::run_id::maybe_gc_after_run_created(&artifacts.work_dir, &artifacts.run_dir);
Ok(())
}
async fn prepare_router_run(
router_args: &RouterArgs,
opts: AgentRouteOpts<'_>,
) -> Result<RouterRunPrep, String> {
let client = new_router_client(opts.shared)?;
let request = require_cli_request(router_args.request.as_ref(), "")?;
let watch_source = is_existing_md_file_path(&request);
let (text, work_dir) = resolve_user_md_request(&request)?;
let artifacts = malvin::artifacts::create_run_artifacts_from_text_opts(
&text,
Some(work_dir.as_path()),
malvin::run_id::RunDirOptions { gc: false },
)
.map_err(|e| e.to_string())?;
finish_router_run_artifacts(&artifacts, opts, &request)?;
client.ensure_authenticated().map_err(|e| e.to_string())?;
let prompt_store = prepare_router_prompt_store()?;
Ok(RouterRunPrep {
client,
artifacts,
prompt_store,
watch_source,
})
}
pub async fn run_router(router_args: RouterArgs, opts: AgentRouteOpts<'_>) -> Result<(), String> {
let request = require_cli_request(router_args.request.as_ref(), "")?;
if opts.router.quiet {
let interactive = malvin::output::agent_stdout_tee_enabled();
let emit_markdown = interactive && opts.shared.acp_stdout_markdown_enabled();
malvin::output::set_do_dm_stdout_opts(malvin::output::DoDmStdoutOpts {
enabled: true,
emit_markdown,
});
malvin::output::set_heartbeat_stdout_suppressed(true);
}
let result = run_router_body(router_args, opts, &request).await;
if opts.router.quiet {
malvin::output::set_do_dm_stdout_opts(malvin::output::DoDmStdoutOpts::default());
malvin::output::set_heartbeat_stdout_suppressed(false);
}
result
}
async fn run_router_body(
router_args: RouterArgs,
opts: AgentRouteOpts<'_>,
_request: &str,
) -> Result<(), String> {
let mut prep = prepare_router_run(&router_args, opts).await?;
prep.client.prompts_log_run_dir = Some(prep.artifacts.run_dir.clone());
emit_run_logs_line(&prep.artifacts)?;
let loop_outcome =
router_flow_loop::run_router_agent_loops(router_flow_loop::RouterAgentLoopInput {
client: &mut prep.client,
artifacts: &prep.artifacts,
prompt_store: &prep.prompt_store,
shared: opts.shared,
router: opts.router,
max_loops: router_args.max_loops,
max_hypotheses: router_args.max_hypotheses,
watch_source: prep.watch_source.as_deref(),
})
.await?;
malvin::acp_post_run::merge_acp_restore_check_abort_then_print_timing(
loop_outcome.last_acp,
&prep.artifacts,
&loop_outcome.last_backups,
)
}
#[cfg(test)]
mod kiss_static_fn_item_refs {
use super::{run_router, run_router_body};
#[test]
fn kiss_static_fn_item_refs() {
let _ = run_router;
let _ = run_router_body;
}
}
#[cfg(test)]
#[allow(unused_imports)]
mod kiss_cov_gate_refs {
use super::*;
#[test]
fn kiss_cov_unit_names() {
let _: Option<RouterRunPrep> = None;
let _ = new_router_client;
let _ = finish_router_run_artifacts;
let _ = prepare_router_run;
let _ = router_flow_no_work::chat_has_malvin_done;
}
}
#[cfg(test)]
#[path = "router_flow_kiss_cov_tests.rs"]
mod router_flow_kiss_cov_tests;