use crate::actions::verbs;
use crate::model_pick::Pick;
use crate::ui_state::UiState;
use super::reply::Reply;
use super::{Action, answer, config, control, fan, fleet, monitor, routing};
mod advertise;
mod delete_exec;
mod deps;
mod doors;
mod resolve;
use delete_exec::{delete_agent, unmake};
pub use deps::{Caller, Deps};
pub use doors::{prepare, prompt};
use resolve::resolve_workspace;
pub fn dispatch(deps: &Deps, ui: &mut UiState, ts: &str, action: &Action) -> Result<Reply, String> {
let (bl, root) = (&deps.bl, deps.state_root.as_path());
let ws: &std::path::Path = &match action.workspace() {
Some(name) => resolve_workspace(deps, action, &name)?,
None => std::path::PathBuf::new(),
};
let project: &std::path::Path = &match action.project() {
Some(name) => deps.snapshot.project_path(&name)?,
None => std::path::PathBuf::new(),
};
match action {
Action::Message { agent, content, .. } => {
outcome(verbs::message(&deps.bound(ws), root, ts, agent, content))
}
Action::Stop {
agent, children, ..
} => outcome(verbs::stop(&deps.bound(ws), root, ts, agent, *children)),
Action::Scan { .. } => outcome(verbs::scan(&deps.bound(ws), root, ts)),
Action::Nudge { agent, .. } => {
control::advance(deps, ts, ws, agent).map(|()| Reply::Nudged)
}
Action::Retarget { agent, .. } => outcome(retarget(deps, ts, ws, agent)),
Action::Fork {
parent,
attempt,
goal,
..
} => fork(deps, ts, ws, parent, attempt, goal),
Action::Close { id, name, .. } => spend(verbs::close, deps, ts, project, id, name),
Action::Assign { id, name, .. } => spend(verbs::assign, deps, ts, project, id, name),
Action::Release { id, name, .. } => spend(verbs::unclaim, deps, ts, project, id, name),
Action::Move { id, from, to, .. } => {
outcome(verbs::reassign(bl, root, ts, project, id, from, to))
}
Action::Create {
title, name, body, ..
} => outcome(verbs::create(
bl,
root,
ts,
project,
title,
name,
body.as_deref(),
)),
Action::Update {
id,
name,
title,
body,
note,
..
} => {
let fields = verbs::Update::of(title, body, note);
outcome(verbs::update(bl, root, ts, project, id, name, &fields))
}
Action::Prepare { payload, .. } => staged(deps, ts, ws, project, payload),
Action::Prompt {
prepared,
goal,
seed,
} => prompt(deps, ui, ts, ws, prepared, goal, *seed)
.map(|conversation| Reply::Started { conversation }),
Action::Fan {
prepared,
obligation,
n,
} => fan::spread(deps, ts, prepared, obligation, *n),
Action::Retire { obligation, handle } => fan::retire(deps, ts, obligation, handle),
Action::DeleteWorkspace { typed, .. } => unmake(deps, ui, ts, ws, typed),
Action::DeleteAgent { agent, typed, .. } => delete_agent(deps, ui, ts, ws, agent, typed),
Action::Monitor(verb) => monitor::dispatch(deps, ts, ws, verb),
Action::Fleet(verb) => fleet::dispatch(deps, ts, ws, verb),
Action::AnswerHold { agent, ruling, .. } => {
control::answer_hold(deps, ts, ws, agent, *ruling)
}
Action::Floor { agent, raised, .. } => control::set_floor(deps, ts, ws, agent, *raised),
Action::Ack => wrote(crate::opslog::ack(root, ts), Reply::Acked),
Action::MarkSeen { agent, .. } => acknowledge(deps, ui, ts, ws, agent),
Action::ClearTrail => wrote(crate::opslog::clear(root, ts), Reply::TrailCleared),
Action::ApplyConfig { file, text } => config::apply(deps, ts, file, text),
Action::SetMarks { branch, .. } => config::set_marks(deps, ts, ws, branch),
Action::PickModel {
role,
provider,
model,
..
} => config::pick_model(deps, ts, ws, &Pick::of(role, provider, model)),
Action::Advertise { tools } => advertise::advertise(deps, tools),
Action::Route(verb) => routing::route(deps, ts, verb),
}
}
fn spend(
verb: fn(
&crate::cli_outbound::Cli,
&std::path::Path,
&str,
&std::path::Path,
&str,
&str,
) -> std::io::Result<verbs::Outcome>,
deps: &Deps,
ts: &str,
project: &std::path::Path,
id: &str,
name: &str,
) -> Result<Reply, String> {
outcome(verb(&deps.bl, &deps.state_root, ts, project, id, name))
}
fn staged(
deps: &Deps,
ts: &str,
workspace: &std::path::Path,
repo: &std::path::Path,
payload: &crate::start::Payload,
) -> Result<Reply, String> {
prepare(deps, ts, workspace, repo, payload).map(Reply::Prepared)
}
fn outcome(ran: std::io::Result<verbs::Outcome>) -> Result<Reply, String> {
ran.map(Reply::Outcome).map_err(|e| e.to_string())
}
fn wrote(written: std::io::Result<()>, reply: Reply) -> Result<Reply, String> {
written.map(|()| reply).map_err(|e| e.to_string())
}
fn retarget(
deps: &Deps,
ts: &str,
workspace: &std::path::Path,
agent: &str,
) -> std::io::Result<verbs::Outcome> {
verbs::retarget(&deps.bound(workspace), &deps.state_root, ts, agent)
}
fn fork(
deps: &Deps,
ts: &str,
workspace: &std::path::Path,
parent: &str,
attempt: &crate::fork::Attempt,
goal: &str,
) -> Result<Reply, String> {
control::confinement_gate(workspace)?;
verbs::fork(
&deps.bound(workspace),
&deps.state_root,
ts,
&crate::fork::Fire::at(workspace, parent, attempt, goal, &deps.yog_data_root),
)
.map(Reply::Outcome)
.map_err(|e| e.to_string())
}
fn acknowledge(
deps: &Deps,
ui: &mut UiState,
ts: &str,
workspace: &std::path::Path,
agent: &str,
) -> Result<Reply, String> {
answer::queue::mark_seen(&deps.snapshot, ui, workspace, agent)?;
let rows = answer::queue::queue(&deps.snapshot, ui, ts.parse().unwrap_or(0));
Ok(Reply::Attention(rows))
}