1use anyhow::Result;
2use serde_json::Value;
3
4use crate::migrations::migrate_settings;
5
6const AGENT_KEY: &str = "agent";
7const PROFILES_KEY: &str = "profiles";
8const SETTINGS_KEY: &str = "settings";
9const TOOL_PERMISSIONS_KEY: &str = "tool_permissions";
10const TOOLS_KEY: &str = "tools";
11const OLD_TOOL_NAME: &str = "web_search";
12const NEW_TOOL_NAME: &str = "search_web";
13
14pub fn rename_web_search_to_search_web(value: &mut Value) -> Result<()> {
15 migrate_settings(value, &mut migrate_one)
16}
17
18fn migrate_one(object: &mut serde_json::Map<String, Value>) -> Result<()> {
19 migrate_agent_value(object)?;
20
21 // Root-level profiles have a `settings` wrapper after m_2026_04_01,
22 // but `migrate_settings` calls us with the profile map directly,
23 // so we need to look inside `settings` too.
24 if let Some(settings) = object.get_mut(SETTINGS_KEY).and_then(|v| v.as_object_mut()) {
25 migrate_agent_value(settings)?;
26 }
27
28 Ok(())
29}
30
31fn migrate_agent_value(object: &mut serde_json::Map<String, Value>) -> Result<()> {
32 let Some(agent) = object.get_mut(AGENT_KEY).and_then(|v| v.as_object_mut()) else {
33 return Ok(());
34 };
35
36 if let Some(tools) = agent
37 .get_mut(TOOL_PERMISSIONS_KEY)
38 .and_then(|v| v.as_object_mut())
39 .and_then(|tp| tp.get_mut(TOOLS_KEY))
40 .and_then(|v| v.as_object_mut())
41 {
42 rename_key(tools);
43 }
44
45 if let Some(profiles) = agent.get_mut(PROFILES_KEY).and_then(|v| v.as_object_mut()) {
46 for (_profile_name, profile) in profiles.iter_mut() {
47 if let Some(tools) = profile
48 .as_object_mut()
49 .and_then(|p| p.get_mut(TOOLS_KEY))
50 .and_then(|v| v.as_object_mut())
51 {
52 rename_key(tools);
53 }
54 }
55 }
56
57 Ok(())
58}
59
60fn rename_key(tools: &mut serde_json::Map<String, Value>) {
61 if let Some(value) = tools.remove(OLD_TOOL_NAME) {
62 tools.insert(NEW_TOOL_NAME.to_string(), value);
63 }
64}