diff --git a/codex-rs/core/src/features.rs b/codex-rs/core/src/features.rs index 362126b2e4..a894b3353d 100644 --- a/codex-rs/core/src/features.rs +++ b/codex-rs/core/src/features.rs @@ -686,8 +686,12 @@ pub const FEATURES: &[FeatureSpec] = &[ FeatureSpec { id: Feature::DefaultModeRequestUserInput, key: "default_mode_request_user_input", - stage: Stage::Stable, - default_enabled: true, + stage: Stage::Experimental { + name: "Default mode request_user_input", + menu_description: "Allow Codex to use the request_user_input tool in Default mode when it truly cannot proceed safely with a reasonable assumption. Restart Codex after enabling.", + announcement: "NEW: Allow request_user_input in Default mode. Enable it in /experimental and restart Codex!", + }, + default_enabled: false, }, FeatureSpec { id: Feature::CollaborationModes, @@ -884,6 +888,28 @@ mod tests { assert_eq!(Feature::JsRepl.default_enabled(), false); } + #[test] + fn default_mode_request_user_input_is_experimental_and_user_toggleable() { + let spec = Feature::DefaultModeRequestUserInput.info(); + let stage = spec.stage; + + assert!(matches!(stage, Stage::Experimental { .. })); + assert_eq!( + stage.experimental_menu_name(), + Some("Default mode request_user_input") + ); + assert_eq!( + stage.experimental_menu_description(), + Some( + "Allow Codex to use the request_user_input tool in Default mode when it truly cannot proceed safely with a reasonable assumption. Restart Codex after enabling." + ) + ); + assert_eq!( + Feature::DefaultModeRequestUserInput.default_enabled(), + false + ); + } + #[test] fn image_generation_is_under_development() { assert_eq!(Feature::ImageGeneration.stage(), Stage::UnderDevelopment); diff --git a/codex-rs/core/src/tools/spec.rs b/codex-rs/core/src/tools/spec.rs index 47250100cb..f0c952e078 100644 --- a/codex-rs/core/src/tools/spec.rs +++ b/codex-rs/core/src/tools/spec.rs @@ -2262,9 +2262,7 @@ mod tests { create_exec_command_tool(true, false), create_write_stdin_tool(), PLAN_TOOL.clone(), - create_request_user_input_tool(CollaborationModesConfig { - default_mode_request_user_input: true, - }), + create_request_user_input_tool(CollaborationModesConfig::default()), create_apply_patch_freeform_tool(), ToolSpec::WebSearch { external_web_access: Some(true), @@ -2383,12 +2381,10 @@ mod tests { let request_user_input_tool = find_tool(&tools, "request_user_input"); assert_eq!( request_user_input_tool.spec, - create_request_user_input_tool(CollaborationModesConfig { - default_mode_request_user_input: true, - }) + create_request_user_input_tool(CollaborationModesConfig::default()) ); - features.disable(Feature::DefaultModeRequestUserInput); + features.enable(Feature::DefaultModeRequestUserInput); let tools_config = ToolsConfig::new(&ToolsConfigParams { model_info: &model_info, features: &features, @@ -2399,7 +2395,9 @@ mod tests { let request_user_input_tool = find_tool(&tools, "request_user_input"); assert_eq!( request_user_input_tool.spec, - create_request_user_input_tool(CollaborationModesConfig::default()) + create_request_user_input_tool(CollaborationModesConfig { + default_mode_request_user_input: true, + }) ); } diff --git a/codex-rs/core/tests/suite/request_user_input.rs b/codex-rs/core/tests/suite/request_user_input.rs index 2c15f8d6eb..3ef4d4f1c8 100644 --- a/codex-rs/core/tests/suite/request_user_input.rs +++ b/codex-rs/core/tests/suite/request_user_input.rs @@ -71,15 +71,21 @@ fn call_output_content_and_success( #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn request_user_input_round_trip_resolves_pending() -> anyhow::Result<()> { - request_user_input_round_trip_for_mode(ModeKind::Plan).await + request_user_input_round_trip_for_mode(ModeKind::Plan, |_| {}).await } -async fn request_user_input_round_trip_for_mode(mode: ModeKind) -> anyhow::Result<()> { +async fn request_user_input_round_trip_for_mode( + mode: ModeKind, + configure: C, +) -> anyhow::Result<()> +where + C: FnOnce(&mut Config) + Send + 'static, +{ skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let mut builder = test_codex(); + let mut builder = test_codex().with_config(configure); let TestCodex { codex, cwd, @@ -294,30 +300,28 @@ async fn request_user_input_rejected_in_execute_mode_alias() -> anyhow::Result<( } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn request_user_input_round_trip_in_default_mode_by_default() -> anyhow::Result<()> { - request_user_input_round_trip_for_mode(ModeKind::Default).await +async fn request_user_input_rejected_in_default_mode_by_default() -> anyhow::Result<()> { + assert_request_user_input_rejected("Default", |model| CollaborationMode { + mode: ModeKind::Default, + settings: Settings { + model, + reasoning_effort: None, + developer_instructions: None, + }, + }) + .await } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn request_user_input_rejected_in_default_mode_when_feature_disabled() -> anyhow::Result<()> { - assert_request_user_input_rejected_with_config( - "Default", - |model| CollaborationMode { - mode: ModeKind::Default, - settings: Settings { - model, - reasoning_effort: None, - developer_instructions: None, - }, - }, - |config| { - #[allow(clippy::expect_used)] - config - .features - .disable(Feature::DefaultModeRequestUserInput) - .expect("test config should allow feature update"); - }, - ) +async fn request_user_input_round_trip_in_default_mode_when_feature_enabled() -> anyhow::Result<()> +{ + request_user_input_round_trip_for_mode(ModeKind::Default, |config| { + #[allow(clippy::expect_used)] + config + .features + .enable(Feature::DefaultModeRequestUserInput) + .expect("test config should allow feature update"); + }) .await }