use std::path::Path;
use crate::control::hold;
use crate::control::judge::Ruling;
use crate::opslog::{self, DETACHED_EXIT, OpEntry, Origin, YOG_CONTROL};
use super::dispatch::Deps;
use super::reply::Reply;
mod floor;
pub(super) use floor::set_floor;
const ANSWER: &str = "answer";
const ADVANCE: &str = "advance";
pub(super) fn answer_hold(
deps: &Deps,
ts: &str,
workspace: &Path,
agent: &str,
ruling: Ruling,
) -> Result<Reply, String> {
let held = hold::read(workspace, agent).ok_or_else(|| {
format!("nothing is held on {agent:?} in {} — the capability boundary is parking no invocation there", workspace.display())
})?;
let row = OpEntry {
ts: ts.to_owned(),
argv: vec![
YOG_CONTROL.to_owned(),
ANSWER.to_owned(),
held.tool_use_id.clone(),
ruling.word().to_owned(),
],
cwd: crate::nav::ws_key(workspace),
exit: 0,
stdout: held.reason.clone(),
stderr: String::new(),
origin: Origin::Conversation,
};
opslog::append(&deps.state_root, &row).map_err(|e| e.to_string())?;
let advanced = ruling != Ruling::Hold && advance(deps, ts, workspace, agent).is_ok();
Ok(Reply::Answered {
tool_use: held.tool_use_id,
tool: held.tool,
ruling,
advanced,
})
}
fn advance(deps: &Deps, ts: &str, workspace: &Path, agent: &str) -> Result<(), String> {
let ws_s = workspace.to_string_lossy();
let sink = opslog::detached::sink(&deps.state_root, ts, workspace);
let spawn =
deps.lernie
.spawn_detached(Some(workspace), &sink, &[ADVANCE, ws_s.as_ref(), agent]);
let argv = vec![
deps.lernie.binary().display().to_string(),
ADVANCE.to_owned(),
ws_s.into_owned(),
agent.to_owned(),
];
let cwd = crate::nav::ws_key(workspace);
let entry = match spawn.as_ref().err() {
Some(e) => OpEntry::synthetic_failure(
ts.to_owned(),
argv,
cwd,
e.to_string(),
Origin::Conversation,
),
None => OpEntry {
ts: ts.to_owned(),
argv,
cwd,
exit: DETACHED_EXIT,
stdout: String::new(),
stderr: String::new(),
origin: Origin::Conversation,
},
};
opslog::append(&deps.state_root, &entry).map_err(|e| e.to_string())?;
spawn.map(|_pid| ()).map_err(|e| e.to_string())
}
pub(super) fn confinement_gate(workspace: &Path) -> Result<(), String> {
if crate::control::policy::Policy::read(workspace).confinement_required {
return Err(format!(
"{}: its capability policy declares `confinement: required`, and this platform \
provides no confinement layer — no drone is fired. Remove the line from \
{} on config/default to fire without one.",
workspace.display(),
crate::control::policy::CAPABILITY_YAML,
));
}
Ok(())
}
#[cfg(test)]
mod tests;