diff --git a/codex-rs/core/src/features.rs b/codex-rs/core/src/features.rs index 20f405bf28..24bbbaa00a 100644 --- a/codex-rs/core/src/features.rs +++ b/codex-rs/core/src/features.rs @@ -32,6 +32,8 @@ pub enum Feature { GhostCommit, /// Include the view_image tool. ViewImageTool, + /// Include the plan tool. + PlanTool, /// Send warnings to the model to correct it on the tool usage. ModelWarnings, /// Enable the default shell tool. @@ -280,6 +282,12 @@ pub const FEATURES: &[FeatureSpec] = &[ stage: Stage::Stable, default_enabled: true, }, + FeatureSpec { + id: Feature::PlanTool, + key: "plan_tool", + stage: Stage::Stable, + default_enabled: true, + }, FeatureSpec { id: Feature::ShellTool, key: "shell_tool", diff --git a/codex-rs/core/src/tools/spec.rs b/codex-rs/core/src/tools/spec.rs index ea445e8789..42a8983b16 100644 --- a/codex-rs/core/src/tools/spec.rs +++ b/codex-rs/core/src/tools/spec.rs @@ -22,6 +22,7 @@ pub(crate) struct ToolsConfig { pub apply_patch_tool_type: Option, pub web_search_request: bool, pub include_view_image_tool: bool, + pub include_plan_tool: bool, pub experimental_supported_tools: Vec, } @@ -39,6 +40,7 @@ impl ToolsConfig { let include_apply_patch_tool = features.enabled(Feature::ApplyPatchFreeform); let include_web_search_request = features.enabled(Feature::WebSearchRequest); let include_view_image_tool = features.enabled(Feature::ViewImageTool); + let include_plan_tool = features.enabled(Feature::PlanTool); let shell_type = if !features.enabled(Feature::ShellTool) { ConfigShellToolType::Disabled @@ -65,6 +67,7 @@ impl ToolsConfig { apply_patch_tool_type, web_search_request: include_web_search_request, include_view_image_tool, + include_plan_tool, experimental_supported_tools: model_family.experimental_supported_tools.clone(), } } @@ -1037,8 +1040,10 @@ pub(crate) fn build_specs( builder.register_handler("list_mcp_resource_templates", mcp_resource_handler.clone()); builder.register_handler("read_mcp_resource", mcp_resource_handler); - builder.push_spec(PLAN_TOOL.clone()); - builder.register_handler("update_plan", plan_handler); + if config.include_plan_tool { + builder.push_spec(PLAN_TOOL.clone()); + builder.register_handler("update_plan", plan_handler); + } if let Some(apply_patch_tool_type) = &config.apply_patch_tool_type { match apply_patch_tool_type { @@ -1413,6 +1418,23 @@ mod tests { ); } + #[test] + fn test_gpt_5_defaults_without_plan_tool() { + let mut features = Features::with_defaults(); + features.disable(Feature::PlanTool); + assert_model_tools( + "gpt-5", + &features, + &[ + "shell", + "list_mcp_resources", + "list_mcp_resource_templates", + "read_mcp_resource", + "view_image", + ], + ); + } + #[test] fn test_gpt_5_1_defaults() { assert_model_tools( diff --git a/docs/config.md b/docs/config.md index 4659c2604c..ff58079a9d 100644 --- a/docs/config.md +++ b/docs/config.md @@ -45,6 +45,7 @@ Supported features: | `rmcp_client` | false | Experimental | Enable oauth support for streamable HTTP MCP servers | | `apply_patch_freeform` | false | Beta | Include the freeform `apply_patch` tool | | `view_image_tool` | true | Stable | Include the `view_image` tool | +| `plan_tool` | true | Stable | Include the `update_plan` tool | | `web_search_request` | false | Stable | Allow the model to issue web searches | | `ghost_commit` | false | Experimental | Create a ghost commit each turn | | `enable_experimental_windows_sandbox` | false | Experimental | Use the Windows restricted-token sandbox | diff --git a/docs/example-config.md b/docs/example-config.md index 0d09a61869..71bf8ea900 100644 --- a/docs/example-config.md +++ b/docs/example-config.md @@ -217,6 +217,7 @@ unified_exec = false rmcp_client = false apply_patch_freeform = false view_image_tool = true +plan_tool = true web_search_request = false ghost_commit = false enable_experimental_windows_sandbox = false