loopd: dispatch subagent, not the port
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
This commit is contained in:
@@ -20,3 +20,60 @@ pub fn schema() -> crate::llama::ToolSchema {
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// `sub-<parent>-<n>`, or the raw string that would not fit a `SessionId`.
|
||||
pub fn child_id(
|
||||
parent: &bxcore::SessionId,
|
||||
n: bxcore::CallId,
|
||||
) -> Result<bxcore::SessionId, String> {
|
||||
let raw = format!("sub-{}-{}", parent.as_str(), n.0);
|
||||
bxcore::SessionId::new(&raw).map_err(|_| raw)
|
||||
}
|
||||
|
||||
/// Inner arguments of `subagent` (or of `call_tool` after unwrap). Unknown keys are errors.
|
||||
pub fn parse_task(arguments: &str) -> Result<String, String> {
|
||||
let value: serde_json::Value = match serde_json::from_str(arguments) {
|
||||
Ok(value) => value,
|
||||
Err(_) => return Err("arguments must be a JSON object".to_string()),
|
||||
};
|
||||
let obj = match value.as_object() {
|
||||
Some(obj) => obj,
|
||||
None => return Err("arguments must be a JSON object".to_string()),
|
||||
};
|
||||
for key in obj.keys() {
|
||||
if key != "task" {
|
||||
return Err(format!("unknown field: {key}"));
|
||||
}
|
||||
}
|
||||
match obj.get("task") {
|
||||
Some(serde_json::Value::String(text)) => Ok(text.clone()),
|
||||
Some(_) => Err("task must be a string".to_string()),
|
||||
None => Err("task is required".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dispatch_args(arguments: &str) -> crate::tools::Dispatch {
|
||||
match parse_task(arguments) {
|
||||
Ok(task) => crate::tools::Dispatch::Subagent { task },
|
||||
Err(detail) => crate::tools::Dispatch::Local(crate::tools::denial_message(
|
||||
bxcore::DenyReason::InvalidArguments,
|
||||
Some(&detail),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn is task 03. The match arm in `run_call` needs this name now.
|
||||
pub fn run(
|
||||
rt: &crate::turn::Runtime<'_>,
|
||||
parent: &crate::session::Session,
|
||||
task: &str,
|
||||
call_id: bxcore::CallId,
|
||||
on_event: &mut dyn FnMut(&bxcore::TurnEvent),
|
||||
) -> (String, bxcore::DataClass, bool) {
|
||||
let _ = (rt, parent, task, call_id, on_event);
|
||||
(
|
||||
"subagent stub".to_string(),
|
||||
bxcore::DataClass::Public,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -288,6 +288,7 @@ fn call_tool_schema() -> ToolSchema {
|
||||
pub enum Dispatch {
|
||||
Local(String),
|
||||
Port { tool: String, arguments: String },
|
||||
Subagent { task: String },
|
||||
}
|
||||
|
||||
/// Decides whether `loopd` handles a tool request itself or sends it to the port.
|
||||
@@ -301,6 +302,9 @@ pub fn dispatch(registry: &Registry, name: &str, arguments: &str) -> Dispatch {
|
||||
if name == CLOCK {
|
||||
return Dispatch::Local(bxcore::Timestamp::now().to_rfc3339());
|
||||
}
|
||||
if name == crate::subagent::SUBAGENT {
|
||||
return crate::subagent::dispatch_args(arguments);
|
||||
}
|
||||
Dispatch::Port {
|
||||
tool: name.to_string(),
|
||||
arguments: arguments.to_string(),
|
||||
@@ -348,6 +352,9 @@ fn dispatch_call_tool(registry: &Registry, arguments: &str) -> Dispatch {
|
||||
Some(_) => {
|
||||
let args_text =
|
||||
serde_json::to_string(args_value).unwrap_or_else(|_| "null".to_string());
|
||||
if name == crate::subagent::SUBAGENT {
|
||||
return crate::subagent::dispatch_args(&args_text);
|
||||
}
|
||||
Dispatch::Port {
|
||||
tool: name.to_string(),
|
||||
arguments: args_text,
|
||||
|
||||
@@ -281,6 +281,9 @@ fn run_call(
|
||||
) -> (String, DataClass, bool) {
|
||||
match dispatch(rt.registry, &call.name, &call.arguments) {
|
||||
crate::tools::Dispatch::Local(text) => (text, DataClass::Public, false),
|
||||
crate::tools::Dispatch::Subagent { task } => {
|
||||
crate::subagent::run(rt, session, &task, call_id, on_event)
|
||||
}
|
||||
crate::tools::Dispatch::Port { tool, arguments } => {
|
||||
let request = ToolRequest {
|
||||
session: session.id().clone(),
|
||||
|
||||
Reference in New Issue
Block a user