diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index 8b653609de..96ea4130dd 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -539,6 +539,9 @@ "request_rule": { "type": "boolean" }, + "request_user_input_tool": { + "type": "boolean" + }, "responses_websocket_response_processed": { "type": "boolean" }, @@ -4462,6 +4465,9 @@ "request_rule": { "type": "boolean" }, + "request_user_input_tool": { + "type": "boolean" + }, "responses_websocket_response_processed": { "type": "boolean" }, diff --git a/codex-rs/core/src/tools/spec_plan.rs b/codex-rs/core/src/tools/spec_plan.rs index c3656f2434..78311a10a0 100644 --- a/codex-rs/core/src/tools/spec_plan.rs +++ b/codex-rs/core/src/tools/spec_plan.rs @@ -568,9 +568,11 @@ fn add_core_utility_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut planned_tools.add(UpdateGoalHandler); } - planned_tools.add(RequestUserInputHandler { - available_modes: request_user_input_available_modes(features), - }); + if features.enabled(Feature::RequestUserInputTool) { + planned_tools.add(RequestUserInputHandler { + available_modes: request_user_input_available_modes(features), + }); + } if features.enabled(Feature::RequestPermissionsTool) { planned_tools.add(RequestPermissionsHandler); diff --git a/codex-rs/core/src/tools/spec_plan_tests.rs b/codex-rs/core/src/tools/spec_plan_tests.rs index 32913bf2e7..3146b03ff9 100644 --- a/codex-rs/core/src/tools/spec_plan_tests.rs +++ b/codex-rs/core/src/tools/spec_plan_tests.rs @@ -442,6 +442,20 @@ async fn host_context_gates_goal_and_agent_job_tools() { worker_agent_job.assert_visible_contains(&["spawn_agents_on_csv", "report_agent_job_result"]); } +#[tokio::test] +async fn request_user_input_tool_can_be_disabled_by_feature() { + let enabled = probe(|_| {}).await; + enabled.assert_visible_contains(&["request_user_input"]); + enabled.assert_registered_contains(&["request_user_input"]); + + let disabled = probe(|turn| { + set_feature(turn, Feature::RequestUserInputTool, /*enabled*/ false); + }) + .await; + disabled.assert_visible_lacks(&["request_user_input"]); + disabled.assert_registered_lacks(&["request_user_input"]); +} + #[tokio::test] async fn mcp_and_tool_search_follow_direct_and_deferred_tool_exposure() { let direct_mcp = probe_with( diff --git a/codex-rs/features/src/lib.rs b/codex-rs/features/src/lib.rs index 4db7b56d25..17b709d375 100644 --- a/codex-rs/features/src/lib.rs +++ b/codex-rs/features/src/lib.rs @@ -98,6 +98,8 @@ pub enum Feature { ExecPermissionApprovals, /// Expose the built-in request_permissions tool. RequestPermissionsTool, + /// Expose the built-in request_user_input tool. + RequestUserInputTool, /// Allow the model to request web searches that fetch live content. WebSearchRequest, /// Allow the model to request web searches that fetch cached content. @@ -861,6 +863,12 @@ pub const FEATURES: &[FeatureSpec] = &[ stage: Stage::UnderDevelopment, default_enabled: false, }, + FeatureSpec { + id: Feature::RequestUserInputTool, + key: "request_user_input_tool", + stage: Stage::Stable, + default_enabled: true, + }, FeatureSpec { id: Feature::UseLinuxSandboxBwrap, key: "use_linux_sandbox_bwrap",