feat: replace subagent and skills with unified summon extension (#6964)

Signed-off-by: Travis Longwell <travis@block.xyz>
This commit is contained in:
tlongwell-block
2026-02-10 14:13:38 -05:00
committed by GitHub
parent 1168d7d9b1
commit 7ea19f5c83
26 changed files with 2616 additions and 1935 deletions
+3 -106
View File
@@ -20,10 +20,6 @@ use crate::agents::final_output_tool::{FINAL_OUTPUT_CONTINUATION_MESSAGE, FINAL_
use crate::agents::platform_tools::PLATFORM_MANAGE_SCHEDULE_TOOL_NAME;
use crate::agents::prompt_manager::PromptManager;
use crate::agents::retry::{RetryManager, RetryResult};
use crate::agents::subagent_task_config::TaskConfig;
use crate::agents::subagent_tool::{
create_subagent_tool, handle_subagent_tool, SUBAGENT_TOOL_NAME,
};
use crate::agents::types::{FrontendTool, SessionConfig, SharedProvider, ToolResultReceiver};
use crate::config::permission::PermissionManager;
use crate::config::{get_enabled_extensions, Config, GooseMode};
@@ -42,11 +38,11 @@ use crate::permission::permission_judge::PermissionCheckResult;
use crate::permission::PermissionConfirmation;
use crate::providers::base::Provider;
use crate::providers::errors::ProviderError;
use crate::recipe::{Author, Recipe, Response, Settings, SubRecipe};
use crate::recipe::{Author, Recipe, Response, Settings};
use crate::scheduler_trait::SchedulerTrait;
use crate::security::security_inspector::SecurityInspector;
use crate::session::extension_data::{EnabledExtensionsState, ExtensionState};
use crate::session::{Session, SessionManager, SessionType};
use crate::session::{Session, SessionManager};
use crate::tool_inspection::ToolInspectionManager;
use crate::tool_monitor::RepetitionInspector;
use crate::utils::is_token_cancelled;
@@ -121,7 +117,6 @@ pub struct Agent {
pub config: AgentConfig,
pub extension_manager: Arc<ExtensionManager>,
pub(super) sub_recipes: Mutex<HashMap<String, SubRecipe>>,
pub(super) final_output_tool: Arc<Mutex<Option<FinalOutputTool>>>,
pub(super) frontend_tools: Mutex<HashMap<String, FrontendTool>>,
pub(super) frontend_instructions: Mutex<Option<String>>,
@@ -210,7 +205,6 @@ impl Agent {
provider: provider.clone(),
config,
extension_manager: Arc::new(ExtensionManager::new(provider.clone(), session_manager)),
sub_recipes: Mutex::new(HashMap::new()),
final_output_tool: Arc::new(Mutex::new(None)),
frontend_tools: Mutex::new(HashMap::new()),
frontend_instructions: Mutex::new(None),
@@ -452,23 +446,11 @@ impl Agent {
self.extend_system_prompt(final_output_system_prompt).await;
}
pub async fn add_sub_recipes(&self, sub_recipes_to_add: Vec<SubRecipe>) {
let mut sub_recipes = self.sub_recipes.lock().await;
for sr in sub_recipes_to_add {
sub_recipes.insert(sr.name.clone(), sr);
}
}
pub async fn apply_recipe_components(
&self,
sub_recipes: Option<Vec<SubRecipe>>,
response: Option<Response>,
include_final_output: bool,
) {
if let Some(sub_recipes) = sub_recipes {
self.add_sub_recipes(sub_recipes).await;
}
if include_final_output {
if let Some(response) = response {
self.add_final_output_tool(response).await;
@@ -485,18 +467,6 @@ impl Agent {
cancellation_token: Option<CancellationToken>,
session: &Session,
) -> (String, Result<ToolCallResult, ErrorData>) {
// Prevent subagents from creating other subagents
if session.session_type == SessionType::SubAgent && tool_call.name == SUBAGENT_TOOL_NAME {
return (
request_id,
Err(ErrorData::new(
ErrorCode::INVALID_REQUEST,
"Subagents cannot create other subagents".to_string(),
None,
)),
);
}
if tool_call.name == PLATFORM_MANAGE_SCHEDULE_TOOL_NAME {
let arguments = tool_call
.arguments
@@ -531,49 +501,7 @@ impl Agent {
}
debug!("WAITING_TOOL_START: {}", tool_call.name);
let result: ToolCallResult = if tool_call.name == SUBAGENT_TOOL_NAME {
let provider = match self.provider().await {
Ok(p) => p,
Err(_) => {
return (
request_id,
Err(ErrorData::new(
ErrorCode::INTERNAL_ERROR,
"Provider is required".to_string(),
None,
)),
);
}
};
let extensions = self.get_extension_configs().await;
let max_turns_from_recipe = session
.recipe
.as_ref()
.and_then(|r| r.settings.as_ref())
.and_then(|s| s.max_turns);
let task_config =
TaskConfig::new(provider, &session.id, &session.working_dir, extensions)
.with_max_turns(max_turns_from_recipe);
let sub_recipes = self.sub_recipes.lock().await.clone();
let arguments = tool_call
.arguments
.clone()
.map(Value::Object)
.unwrap_or(Value::Object(serde_json::Map::new()));
handle_subagent_tool(
&self.config,
arguments,
task_config,
sub_recipes,
session.working_dir.clone(),
cancellation_token,
)
} else if self.is_frontend_tool(&tool_call.name).await {
let result: ToolCallResult = if self.is_frontend_tool(&tool_call.name).await {
// For frontend tools, return an error indicating we need frontend execution
ToolCallResult::from(Err(ErrorData::new(
ErrorCode::INTERNAL_ERROR,
@@ -807,30 +735,6 @@ impl Agent {
Ok(())
}
pub async fn subagents_enabled(&self, session_id: &str) -> bool {
if self.config.goose_mode != GooseMode::Auto {
return false;
}
let context = self.extension_manager.get_context();
if matches!(
context
.session_manager
.get_session(session_id, false)
.await
.ok()
.map(|session| session.session_type),
Some(SessionType::SubAgent)
) {
return false;
}
!self
.extension_manager
.list_extensions()
.await
.map(|ext| ext.is_empty())
.unwrap_or(true)
}
pub async fn list_tools(&self, session_id: &str, extension_name: Option<String>) -> Vec<Tool> {
let mut prefixed_tools = self
.extension_manager
@@ -838,7 +742,6 @@ impl Agent {
.await
.unwrap_or_default();
let subagents_enabled = self.subagents_enabled(session_id).await;
if (extension_name.is_none() || extension_name.as_deref() == Some("platform"))
&& self.config.scheduler_service.is_some()
{
@@ -849,12 +752,6 @@ impl Agent {
if let Some(final_output_tool) = self.final_output_tool.lock().await.as_ref() {
prefixed_tools.push(final_output_tool.tool());
}
if subagents_enabled {
let sub_recipes = self.sub_recipes.lock().await;
let sub_recipes_vec: Vec<_> = sub_recipes.values().cloned().collect();
prefixed_tools.push(create_subagent_tool(&sub_recipes_vec));
}
}
prefixed_tools