harness_rs_loop/
subagent.rs1use crate::{AgentLoop, Outcome};
12use harness_core::{
13 Guide, HarnessError, Model, Sensor, SubagentStatus, Task, Tool, World,
14};
15use std::sync::Arc;
16
17pub struct SubagentSpec {
19 pub name: String,
20 pub task: Task,
21 pub tools: Vec<Arc<dyn Tool>>,
22 pub guides: Vec<Arc<dyn Guide>>,
23 pub sensors: Vec<Arc<dyn Sensor>>,
24 pub max_iters: u32,
25}
26
27impl SubagentSpec {
28 pub fn new(name: impl Into<String>, task: Task) -> Self {
29 Self {
30 name: name.into(),
31 task,
32 tools: Vec::new(),
33 guides: Vec::new(),
34 sensors: Vec::new(),
35 max_iters: 12,
36 }
37 }
38
39 pub fn with_tool(mut self, t: Arc<dyn Tool>) -> Self {
40 self.tools.push(t);
41 self
42 }
43
44 pub fn with_guide(mut self, g: Arc<dyn Guide>) -> Self {
45 self.guides.push(g);
46 self
47 }
48
49 pub fn with_sensor(mut self, s: Arc<dyn Sensor>) -> Self {
50 self.sensors.push(s);
51 self
52 }
53
54 pub fn with_max_iters(mut self, n: u32) -> Self {
55 self.max_iters = n;
56 self
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct SubagentReport {
63 pub name: String,
64 pub status: SubagentStatus,
65 pub text: Option<String>,
66 pub iters: u32,
67}
68
69pub struct Subagent<M: Model> {
71 pub spec: SubagentSpec,
72 pub loop_: AgentLoop<M>,
73}
74
75impl<M: Model> Subagent<M> {
76 pub fn new(model: M, spec: SubagentSpec) -> Self {
77 let mut loop_ = AgentLoop::new(model);
78 for t in &spec.tools { loop_ = loop_.with_tool(t.clone()); }
79 for g in &spec.guides { loop_ = loop_.with_guide(g.clone()); }
80 for s in &spec.sensors { loop_ = loop_.with_sensor(s.clone()); }
81 Self { spec, loop_ }
82 }
83
84 pub async fn run(self, world: &mut World) -> Result<SubagentReport, HarnessError> {
85 let name = self.spec.name.clone();
86 let max = self.spec.max_iters;
87 let task = self.spec.task.clone();
88 let outcome = self.loop_.run_with_max_iters(task, world, max).await?;
89 let report = match outcome {
90 Outcome::Done { text, iters } => SubagentReport {
91 name,
92 status: SubagentStatus::Done,
93 text,
94 iters,
95 },
96 Outcome::BudgetExhausted { iters } => SubagentReport {
97 name,
98 status: SubagentStatus::Blocked,
99 text: None,
100 iters,
101 },
102 };
103 tracing::info!(
104 subagent = %report.name,
105 status = ?report.status,
106 iters = report.iters,
107 "subagent completed"
108 );
109 Ok(report)
110 }
111}