diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 13d036b128666700d45ae39013b2dd8f3da5abf9..9d809b8abdec27af96178f19b1586e5905c9f233 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -80,7 +80,7 @@ jobs: # If assets/ changed, add crates that depend on those assets if echo "$CHANGED_FILES" | grep -qP '^assets/'; then - FILE_CHANGED_PKGS=$(printf '%s\n%s\n%s\n%s' "$FILE_CHANGED_PKGS" "settings" "storybook" "assets" | sort -u) + FILE_CHANGED_PKGS=$(printf '%s\n%s\n%s' "$FILE_CHANGED_PKGS" "settings" "assets" | sort -u) fi # Combine all changed packages diff --git a/assets/settings/default.json b/assets/settings/default.json index 56dff8b2ad632ed74045887ea274cfa112140b4b..799a34d6a6f4dea367cc2c5cc4ce774ff0ad312e 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -1075,7 +1075,7 @@ "terminal": true, "thinking": true, "update_plan": true, - "web_search": true, + "search_web": true, }, }, "ask": { @@ -1095,7 +1095,7 @@ "spawn_agent": true, "thinking": true, "update_plan": true, - "web_search": true, + "search_web": true, }, }, "minimal": { diff --git a/crates/agent/src/tools/web_search_tool.rs b/crates/agent/src/tools/web_search_tool.rs index c697a5b78f1fe8c84d6ed58db13f651a493ae8c3..75d7689fd7c8e22a4daf45f96f5517f7888977a4 100644 --- a/crates/agent/src/tools/web_search_tool.rs +++ b/crates/agent/src/tools/web_search_tool.rs @@ -53,7 +53,7 @@ impl AgentTool for WebSearchTool { type Input = WebSearchToolInput; type Output = WebSearchToolOutput; - const NAME: &'static str = "web_search"; + const NAME: &'static str = "search_web"; fn kind() -> acp::ToolKind { acp::ToolKind::Fetch diff --git a/crates/migrator/src/migrations.rs b/crates/migrator/src/migrations.rs index 625bd27e91e117662f9a47edaaac2ddaa7d2ba1c..ed9c6ff51513b706a7eda93fafe59438feb90c59 100644 --- a/crates/migrator/src/migrations.rs +++ b/crates/migrator/src/migrations.rs @@ -328,3 +328,9 @@ pub(crate) mod m_2026_04_01 { pub(crate) use settings::restructure_profiles_with_settings_key; } + +pub(crate) mod m_2026_04_10 { + mod settings; + + pub(crate) use settings::rename_web_search_to_search_web; +} diff --git a/crates/migrator/src/migrations/m_2026_04_10/settings.rs b/crates/migrator/src/migrations/m_2026_04_10/settings.rs new file mode 100644 index 0000000000000000000000000000000000000000..5430523149480772b8070f734f5c03daac8505d2 --- /dev/null +++ b/crates/migrator/src/migrations/m_2026_04_10/settings.rs @@ -0,0 +1,64 @@ +use anyhow::Result; +use serde_json::Value; + +use crate::migrations::migrate_settings; + +const AGENT_KEY: &str = "agent"; +const PROFILES_KEY: &str = "profiles"; +const SETTINGS_KEY: &str = "settings"; +const TOOL_PERMISSIONS_KEY: &str = "tool_permissions"; +const TOOLS_KEY: &str = "tools"; +const OLD_TOOL_NAME: &str = "web_search"; +const NEW_TOOL_NAME: &str = "search_web"; + +pub fn rename_web_search_to_search_web(value: &mut Value) -> Result<()> { + migrate_settings(value, &mut migrate_one) +} + +fn migrate_one(object: &mut serde_json::Map) -> Result<()> { + migrate_agent_value(object)?; + + // Root-level profiles have a `settings` wrapper after m_2026_04_01, + // but `migrate_settings` calls us with the profile map directly, + // so we need to look inside `settings` too. + if let Some(settings) = object.get_mut(SETTINGS_KEY).and_then(|v| v.as_object_mut()) { + migrate_agent_value(settings)?; + } + + Ok(()) +} + +fn migrate_agent_value(object: &mut serde_json::Map) -> Result<()> { + let Some(agent) = object.get_mut(AGENT_KEY).and_then(|v| v.as_object_mut()) else { + return Ok(()); + }; + + if let Some(tools) = agent + .get_mut(TOOL_PERMISSIONS_KEY) + .and_then(|v| v.as_object_mut()) + .and_then(|tp| tp.get_mut(TOOLS_KEY)) + .and_then(|v| v.as_object_mut()) + { + rename_key(tools); + } + + if let Some(profiles) = agent.get_mut(PROFILES_KEY).and_then(|v| v.as_object_mut()) { + for (_profile_name, profile) in profiles.iter_mut() { + if let Some(tools) = profile + .as_object_mut() + .and_then(|p| p.get_mut(TOOLS_KEY)) + .and_then(|v| v.as_object_mut()) + { + rename_key(tools); + } + } + } + + Ok(()) +} + +fn rename_key(tools: &mut serde_json::Map) { + if let Some(value) = tools.remove(OLD_TOOL_NAME) { + tools.insert(NEW_TOOL_NAME.to_string(), value); + } +} diff --git a/crates/migrator/src/migrator.rs b/crates/migrator/src/migrator.rs index f49d102213c446be17c7d240d272cf4b516d912c..4a9873f9b574d9194052156cbdb685f93bccfcb4 100644 --- a/crates/migrator/src/migrator.rs +++ b/crates/migrator/src/migrator.rs @@ -249,6 +249,7 @@ pub fn migrate_settings(text: &str) -> Result> { ), MigrationType::Json(migrations::m_2026_03_30::make_play_sound_when_agent_done_an_enum), MigrationType::Json(migrations::m_2026_04_01::restructure_profiles_with_settings_key), + MigrationType::Json(migrations::m_2026_04_10::rename_web_search_to_search_web), ]; run_migrations(text, migrations) } @@ -4682,4 +4683,301 @@ mod tests { None, ); } + + #[test] + fn test_rename_web_search_to_search_web_in_tool_permissions() { + assert_migrate_with_migrations( + &[MigrationType::Json( + migrations::m_2026_04_10::rename_web_search_to_search_web, + )], + &r#" + { + "agent": { + "tool_permissions": { + "tools": { + "web_search": { + "allow": true + } + } + } + } + } + "# + .unindent(), + Some( + &r#" + { + "agent": { + "tool_permissions": { + "tools": { + "search_web": { + "allow": true + } + } + } + } + } + "# + .unindent(), + ), + ); + } + + #[test] + fn test_rename_web_search_to_search_web_in_profiles() { + assert_migrate_with_migrations( + &[MigrationType::Json( + migrations::m_2026_04_10::rename_web_search_to_search_web, + )], + &r#" + { + "agent": { + "profiles": { + "write": { + "tools": { + "web_search": false + } + } + } + } + } + "# + .unindent(), + Some( + &r#" + { + "agent": { + "profiles": { + "write": { + "tools": { + "search_web": false + } + } + } + } + } + "# + .unindent(), + ), + ); + } + + #[test] + fn test_rename_web_search_to_search_web_no_change_when_already_migrated() { + assert_migrate_with_migrations( + &[MigrationType::Json( + migrations::m_2026_04_10::rename_web_search_to_search_web, + )], + &r#" + { + "agent": { + "tool_permissions": { + "tools": { + "search_web": { + "allow": true + } + } + } + } + } + "# + .unindent(), + None, + ); + } + + #[test] + fn test_rename_web_search_to_search_web_no_clobber() { + assert_migrate_with_migrations( + &[MigrationType::Json( + migrations::m_2026_04_10::rename_web_search_to_search_web, + )], + &r#" + { + "agent": { + "tool_permissions": { + "tools": { + "web_search": { + "allow": false + }, + "search_web": { + "allow": true + } + } + } + } + } + "# + .unindent(), + Some( + &r#" + { + "agent": { + "tool_permissions": { + "tools": { + "search_web": { + "allow": false + } + } + } + } + } + "# + .unindent(), + ), + ); + } + + #[test] + fn test_rename_web_search_to_search_web_platform_override() { + assert_migrate_with_migrations( + &[MigrationType::Json( + migrations::m_2026_04_10::rename_web_search_to_search_web, + )], + &r#" + { + "linux": { + "agent": { + "tool_permissions": { + "tools": { + "web_search": { + "allow": true + } + } + } + } + } + } + "# + .unindent(), + Some( + &r#" + { + "linux": { + "agent": { + "tool_permissions": { + "tools": { + "search_web": { + "allow": true + } + } + } + } + } + } + "# + .unindent(), + ), + ); + } + + #[test] + fn test_rename_web_search_to_search_web_release_channel_override() { + assert_migrate_with_migrations( + &[MigrationType::Json( + migrations::m_2026_04_10::rename_web_search_to_search_web, + )], + &r#" + { + "nightly": { + "agent": { + "tool_permissions": { + "tools": { + "web_search": { + "default": "allow" + } + } + } + } + } + } + "# + .unindent(), + Some( + &r#" + { + "nightly": { + "agent": { + "tool_permissions": { + "tools": { + "search_web": { + "default": "allow" + } + } + } + } + } + } + "# + .unindent(), + ), + ); + } + + #[test] + fn test_rename_web_search_to_search_web_no_agent() { + assert_migrate_with_migrations( + &[MigrationType::Json( + migrations::m_2026_04_10::rename_web_search_to_search_web, + )], + &r#" + { + "buffer_font_size": 14 + } + "# + .unindent(), + None, + ); + } + + #[test] + fn test_rename_web_search_to_search_web_root_level_profile() { + assert_migrate_with_migrations( + &[MigrationType::Json( + migrations::m_2026_04_10::rename_web_search_to_search_web, + )], + &r#" + { + "profiles": { + "Work": { + "settings": { + "agent": { + "tool_permissions": { + "tools": { + "web_search": { + "default": "allow" + } + } + } + } + } + } + } + } + "# + .unindent(), + Some( + &r#" + { + "profiles": { + "Work": { + "settings": { + "agent": { + "tool_permissions": { + "tools": { + "search_web": { + "default": "allow" + } + } + } + } + } + } + } + } + "# + .unindent(), + ), + ); + } } diff --git a/crates/settings_ui/src/pages/tool_permissions_setup.rs b/crates/settings_ui/src/pages/tool_permissions_setup.rs index 61d6c8c6f4cb09246bc3b6ee11e87e065ed52b3a..bbfcd1849dd561764a031a95cdc28fadfdeab87e 100644 --- a/crates/settings_ui/src/pages/tool_permissions_setup.rs +++ b/crates/settings_ui/src/pages/tool_permissions_setup.rs @@ -69,7 +69,7 @@ const TOOLS: &[ToolInfo] = &[ regex_explanation: "Patterns are matched against the URL being fetched.", }, ToolInfo { - id: "web_search", + id: "search_web", name: "Web Search", description: "Web search queries", regex_explanation: "Patterns are matched against the search query.", @@ -309,7 +309,7 @@ fn get_tool_render_fn( "create_directory" => render_create_directory_tool_config, "save_file" => render_save_file_tool_config, "fetch" => render_fetch_tool_config, - "web_search" => render_web_search_tool_config, + "search_web" => render_web_search_tool_config, "restore_file_from_disk" => render_restore_file_from_disk_tool_config, _ => render_terminal_tool_config, // fallback } @@ -1389,7 +1389,7 @@ tool_config_page_fn!(render_move_path_tool_config, "move_path"); tool_config_page_fn!(render_create_directory_tool_config, "create_directory"); tool_config_page_fn!(render_save_file_tool_config, "save_file"); tool_config_page_fn!(render_fetch_tool_config, "fetch"); -tool_config_page_fn!(render_web_search_tool_config, "web_search"); +tool_config_page_fn!(render_web_search_tool_config, "search_web"); tool_config_page_fn!( render_restore_file_from_disk_tool_config, "restore_file_from_disk" diff --git a/docs/.doc-examples/reference.md b/docs/.doc-examples/reference.md index ce774e02c8e3fac0914388c891898841209c13e0..5b250fc0047ba38d373bd39baf945f02addb18f5 100644 --- a/docs/.doc-examples/reference.md +++ b/docs/.doc-examples/reference.md @@ -68,7 +68,7 @@ Reads the content of a specified file in the project, allowing access to file co Allows the Agent to work through problems, brainstorm ideas, or plan without executing actions, useful for complex problem-solving. -### `web_search` {#web-search} +### `search_web` {#search-web} Searches the web for information, providing results with snippets and links from relevant web pages, useful for accessing real-time information. diff --git a/docs/src/ai/tool-permissions.md b/docs/src/ai/tool-permissions.md index 27ee114e343366ab2700580fa92eea010f40966b..61d599a9d1e04074420e7587428f4b6c8de20e0b 100644 --- a/docs/src/ai/tool-permissions.md +++ b/docs/src/ai/tool-permissions.md @@ -54,7 +54,7 @@ The `tool_permissions` setting lets you customize tool permissions by specifying | `restore_file_from_disk` | The file paths | | `save_file` | The file paths | | `fetch` | The URL | -| `web_search` | The search query | +| `search_web` | The search query | For MCP tools, use the format `mcp::`. For example, a tool called `create_issue` on a server called `github` would be `mcp:github:create_issue`. diff --git a/docs/src/ai/tools.md b/docs/src/ai/tools.md index bc57f3c378fbc03429fe84993c349b0a5b3ce0d0..3ada0ce025976a35250c6960745ef69ac0d2c5db 100644 --- a/docs/src/ai/tools.md +++ b/docs/src/ai/tools.md @@ -57,7 +57,7 @@ Reads the content of a specified file in the project, allowing access to file co Allows the Agent to work through problems, brainstorm ideas, or plan without executing actions, useful for complex problem-solving. -### `web_search` +### `search_web` Searches the web for information, providing results with snippets and links from relevant web pages, useful for accessing real-time information. diff --git a/tooling/xtask/src/tasks/workflows/run_tests.rs b/tooling/xtask/src/tasks/workflows/run_tests.rs index 65b44123c7b76f49e7c349318aab1bc2fb856c1f..8f2db5f03727fc1d30d93e22a683483bdc241855 100644 --- a/tooling/xtask/src/tasks/workflows/run_tests.rs +++ b/tooling/xtask/src/tasks/workflows/run_tests.rs @@ -202,7 +202,7 @@ fn orchestrate_impl(rules: &[&PathCondition], target: OrchestrateTarget) -> Name # If assets/ changed, add crates that depend on those assets if echo "$CHANGED_FILES" | grep -qP '^assets/'; then - FILE_CHANGED_PKGS=$(printf '%s\n%s\n%s\n%s' "$FILE_CHANGED_PKGS" "settings" "storybook" "assets" | sort -u) + FILE_CHANGED_PKGS=$(printf '%s\n%s\n%s' "$FILE_CHANGED_PKGS" "settings" "assets" | sort -u) fi # Combine all changed packages