Support disabling tool suggest for specific tools. (#20072)

## Summary
- Add `disable_tool_suggest` to app and plugin config, schema, and
TypeScript output
- Exclude disabled connectors and plugins from tool suggestion discovery
- Persist "never show again" tool-suggestion choices back into
`config.toml`
- Update config docs and add coverage for connector and plugin
suppression

## Testing
- Added and updated unit tests for config persistence and tool-suggest
filtering
- Not run (not requested)
This commit is contained in:
Matthew Zeng
2026-04-28 17:19:34 -07:00
committed by GitHub
parent 1211a90a35
commit ebdf3a878c
17 changed files with 681 additions and 6 deletions

View File

@@ -46,6 +46,7 @@ use codex_config::types::OtelConfig;
use codex_config::types::OtelConfigToml;
use codex_config::types::OtelExporterKind;
use codex_config::types::ToolSuggestConfig;
use codex_config::types::ToolSuggestDisabledTool;
use codex_config::types::ToolSuggestDiscoverable;
use codex_config::types::TuiKeymap;
use codex_config::types::TuiNotificationSettings;
@@ -95,6 +96,7 @@ use codex_utils_absolute_path::AbsolutePathBufGuard;
use serde::Deserialize;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::collections::HashSet;
use std::io::ErrorKind;
use std::path::Path;
use std::path::PathBuf;
@@ -1416,10 +1418,29 @@ pub struct AgentRoleConfig {
pub nickname_candidates: Option<Vec<String>>,
}
fn resolve_tool_suggest_config(config_toml: &ConfigToml) -> ToolSuggestConfig {
let discoverables = config_toml
.tool_suggest
.as_ref()
fn resolve_tool_suggest_config(
config_toml: &ConfigToml,
config_layer_stack: &ConfigLayerStack,
) -> ToolSuggestConfig {
resolve_tool_suggest_config_from_config(config_toml.tool_suggest.as_ref(), config_layer_stack)
}
pub(crate) fn resolve_tool_suggest_config_from_layer_stack(
config_layer_stack: &ConfigLayerStack,
) -> ToolSuggestConfig {
let tool_suggest = config_layer_stack
.effective_config()
.get("tool_suggest")
.cloned()
.and_then(|value| value.try_into::<ToolSuggestConfig>().ok());
resolve_tool_suggest_config_from_config(tool_suggest.as_ref(), config_layer_stack)
}
fn resolve_tool_suggest_config_from_config(
tool_suggest: Option<&ToolSuggestConfig>,
config_layer_stack: &ConfigLayerStack,
) -> ToolSuggestConfig {
let discoverables = tool_suggest
.into_iter()
.flat_map(|tool_suggest| tool_suggest.discoverables.iter())
.filter_map(|discoverable| {
@@ -1434,8 +1455,47 @@ fn resolve_tool_suggest_config(config_toml: &ConfigToml) -> ToolSuggestConfig {
}
})
.collect();
let mut seen_disabled_tools = HashSet::new();
let mut disabled_tools = Vec::new();
let mut add_disabled_tool = |disabled_tool: ToolSuggestDisabledTool| {
if let Some(disabled_tool) = disabled_tool.normalized()
&& seen_disabled_tools.insert(disabled_tool.clone())
{
disabled_tools.push(disabled_tool);
}
};
ToolSuggestConfig { discoverables }
let layers = config_layer_stack.get_layers(
ConfigLayerStackOrdering::LowestPrecedenceFirst,
/*include_disabled*/ false,
);
if layers.is_empty() {
for disabled_tool in tool_suggest
.into_iter()
.flat_map(|tool_suggest| tool_suggest.disabled_tools.iter().cloned())
{
add_disabled_tool(disabled_tool);
}
} else {
for layer in layers {
let Some(tool_suggest) = layer
.config
.get("tool_suggest")
.cloned()
.and_then(|value| value.try_into::<ToolSuggestConfig>().ok())
else {
continue;
};
for disabled_tool in tool_suggest.disabled_tools {
add_disabled_tool(disabled_tool);
}
}
}
ToolSuggestConfig {
discoverables,
disabled_tools,
}
}
fn thread_store_config(
@@ -1840,7 +1900,7 @@ impl Config {
.clone(),
None => ConfigProfile::default(),
};
let tool_suggest = resolve_tool_suggest_config(&cfg);
let tool_suggest = resolve_tool_suggest_config(&cfg, &config_layer_stack);
let feature_overrides = FeatureOverrides {
include_apply_patch_tool: include_apply_patch_tool_override,
web_search_request: override_tools_web_search_request,