use crate::config::ToolControl;
use crate::prompt::Error;
use crate::prompt::tool::control::{self, ControlError, ControlRequest, Verdict};
use serde_json::Value;
use std::path::Path;
use std::sync::atomic::AtomicBool;
pub(super) enum Gate {
Proceed,
Refuse(String),
Hold(String),
Stopped,
}
#[allow(clippy::too_many_arguments)] pub(super) fn adjudicate(
configured: Option<&ToolControl>,
role: &str,
id: &str,
name: &str,
input: &Value,
conv_repo: &Path,
agent_id: &str,
stop: &AtomicBool,
) -> Result<Gate, Error> {
let Some(tool_control) = configured else {
return Ok(Gate::Proceed);
};
let request = ControlRequest {
id,
name,
input,
role,
agent_id,
};
match control::consult(&tool_control.command, &request, conv_repo, stop) {
Ok(Verdict::Pass) => Ok(Gate::Proceed),
Ok(Verdict::Refuse { reason }) => Ok(Gate::Refuse(reason)),
Ok(Verdict::Hold { reason }) => Ok(Gate::Hold(reason)),
Err(ControlError::KilledBySignal { .. }) if super::stop_signal::stopped(stop) => {
Ok(Gate::Stopped)
}
Err(source) => Err(Error::ToolControl {
command: tool_control.command.clone(),
tool: name.to_string(),
detail: source.to_string(),
}),
}
}
pub(super) fn refusal_text(tool: &str, reason: &str) -> String {
format!(
"{tool:?} was refused by the workflow's tool control (ARCH §3.3 Tool control): {reason}"
)
}