Allow disabling the update_plan tool (#35054)

## What changed

- Add a default-on `tools.update_plan.enabled` configuration option.
- Omit `update_plan` from the visible and registered tool sets when the option is disabled.

## Testing

- Cover configuration resolution and tool registration for the disabled setting.

GitOrigin-RevId: c13aa463a6911956fca9f0ef5b74841b543798c4
This commit is contained in:
tongzhou wang
2026-07-24 00:10:22 +00:00
committed by copyberry
parent bb24b67d33
commit fb4e6ba2f4
7 changed files with 80 additions and 1 deletions
+8
View File
@@ -636,6 +636,7 @@ pub struct ToolsToml {
)]
pub web_search: Option<WebSearchToolConfig>,
pub experimental_request_user_input: Option<ExperimentalRequestUserInput>,
pub update_plan: Option<UpdatePlanToolConfig>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema)]
@@ -645,6 +646,13 @@ pub struct ExperimentalRequestUserInput {
pub enabled: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema)]
#[schemars(deny_unknown_fields)]
pub struct UpdatePlanToolConfig {
#[serde(default = "default_true")]
pub enabled: bool,
}
#[derive(Deserialize)]
#[serde(untagged)]
enum WebSearchToolConfigInput {
+13
View File
@@ -3147,6 +3147,9 @@
"experimental_request_user_input": {
"$ref": "#/definitions/ExperimentalRequestUserInput"
},
"update_plan": {
"$ref": "#/definitions/UpdatePlanToolConfig"
},
"web_search": {
"allOf": [
{
@@ -4648,6 +4651,16 @@
},
"type": "object"
},
"UpdatePlanToolConfig": {
"additionalProperties": false,
"properties": {
"enabled": {
"default": true,
"type": "boolean"
}
},
"type": "object"
},
"UriBasedFileOpener": {
"oneOf": [
{
+26
View File
@@ -361,6 +361,7 @@ web_search = true
Some(ToolsToml {
web_search: None,
experimental_request_user_input: None,
update_plan: None,
})
);
}
@@ -380,6 +381,7 @@ web_search = false
Some(ToolsToml {
web_search: None,
experimental_request_user_input: None,
update_plan: None,
})
);
}
@@ -398,6 +400,7 @@ fn tools_experimental_request_user_input_defaults_to_enabled() {
Some(ToolsToml {
web_search: None,
experimental_request_user_input: Some(ExperimentalRequestUserInput { enabled: true }),
update_plan: None,
})
);
}
@@ -417,6 +420,7 @@ enabled = false
Some(ToolsToml {
web_search: None,
experimental_request_user_input: Some(ExperimentalRequestUserInput { enabled: false }),
update_plan: None,
})
);
}
@@ -431,6 +435,7 @@ async fn load_config_resolves_experimental_request_user_input_enabled() -> std::
experimental_request_user_input: Some(ExperimentalRequestUserInput {
enabled: false,
}),
update_plan: None,
}),
..ConfigToml::default()
},
@@ -502,6 +507,27 @@ async fn load_config_resolves_non_prefixed_mcp_tool_servers() -> std::io::Result
Ok(())
}
#[tokio::test]
async fn load_config_resolves_update_plan_enabled() -> std::io::Result<()> {
let codex_home = tempdir()?;
let config_toml = toml::from_str(
r#"
[tools.update_plan]
enabled = false
"#,
)
.expect("TOML deserialization should succeed");
let config = Config::load_from_base_config_with_overrides(
config_toml,
ConfigOverrides::default(),
codex_home.abs(),
)
.await?;
assert!(!config.update_plan_enabled);
Ok(())
}
#[tokio::test]
async fn load_config_resolves_code_mode_config() -> std::io::Result<()> {
let codex_home = tempdir()?;
+13
View File
@@ -1030,6 +1030,9 @@ pub struct Config {
/// Whether to register the experimental request_user_input tool.
pub experimental_request_user_input_enabled: bool,
/// Whether to register the update_plan tool.
pub update_plan_enabled: bool,
/// Configuration for the experimental code-mode tool surface.
pub code_mode: CodeModeConfig,
@@ -2537,6 +2540,14 @@ fn resolve_experimental_request_user_input_enabled(config_toml: &ConfigToml) ->
.is_none_or(|config| config.enabled)
}
fn resolve_update_plan_enabled(config_toml: &ConfigToml) -> bool {
config_toml
.tools
.as_ref()
.and_then(|tools| tools.update_plan.as_ref())
.is_none_or(|config| config.enabled)
}
fn resolve_orchestrator_feature_enabled(
feature: Option<&codex_config::config_toml::OrchestratorFeatureToml>,
) -> bool {
@@ -3577,6 +3588,7 @@ impl Config {
let web_search_config = resolve_web_search_config(&cfg);
let experimental_request_user_input_enabled =
resolve_experimental_request_user_input_enabled(&cfg);
let update_plan_enabled = resolve_update_plan_enabled(&cfg);
let code_mode = resolve_code_mode_config(&cfg);
let multi_agent_v2 = resolve_multi_agent_v2_config(&cfg);
let token_budget = resolve_token_budget_config(&cfg, &features)?;
@@ -4108,6 +4120,7 @@ impl Config {
web_search_mode: constrained_web_search_mode.value,
web_search_config,
experimental_request_user_input_enabled,
update_plan_enabled,
code_mode,
use_experimental_unified_exec_tool,
background_terminal_max_timeout,
+3 -1
View File
@@ -713,7 +713,9 @@ fn add_core_utility_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut
let features = turn_context.config.features.get();
let environment_mode = tool_environment_mode(context.environments);
planned_tools.add(PlanHandler);
if turn_context.config.update_plan_enabled {
planned_tools.add(PlanHandler);
}
if features.enabled(Feature::DeferredExecutor) {
planned_tools.add(WaitForEnvironmentHandler);
@@ -466,6 +466,22 @@ async fn request_user_input_tool_respects_experimental_config_gate() {
disabled.assert_registered_lacks(&["request_user_input"]);
}
#[tokio::test]
async fn update_plan_tool_respects_config_gate() {
let enabled = probe(|_| {}).await;
enabled.assert_visible_contains(&["update_plan"]);
enabled.assert_registered_contains(&["update_plan"]);
let disabled = probe(|turn| {
update_config(turn, |config| {
config.update_plan_enabled = false;
});
})
.await;
disabled.assert_visible_lacks(&["update_plan"]);
disabled.assert_registered_lacks(&["update_plan"]);
}
#[tokio::test]
async fn request_user_input_stays_direct_in_code_mode_only() {
let plan = probe(|turn| {
@@ -294,6 +294,7 @@ fn new_config(model: Option<String>, arg0_paths: Arg0DispatchPaths) -> anyhow::R
web_search_mode: Constrained::allow_any(WebSearchMode::Disabled),
web_search_config: None,
experimental_request_user_input_enabled: true,
update_plan_enabled: true,
code_mode: Default::default(),
use_experimental_unified_exec_tool: false,
background_terminal_max_timeout: 300_000,