From e26f0a331f392d7ae12988d3744dc1a11bc8cb02 Mon Sep 17 00:00:00 2001 From: Bennet Bo Fenner Date: Tue, 15 Apr 2025 06:42:31 -0600 Subject: [PATCH] agent: Make `ToolWorkingSet` an `Entity` (#28757) Motivation is to emit events when enabled tools change, want to use this in #28755 Release Notes: - N/A --- crates/agent/src/agent_diff.rs | 3 +- crates/agent/src/assistant_configuration.rs | 6 +- .../manage_profiles_modal.rs | 4 +- .../assistant_configuration/tool_picker.rs | 6 +- crates/agent/src/assistant_panel.rs | 2 +- crates/agent/src/profile_selector.rs | 2 +- crates/agent/src/thread.rs | 16 +- crates/agent/src/thread_store.rs | 131 ++++++------ crates/agent/src/tool_use.rs | 22 +- crates/assistant_tool/src/tool_working_set.rs | 189 +++++++----------- crates/eval/src/example.rs | 4 +- 11 files changed, 183 insertions(+), 202 deletions(-) diff --git a/crates/agent/src/agent_diff.rs b/crates/agent/src/agent_diff.rs index 13f2f991fb..8fdcbbcb58 100644 --- a/crates/agent/src/agent_diff.rs +++ b/crates/agent/src/agent_diff.rs @@ -894,6 +894,7 @@ mod tests { use super::*; use crate::{ThreadStore, thread_store}; use assistant_settings::AssistantSettings; + use assistant_tool::ToolWorkingSet; use context_server::ContextServerSettings; use editor::EditorSettings; use gpui::TestAppContext; @@ -937,7 +938,7 @@ mod tests { .update(|cx| { ThreadStore::load( project.clone(), - Arc::default(), + cx.new(|_| ToolWorkingSet::default()), Arc::new(PromptBuilder::new(None).unwrap()), cx, ) diff --git a/crates/agent/src/assistant_configuration.rs b/crates/agent/src/assistant_configuration.rs index f464abe1b0..7616b1f8b0 100644 --- a/crates/agent/src/assistant_configuration.rs +++ b/crates/agent/src/assistant_configuration.rs @@ -29,7 +29,7 @@ pub struct AssistantConfiguration { configuration_views_by_provider: HashMap, context_server_manager: Entity, expanded_context_server_tools: HashMap, bool>, - tools: Arc, + tools: Entity, _registry_subscription: Subscription, } @@ -37,7 +37,7 @@ impl AssistantConfiguration { pub fn new( fs: Arc, context_server_manager: Entity, - tools: Arc, + tools: Entity, window: &mut Window, cx: &mut Context, ) -> Self { @@ -226,7 +226,7 @@ impl AssistantConfiguration { fn render_context_servers_section(&mut self, cx: &mut Context) -> impl IntoElement { let context_servers = self.context_server_manager.read(cx).all_servers().clone(); - let tools_by_source = self.tools.tools_by_source(cx); + let tools_by_source = self.tools.read(cx).tools_by_source(cx); let empty = Vec::new(); const SUBHEADING: &str = "Connect to context servers via the Model Context Protocol either via Zed extensions or directly."; diff --git a/crates/agent/src/assistant_configuration/manage_profiles_modal.rs b/crates/agent/src/assistant_configuration/manage_profiles_modal.rs index a4c72cbac9..6f5172a8d4 100644 --- a/crates/agent/src/assistant_configuration/manage_profiles_modal.rs +++ b/crates/agent/src/assistant_configuration/manage_profiles_modal.rs @@ -84,7 +84,7 @@ pub struct NewProfileMode { pub struct ManageProfilesModal { fs: Arc, - tools: Arc, + tools: Entity, thread_store: WeakEntity, focus_handle: FocusHandle, mode: Mode, @@ -117,7 +117,7 @@ impl ManageProfilesModal { pub fn new( fs: Arc, - tools: Arc, + tools: Entity, thread_store: WeakEntity, window: &mut Window, cx: &mut Context, diff --git a/crates/agent/src/assistant_configuration/tool_picker.rs b/crates/agent/src/assistant_configuration/tool_picker.rs index eabd9e172b..2b105e87a2 100644 --- a/crates/agent/src/assistant_configuration/tool_picker.rs +++ b/crates/agent/src/assistant_configuration/tool_picker.rs @@ -60,7 +60,7 @@ pub struct ToolPickerDelegate { impl ToolPickerDelegate { pub fn new( fs: Arc, - tool_set: Arc, + tool_set: Entity, thread_store: WeakEntity, profile_id: AgentProfileId, profile: AgentProfile, @@ -68,7 +68,7 @@ impl ToolPickerDelegate { ) -> Self { let mut tool_entries = Vec::new(); - for (source, tools) in tool_set.tools_by_source(cx) { + for (source, tools) in tool_set.read(cx).tools_by_source(cx) { tool_entries.extend(tools.into_iter().map(|tool| ToolEntry { name: tool.name().into(), source: source.clone(), @@ -192,7 +192,7 @@ impl PickerDelegate for ToolPickerDelegate { if active_profile_id == &self.profile_id { self.thread_store .update(cx, |this, cx| { - this.load_profile(&self.profile, cx); + this.load_profile(self.profile.clone(), cx); }) .log_err(); } diff --git a/crates/agent/src/assistant_panel.rs b/crates/agent/src/assistant_panel.rs index b5dcee9dc2..fa953d93a8 100644 --- a/crates/agent/src/assistant_panel.rs +++ b/crates/agent/src/assistant_panel.rs @@ -203,7 +203,7 @@ impl AssistantPanel { cx: AsyncWindowContext, ) -> Task>> { cx.spawn(async move |cx| { - let tools = Arc::new(ToolWorkingSet::default()); + let tools = cx.new(|_| ToolWorkingSet::default())?; let thread_store = workspace .update(cx, |workspace, cx| { let project = workspace.project().clone(); diff --git a/crates/agent/src/profile_selector.rs b/crates/agent/src/profile_selector.rs index dfcafba5fc..c033bf9c58 100644 --- a/crates/agent/src/profile_selector.rs +++ b/crates/agent/src/profile_selector.rs @@ -86,7 +86,7 @@ impl ProfileSelector { thread_store .update(cx, |this, cx| { - this.load_profile_by_id(&profile_id, cx); + this.load_profile_by_id(profile_id.clone(), cx); }) .log_err(); } diff --git a/crates/agent/src/thread.rs b/crates/agent/src/thread.rs index fe4844bd86..ada5c068a7 100644 --- a/crates/agent/src/thread.rs +++ b/crates/agent/src/thread.rs @@ -254,7 +254,7 @@ pub struct Thread { pending_completions: Vec, project: Entity, prompt_builder: Arc, - tools: Arc, + tools: Entity, tool_use: ToolUseState, action_log: Entity, last_restore_checkpoint: Option, @@ -278,7 +278,7 @@ pub struct ExceededWindowError { impl Thread { pub fn new( project: Entity, - tools: Arc, + tools: Entity, prompt_builder: Arc, system_prompt: SharedProjectContext, cx: &mut Context, @@ -322,7 +322,7 @@ impl Thread { id: ThreadId, serialized: SerializedThread, project: Entity, - tools: Arc, + tools: Entity, prompt_builder: Arc, project_context: SharedProjectContext, cx: &mut Context, @@ -458,7 +458,7 @@ impl Thread { !self.pending_completions.is_empty() || !self.all_tools_finished() } - pub fn tools(&self) -> &Arc { + pub fn tools(&self) -> &Entity { &self.tools } @@ -846,6 +846,7 @@ impl Thread { let mut tools = Vec::new(); tools.extend( self.tools() + .read(cx) .enabled_tools(cx) .into_iter() .filter_map(|tool| { @@ -1354,7 +1355,7 @@ impl Thread { .collect::>(); for tool_use in pending_tool_uses.iter() { - if let Some(tool) = self.tools.tool(&tool_use.name, cx) { + if let Some(tool) = self.tools.read(cx).tool(&tool_use.name, cx) { if tool.needs_confirmation(&tool_use.input, cx) && !AssistantSettings::get_global(cx).always_allow_tool_actions { @@ -1406,7 +1407,7 @@ impl Thread { ) -> Task<()> { let tool_name: Arc = tool.name().into(); - let run_tool = if self.tools.is_disabled(&tool.source(), &tool_name) { + let run_tool = if self.tools.read(cx).is_disabled(&tool.source(), &tool_name) { Task::ready(Err(anyhow!("tool is disabled: {tool_name}"))) } else { tool.run( @@ -1521,6 +1522,7 @@ impl Thread { let enabled_tool_names: Vec = self .tools() + .read(cx) .enabled_tools(cx) .iter() .map(|tool| tool.name().to_string()) @@ -2341,7 +2343,7 @@ fn main() {{ .update(|_, cx| { ThreadStore::load( project.clone(), - Arc::default(), + cx.new(|_| ToolWorkingSet::default()), Arc::new(PromptBuilder::new(None).unwrap()), cx, ) diff --git a/crates/agent/src/thread_store.rs b/crates/agent/src/thread_store.rs index ebb673a86f..6fb0f6c7a2 100644 --- a/crates/agent/src/thread_store.rs +++ b/crates/agent/src/thread_store.rs @@ -56,7 +56,7 @@ impl SharedProjectContext { pub struct ThreadStore { project: Entity, - tools: Arc, + tools: Entity, prompt_builder: Arc, context_server_manager: Entity, context_server_tool_ids: HashMap, Vec>, @@ -74,7 +74,7 @@ impl EventEmitter for ThreadStore {} impl ThreadStore { pub fn load( project: Entity, - tools: Arc, + tools: Entity, prompt_builder: Arc, cx: &mut App, ) -> Task> { @@ -88,7 +88,7 @@ impl ThreadStore { fn new( project: Entity, - tools: Arc, + tools: Entity, prompt_builder: Arc, cx: &mut Context, ) -> Self { @@ -248,7 +248,7 @@ impl ThreadStore { self.context_server_manager.clone() } - pub fn tools(&self) -> Arc { + pub fn tools(&self) -> Entity { self.tools.clone() } @@ -355,52 +355,60 @@ impl ThreadStore { }) } - fn load_default_profile(&self, cx: &Context) { + fn load_default_profile(&self, cx: &mut Context) { let assistant_settings = AssistantSettings::get_global(cx); - self.load_profile_by_id(&assistant_settings.default_profile, cx); + self.load_profile_by_id(assistant_settings.default_profile.clone(), cx); } - pub fn load_profile_by_id(&self, profile_id: &AgentProfileId, cx: &Context) { + pub fn load_profile_by_id(&self, profile_id: AgentProfileId, cx: &mut Context) { let assistant_settings = AssistantSettings::get_global(cx); - if let Some(profile) = assistant_settings.profiles.get(profile_id) { - self.load_profile(profile, cx); + if let Some(profile) = assistant_settings.profiles.get(&profile_id) { + self.load_profile(profile.clone(), cx); } } - pub fn load_profile(&self, profile: &AgentProfile, cx: &Context) { - self.tools.disable_all_tools(); - self.tools.enable( - ToolSource::Native, - &profile - .tools - .iter() - .filter_map(|(tool, enabled)| enabled.then(|| tool.clone())) - .collect::>(), - ); + pub fn load_profile(&self, profile: AgentProfile, cx: &mut Context) { + self.tools.update(cx, |tools, cx| { + tools.disable_all_tools(cx); + tools.enable( + ToolSource::Native, + &profile + .tools + .iter() + .filter_map(|(tool, enabled)| enabled.then(|| tool.clone())) + .collect::>(), + cx, + ); + }); if profile.enable_all_context_servers { for context_server in self.context_server_manager.read(cx).all_servers() { - self.tools.enable_source( - ToolSource::ContextServer { - id: context_server.id().into(), - }, - cx, - ); + self.tools.update(cx, |tools, cx| { + tools.enable_source( + ToolSource::ContextServer { + id: context_server.id().into(), + }, + cx, + ); + }); } } else { for (context_server_id, preset) in &profile.context_servers { - self.tools.enable( - ToolSource::ContextServer { - id: context_server_id.clone().into(), - }, - &preset - .tools - .iter() - .filter_map(|(tool, enabled)| enabled.then(|| tool.clone())) - .collect::>(), - ) + self.tools.update(cx, |tools, cx| { + tools.enable( + ToolSource::ContextServer { + id: context_server_id.clone().into(), + }, + &preset + .tools + .iter() + .filter_map(|(tool, enabled)| enabled.then(|| tool.clone())) + .collect::>(), + cx, + ) + }) } } } @@ -434,29 +442,36 @@ impl ThreadStore { if protocol.capable(context_server::protocol::ServerCapability::Tools) { if let Some(tools) = protocol.list_tools().await.log_err() { - let tool_ids = tools - .tools - .into_iter() - .map(|tool| { - log::info!( - "registering context server tool: {:?}", - tool.name - ); - tool_working_set.insert(Arc::new( - ContextServerTool::new( - context_server_manager.clone(), - server.id(), - tool, - ), - )) + let tool_ids = tool_working_set + .update(cx, |tool_working_set, _| { + tools + .tools + .into_iter() + .map(|tool| { + log::info!( + "registering context server tool: {:?}", + tool.name + ); + tool_working_set.insert(Arc::new( + ContextServerTool::new( + context_server_manager.clone(), + server.id(), + tool, + ), + )) + }) + .collect::>() }) - .collect::>(); + .log_err(); - this.update(cx, |this, cx| { - this.context_server_tool_ids.insert(server_id, tool_ids); - this.load_default_profile(cx); - }) - .log_err(); + if let Some(tool_ids) = tool_ids { + this.update(cx, |this, cx| { + this.context_server_tool_ids + .insert(server_id, tool_ids); + this.load_default_profile(cx); + }) + .log_err(); + } } } } @@ -466,7 +481,9 @@ impl ThreadStore { } context_server::manager::Event::ServerStopped { server_id } => { if let Some(tool_ids) = self.context_server_tool_ids.remove(server_id) { - tool_working_set.remove(&tool_ids); + tool_working_set.update(cx, |tool_working_set, _| { + tool_working_set.remove(&tool_ids); + }); self.load_default_profile(cx); } } diff --git a/crates/agent/src/tool_use.rs b/crates/agent/src/tool_use.rs index 285e72d993..93576d57a3 100644 --- a/crates/agent/src/tool_use.rs +++ b/crates/agent/src/tool_use.rs @@ -5,7 +5,7 @@ use assistant_tool::{Tool, ToolWorkingSet}; use collections::HashMap; use futures::FutureExt as _; use futures::future::Shared; -use gpui::{App, SharedString, Task}; +use gpui::{App, Entity, SharedString, Task}; use language_model::{ LanguageModelRegistry, LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse, LanguageModelToolUseId, MessageContent, Role, @@ -49,7 +49,7 @@ impl ToolUseStatus { } pub struct ToolUseState { - tools: Arc, + tools: Entity, tool_uses_by_assistant_message: HashMap>, tool_uses_by_user_message: HashMap>, tool_results: HashMap, @@ -59,7 +59,7 @@ pub struct ToolUseState { pub const USING_TOOL_MARKER: &str = ""; impl ToolUseState { - pub fn new(tools: Arc) -> Self { + pub fn new(tools: Entity) -> Self { Self { tools, tool_uses_by_assistant_message: HashMap::default(), @@ -73,7 +73,7 @@ impl ToolUseState { /// /// Accepts a function to filter the tools that should be used to populate the state. pub fn from_serialized_messages( - tools: Arc, + tools: Entity, messages: &[SerializedMessage], mut filter_by_tool_name: impl FnMut(&str) -> bool, ) -> Self { @@ -199,12 +199,12 @@ impl ToolUseState { } })(); - let (icon, needs_confirmation) = if let Some(tool) = self.tools.tool(&tool_use.name, cx) - { - (tool.icon(), tool.needs_confirmation(&tool_use.input, cx)) - } else { - (IconName::Cog, false) - }; + let (icon, needs_confirmation) = + if let Some(tool) = self.tools.read(cx).tool(&tool_use.name, cx) { + (tool.icon(), tool.needs_confirmation(&tool_use.input, cx)) + } else { + (IconName::Cog, false) + }; tool_uses.push(ToolUse { id: tool_use.id.clone(), @@ -226,7 +226,7 @@ impl ToolUseState { input: &serde_json::Value, cx: &App, ) -> SharedString { - if let Some(tool) = self.tools.tool(tool_name, cx) { + if let Some(tool) = self.tools.read(cx).tool(tool_name, cx) { tool.ui_text(input).into() } else { format!("Unknown tool {tool_name:?}").into() diff --git a/crates/assistant_tool/src/tool_working_set.rs b/crates/assistant_tool/src/tool_working_set.rs index 97060cfdad..c7e20d3517 100644 --- a/crates/assistant_tool/src/tool_working_set.rs +++ b/crates/assistant_tool/src/tool_working_set.rs @@ -1,8 +1,7 @@ use std::sync::Arc; use collections::{HashMap, HashSet, IndexMap}; -use gpui::App; -use parking_lot::Mutex; +use gpui::{App, Context, EventEmitter}; use crate::{Tool, ToolRegistry, ToolSource}; @@ -12,11 +11,6 @@ pub struct ToolId(usize); /// A working set of tools for use in one instance of the Assistant Panel. #[derive(Default)] pub struct ToolWorkingSet { - state: Mutex, -} - -#[derive(Default)] -struct WorkingSetState { context_server_tools_by_id: HashMap>, context_server_tools_by_name: HashMap>, enabled_sources: HashSet, @@ -24,99 +18,27 @@ struct WorkingSetState { next_tool_id: ToolId, } +pub enum ToolWorkingSetEvent { + EnabledToolsChanged, +} + +impl EventEmitter for ToolWorkingSet {} + impl ToolWorkingSet { pub fn tool(&self, name: &str, cx: &App) -> Option> { - self.state - .lock() - .context_server_tools_by_name + self.context_server_tools_by_name .get(name) .cloned() .or_else(|| ToolRegistry::global(cx).tool(name)) } pub fn tools(&self, cx: &App) -> Vec> { - self.state.lock().tools(cx) - } - - pub fn tools_by_source(&self, cx: &App) -> IndexMap>> { - self.state.lock().tools_by_source(cx) - } - - pub fn enabled_tools(&self, cx: &App) -> Vec> { - self.state.lock().enabled_tools(cx) - } - - pub fn disable_all_tools(&self) { - let mut state = self.state.lock(); - state.disable_all_tools(); - } - - pub fn enable_source(&self, source: ToolSource, cx: &App) { - let mut state = self.state.lock(); - state.enable_source(source, cx); - } - - pub fn disable_source(&self, source: &ToolSource) { - let mut state = self.state.lock(); - state.disable_source(source); - } - - pub fn insert(&self, tool: Arc) -> ToolId { - let mut state = self.state.lock(); - let tool_id = state.next_tool_id; - state.next_tool_id.0 += 1; - state - .context_server_tools_by_id - .insert(tool_id, tool.clone()); - state.tools_changed(); - tool_id - } - - pub fn is_enabled(&self, source: &ToolSource, name: &Arc) -> bool { - self.state.lock().is_enabled(source, name) - } - - pub fn is_disabled(&self, source: &ToolSource, name: &Arc) -> bool { - self.state.lock().is_disabled(source, name) - } - - pub fn enable(&self, source: ToolSource, tools_to_enable: &[Arc]) { - let mut state = self.state.lock(); - state.enable(source, tools_to_enable); - } - - pub fn disable(&self, source: ToolSource, tools_to_disable: &[Arc]) { - let mut state = self.state.lock(); - state.disable(source, tools_to_disable); - } - - pub fn remove(&self, tool_ids_to_remove: &[ToolId]) { - let mut state = self.state.lock(); - state - .context_server_tools_by_id - .retain(|id, _| !tool_ids_to_remove.contains(id)); - state.tools_changed(); - } -} - -impl WorkingSetState { - fn tools_changed(&mut self) { - self.context_server_tools_by_name.clear(); - self.context_server_tools_by_name.extend( - self.context_server_tools_by_id - .values() - .map(|tool| (tool.name(), tool.clone())), - ); - } - - fn tools(&self, cx: &App) -> Vec> { let mut tools = ToolRegistry::global(cx).tools(); tools.extend(self.context_server_tools_by_id.values().cloned()); - tools } - fn tools_by_source(&self, cx: &App) -> IndexMap>> { + pub fn tools_by_source(&self, cx: &App) -> IndexMap>> { let mut tools_by_source = IndexMap::default(); for tool in self.tools(cx) { @@ -135,7 +57,7 @@ impl WorkingSetState { tools_by_source } - fn enabled_tools(&self, cx: &App) -> Vec> { + pub fn enabled_tools(&self, cx: &App) -> Vec> { let all_tools = self.tools(cx); all_tools @@ -144,31 +66,12 @@ impl WorkingSetState { .collect() } - fn is_enabled(&self, source: &ToolSource, name: &Arc) -> bool { - self.enabled_tools_by_source - .get(source) - .map_or(false, |enabled_tools| enabled_tools.contains(name)) + pub fn disable_all_tools(&mut self, cx: &mut Context) { + self.enabled_tools_by_source.clear(); + cx.emit(ToolWorkingSetEvent::EnabledToolsChanged); } - fn is_disabled(&self, source: &ToolSource, name: &Arc) -> bool { - !self.is_enabled(source, name) - } - - fn enable(&mut self, source: ToolSource, tools_to_enable: &[Arc]) { - self.enabled_tools_by_source - .entry(source) - .or_default() - .extend(tools_to_enable.into_iter().cloned()); - } - - fn disable(&mut self, source: ToolSource, tools_to_disable: &[Arc]) { - self.enabled_tools_by_source - .entry(source) - .or_default() - .retain(|name| !tools_to_disable.contains(name)); - } - - fn enable_source(&mut self, source: ToolSource, cx: &App) { + pub fn enable_source(&mut self, source: ToolSource, cx: &mut Context) { self.enabled_sources.insert(source.clone()); let tools_by_source = self.tools_by_source(cx); @@ -181,14 +84,72 @@ impl WorkingSetState { .collect::>(), ); } + cx.emit(ToolWorkingSetEvent::EnabledToolsChanged); } - fn disable_source(&mut self, source: &ToolSource) { + pub fn disable_source(&mut self, source: &ToolSource, cx: &mut Context) { self.enabled_sources.remove(source); self.enabled_tools_by_source.remove(source); + cx.emit(ToolWorkingSetEvent::EnabledToolsChanged); } - fn disable_all_tools(&mut self) { - self.enabled_tools_by_source.clear(); + pub fn insert(&mut self, tool: Arc) -> ToolId { + let tool_id = self.next_tool_id; + self.next_tool_id.0 += 1; + self.context_server_tools_by_id + .insert(tool_id, tool.clone()); + self.tools_changed(); + tool_id + } + + pub fn is_enabled(&self, source: &ToolSource, name: &Arc) -> bool { + self.enabled_tools_by_source + .get(source) + .map_or(false, |enabled_tools| enabled_tools.contains(name)) + } + + pub fn is_disabled(&self, source: &ToolSource, name: &Arc) -> bool { + !self.is_enabled(source, name) + } + + pub fn enable( + &mut self, + source: ToolSource, + tools_to_enable: &[Arc], + cx: &mut Context, + ) { + self.enabled_tools_by_source + .entry(source) + .or_default() + .extend(tools_to_enable.into_iter().cloned()); + cx.emit(ToolWorkingSetEvent::EnabledToolsChanged); + } + + pub fn disable( + &mut self, + source: ToolSource, + tools_to_disable: &[Arc], + cx: &mut Context, + ) { + self.enabled_tools_by_source + .entry(source) + .or_default() + .retain(|name| !tools_to_disable.contains(name)); + cx.emit(ToolWorkingSetEvent::EnabledToolsChanged); + } + + pub fn remove(&mut self, tool_ids_to_remove: &[ToolId]) { + self.context_server_tools_by_id + .retain(|id, _| !tool_ids_to_remove.contains(id)); + self.tools_changed(); + } + + fn tools_changed(&mut self) { + self.context_server_tools_by_name.clear(); + self.context_server_tools_by_name.extend( + self.context_server_tools_by_id + .values() + .map(|tool| (tool.name(), tool.clone())), + ); } } diff --git a/crates/eval/src/example.rs b/crates/eval/src/example.rs index 531b1ea275..1d74443b1a 100644 --- a/crates/eval/src/example.rs +++ b/crates/eval/src/example.rs @@ -6,7 +6,7 @@ use collections::HashMap; use dap::DapRegistry; use futures::channel::mpsc; use futures::{FutureExt, StreamExt as _, select_biased}; -use gpui::{App, AsyncApp, Entity, Task}; +use gpui::{App, AppContext as _, AsyncApp, Entity, Task}; use handlebars::Handlebars; use language::{DiagnosticSeverity, OffsetRangeExt}; use language_model::{ @@ -181,7 +181,7 @@ impl Example { project.create_worktree(&worktree_path, true, cx) }); - let tools = Arc::new(ToolWorkingSet::default()); + let tools = cx.new(|_| ToolWorkingSet::default()); let thread_store = ThreadStore::load(project.clone(), tools, app_state.prompt_builder.clone(), cx); let this = self.clone();