From 5e9c31595e718a64e9acea0ea6ffc11b933c7520 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Tue, 17 Feb 2026 16:11:35 -0700 Subject: [PATCH] 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) --- src/config/agent.rs | 8 +++- src/config/prompts.rs | 25 ++++++------ src/function/mod.rs | 5 +++ src/function/supervisor.rs | 79 ++++++++++++++++++++------------------ 4 files changed, 68 insertions(+), 49 deletions(-) diff --git a/src/config/agent.rs b/src/config/agent.rs index b71c2df..bcd0ac8 100644 --- a/src/config/agent.rs +++ b/src/config/agent.rs @@ -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) } diff --git a/src/config/prompts.rs b/src/config/prompts.rs index 1efb172..939daa3 100644 --- a/src/config/prompts.rs +++ b/src/config/prompts.rs @@ -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 --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: @@ -94,4 +84,17 @@ pub(in crate::config) const DEFAULT_SPAWN_INSTRUCTIONS: &str = indoc! {" # If dependents have --agent set, they auto-dispatch agent__task_complete --task_id task_1 ``` -"}; \ No newline at end of file +"}; + +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 --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." +}; diff --git a/src/function/mod.rs b/src/function/mod.rs index 1c92379..031430b 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -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) diff --git a/src/function/supervisor.rs b/src/function/supervisor.rs index 7ca0f70..075b11f 100644 --- a/src/function/supervisor.rs +++ b/src/function/supervisor.rs @@ -118,43 +118,6 @@ pub fn supervisor_function_declarations() -> Vec { }, 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 { ] } +pub fn teammate_function_declarations() -> Vec { + 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,