core: allow excluding tool namespaces from code mode (#26320)

## Why

Research and training setups need to control which tool namespaces
appear inside code mode's nested `tools` surface without disabling those
tools entirely. This makes it possible to train against a deliberately
reduced nested-tool setup while preserving the normal direct and
deferred tool paths.

## What

- Extend `features.code_mode` to accept structured configuration while
preserving the existing boolean syntax.
- Add an exact `excluded_tool_namespaces` list under
`[features.code_mode]`:

  ```toml
  [features.code_mode]
  enabled = true
  excluded_tool_namespaces = ["mcp__codex_apps", "multi_agent_v1"]
  ```

- Filter matching canonical `ToolName` namespaces when constructing code
mode's nested router and code-mode-specific direct tool descriptions.
- Keep excluded tools registered, directly exposed in mixed code mode,
and discoverable through top-level `tool_search` when otherwise
eligible.
- Derive deferred nested-tool guidance after namespace filtering so the
`exec` description does not advertise excluded-only deferred tools.
- Preserve the boolean/table representation when materializing config
locks and update the generated config schema.

## Testing

- `just test -p codex-features`
- `just test -p codex-config`
- `just test -p codex-core load_config_resolves_code_mode_config`
- `just test -p codex-core
lock_contains_prompts_and_materializes_features`
- `just test -p codex-core
excluded_deferred_namespaces_do_not_enable_nested_tool_guidance`
- `just test -p codex-core
code_mode_excludes_configured_nested_tool_namespaces`
- `cargo check -p codex-thread-manager-sample`
This commit is contained in:
sayan-oai
2026-06-04 18:40:18 +00:00
committed by GitHub
parent 9e41f8ddbe
commit 8b1238856b
11 changed files with 286 additions and 16 deletions
+20
View File
@@ -4,6 +4,26 @@ use serde::Deserialize;
use serde::Serialize;
use std::collections::BTreeMap;
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct CodeModeConfigToml {
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
/// Exact tool namespaces to omit from the code-mode nested tool surface.
#[serde(skip_serializing_if = "Option::is_none")]
pub excluded_tool_namespaces: Option<Vec<String>>,
}
impl FeatureConfig for CodeModeConfigToml {
fn enabled(&self) -> Option<bool> {
self.enabled
}
fn set_enabled(&mut self, enabled: bool) {
self.enabled = Some(enabled);
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct MultiAgentV2ConfigToml {
+10 -1
View File
@@ -17,6 +17,7 @@ use toml::Table;
mod feature_configs;
mod legacy;
pub use feature_configs::AppsMcpPathOverrideConfigToml;
pub use feature_configs::CodeModeConfigToml;
pub use feature_configs::MultiAgentV2ConfigToml;
pub use feature_configs::NetworkProxyConfigToml;
pub use feature_configs::NetworkProxyDomainPermissionToml;
@@ -600,6 +601,8 @@ pub fn is_known_feature_key(key: &str) -> bool {
/// Deserializable features table for TOML.
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, JsonSchema)]
pub struct FeaturesToml {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub code_mode: Option<FeatureToml<CodeModeConfigToml>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub multi_agent_v2: Option<FeatureToml<MultiAgentV2ConfigToml>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -620,6 +623,9 @@ impl Features {
impl FeaturesToml {
pub fn entries(&self) -> BTreeMap<String, bool> {
let mut entries = self.entries.clone();
if let Some(enabled) = self.code_mode.as_ref().and_then(FeatureToml::enabled) {
entries.insert(Feature::CodeMode.key().to_string(), enabled);
}
if let Some(enabled) = self.multi_agent_v2.as_ref().and_then(FeatureToml::enabled) {
entries.insert(Feature::MultiAgentV2.key().to_string(), enabled);
}
@@ -638,6 +644,7 @@ impl FeaturesToml {
pub fn materialize_resolved_enabled(&mut self, features: &Features) {
let Self {
code_mode,
multi_agent_v2,
apps_mcp_path_override,
network_proxy,
@@ -648,7 +655,9 @@ impl FeaturesToml {
}
for spec in FEATURES {
let enabled = features.enabled(spec.id);
if spec.id == Feature::MultiAgentV2 {
if spec.id == Feature::CodeMode {
materialize_resolved_feature_enabled(code_mode, enabled);
} else if spec.id == Feature::MultiAgentV2 {
materialize_resolved_feature_enabled(multi_agent_v2, enabled);
} else if spec.id == Feature::AppsMcpPathOverride {
materialize_resolved_feature_enabled(apps_mcp_path_override, enabled);