use std::sync::Arc;
use serde_json::json;
use tinyagents::error::TinyAgentsError;
use tinyagents::harness::context::{RunConfig, RunContext};
use tinyagents::harness::message::Message;
use tinyagents::harness::providers::MockModel;
use tinyagents::harness::runtime::AgentHarness;
use tinyagents::harness::testkit::{EventRecorder, FakeTool, Trajectory};
use tinyagents::harness::tool::{Tool, ToolCall};
use tinyagents::{SubAgent, SubAgentTool};
fn failing_child_harness() -> AgentHarness<()> {
let mut harness: AgentHarness<()> = AgentHarness::new();
harness.register_model(
"child-model",
Arc::new(MockModel::with_tool_call("broken", json!({}))),
);
harness.register_tool(Arc::new(FakeTool::failing("broken", "boom")));
harness
}
#[tokio::test]
async fn subagent_invoke_propagates_tool_failure() {
let subagent = SubAgent::new(
"broken_worker",
"a worker whose tool always fails",
Arc::new(failing_child_harness()),
);
let err = subagent
.invoke(&(), (), 0, "do the thing")
.await
.expect_err("the child tool failure must propagate out of SubAgent::invoke");
match err {
TinyAgentsError::Tool(msg) => assert_eq!(msg, "boom"),
other => panic!("expected TinyAgentsError::Tool(\"boom\"), got {other:?}"),
}
}
#[tokio::test]
async fn subagent_tool_call_surfaces_failure_as_err() {
let subagent = Arc::new(SubAgent::new(
"broken_worker",
"a worker whose tool always fails",
Arc::new(failing_child_harness()),
));
let tool = SubAgentTool::new(subagent);
let err = tool
.call(
&(),
ToolCall::new("c1", "broken_worker", json!({ "input": "x" })),
)
.await
.expect_err("SubAgentTool::call must surface the child failure as an Err");
match err {
TinyAgentsError::Tool(msg) => assert_eq!(msg, "boom"),
other => panic!("expected TinyAgentsError::Tool(\"boom\"), got {other:?}"),
}
}
#[tokio::test]
async fn orchestrator_observes_failing_subagent_tool() {
let subagent = Arc::new(SubAgent::new(
"broken_worker",
"a worker whose tool always fails",
Arc::new(failing_child_harness()),
));
let tool = Arc::new(SubAgentTool::new(subagent));
let mut orchestrator: AgentHarness<()> = AgentHarness::new();
orchestrator.register_tool(tool);
orchestrator.register_model(
"parent-model",
Arc::new(MockModel::with_tool_call(
"broken_worker",
json!({ "input": "delegate" }),
)),
);
let recorder = EventRecorder::new();
let ctx = RunContext::new(RunConfig::new("orchestrator-run"), ()).with_events(recorder.sink());
let err = orchestrator
.invoke_in_context(&(), ctx, vec![Message::user("delegate this")])
.await
.expect_err("the failing sub-agent tool must abort the orchestrator run");
match err {
TinyAgentsError::Tool(msg) => assert_eq!(msg, "boom"),
other => panic!("expected TinyAgentsError::Tool(\"boom\"), got {other:?}"),
}
let traj = Trajectory::from_events(recorder.events());
assert!(
traj.failed(),
"orchestrator run should record a RunFailed event when the sub-agent tool fails"
);
}