Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/agent/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3922,6 +3922,9 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
#[gpui::test]
async fn test_update_plan_tool_updates_thread_events(cx: &mut TestAppContext) {
let ThreadTest { thread, model, .. } = setup(cx, TestModel::Fake).await;
cx.update(|cx| {
cx.update_flags(true, vec!["update-plan-tool".to_string()]);
});
thread.update(cx, |thread, _cx| thread.add_tool(UpdatePlanTool));
let fake_model = model.as_fake();

Expand Down
16 changes: 3 additions & 13 deletions crates/agent/src/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use acp_thread::{MentionUri, UserMessageId};
use action_log::ActionLog;
use agent_settings::UserAgentsMd;
use feature_flags::{
CreateThreadToolFeatureFlag, FeatureFlagAppExt as _, HandoffFeatureFlag, LspToolFeatureFlag,
RenameToolFeatureFlag, UpdatePlanToolFeatureFlag, UpdateTitleToolFeatureFlag,
FeatureFlagAppExt as _, HandoffFeatureFlag, UpdatePlanToolFeatureFlag,
UpdateTitleToolFeatureFlag,
};
use zed_env_vars::{EnvVar, env_var};

Expand Down Expand Up @@ -3503,17 +3503,7 @@ impl Thread {
None
}
})
.filter(|(tool_name, _)| match tool_name.as_ref() {
RenameTool::NAME => cx.has_flag::<RenameToolFeatureFlag>(),
FindReferencesTool::NAME
| GetCodeActionsTool::NAME
| ApplyCodeActionTool::NAME
| GoToDefinitionTool::NAME => cx.has_flag::<LspToolFeatureFlag>(),
CreateThreadTool::NAME | ListAgentsAndModelsTool::NAME => {
cx.has_flag::<CreateThreadToolFeatureFlag>()
}
_ => true,
})
.filter(|(tool_name, _)| crate::tools::tool_feature_flag_enabled(tool_name, cx))
.collect::<BTreeMap<_, _>>();

let mut context_server_tools = Vec::new();
Expand Down
33 changes: 32 additions & 1 deletion crates/agent/src/tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ mod web_search_tool;
mod write_file_tool;

use crate::AgentTool;
use feature_flags::{
CreateThreadToolFeatureFlag, FeatureFlagAppExt as _, LspToolFeatureFlag, RenameToolFeatureFlag,
UpdatePlanToolFeatureFlag, UpdateTitleToolFeatureFlag,
};
use gpui::App;
use language_model::{LanguageModelRequestTool, LanguageModelToolSchemaFormat};
use serde::{
Deserialize, Deserializer,
Expand Down Expand Up @@ -158,7 +163,7 @@ macro_rules! tools {
}

// Adding a tool here (and constructing it in `Thread::add_default_tools`) is
// not enough to make the model actually receive it. Two further gates will
// not enough to make the model actually receive it. Three further gates will
// silently drop the tool rather than fail to compile:
//
// 1. `assets/settings/default.json`: the `write` and `ask` agent profiles each
Expand All @@ -169,6 +174,9 @@ macro_rules! tools {
// `crates/settings_ui/src/pages/tool_permissions_setup.rs`: every tool must
// be in the permission-UI `TOOLS` list (if it calls
// `decide_permission_from_settings`) or in `EXCLUDED_TOOLS`.
// 3. `tool_feature_flag_enabled`: some tools are gated behind a feature flag and
// are dropped unless it is active. The agent-profile UI uses the same gate so
// it never offers a tool the agent can't actually use.
tools! {
ApplyCodeActionTool,
CopyPathTool,
Expand Down Expand Up @@ -196,3 +204,26 @@ tools! {
WebSearchTool,
WriteFileTool,
}

/// Some built-in tools are gated behind a feature flag and only become usable
/// once that flag is active. Tools without a flag are always available.
///
/// This is the single source of truth for that gating: `Thread::enabled_tools`
/// uses it to decide what the model receives, and the agent-profile
/// configuration UI uses it to decide what to offer — so the UI can never list
/// a tool the agent would silently drop (see #56778).
pub fn tool_feature_flag_enabled(tool_name: &str, cx: &App) -> bool {
match tool_name {
RenameTool::NAME => cx.has_flag::<RenameToolFeatureFlag>(),
FindReferencesTool::NAME
| GetCodeActionsTool::NAME
| ApplyCodeActionTool::NAME
| GoToDefinitionTool::NAME => cx.has_flag::<LspToolFeatureFlag>(),
CreateThreadTool::NAME | ListAgentsAndModelsTool::NAME => {
cx.has_flag::<CreateThreadToolFeatureFlag>()
}
UpdatePlanTool::NAME => cx.has_flag::<UpdatePlanToolFeatureFlag>(),
UpdateTitleTool::NAME => cx.has_flag::<UpdateTitleToolFeatureFlag>(),
_ => true,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,10 @@ impl ManageProfilesModal {
let supported_by_provider = provider.as_ref().map_or(true, |provider| {
agent::tool_supports_provider(name, provider)
});
supported_by_provider
// Don't offer tools the agent can't actually use: tools gated
// behind an inactive feature flag are silently dropped before
// they reach the model (#56778).
supported_by_provider && agent::tool_feature_flag_enabled(name, cx)
})
.map(Arc::from)
.collect();
Expand Down
Loading