fix: Forgot to automatically add the bidirectional communication back up to parent agents from sub-agents (i.e. need to be able to check inbox and send messages)

This commit is contained in:
2026-02-17 16:11:35 -07:00
parent 39d9b25e47
commit 5e9c31595e
4 changed files with 68 additions and 49 deletions
+7 -1
View File
@@ -6,6 +6,9 @@ use crate::{
function::{Functions, run_llm_function},
};
use crate::config::prompts::{
DEFAULT_SPAWN_INSTRUCTIONS, DEFAULT_TEAMMATE_INSTRUCTIONS, DEFAULT_TODO_INSTRUCTIONS,
};
use crate::vault::SECRET_RE;
use anyhow::{Context, Result};
use fancy_regex::Captures;
@@ -13,7 +16,6 @@ use inquire::{Text, validator::Validation};
use rust_embed::Embed;
use serde::{Deserialize, Serialize};
use std::{ffi::OsStr, path::Path};
use crate::config::prompts::{DEFAULT_SPAWN_INSTRUCTIONS, DEFAULT_TODO_INSTRUCTIONS};
const DEFAULT_AGENT_NAME: &str = "rag";
@@ -200,6 +202,8 @@ impl Agent {
functions.append_supervisor_functions();
}
functions.append_teammate_functions();
agent_config.replace_tools_placeholder(&functions);
Ok(Self {
@@ -340,6 +344,8 @@ impl Agent {
output.push_str(DEFAULT_SPAWN_INSTRUCTIONS);
}
output.push_str(DEFAULT_TEAMMATE_INSTRUCTIONS);
self.interpolate_text(&output)
}
+13 -10
View File
@@ -29,8 +29,6 @@ pub(in crate::config) const DEFAULT_SPAWN_INSTRUCTIONS: &str = indoc! {"
| `agent__collect` | Blocking wait: wait for an agent to finish, return its output. |
| `agent__list` | List all spawned agents and their status. |
| `agent__cancel` | Cancel a running agent by ID. |
| `agent__send_message` | Send a text message to a sibling or child agent's inbox. |
| `agent__check_inbox` | Check your own inbox for messages from other agents. |
| `agent__task_create` | Create a task in the dependency-aware task queue. |
| `agent__task_list` | List all tasks and their status/dependencies. |
| `agent__task_complete` | Mark a task done; returns any newly unblocked tasks. Auto-dispatches agents for tasks with a designated agent. |
@@ -69,14 +67,6 @@ pub(in crate::config) const DEFAULT_SPAWN_INSTRUCTIONS: &str = indoc! {"
**NEVER spawn sequentially when tasks are independent.** Parallel is always better.
### Teammate Messaging
Sibling agents (spawned by the same parent) can communicate directly:
- `agent__send_message --to <agent_id> --content \"your message\"`: Send to a running sibling
- `agent__check_inbox`: Check for messages from siblings or parent
Use teammate messaging when agents need to coordinate or share intermediate findings.
### Task Queue (for complex dependency chains)
When tasks have ordering requirements, use the task queue:
@@ -95,3 +85,16 @@ pub(in crate::config) const DEFAULT_SPAWN_INSTRUCTIONS: &str = indoc! {"
agent__task_complete --task_id task_1
```
"};
pub(in crate::config) const DEFAULT_TEAMMATE_INSTRUCTIONS: &str = indoc! {"
## Teammate Messaging
You have tools to communicate with other agents running alongside you:
- `agent__send_message --id <agent_id> --message \"...\"`: Send a message to a sibling or parent agent.
- `agent__check_inbox`: Check for messages sent to you by other agents.
If you are working alongside other agents (e.g. reviewing different files, exploring different areas):
- **Check your inbox** before finalizing your work to incorporate any cross-cutting findings from teammates.
- **Send messages** to teammates when you discover something that affects their work.
- Messages are delivered to the agent's inbox and read on their next `check_inbox` call."
};
+5
View File
@@ -276,6 +276,11 @@ impl Functions {
.extend(supervisor::supervisor_function_declarations());
}
pub fn append_teammate_functions(&mut self) {
self.declarations
.extend(supervisor::teammate_function_declarations());
}
pub fn clear_mcp_meta_functions(&mut self) {
self.declarations.retain(|d| {
!d.name.starts_with(MCP_INVOKE_META_FUNCTION_NAME_PREFIX)
+42 -37
View File
@@ -118,43 +118,6 @@ pub fn supervisor_function_declarations() -> Vec<FunctionDeclaration> {
},
agent: false,
},
FunctionDeclaration {
name: format!("{SUPERVISOR_FUNCTION_PREFIX}send_message"),
description: "Send a text message to a running subagent's inbox.".to_string(),
parameters: JsonSchema {
type_value: Some("object".to_string()),
properties: Some(IndexMap::from([
(
"id".to_string(),
JsonSchema {
type_value: Some("string".to_string()),
description: Some("The target agent ID".into()),
..Default::default()
},
),
(
"message".to_string(),
JsonSchema {
type_value: Some("string".to_string()),
description: Some("The message text to send".into()),
..Default::default()
},
),
])),
required: Some(vec!["id".to_string(), "message".to_string()]),
..Default::default()
},
agent: false,
},
FunctionDeclaration {
name: format!("{SUPERVISOR_FUNCTION_PREFIX}check_inbox"),
description: "Check for and drain all pending messages in your inbox.".to_string(),
parameters: JsonSchema {
type_value: Some("object".to_string()),
..Default::default()
},
agent: false,
},
FunctionDeclaration {
name: format!("{SUPERVISOR_FUNCTION_PREFIX}task_create"),
description: "Create a task in the task queue. Returns the task ID.".to_string(),
@@ -241,6 +204,48 @@ pub fn supervisor_function_declarations() -> Vec<FunctionDeclaration> {
]
}
pub fn teammate_function_declarations() -> Vec<FunctionDeclaration> {
vec![
FunctionDeclaration {
name: format!("{SUPERVISOR_FUNCTION_PREFIX}send_message"),
description: "Send a text message to a sibling or child agent's inbox. Use to share cross-cutting findings or coordinate with teammates.".to_string(),
parameters: JsonSchema {
type_value: Some("object".to_string()),
properties: Some(IndexMap::from([
(
"id".to_string(),
JsonSchema {
type_value: Some("string".to_string()),
description: Some("The target agent ID".into()),
..Default::default()
},
),
(
"message".to_string(),
JsonSchema {
type_value: Some("string".to_string()),
description: Some("The message text to send".into()),
..Default::default()
},
),
])),
required: Some(vec!["id".to_string(), "message".to_string()]),
..Default::default()
},
agent: false,
},
FunctionDeclaration {
name: format!("{SUPERVISOR_FUNCTION_PREFIX}check_inbox"),
description: "Check for and drain all pending messages in your inbox from sibling agents or your parent.".to_string(),
parameters: JsonSchema {
type_value: Some("object".to_string()),
..Default::default()
},
agent: false,
},
]
}
pub async fn handle_supervisor_tool(
config: &GlobalConfig,
cmd_name: &str,