[codex] Delete tool handler plan indirection (#21427)

## Why

The spec split in the parent PR still left an intermediate registry plan
that recorded `ToolHandlerKind` values and translated them into concrete
handlers later. That kept tool registration dependent on static enum
bookkeeping instead of registering handlers from the same code that
assembles their specs.

## What Changed

- Make `build_tool_registry_builder` register concrete handlers directly
while adding specs.
- Add small `ToolRegistryBuilder` helpers for spec augmentation and
nested code-mode inspection.
- Remove `ToolHandlerKind`, `ToolHandlerSpec`, and `ToolRegistryPlan`.
- Update spec-plan tests to assert against the built `ToolRegistry`
instead of static handler descriptors.

## Validation

- `cargo check -p codex-core`
- `cargo test -p codex-core tools::spec_plan::tests`
- `cargo test -p codex-core tools::spec::tests`
- `just fix -p codex-core`
This commit is contained in:
pakrym-oai
2026-05-06 20:36:24 -07:00
committed by GitHub
parent 5a4b2702f2
commit e394625ea2
5 changed files with 237 additions and 484 deletions

View File

@@ -522,10 +522,6 @@ impl ToolRegistryBuilder {
}
}
pub fn push_spec(&mut self, spec: ToolSpec) {
self.push_spec_with_parallel_support(spec, /*supports_parallel_tool_calls*/ false);
}
pub fn push_spec_with_parallel_support(
&mut self,
spec: ToolSpec,
@@ -535,6 +531,20 @@ impl ToolRegistryBuilder {
.push(ConfiguredToolSpec::new(spec, supports_parallel_tool_calls));
}
pub(crate) fn push_spec(
&mut self,
spec: ToolSpec,
supports_parallel_tool_calls: bool,
code_mode_enabled: bool,
) {
let spec = if code_mode_enabled {
codex_tools::augment_tool_spec_for_code_mode(spec)
} else {
spec
};
self.push_spec_with_parallel_support(spec, supports_parallel_tool_calls);
}
pub fn register_handler<H>(&mut self, handler: Arc<H>)
where
H: ToolHandler + 'static,
@@ -547,6 +557,10 @@ impl ToolRegistryBuilder {
}
}
pub(crate) fn specs(&self) -> &[ConfiguredToolSpec] {
&self.specs
}
pub fn build(self) -> (Vec<ConfiguredToolSpec>, ToolRegistry) {
let registry = ToolRegistry::new(self.handlers);
(self.specs, registry)

View File

@@ -1,18 +1,15 @@
use crate::shell::Shell;
use crate::shell::ShellType;
use crate::tools::handlers::agent_jobs::ReportAgentJobResultHandler;
use crate::tools::handlers::agent_jobs::SpawnAgentsOnCsvHandler;
use crate::tools::handlers::multi_agents_common::DEFAULT_WAIT_TIMEOUT_MS;
use crate::tools::handlers::multi_agents_common::MAX_WAIT_TIMEOUT_MS;
use crate::tools::handlers::multi_agents_common::MIN_WAIT_TIMEOUT_MS;
use crate::tools::handlers::multi_agents_spec::WaitAgentTimeoutOptions;
use crate::tools::registry::ToolRegistryBuilder;
use crate::tools::spec_plan::build_tool_registry_plan;
use crate::tools::spec_plan_types::ToolHandlerKind;
use crate::tools::spec_plan::build_tool_registry_builder;
use crate::tools::spec_plan_types::ToolNamespace;
use crate::tools::spec_plan_types::ToolRegistryPlanDeferredTool;
use crate::tools::spec_plan_types::ToolRegistryPlanMcpTool;
use crate::tools::spec_plan_types::ToolRegistryPlanParams;
use crate::tools::spec_plan_types::ToolRegistryBuildDeferredTool;
use crate::tools::spec_plan_types::ToolRegistryBuildMcpTool;
use crate::tools::spec_plan_types::ToolRegistryBuildParams;
use codex_mcp::ToolInfo;
use codex_protocol::dynamic_tools::DynamicToolSpec;
use codex_tools::AdditionalProperties;
@@ -22,7 +19,6 @@ use codex_tools::ResponsesApiTool;
use codex_tools::ToolName;
use codex_tools::ToolUserShellType;
use codex_tools::ToolsConfig;
use codex_tools::augment_tool_spec_for_code_mode;
use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::Arc;
@@ -38,7 +34,7 @@ pub(crate) fn tool_user_shell_type(user_shell: &Shell) -> ToolUserShellType {
}
struct McpToolPlanInputs<'a> {
mcp_tools: Vec<ToolRegistryPlanMcpTool<'a>>,
mcp_tools: Vec<ToolRegistryBuildMcpTool<'a>>,
tool_namespaces: HashMap<String, ToolNamespace>,
}
@@ -46,7 +42,7 @@ fn map_mcp_tools_for_plan(mcp_tools: &HashMap<String, ToolInfo>) -> McpToolPlanI
McpToolPlanInputs {
mcp_tools: mcp_tools
.values()
.map(|tool| ToolRegistryPlanMcpTool {
.map(|tool| ToolRegistryBuildMcpTool {
name: tool.canonical_tool_name(),
tool: &tool.tool,
})
@@ -74,51 +70,15 @@ pub(crate) fn build_specs_with_discoverable_tools(
discoverable_tools: Option<Vec<DiscoverableTool>>,
dynamic_tools: &[DynamicToolSpec],
) -> ToolRegistryBuilder {
use crate::tools::handlers::ApplyPatchHandler;
use crate::tools::handlers::CodeModeExecuteHandler;
use crate::tools::handlers::CodeModeWaitHandler;
use crate::tools::handlers::ContainerExecHandler;
use crate::tools::handlers::CreateGoalHandler;
use crate::tools::handlers::DynamicToolHandler;
use crate::tools::handlers::ExecCommandHandler;
use crate::tools::handlers::GetGoalHandler;
use crate::tools::handlers::ListMcpResourceTemplatesHandler;
use crate::tools::handlers::ListMcpResourcesHandler;
use crate::tools::handlers::LocalShellHandler;
use crate::tools::handlers::McpHandler;
use crate::tools::handlers::PlanHandler;
use crate::tools::handlers::ReadMcpResourceHandler;
use crate::tools::handlers::RequestPermissionsHandler;
use crate::tools::handlers::RequestPluginInstallHandler;
use crate::tools::handlers::RequestUserInputHandler;
use crate::tools::handlers::ShellCommandHandler;
use crate::tools::handlers::ShellHandler;
use crate::tools::handlers::TestSyncHandler;
use crate::tools::handlers::ToolSearchHandler;
use crate::tools::handlers::UnavailableToolHandler;
use crate::tools::handlers::UpdateGoalHandler;
use crate::tools::handlers::ViewImageHandler;
use crate::tools::handlers::WriteStdinHandler;
use crate::tools::handlers::multi_agents::CloseAgentHandler;
use crate::tools::handlers::multi_agents::ResumeAgentHandler;
use crate::tools::handlers::multi_agents::SendInputHandler;
use crate::tools::handlers::multi_agents::SpawnAgentHandler;
use crate::tools::handlers::multi_agents::WaitAgentHandler;
use crate::tools::handlers::multi_agents_v2::CloseAgentHandler as CloseAgentHandlerV2;
use crate::tools::handlers::multi_agents_v2::FollowupTaskHandler as FollowupTaskHandlerV2;
use crate::tools::handlers::multi_agents_v2::ListAgentsHandler as ListAgentsHandlerV2;
use crate::tools::handlers::multi_agents_v2::SendMessageHandler as SendMessageHandlerV2;
use crate::tools::handlers::multi_agents_v2::SpawnAgentHandler as SpawnAgentHandlerV2;
use crate::tools::handlers::multi_agents_v2::WaitAgentHandler as WaitAgentHandlerV2;
use crate::tools::handlers::unavailable_tool_message;
use crate::tools::tool_search_entry::build_tool_search_entries_for_config;
let mut builder = ToolRegistryBuilder::new();
let mcp_tool_plan_inputs = mcp_tools.as_ref().map(map_mcp_tools_for_plan);
let deferred_mcp_tool_sources = deferred_mcp_tools.as_ref().map(|tools| {
tools
.values()
.map(|tool| ToolRegistryPlanDeferredTool {
.map(|tool| ToolRegistryBuildDeferredTool {
name: tool.canonical_tool_name(),
server_name: tool.server_name.as_str(),
connector_name: tool.connector_name.as_deref(),
@@ -138,9 +98,19 @@ pub(crate) fn build_specs_with_discoverable_tools(
};
let default_wait_timeout_ms =
DEFAULT_WAIT_TIMEOUT_MS.clamp(min_wait_timeout_ms, MAX_WAIT_TIMEOUT_MS);
let plan = build_tool_registry_plan(
let deferred_dynamic_tools = dynamic_tools
.iter()
.filter(|tool| tool.defer_loading && (config.namespace_tools || tool.namespace.is_none()))
.cloned()
.collect::<Vec<_>>();
let tool_search_entries = build_tool_search_entries_for_config(
config,
ToolRegistryPlanParams {
deferred_mcp_tools.as_ref(),
&deferred_dynamic_tools,
);
let mut builder = build_tool_registry_builder(
config,
ToolRegistryBuildParams {
mcp_tools: mcp_tool_plan_inputs
.as_ref()
.map(|inputs| inputs.mcp_tools.as_slice()),
@@ -156,164 +126,15 @@ pub(crate) fn build_specs_with_discoverable_tools(
min_timeout_ms: min_wait_timeout_ms,
max_timeout_ms: MAX_WAIT_TIMEOUT_MS,
},
tool_search_entries: &tool_search_entries,
},
);
let deferred_dynamic_tools = dynamic_tools
.iter()
.filter(|tool| tool.defer_loading && (config.namespace_tools || tool.namespace.is_none()))
.cloned()
.collect::<Vec<_>>();
let mut existing_spec_names = plan
.specs
let mut existing_spec_names = builder
.specs()
.iter()
.map(|configured_tool| configured_tool.name().to_string())
.collect::<HashSet<_>>();
for spec in plan.specs {
if spec.supports_parallel_tool_calls {
builder.push_spec_with_parallel_support(
spec.spec, /*supports_parallel_tool_calls*/ true,
);
} else {
builder.push_spec(spec.spec);
}
}
for handler in plan.handlers {
let name = handler.name;
match handler.kind {
ToolHandlerKind::ApplyPatch => {
builder.register_handler(Arc::new(ApplyPatchHandler));
}
ToolHandlerKind::CloseAgentV1 => {
builder.register_handler(Arc::new(CloseAgentHandler));
}
ToolHandlerKind::CloseAgentV2 => {
builder.register_handler(Arc::new(CloseAgentHandlerV2));
}
ToolHandlerKind::CodeModeExecute => {
builder.register_handler(Arc::new(CodeModeExecuteHandler));
}
ToolHandlerKind::CodeModeWait => {
builder.register_handler(Arc::new(CodeModeWaitHandler));
}
ToolHandlerKind::ContainerExec => {
builder.register_handler(Arc::new(ContainerExecHandler));
}
ToolHandlerKind::CreateGoal => {
builder.register_handler(Arc::new(CreateGoalHandler));
}
ToolHandlerKind::DynamicTool => {
builder.register_handler(Arc::new(DynamicToolHandler::new(name)));
}
ToolHandlerKind::ExecCommand => {
builder.register_handler(Arc::new(ExecCommandHandler));
}
ToolHandlerKind::FollowupTaskV2 => {
builder.register_handler(Arc::new(FollowupTaskHandlerV2));
}
ToolHandlerKind::GetGoal => {
builder.register_handler(Arc::new(GetGoalHandler));
}
ToolHandlerKind::ListAgentsV2 => {
builder.register_handler(Arc::new(ListAgentsHandlerV2));
}
ToolHandlerKind::ListMcpResources => {
builder.register_handler(Arc::new(ListMcpResourcesHandler));
}
ToolHandlerKind::ListMcpResourceTemplates => {
builder.register_handler(Arc::new(ListMcpResourceTemplatesHandler));
}
ToolHandlerKind::LocalShell => {
builder.register_handler(Arc::new(LocalShellHandler));
}
ToolHandlerKind::Mcp => {
builder.register_handler(Arc::new(McpHandler::new(name)));
}
ToolHandlerKind::Plan => {
builder.register_handler(Arc::new(PlanHandler));
}
ToolHandlerKind::ReadMcpResource => {
builder.register_handler(Arc::new(ReadMcpResourceHandler));
}
ToolHandlerKind::ReportAgentJobResult => {
builder.register_handler(Arc::new(ReportAgentJobResultHandler));
}
ToolHandlerKind::RequestPermissions => {
builder.register_handler(Arc::new(RequestPermissionsHandler));
}
ToolHandlerKind::RequestUserInput => {
builder.register_handler(Arc::new(RequestUserInputHandler {
available_modes: config.request_user_input_available_modes.clone(),
}));
}
ToolHandlerKind::ResumeAgentV1 => {
builder.register_handler(Arc::new(ResumeAgentHandler));
}
ToolHandlerKind::SendInputV1 => {
builder.register_handler(Arc::new(SendInputHandler));
}
ToolHandlerKind::SendMessageV2 => {
builder.register_handler(Arc::new(SendMessageHandlerV2));
}
ToolHandlerKind::Shell => {
builder.register_handler(Arc::new(ShellHandler));
}
ToolHandlerKind::ShellCommand => {
builder.register_handler(Arc::new(ShellCommandHandler::from(
config.shell_command_backend,
)));
}
ToolHandlerKind::SpawnAgentsOnCsv => {
builder.register_handler(Arc::new(SpawnAgentsOnCsvHandler));
}
ToolHandlerKind::SpawnAgentV1 => {
builder.register_handler(Arc::new(SpawnAgentHandler));
}
ToolHandlerKind::SpawnAgentV2 => {
builder.register_handler(Arc::new(SpawnAgentHandlerV2));
}
ToolHandlerKind::TestSync => {
builder.register_handler(Arc::new(TestSyncHandler));
}
ToolHandlerKind::ToolSearch => {
let entries = build_tool_search_entries_for_config(
config,
deferred_mcp_tools.as_ref(),
&deferred_dynamic_tools,
);
builder.register_handler(Arc::new(ToolSearchHandler::new(entries)));
}
ToolHandlerKind::RequestPluginInstall => {
builder.register_handler(Arc::new(RequestPluginInstallHandler));
}
ToolHandlerKind::UpdateGoal => {
builder.register_handler(Arc::new(UpdateGoalHandler));
}
ToolHandlerKind::ViewImage => {
builder.register_handler(Arc::new(ViewImageHandler));
}
ToolHandlerKind::WaitAgentV1 => {
builder.register_handler(Arc::new(WaitAgentHandler));
}
ToolHandlerKind::WaitAgentV2 => {
builder.register_handler(Arc::new(WaitAgentHandlerV2));
}
ToolHandlerKind::WriteStdin => {
builder.register_handler(Arc::new(WriteStdinHandler));
}
}
}
if let Some(deferred_mcp_tools) = deferred_mcp_tools.as_ref() {
for (_, tool) in deferred_mcp_tools.iter().filter(|(name, _)| {
!mcp_tools
.as_ref()
.is_some_and(|tools| tools.contains_key(*name))
}) {
builder.register_handler(Arc::new(McpHandler::new(tool.canonical_tool_name())));
}
}
for unavailable_tool in unavailable_called_tools {
let tool_name = unavailable_tool.display();
if existing_spec_names.insert(tool_name.clone()) {
@@ -332,12 +153,11 @@ pub(crate) fn build_specs_with_discoverable_tools(
output_schema: None,
defer_loading: None,
});
let spec = if config.code_mode_enabled {
augment_tool_spec_for_code_mode(spec)
} else {
spec
};
builder.push_spec(spec);
builder.push_spec(
spec,
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
}
builder.register_handler(Arc::new(UnavailableToolHandler::new(unavailable_tool)));
}

View File

@@ -1,5 +1,31 @@
use crate::tools::code_mode::execute_spec::create_code_mode_tool;
use crate::tools::code_mode::wait_spec::create_wait_tool;
use crate::tools::handlers::ApplyPatchHandler;
use crate::tools::handlers::CodeModeExecuteHandler;
use crate::tools::handlers::CodeModeWaitHandler;
use crate::tools::handlers::ContainerExecHandler;
use crate::tools::handlers::CreateGoalHandler;
use crate::tools::handlers::DynamicToolHandler;
use crate::tools::handlers::ExecCommandHandler;
use crate::tools::handlers::GetGoalHandler;
use crate::tools::handlers::ListMcpResourceTemplatesHandler;
use crate::tools::handlers::ListMcpResourcesHandler;
use crate::tools::handlers::LocalShellHandler;
use crate::tools::handlers::McpHandler;
use crate::tools::handlers::PlanHandler;
use crate::tools::handlers::ReadMcpResourceHandler;
use crate::tools::handlers::RequestPermissionsHandler;
use crate::tools::handlers::RequestPluginInstallHandler;
use crate::tools::handlers::RequestUserInputHandler;
use crate::tools::handlers::ShellCommandHandler;
use crate::tools::handlers::ShellHandler;
use crate::tools::handlers::TestSyncHandler;
use crate::tools::handlers::ToolSearchHandler;
use crate::tools::handlers::UpdateGoalHandler;
use crate::tools::handlers::ViewImageHandler;
use crate::tools::handlers::WriteStdinHandler;
use crate::tools::handlers::agent_jobs::ReportAgentJobResultHandler;
use crate::tools::handlers::agent_jobs::SpawnAgentsOnCsvHandler;
use crate::tools::handlers::agent_jobs_spec::create_report_agent_job_result_tool;
use crate::tools::handlers::agent_jobs_spec::create_spawn_agents_on_csv_tool;
use crate::tools::handlers::apply_patch_spec::create_apply_patch_freeform_tool;
@@ -10,6 +36,11 @@ use crate::tools::handlers::goal_spec::create_update_goal_tool;
use crate::tools::handlers::mcp_resource_spec::create_list_mcp_resource_templates_tool;
use crate::tools::handlers::mcp_resource_spec::create_list_mcp_resources_tool;
use crate::tools::handlers::mcp_resource_spec::create_read_mcp_resource_tool;
use crate::tools::handlers::multi_agents::CloseAgentHandler;
use crate::tools::handlers::multi_agents::ResumeAgentHandler;
use crate::tools::handlers::multi_agents::SendInputHandler;
use crate::tools::handlers::multi_agents::SpawnAgentHandler;
use crate::tools::handlers::multi_agents::WaitAgentHandler;
use crate::tools::handlers::multi_agents_spec::SpawnAgentToolOptions;
use crate::tools::handlers::multi_agents_spec::create_close_agent_tool_v1;
use crate::tools::handlers::multi_agents_spec::create_close_agent_tool_v2;
@@ -22,9 +53,14 @@ use crate::tools::handlers::multi_agents_spec::create_spawn_agent_tool_v1;
use crate::tools::handlers::multi_agents_spec::create_spawn_agent_tool_v2;
use crate::tools::handlers::multi_agents_spec::create_wait_agent_tool_v1;
use crate::tools::handlers::multi_agents_spec::create_wait_agent_tool_v2;
use crate::tools::handlers::multi_agents_v2::CloseAgentHandler as CloseAgentHandlerV2;
use crate::tools::handlers::multi_agents_v2::FollowupTaskHandler as FollowupTaskHandlerV2;
use crate::tools::handlers::multi_agents_v2::ListAgentsHandler as ListAgentsHandlerV2;
use crate::tools::handlers::multi_agents_v2::SendMessageHandler as SendMessageHandlerV2;
use crate::tools::handlers::multi_agents_v2::SpawnAgentHandler as SpawnAgentHandlerV2;
use crate::tools::handlers::multi_agents_v2::WaitAgentHandler as WaitAgentHandlerV2;
use crate::tools::handlers::plan_spec::create_update_plan_tool;
use crate::tools::handlers::request_plugin_install_spec::create_request_plugin_install_tool;
use crate::tools::handlers::request_user_input_spec::REQUEST_USER_INPUT_TOOL_NAME;
use crate::tools::handlers::request_user_input_spec::create_request_user_input_tool;
use crate::tools::handlers::request_user_input_spec::request_user_input_tool_description;
use crate::tools::handlers::shell_spec::CommandToolOptions;
@@ -43,17 +79,14 @@ use crate::tools::handlers::view_image_spec::create_view_image_tool;
use crate::tools::hosted_spec::WebSearchToolOptions;
use crate::tools::hosted_spec::create_image_generation_tool;
use crate::tools::hosted_spec::create_web_search_tool;
use crate::tools::spec_plan_types::ToolHandlerKind;
use crate::tools::spec_plan_types::ToolRegistryPlan;
use crate::tools::spec_plan_types::ToolRegistryPlanParams;
use crate::tools::registry::ToolRegistryBuilder;
use crate::tools::spec_plan_types::ToolRegistryBuildParams;
use crate::tools::spec_plan_types::agent_type_description;
use codex_protocol::openai_models::ApplyPatchToolType;
use codex_protocol::openai_models::ConfigShellToolType;
use codex_tools::REQUEST_PLUGIN_INSTALL_TOOL_NAME;
use codex_tools::ResponsesApiNamespace;
use codex_tools::ResponsesApiNamespaceTool;
use codex_tools::TOOL_SEARCH_DEFAULT_LIMIT;
use codex_tools::TOOL_SEARCH_TOOL_NAME;
use codex_tools::ToolEnvironmentMode;
use codex_tools::ToolName;
use codex_tools::ToolSearchSource;
@@ -68,12 +101,13 @@ use codex_tools::default_namespace_description;
use codex_tools::dynamic_tool_to_loadable_tool_spec;
use codex_tools::mcp_tool_to_responses_api_tool;
use std::collections::BTreeMap;
use std::sync::Arc;
pub fn build_tool_registry_plan(
pub fn build_tool_registry_builder(
config: &ToolsConfig,
params: ToolRegistryPlanParams<'_>,
) -> ToolRegistryPlan {
let mut plan = ToolRegistryPlan::new();
params: ToolRegistryBuildParams<'_>,
) -> ToolRegistryBuilder {
let mut builder = ToolRegistryBuilder::new();
let exec_permission_approvals_enabled = config.exec_permission_approvals_enabled;
if config.code_mode_enabled {
@@ -92,22 +126,22 @@ pub fn build_tool_registry_plan(
})
.collect::<BTreeMap<_, _>>();
let nested_config = config.for_code_mode_nested_tools();
let nested_plan = build_tool_registry_plan(
let nested_builder = build_tool_registry_builder(
&nested_config,
ToolRegistryPlanParams {
ToolRegistryBuildParams {
discoverable_tools: None,
..params
},
);
let mut enabled_tools = collect_code_mode_exec_prompt_tool_definitions(
nested_plan
.specs
nested_builder
.specs()
.iter()
.map(|configured_tool| &configured_tool.spec),
);
enabled_tools
.sort_by(|left, right| compare_code_mode_tools(left, right, &namespace_descriptions));
plan.push_spec(
builder.push_spec(
create_code_mode_tool(
&enabled_tools,
&namespace_descriptions,
@@ -120,19 +154,13 @@ pub fn build_tool_registry_plan(
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler(
codex_code_mode::PUBLIC_TOOL_NAME,
ToolHandlerKind::CodeModeExecute,
);
plan.push_spec(
builder.register_handler(Arc::new(CodeModeExecuteHandler));
builder.push_spec(
create_wait_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler(
codex_code_mode::WAIT_TOOL_NAME,
ToolHandlerKind::CodeModeWait,
);
builder.register_handler(Arc::new(CodeModeWaitHandler));
}
if config.environment_mode.has_environment() {
@@ -140,7 +168,7 @@ pub fn build_tool_registry_plan(
matches!(config.environment_mode, ToolEnvironmentMode::Multiple);
match &config.shell_type {
ConfigShellToolType::Default => {
plan.push_spec(
builder.push_spec(
create_shell_tool(ShellToolOptions {
exec_permission_approvals_enabled,
}),
@@ -149,14 +177,14 @@ pub fn build_tool_registry_plan(
);
}
ConfigShellToolType::Local => {
plan.push_spec(
builder.push_spec(
create_local_shell_tool(),
/*supports_parallel_tool_calls*/ true,
config.code_mode_enabled,
);
}
ConfigShellToolType::UnifiedExec => {
plan.push_spec(
builder.push_spec(
create_exec_command_tool_with_environment_id(
CommandToolOptions {
allow_login_shell: config.allow_login_shell,
@@ -167,17 +195,17 @@ pub fn build_tool_registry_plan(
/*supports_parallel_tool_calls*/ true,
config.code_mode_enabled,
);
plan.push_spec(
builder.push_spec(
create_write_stdin_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler("exec_command", ToolHandlerKind::ExecCommand);
plan.register_handler("write_stdin", ToolHandlerKind::WriteStdin);
builder.register_handler(Arc::new(ExecCommandHandler));
builder.register_handler(Arc::new(WriteStdinHandler));
}
ConfigShellToolType::Disabled => {}
ConfigShellToolType::ShellCommand => {
plan.push_spec(
builder.push_spec(
create_shell_command_tool(CommandToolOptions {
allow_login_shell: config.allow_login_shell,
exec_permission_approvals_enabled,
@@ -192,82 +220,80 @@ pub fn build_tool_registry_plan(
if config.environment_mode.has_environment()
&& config.shell_type != ConfigShellToolType::Disabled
{
plan.register_handler("shell", ToolHandlerKind::Shell);
plan.register_handler("container.exec", ToolHandlerKind::ContainerExec);
plan.register_handler("local_shell", ToolHandlerKind::LocalShell);
plan.register_handler("shell_command", ToolHandlerKind::ShellCommand);
builder.register_handler(Arc::new(ShellHandler));
builder.register_handler(Arc::new(ContainerExecHandler));
builder.register_handler(Arc::new(LocalShellHandler));
builder.register_handler(Arc::new(ShellCommandHandler::from(
config.shell_command_backend,
)));
}
if params.mcp_tools.is_some() {
plan.push_spec(
builder.push_spec(
create_list_mcp_resources_tool(),
/*supports_parallel_tool_calls*/ true,
config.code_mode_enabled,
);
plan.push_spec(
builder.push_spec(
create_list_mcp_resource_templates_tool(),
/*supports_parallel_tool_calls*/ true,
config.code_mode_enabled,
);
plan.push_spec(
builder.push_spec(
create_read_mcp_resource_tool(),
/*supports_parallel_tool_calls*/ true,
config.code_mode_enabled,
);
plan.register_handler("list_mcp_resources", ToolHandlerKind::ListMcpResources);
plan.register_handler(
"list_mcp_resource_templates",
ToolHandlerKind::ListMcpResourceTemplates,
);
plan.register_handler("read_mcp_resource", ToolHandlerKind::ReadMcpResource);
builder.register_handler(Arc::new(ListMcpResourcesHandler));
builder.register_handler(Arc::new(ListMcpResourceTemplatesHandler));
builder.register_handler(Arc::new(ReadMcpResourceHandler));
}
plan.push_spec(
builder.push_spec(
create_update_plan_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler("update_plan", ToolHandlerKind::Plan);
builder.register_handler(Arc::new(PlanHandler));
if config.goal_tools {
plan.push_spec(
builder.push_spec(
create_get_goal_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler("get_goal", ToolHandlerKind::GetGoal);
plan.push_spec(
builder.register_handler(Arc::new(GetGoalHandler));
builder.push_spec(
create_create_goal_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler("create_goal", ToolHandlerKind::CreateGoal);
plan.push_spec(
builder.register_handler(Arc::new(CreateGoalHandler));
builder.push_spec(
create_update_goal_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler("update_goal", ToolHandlerKind::UpdateGoal);
builder.register_handler(Arc::new(UpdateGoalHandler));
}
plan.push_spec(
builder.push_spec(
create_request_user_input_tool(request_user_input_tool_description(
&config.request_user_input_available_modes,
)),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler(
REQUEST_USER_INPUT_TOOL_NAME,
ToolHandlerKind::RequestUserInput,
);
builder.register_handler(Arc::new(RequestUserInputHandler {
available_modes: config.request_user_input_available_modes.clone(),
}));
if config.request_permissions_tool_enabled {
plan.push_spec(
builder.push_spec(
create_request_permissions_tool(request_permissions_tool_description()),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler("request_permissions", ToolHandlerKind::RequestPermissions);
builder.register_handler(Arc::new(RequestPermissionsHandler));
}
let deferred_dynamic_tools = params
@@ -303,35 +329,28 @@ pub fn build_tool_registry_plan(
});
}
plan.push_spec(
builder.push_spec(
create_tool_search_tool(&search_source_infos, TOOL_SEARCH_DEFAULT_LIMIT),
/*supports_parallel_tool_calls*/ true,
config.code_mode_enabled,
);
plan.register_handler(TOOL_SEARCH_TOOL_NAME, ToolHandlerKind::ToolSearch);
if let Some(deferred_mcp_tools) = deferred_mcp_tools_for_search {
for tool in deferred_mcp_tools {
plan.register_handler(tool.name.clone(), ToolHandlerKind::Mcp);
}
}
builder.register_handler(Arc::new(ToolSearchHandler::new(
params.tool_search_entries.to_vec(),
)));
}
if config.tool_suggest
&& let Some(discoverable_tools) =
params.discoverable_tools.filter(|tools| !tools.is_empty())
{
plan.push_spec(
builder.push_spec(
create_request_plugin_install_tool(&collect_request_plugin_install_entries(
discoverable_tools,
)),
/*supports_parallel_tool_calls*/ true,
/*code_mode_enabled*/ false,
);
plan.register_handler(
REQUEST_PLUGIN_INSTALL_TOOL_NAME,
ToolHandlerKind::RequestPluginInstall,
);
builder.register_handler(Arc::new(RequestPluginInstallHandler));
}
if config.environment_mode.has_environment()
@@ -339,21 +358,21 @@ pub fn build_tool_registry_plan(
{
match apply_patch_tool_type {
ApplyPatchToolType::Freeform => {
plan.push_spec(
builder.push_spec(
create_apply_patch_freeform_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
}
ApplyPatchToolType::Function => {
plan.push_spec(
builder.push_spec(
create_apply_patch_json_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
}
}
plan.register_handler("apply_patch", ToolHandlerKind::ApplyPatch);
builder.register_handler(Arc::new(ApplyPatchHandler));
}
if config
@@ -361,12 +380,12 @@ pub fn build_tool_registry_plan(
.iter()
.any(|tool| tool == "test_sync_tool")
{
plan.push_spec(
builder.push_spec(
create_test_sync_tool(),
/*supports_parallel_tool_calls*/ true,
config.code_mode_enabled,
);
plan.register_handler("test_sync_tool", ToolHandlerKind::TestSync);
builder.register_handler(Arc::new(TestSyncHandler));
}
if let Some(web_search_tool) = create_web_search_tool(WebSearchToolOptions {
@@ -374,7 +393,7 @@ pub fn build_tool_registry_plan(
web_search_config: config.web_search_config.as_ref(),
web_search_tool_type: config.web_search_tool_type,
}) {
plan.push_spec(
builder.push_spec(
web_search_tool,
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
@@ -382,7 +401,7 @@ pub fn build_tool_registry_plan(
}
if config.image_gen_tool {
plan.push_spec(
builder.push_spec(
create_image_generation_tool("png"),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
@@ -390,21 +409,21 @@ pub fn build_tool_registry_plan(
}
if config.environment_mode.has_environment() {
plan.push_spec(
builder.push_spec(
create_view_image_tool(ViewImageToolOptions {
can_request_original_image_detail: config.can_request_original_image_detail,
}),
/*supports_parallel_tool_calls*/ true,
config.code_mode_enabled,
);
plan.register_handler("view_image", ToolHandlerKind::ViewImage);
builder.register_handler(Arc::new(ViewImageHandler));
}
if config.collab_tools {
if config.multi_agent_v2 {
let agent_type_description =
agent_type_description(config, params.default_agent_type_description);
plan.push_spec(
builder.push_spec(
create_spawn_agent_tool_v2(SpawnAgentToolOptions {
available_models: &config.available_models,
agent_type_description,
@@ -416,41 +435,41 @@ pub fn build_tool_registry_plan(
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.push_spec(
builder.push_spec(
create_send_message_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.push_spec(
builder.push_spec(
create_followup_task_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.push_spec(
builder.push_spec(
create_wait_agent_tool_v2(params.wait_agent_timeouts),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.push_spec(
builder.push_spec(
create_close_agent_tool_v2(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.push_spec(
builder.push_spec(
create_list_agents_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler("spawn_agent", ToolHandlerKind::SpawnAgentV2);
plan.register_handler("send_message", ToolHandlerKind::SendMessageV2);
plan.register_handler("followup_task", ToolHandlerKind::FollowupTaskV2);
plan.register_handler("wait_agent", ToolHandlerKind::WaitAgentV2);
plan.register_handler("close_agent", ToolHandlerKind::CloseAgentV2);
plan.register_handler("list_agents", ToolHandlerKind::ListAgentsV2);
builder.register_handler(Arc::new(SpawnAgentHandlerV2));
builder.register_handler(Arc::new(SendMessageHandlerV2));
builder.register_handler(Arc::new(FollowupTaskHandlerV2));
builder.register_handler(Arc::new(WaitAgentHandlerV2));
builder.register_handler(Arc::new(CloseAgentHandlerV2));
builder.register_handler(Arc::new(ListAgentsHandlerV2));
} else {
let agent_type_description =
agent_type_description(config, params.default_agent_type_description);
plan.push_spec(
builder.push_spec(
create_spawn_agent_tool_v1(SpawnAgentToolOptions {
available_models: &config.available_models,
agent_type_description,
@@ -462,51 +481,48 @@ pub fn build_tool_registry_plan(
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.push_spec(
builder.push_spec(
create_send_input_tool_v1(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.push_spec(
builder.push_spec(
create_resume_agent_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler("resume_agent", ToolHandlerKind::ResumeAgentV1);
plan.push_spec(
builder.register_handler(Arc::new(ResumeAgentHandler));
builder.push_spec(
create_wait_agent_tool_v1(params.wait_agent_timeouts),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.push_spec(
builder.push_spec(
create_close_agent_tool_v1(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler("spawn_agent", ToolHandlerKind::SpawnAgentV1);
plan.register_handler("send_input", ToolHandlerKind::SendInputV1);
plan.register_handler("wait_agent", ToolHandlerKind::WaitAgentV1);
plan.register_handler("close_agent", ToolHandlerKind::CloseAgentV1);
builder.register_handler(Arc::new(SpawnAgentHandler));
builder.register_handler(Arc::new(SendInputHandler));
builder.register_handler(Arc::new(WaitAgentHandler));
builder.register_handler(Arc::new(CloseAgentHandler));
}
}
if config.agent_jobs_tools {
plan.push_spec(
builder.push_spec(
create_spawn_agents_on_csv_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler("spawn_agents_on_csv", ToolHandlerKind::SpawnAgentsOnCsv);
builder.register_handler(Arc::new(SpawnAgentsOnCsvHandler));
if config.agent_jobs_worker_tools {
plan.push_spec(
builder.push_spec(
create_report_agent_job_result_tool(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
plan.register_handler(
"report_agent_job_result",
ToolHandlerKind::ReportAgentJobResult,
);
builder.register_handler(Arc::new(ReportAgentJobResultHandler));
}
}
@@ -548,7 +564,7 @@ pub fn build_tool_registry_plan(
match mcp_tool_to_responses_api_tool(&tool.name, tool.tool) {
Ok(converted_tool) => {
tools.push(ResponsesApiNamespaceTool::Function(converted_tool));
plan.register_handler(tool.name, ToolHandlerKind::Mcp);
builder.register_handler(Arc::new(McpHandler::new(tool.name)));
}
Err(error) => {
let tool_name = &tool.name;
@@ -559,8 +575,8 @@ pub fn build_tool_registry_plan(
}
}
if !tools.is_empty() {
plan.push_spec(
if config.namespace_tools && !tools.is_empty() {
builder.push_spec(
ToolSpec::Namespace(ResponsesApiNamespace {
name: namespace,
description,
@@ -579,7 +595,7 @@ pub fn build_tool_registry_plan(
Ok(loadable_tool) => {
let handler_name = ToolName::new(tool.namespace.clone(), tool.name.clone());
dynamic_tool_specs.push(loadable_tool);
plan.register_handler(handler_name, ToolHandlerKind::DynamicTool);
builder.register_handler(Arc::new(DynamicToolHandler::new(handler_name)));
}
Err(error) => {
tracing::error!(
@@ -590,19 +606,28 @@ pub fn build_tool_registry_plan(
}
}
for spec in coalesce_loadable_tool_specs(dynamic_tool_specs) {
plan.push_spec(
spec.into(),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
let spec = spec.into();
if config.namespace_tools || !matches!(spec, ToolSpec::Namespace(_)) {
builder.push_spec(
spec,
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
}
}
if !config.namespace_tools {
plan.specs
.retain(|configured_tool| !matches!(&configured_tool.spec, ToolSpec::Namespace(_)));
if let Some(deferred_mcp_tools) = params.deferred_mcp_tools {
for tool in deferred_mcp_tools {
let registered_directly = params
.mcp_tools
.is_some_and(|mcp_tools| mcp_tools.iter().any(|direct| direct.name == tool.name));
if !registered_directly {
builder.register_handler(Arc::new(McpHandler::new(tool.name.clone())));
}
}
}
plan
builder
}
fn compare_code_mode_tools(

View File

@@ -1,11 +1,12 @@
use super::*;
use crate::tools::handlers::multi_agents_spec::WaitAgentTimeoutOptions;
use crate::tools::handlers::request_user_input_spec::REQUEST_USER_INPUT_TOOL_NAME;
use crate::tools::handlers::shell_spec::CommandToolOptions;
use crate::tools::handlers::shell_spec::create_exec_command_tool;
use crate::tools::spec_plan_types::ToolHandlerSpec;
use crate::tools::registry::ToolRegistry;
use crate::tools::spec_plan_types::ToolNamespace;
use crate::tools::spec_plan_types::ToolRegistryPlanDeferredTool;
use crate::tools::spec_plan_types::ToolRegistryPlanMcpTool;
use crate::tools::spec_plan_types::ToolRegistryBuildDeferredTool;
use crate::tools::spec_plan_types::ToolRegistryBuildMcpTool;
use codex_app_server_protocol::AppInfo;
use codex_features::Feature;
use codex_features::Features;
@@ -29,10 +30,12 @@ use codex_tools::FreeformTool;
use codex_tools::JsonSchema;
use codex_tools::JsonSchemaPrimitiveType;
use codex_tools::JsonSchemaType;
use codex_tools::REQUEST_PLUGIN_INSTALL_TOOL_NAME;
use codex_tools::ResponsesApiNamespaceTool;
use codex_tools::ResponsesApiTool;
use codex_tools::ResponsesApiWebSearchFilters;
use codex_tools::ResponsesApiWebSearchUserLocation;
use codex_tools::TOOL_SEARCH_TOOL_NAME;
use codex_tools::ToolEnvironmentMode;
use codex_tools::ToolName;
use codex_tools::ToolsConfigParams;
@@ -1244,7 +1247,7 @@ fn namespace_specs_are_hidden_when_namespace_tools_are_disabled() {
});
tools_config.namespace_tools = false;
let (tools, handlers) = build_specs(
let (tools, registry) = build_specs(
&tools_config,
Some(HashMap::from([(
ToolName::namespaced("mcp__sample__", "echo"),
@@ -1255,10 +1258,7 @@ fn namespace_specs_are_hidden_when_namespace_tools_are_disabled() {
);
assert_lacks_tool_name(&tools, "mcp__sample__");
assert!(handlers.contains(&ToolHandlerSpec {
name: ToolName::namespaced("mcp__sample__", "echo"),
kind: ToolHandlerKind::Mcp,
}));
assert!(registry.has_handler(&ToolName::namespaced("mcp__sample__", "echo")));
}
#[test]
@@ -1412,7 +1412,7 @@ fn search_tool_description_lists_each_mcp_source_once() {
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, handlers) = build_specs(
let (tools, registry) = build_specs(
&tools_config,
Some(HashMap::from([
(
@@ -1477,14 +1477,11 @@ fn search_tool_description_lists_each_mcp_source_once() {
assert!(description.contains("- rmcp: Remote memory tools."));
assert!(!description.contains("mcp__rmcp__echo"));
assert!(handlers.contains(&ToolHandlerSpec {
name: ToolName::namespaced("mcp__codex_apps__calendar", "_create_event"),
kind: ToolHandlerKind::Mcp,
}));
assert!(handlers.contains(&ToolHandlerSpec {
name: ToolName::namespaced("mcp__rmcp__", "echo"),
kind: ToolHandlerKind::Mcp,
}));
assert!(registry.has_handler(&ToolName::namespaced(
"mcp__codex_apps__calendar",
"_create_event",
)));
assert!(registry.has_handler(&ToolName::namespaced("mcp__rmcp__", "echo")));
}
#[test]
@@ -1578,7 +1575,7 @@ fn search_tool_is_hidden_when_only_deferred_namespace_tools_are_available() {
});
tools_config.namespace_tools = false;
let (tools, handlers) = build_specs(
let (tools, registry) = build_specs(
&tools_config,
/*mcp_tools*/ None,
Some(vec![deferred_mcp_tool(
@@ -1592,10 +1589,7 @@ fn search_tool_is_hidden_when_only_deferred_namespace_tools_are_available() {
);
assert_lacks_tool_name(&tools, TOOL_SEARCH_TOOL_NAME);
assert!(!handlers.contains(&ToolHandlerSpec {
name: ToolName::plain(TOOL_SEARCH_TOOL_NAME),
kind: ToolHandlerKind::ToolSearch,
}));
assert!(!registry.has_handler(&ToolName::plain(TOOL_SEARCH_TOOL_NAME)));
}
#[test]
@@ -1639,7 +1633,7 @@ fn search_tool_registers_for_deferred_dynamic_tools() {
},
];
let (tools, handlers) = build_specs(
let (tools, registry) = build_specs(
&tools_config,
/*mcp_tools*/ None,
/*deferred_mcp_tools*/ None,
@@ -1670,18 +1664,9 @@ fn search_tool_registers_for_deferred_dynamic_tools() {
let dynamic_tool = find_namespace_function_tool(&tools, "codex_app", tool_name);
assert_eq!(dynamic_tool.defer_loading, Some(true));
}
assert!(handlers.contains(&ToolHandlerSpec {
name: ToolName::plain(TOOL_SEARCH_TOOL_NAME),
kind: ToolHandlerKind::ToolSearch,
}));
assert!(handlers.contains(&ToolHandlerSpec {
name: ToolName::namespaced("codex_app", "automation_update"),
kind: ToolHandlerKind::DynamicTool,
}));
assert!(handlers.contains(&ToolHandlerSpec {
name: ToolName::namespaced("codex_app", "automation_list"),
kind: ToolHandlerKind::DynamicTool,
}));
assert!(registry.has_handler(&ToolName::plain(TOOL_SEARCH_TOOL_NAME)));
assert!(registry.has_handler(&ToolName::namespaced("codex_app", "automation_update")));
assert!(registry.has_handler(&ToolName::namespaced("codex_app", "automation_list")));
}
#[test]
@@ -1718,7 +1703,7 @@ fn search_tool_keeps_plain_deferred_dynamic_tools_when_namespace_tools_are_disab
},
];
let (tools, handlers) = build_specs(
let (tools, registry) = build_specs(
&tools_config,
/*mcp_tools*/ None,
/*deferred_mcp_tools*/ None,
@@ -1727,10 +1712,7 @@ fn search_tool_keeps_plain_deferred_dynamic_tools_when_namespace_tools_are_disab
assert_contains_tool_names(&tools, &[TOOL_SEARCH_TOOL_NAME, "plain_dynamic"]);
assert_lacks_tool_name(&tools, "codex_app");
assert!(handlers.contains(&ToolHandlerSpec {
name: ToolName::plain(TOOL_SEARCH_TOOL_NAME),
kind: ToolHandlerKind::ToolSearch,
}));
assert!(registry.has_handler(&ToolName::plain(TOOL_SEARCH_TOOL_NAME)));
}
#[test]
@@ -1862,17 +1844,14 @@ fn request_plugin_install_description_lists_discoverable_tools() {
})),
];
let (tools, handlers) = build_specs_with_discoverable_tools(
let (tools, registry) = build_specs_with_discoverable_tools(
&tools_config,
/*mcp_tools*/ None,
/*deferred_mcp_tools*/ None,
Some(discoverable_tools),
&[],
);
assert!(handlers.contains(&ToolHandlerSpec {
name: ToolName::plain(REQUEST_PLUGIN_INSTALL_TOOL_NAME),
kind: ToolHandlerKind::RequestPluginInstall,
}));
assert!(registry.has_handler(&ToolName::plain(REQUEST_PLUGIN_INSTALL_TOOL_NAME)));
let request_plugin_install = find_tool(&tools, REQUEST_PLUGIN_INSTALL_TOOL_NAME);
let ToolSpec::Function(ResponsesApiTool {
@@ -2225,9 +2204,9 @@ fn search_capable_model_info() -> ModelInfo {
fn build_specs<'a>(
config: &ToolsConfig,
mcp_tools: Option<HashMap<ToolName, rmcp::model::Tool>>,
deferred_mcp_tools: Option<Vec<ToolRegistryPlanDeferredTool<'a>>>,
deferred_mcp_tools: Option<Vec<ToolRegistryBuildDeferredTool<'a>>>,
dynamic_tools: &[DynamicToolSpec],
) -> (Vec<ConfiguredToolSpec>, Vec<ToolHandlerSpec>) {
) -> (Vec<ConfiguredToolSpec>, ToolRegistry) {
build_specs_with_discoverable_tools(
config,
mcp_tools,
@@ -2240,10 +2219,10 @@ fn build_specs<'a>(
fn build_specs_with_discoverable_tools<'a>(
config: &ToolsConfig,
mcp_tools: Option<HashMap<ToolName, rmcp::model::Tool>>,
deferred_mcp_tools: Option<Vec<ToolRegistryPlanDeferredTool<'a>>>,
deferred_mcp_tools: Option<Vec<ToolRegistryBuildDeferredTool<'a>>>,
discoverable_tools: Option<Vec<DiscoverableTool>>,
dynamic_tools: &[DynamicToolSpec],
) -> (Vec<ConfiguredToolSpec>, Vec<ToolHandlerSpec>) {
) -> (Vec<ConfiguredToolSpec>, ToolRegistry) {
build_specs_with_optional_tool_namespaces(
config,
mcp_tools,
@@ -2257,23 +2236,23 @@ fn build_specs_with_discoverable_tools<'a>(
fn build_specs_with_optional_tool_namespaces<'a>(
config: &ToolsConfig,
mcp_tools: Option<HashMap<ToolName, rmcp::model::Tool>>,
deferred_mcp_tools: Option<Vec<ToolRegistryPlanDeferredTool<'a>>>,
deferred_mcp_tools: Option<Vec<ToolRegistryBuildDeferredTool<'a>>>,
tool_namespaces: Option<HashMap<String, ToolNamespace>>,
discoverable_tools: Option<Vec<DiscoverableTool>>,
dynamic_tools: &[DynamicToolSpec],
) -> (Vec<ConfiguredToolSpec>, Vec<ToolHandlerSpec>) {
) -> (Vec<ConfiguredToolSpec>, ToolRegistry) {
let mcp_tool_inputs = mcp_tools.as_ref().map(|mcp_tools| {
mcp_tools
.iter()
.map(|(name, tool)| ToolRegistryPlanMcpTool {
.map(|(name, tool)| ToolRegistryBuildMcpTool {
name: name.clone(),
tool,
})
.collect::<Vec<_>>()
});
let plan = build_tool_registry_plan(
let builder = build_tool_registry_builder(
config,
ToolRegistryPlanParams {
ToolRegistryBuildParams {
mcp_tools: mcp_tool_inputs.as_deref(),
deferred_mcp_tools: deferred_mcp_tools.as_deref(),
tool_namespaces: tool_namespaces.as_ref(),
@@ -2281,9 +2260,10 @@ fn build_specs_with_optional_tool_namespaces<'a>(
dynamic_tools,
default_agent_type_description: DEFAULT_AGENT_TYPE_DESCRIPTION,
wait_agent_timeouts: wait_agent_timeout_options(),
tool_search_entries: &[],
},
);
(plan.specs, plan.handlers)
builder.build()
}
fn mcp_tool(name: &str, description: &str, input_schema: serde_json::Value) -> rmcp::model::Tool {
@@ -2397,8 +2377,8 @@ fn deferred_mcp_tool<'a>(
server_name: &'a str,
connector_name: Option<&'a str>,
description: Option<&'a str>,
) -> ToolRegistryPlanDeferredTool<'a> {
ToolRegistryPlanDeferredTool {
) -> ToolRegistryBuildDeferredTool<'a> {
ToolRegistryBuildDeferredTool {
name: ToolName::namespaced(tool_namespace, tool_name),
server_name,
connector_name,

View File

@@ -1,75 +1,20 @@
use crate::tools::handlers::multi_agents_spec::WaitAgentTimeoutOptions;
use codex_protocol::dynamic_tools::DynamicToolSpec;
use codex_tools::ConfiguredToolSpec;
use codex_tools::DiscoverableTool;
use codex_tools::ToolName;
use codex_tools::ToolSpec;
use codex_tools::ToolsConfig;
use codex_tools::augment_tool_spec_for_code_mode;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ToolHandlerKind {
ApplyPatch,
CloseAgentV1,
CloseAgentV2,
CodeModeExecute,
CodeModeWait,
ContainerExec,
CreateGoal,
DynamicTool,
ExecCommand,
FollowupTaskV2,
GetGoal,
ListAgentsV2,
ListMcpResourceTemplates,
ListMcpResources,
LocalShell,
Mcp,
Plan,
ReadMcpResource,
ReportAgentJobResult,
RequestPluginInstall,
RequestPermissions,
RequestUserInput,
ResumeAgentV1,
SendInputV1,
SendMessageV2,
Shell,
ShellCommand,
SpawnAgentsOnCsv,
SpawnAgentV1,
SpawnAgentV2,
TestSync,
ToolSearch,
UpdateGoal,
ViewImage,
WaitAgentV1,
WaitAgentV2,
WriteStdin,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ToolHandlerSpec {
pub name: ToolName,
pub kind: ToolHandlerKind,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ToolRegistryPlan {
pub specs: Vec<ConfiguredToolSpec>,
pub handlers: Vec<ToolHandlerSpec>,
}
#[derive(Debug, Clone, Copy)]
pub struct ToolRegistryPlanParams<'a> {
pub mcp_tools: Option<&'a [ToolRegistryPlanMcpTool<'a>]>,
pub deferred_mcp_tools: Option<&'a [ToolRegistryPlanDeferredTool<'a>]>,
#[derive(Clone, Copy)]
pub struct ToolRegistryBuildParams<'a> {
pub mcp_tools: Option<&'a [ToolRegistryBuildMcpTool<'a>]>,
pub deferred_mcp_tools: Option<&'a [ToolRegistryBuildDeferredTool<'a>]>,
pub tool_namespaces: Option<&'a HashMap<String, ToolNamespace>>,
pub discoverable_tools: Option<&'a [DiscoverableTool]>,
pub dynamic_tools: &'a [DynamicToolSpec],
pub default_agent_type_description: &'a str,
pub wait_agent_timeouts: WaitAgentTimeoutOptions,
pub tool_search_entries: &'a [crate::tools::tool_search_entry::ToolSearchEntry],
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -82,50 +27,19 @@ pub struct ToolNamespace {
/// while registering its runtime handler with the canonical namespace/name
/// identity.
#[derive(Debug, Clone)]
pub struct ToolRegistryPlanMcpTool<'a> {
pub struct ToolRegistryBuildMcpTool<'a> {
pub name: ToolName,
pub tool: &'a rmcp::model::Tool,
}
#[derive(Debug, Clone)]
pub struct ToolRegistryPlanDeferredTool<'a> {
pub struct ToolRegistryBuildDeferredTool<'a> {
pub name: ToolName,
pub server_name: &'a str,
pub connector_name: Option<&'a str>,
pub description: Option<&'a str>,
}
impl ToolRegistryPlan {
pub(crate) fn new() -> Self {
Self {
specs: Vec::new(),
handlers: Vec::new(),
}
}
pub(crate) fn push_spec(
&mut self,
spec: ToolSpec,
supports_parallel_tool_calls: bool,
code_mode_enabled: bool,
) {
let spec = if code_mode_enabled {
augment_tool_spec_for_code_mode(spec)
} else {
spec
};
self.specs
.push(ConfiguredToolSpec::new(spec, supports_parallel_tool_calls));
}
pub(crate) fn register_handler(&mut self, name: impl Into<ToolName>, kind: ToolHandlerKind) {
self.handlers.push(ToolHandlerSpec {
name: name.into(),
kind,
});
}
}
pub(crate) fn agent_type_description(
config: &ToolsConfig,
default_agent_type_description: &str,