pub(crate) mod caller;
pub(crate) mod cli;
use super::tool_step::{permit, seam};
use crate::config::ToolControl;
use crate::prompt::tool::inject::InjectedTool;
use serde_json::Value;
use std::path::Path;
use std::sync::atomic::AtomicBool;
pub(crate) const COMPOSING: [&str; 1] = [crate::prompt::tool::builtin::PYTHON];
pub(crate) struct Passage<'a> {
pub(crate) id: &'a str,
pub(crate) name: &'a str,
pub(crate) input: &'a Value,
pub(crate) role: &'a str,
pub(crate) grant: &'a [String],
pub(crate) injected: &'a [InjectedTool],
pub(crate) tool_control: Option<&'a ToolControl>,
pub(crate) conv_repo: &'a Path,
pub(crate) conv_id: &'a str,
pub(crate) stop: &'a AtomicBool,
}
pub(crate) enum Verdict {
Declined(String),
Proceed,
}
pub(crate) fn gate(p: &Passage<'_>) -> Result<Option<Verdict>, crate::prompt::Error> {
if COMPOSING.contains(&p.name) {
return Ok(Some(Verdict::Declined(depth_decline(p.name))));
}
if let Some(decline) = permit::refusal(p.role, p.grant, p.injected, p.name) {
return Ok(Some(Verdict::Declined(decline)));
}
match seam::adjudicate(
p.tool_control,
p.role,
p.id,
p.name,
p.input,
p.conv_repo,
p.conv_id,
p.stop,
)? {
seam::Gate::Stopped => Ok(None),
seam::Gate::Refuse(reason) => {
Ok(Some(Verdict::Declined(seam::refusal_text(p.name, &reason))))
}
seam::Gate::Hold(reason) => Ok(Some(Verdict::Declined(hold_decline(p.name, &reason)))),
seam::Gate::Proceed => Ok(Some(Verdict::Proceed)),
}
}
fn depth_decline(name: &str) -> String {
format!(
"{name:?} composes tool invocations of its own and may not be one \
(depth 1): issue its invocations directly."
)
}
fn hold_decline(name: &str, reason: &str) -> String {
format!(
"the workflow's tool control held {name:?} ({reason}), and a hold cannot park \
an invocation another tool is composing: re-issue this invocation as a \
top-level tool_use to have it reviewed (ARCH §3.3 Tool control)."
)
}