Skip to content

Commit da42d4e

Browse files
committed
adds subagent summary tool
1 parent 5004b8b commit da42d4e

File tree

5 files changed

+130
-0
lines changed

5 files changed

+130
-0
lines changed

crates/agent/src/agent/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1504,6 +1504,7 @@ impl Agent {
15041504
BuiltInTool::Mkdir(_) => Ok(()),
15051505
BuiltInTool::ExecuteCmd(_) => Ok(()),
15061506
BuiltInTool::Introspect(_) => Ok(()),
1507+
BuiltInTool::Summary(_) => Ok(()),
15071508
BuiltInTool::SpawnSubagent => Ok(()),
15081509
BuiltInTool::ImageRead(t) => t.validate().await.map_err(ToolParseErrorKind::invalid_args),
15091510
},
@@ -1612,6 +1613,10 @@ impl Agent {
16121613
BuiltInTool::Ls(t) => Box::pin(async move { t.execute(&provider).await }),
16131614
BuiltInTool::Mkdir(_) => panic!("unimplemented"),
16141615
BuiltInTool::SpawnSubagent => panic!("unimplemented"),
1616+
BuiltInTool::Summary(t) => {
1617+
let result_tx = self.agent_event_tx.clone();
1618+
Box::pin(async move { t.execute(result_tx).await })
1619+
},
16151620
},
16161621
ToolKind::Mcp(t) => {
16171622
let mcp_tool = t.clone();

crates/agent/src/agent/permissions.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ pub fn evaluate_tool_permission<P: SystemProvider>(
6767
BuiltInTool::ExecuteCmd(_) => Ok(PermissionEvalResult::Allow),
6868
BuiltInTool::Introspect(_) => Ok(PermissionEvalResult::Allow),
6969
BuiltInTool::SpawnSubagent => Ok(PermissionEvalResult::Allow),
70+
BuiltInTool::Summary(_) => Ok(PermissionEvalResult::Allow),
7071
},
7172
ToolKind::Mcp(_) => Ok(if is_allowed {
7273
PermissionEvalResult::Allow

crates/agent/src/agent/protocol.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use super::mcp::{
2323
McpServerEvent,
2424
};
2525
use super::task_executor::TaskExecutorEvent;
26+
use super::tools::summary::Summary;
2627
use super::tools::{
2728
Tool,
2829
ToolExecutionError,
@@ -79,6 +80,9 @@ pub enum AgentEvent {
7980

8081
/// Events from MCP (Model Context Protocol) servers
8182
Mcp(McpServerEvent),
83+
84+
/// Summary of a subagent's execution
85+
SubagentSummary(Summary),
8286
}
8387

8488
impl From<TaskExecutorEvent> for AgentEvent {
@@ -99,6 +103,12 @@ impl From<ToolCall> for AgentEvent {
99103
}
100104
}
101105

106+
impl From<&Summary> for AgentEvent {
107+
fn from(value: &Summary) -> Self {
108+
Self::SubagentSummary(value.clone())
109+
}
110+
}
111+
102112
#[derive(Debug, Clone, Serialize, Deserialize)]
103113
pub enum UpdateEvent {
104114
/// A chunk of the user’s message being streamed.

crates/agent/src/agent/tools/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub mod ls;
88
pub mod mcp;
99
pub mod mkdir;
1010
pub mod rm;
11+
pub mod summary;
1112

1213
use std::borrow::Cow;
1314
use std::sync::Arc;
@@ -31,6 +32,7 @@ use serde::{
3132
Serialize,
3233
};
3334
use strum::IntoEnumIterator;
35+
use summary::Summary;
3436

3537
use super::agent_config::parse::CanonicalToolName;
3638
use super::agent_loop::types::ToolUseBlock;
@@ -100,6 +102,7 @@ pub enum BuiltInToolName {
100102
ExecuteCmd,
101103
ImageRead,
102104
Ls,
105+
Summary,
103106
}
104107

105108
trait BuiltInToolTrait {
@@ -237,6 +240,7 @@ pub enum BuiltInTool {
237240
ImageRead(ImageRead),
238241
ExecuteCmd(ExecuteCmd),
239242
Introspect(Introspect),
243+
Summary(Summary),
240244
/// TODO
241245
SpawnSubagent,
242246
}
@@ -259,6 +263,9 @@ impl BuiltInTool {
259263
BuiltInToolName::Ls => serde_json::from_value::<Ls>(args)
260264
.map(Self::Ls)
261265
.map_err(ToolParseErrorKind::schema_failure),
266+
BuiltInToolName::Summary => serde_json::from_value::<Summary>(args)
267+
.map(Self::Summary)
268+
.map_err(ToolParseErrorKind::schema_failure),
262269
}
263270
}
264271

@@ -269,6 +276,7 @@ impl BuiltInTool {
269276
BuiltInToolName::ExecuteCmd => generate_tool_spec_from_trait::<ExecuteCmd>(),
270277
BuiltInToolName::ImageRead => generate_tool_spec_from_trait::<ImageRead>(),
271278
BuiltInToolName::Ls => generate_tool_spec_from_trait::<Ls>(),
279+
BuiltInToolName::Summary => generate_tool_spec_from_trait::<Summary>(),
272280
}
273281
}
274282

@@ -282,6 +290,7 @@ impl BuiltInTool {
282290
BuiltInTool::ImageRead(_) => BuiltInToolName::ImageRead,
283291
BuiltInTool::ExecuteCmd(_) => BuiltInToolName::ExecuteCmd,
284292
BuiltInTool::Introspect(_) => panic!("unimplemented"),
293+
BuiltInTool::Summary(_) => BuiltInToolName::Summary,
285294
BuiltInTool::SpawnSubagent => panic!("unimplemented"),
286295
}
287296
}
@@ -296,6 +305,7 @@ impl BuiltInTool {
296305
BuiltInTool::ImageRead(_) => BuiltInToolName::ImageRead.into(),
297306
BuiltInTool::ExecuteCmd(_) => BuiltInToolName::ExecuteCmd.into(),
298307
BuiltInTool::Introspect(_) => panic!("unimplemented"),
308+
BuiltInTool::Summary(_) => BuiltInToolName::Summary.into(),
299309
BuiltInTool::SpawnSubagent => panic!("unimplemented"),
300310
}
301311
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
use schemars::{
2+
JsonSchema,
3+
schema_for,
4+
};
5+
use serde::{
6+
Deserialize,
7+
Serialize,
8+
};
9+
use tokio::sync::broadcast;
10+
11+
use super::{
12+
BuiltInToolName,
13+
BuiltInToolTrait,
14+
ToolExecutionError,
15+
ToolExecutionOutput,
16+
ToolExecutionResult,
17+
};
18+
use crate::protocol::AgentEvent;
19+
20+
/// A tool for conveying information from subagent to its main agent
21+
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
22+
/// A tool for conveying information from subagent to its main agent
23+
pub struct Summary {
24+
/// Description of the task that was assigned to the subagent
25+
pub task_description: String,
26+
/// Relevant context and information gathered during task execution
27+
pub context_summary: Option<String>,
28+
/// The final result or outcome of the completed task
29+
pub task_result: String,
30+
}
31+
32+
const SUMMARY_TOOL_DESCRIPTION: &str = r#"
33+
A tool for conveying task summary and results from subagent to main agent.
34+
35+
WHEN TO USE THIS TOOL:
36+
- As a subagent, when a task is completed, use this tool to send the findings / conclusions to the main agent
37+
38+
HOW TO USE:
39+
- Provide the description of the task given
40+
- Optionally provide any context summary that compliments the consumer of the results. This is to aid subsequent actions to be performed with the result being sent
41+
- Provide the result of the task performed
42+
"#;
43+
44+
impl BuiltInToolTrait for Summary {
45+
fn name() -> super::BuiltInToolName {
46+
BuiltInToolName::Ls
47+
}
48+
49+
fn description() -> std::borrow::Cow<'static, str> {
50+
SUMMARY_TOOL_DESCRIPTION.into()
51+
}
52+
53+
fn input_schema() -> std::borrow::Cow<'static, str> {
54+
serde_json::to_string(&Self::tool_schema())
55+
.expect("serializing schema should not fail")
56+
.into()
57+
}
58+
}
59+
60+
impl Summary {
61+
pub fn tool_schema() -> serde_json::Value {
62+
let schema = schema_for!(Self);
63+
serde_json::to_value(schema).expect("creating tool schema should not fail")
64+
}
65+
66+
pub async fn execute(&self, result_tx: broadcast::Sender<AgentEvent>) -> ToolExecutionResult {
67+
result_tx
68+
.send(self.into())
69+
.map(|_res| ToolExecutionOutput::default())
70+
.map_err(|e| ToolExecutionError::Custom(e.to_string()))
71+
}
72+
}
73+
74+
#[cfg(test)]
75+
mod tests {
76+
use super::*;
77+
78+
#[tokio::test]
79+
async fn test_summary_tool_execute() {
80+
let (tx, mut rx) = broadcast::channel(10);
81+
let summary = Summary {
82+
task_description: "test task".to_string(),
83+
context_summary: Some("test context".to_string()),
84+
task_result: "test result".to_string(),
85+
};
86+
let result = summary.execute(tx).await;
87+
assert!(result.is_ok());
88+
89+
let event = rx.recv().await.unwrap();
90+
91+
if let AgentEvent::SubagentSummary(Summary {
92+
task_description,
93+
context_summary,
94+
task_result,
95+
}) = event
96+
{
97+
assert_eq!(task_description, "test task");
98+
assert_eq!(context_summary, Some("test context".to_string()));
99+
assert_eq!(task_result, "test result");
100+
} else {
101+
panic!("Expected AgentEvent::Summary");
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)