Consolidate tool registration into register_core_tools

All three provider profiles (Anthropic, OpenAI, Gemini) independently
registered the same 7 core tools (read_file, write_file, shell, grep,
glob, web_search, web_fetch). Extract a shared register_core_tools()
function in tools.rs that accepts a ToolRegistry, SessionConfig (for
shell timeout customization), and optional WebFetchSummarizer.

Each profile now calls register_core_tools() then adds its
profile-specific tools:
- Anthropic: edit_file (with 120s shell timeout via config)
- OpenAI: apply_patch (default 10s shell timeout)
- Gemini: edit_file, read_many_files, list_dir (default 10s shell timeout)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-02-27 23:40:49 -05:00
parent 96443e2508
commit 5c74f88814
5 changed files with 35 additions and 37 deletions

View file

@ -44,7 +44,7 @@ pub use subagent::{SubAgent, SubAgentEventCallback, SubAgentManager, SubAgentRes
pub use tool_registry::ToolRegistry;
pub use tools::{
make_edit_file_tool, make_glob_tool, make_grep_tool, make_read_file_tool, make_shell_tool,
make_shell_tool_with_config, make_write_file_tool, WebFetchSummarizer,
make_shell_tool_with_config, make_write_file_tool, register_core_tools, WebFetchSummarizer,
};
pub use truncation::{truncate_lines, truncate_output, truncate_tool_output, TruncationMode};
pub use types::{AgentEvent, SessionEvent, SessionState, Turn};

View file

@ -5,11 +5,7 @@ use crate::profiles::BaseProfile;
use crate::provider_profile::{ProfileCapabilities, ProviderProfile};
use crate::skills::Skill;
use crate::tool_registry::ToolRegistry;
use crate::tools::{
make_edit_file_tool, make_glob_tool, make_grep_tool, make_read_file_tool,
make_shell_tool_with_config, make_web_fetch_tool, make_web_search_tool, make_write_file_tool,
WebFetchSummarizer,
};
use crate::tools::{make_edit_file_tool, register_core_tools, WebFetchSummarizer};
use super::EnvContext;
@ -31,14 +27,8 @@ impl AnthropicProfile {
};
let mut registry = ToolRegistry::new();
registry.register(make_read_file_tool());
registry.register(make_write_file_tool());
register_core_tools(&mut registry, &config, summarizer);
registry.register(make_edit_file_tool());
registry.register(make_shell_tool_with_config(&config));
registry.register(make_grep_tool());
registry.register(make_glob_tool());
registry.register(make_web_search_tool());
registry.register(make_web_fetch_tool(summarizer));
Self {
base: BaseProfile {

View file

@ -1,3 +1,4 @@
use crate::config::SessionConfig;
use crate::execution_env::ExecutionEnvironment;
use crate::profiles::assemble_system_prompt;
use crate::profiles::BaseProfile;
@ -5,9 +6,8 @@ use crate::provider_profile::{ProfileCapabilities, ProviderProfile};
use crate::skills::Skill;
use crate::tool_registry::ToolRegistry;
use crate::tools::{
make_edit_file_tool, make_glob_tool, make_grep_tool, make_list_dir_tool,
make_read_file_tool, make_read_many_files_tool, make_shell_tool, make_web_fetch_tool,
make_web_search_tool, make_write_file_tool, WebFetchSummarizer,
make_edit_file_tool, make_list_dir_tool, make_read_many_files_tool, register_core_tools,
WebFetchSummarizer,
};
use super::EnvContext;
@ -24,18 +24,13 @@ impl GeminiProfile {
#[must_use]
pub fn with_summarizer(model: impl Into<String>, summarizer: Option<WebFetchSummarizer>) -> Self {
let config = SessionConfig::default();
let mut registry = ToolRegistry::new();
registry.register(make_read_file_tool());
registry.register(make_read_many_files_tool());
registry.register(make_write_file_tool());
register_core_tools(&mut registry, &config, summarizer);
registry.register(make_edit_file_tool());
registry.register(make_shell_tool());
registry.register(make_grep_tool());
registry.register(make_glob_tool());
registry.register(make_read_many_files_tool());
registry.register(make_list_dir_tool());
registry.register(make_web_search_tool());
registry.register(make_web_fetch_tool(summarizer));
Self {
base: BaseProfile {

View file

@ -1,13 +1,11 @@
use crate::config::SessionConfig;
use crate::execution_env::ExecutionEnvironment;
use crate::profiles::assemble_system_prompt;
use crate::profiles::BaseProfile;
use crate::provider_profile::{ProfileCapabilities, ProviderProfile};
use crate::skills::Skill;
use crate::tool_registry::ToolRegistry;
use crate::tools::{
make_glob_tool, make_grep_tool, make_read_file_tool, make_shell_tool, make_web_fetch_tool,
make_web_search_tool, make_write_file_tool, WebFetchSummarizer,
};
use crate::tools::{register_core_tools, WebFetchSummarizer};
use crate::v4a_patch::make_apply_patch_tool;
use super::EnvContext;
@ -25,16 +23,11 @@ impl OpenAiProfile {
#[must_use]
pub fn with_summarizer(model: impl Into<String>, summarizer: Option<WebFetchSummarizer>) -> Self {
let config = SessionConfig::default();
let mut registry = ToolRegistry::new();
registry.register(make_read_file_tool());
registry.register(make_write_file_tool());
registry.register(make_shell_tool());
registry.register(make_grep_tool());
registry.register(make_glob_tool());
register_core_tools(&mut registry, &config, summarizer);
registry.register(make_apply_patch_tool());
registry.register(make_web_search_tool());
registry.register(make_web_fetch_tool(summarizer));
Self {
base: BaseProfile {

View file

@ -35,6 +35,26 @@ fn html_to_markdown(text: &str) -> String {
converter.convert(text).unwrap_or_else(|_| text.to_string())
}
/// Registers the core tools shared by all provider profiles: `read_file`, `write_file`,
/// `shell`, `grep`, `glob`, `web_search`, and `web_fetch`.
///
/// The shell tool uses `config` to set its default and max timeouts. Pass a custom
/// `SessionConfig` (e.g. with a longer `default_command_timeout_ms`) for providers
/// that need non-default shell behavior.
pub fn register_core_tools(
registry: &mut crate::tool_registry::ToolRegistry,
config: &SessionConfig,
summarizer: Option<WebFetchSummarizer>,
) {
registry.register(make_read_file_tool());
registry.register(make_write_file_tool());
registry.register(make_shell_tool_with_config(config));
registry.register(make_grep_tool());
registry.register(make_glob_tool());
registry.register(make_web_search_tool());
registry.register(make_web_fetch_tool(summarizer));
}
pub(crate) fn required_str<'a>(args: &'a serde_json::Value, key: &str) -> Result<&'a str, String> {
args.get(key)
.and_then(|v| v.as_str())
@ -57,7 +77,7 @@ pub fn make_read_file_tool() -> RegisteredTool {
"required": ["file_path"]
}),
},
executor: Arc::new(|args, env, _cancel| {
executor: Arc::new(|args, ctx| {
Box::pin(async move {
let file_path = required_str(&args, "file_path")?;
let offset = args.get("offset").and_then(serde_json::Value::as_u64);
@ -66,7 +86,7 @@ pub fn make_read_file_tool() -> RegisteredTool {
let offset_usize = offset.map(|v| v as usize);
let limit_usize = limit.map(|v| v as usize);
let content = env.read_file(file_path, offset_usize, limit_usize).await?;
let content = ctx.env.read_file(file_path, offset_usize, limit_usize).await?;
Ok(content)
})
}),