diff --git a/codex-rs/core/tests/suite/guardian_retained_context.rs b/codex-rs/core/tests/suite/guardian_retained_context.rs index 8a4894543c..adc7804806 100644 --- a/codex-rs/core/tests/suite/guardian_retained_context.rs +++ b/codex-rs/core/tests/suite/guardian_retained_context.rs @@ -294,6 +294,11 @@ async fn streamed_question_precedes_reply_across_resume( .with_model("gpt-5.4") .with_history_mode(ThreadHistoryMode::Paginated) .with_config(|config| { + // This fixture retains the question by allowing its response to finish. + config + .features + .disable(Feature::InstantInterrupt) + .expect("disable InstantInterrupt feature"); config.experimental_thread_store = ThreadStoreConfig::Local; }) .build_with_streaming_server(&server) diff --git a/codex-rs/core/tests/suite/pending_input.rs b/codex-rs/core/tests/suite/pending_input.rs index c2e5d10013..e8ae9aa96c 100644 --- a/codex-rs/core/tests/suite/pending_input.rs +++ b/codex-rs/core/tests/suite/pending_input.rs @@ -1964,7 +1964,16 @@ async fn user_input_does_not_preempt_after_reasoning_item() { let (server, _completions) = start_streaming_sse_server(vec![first_chunks, response_completed_chunks("resp-2")]).await; - let codex = build_codex(&server).await; + let codex = test_codex() + .with_config(|config| { + config.update_plan_enabled = true; + config.features.disable(Feature::InstantInterrupt).unwrap(); + }) + .with_model("gpt-5.4") + .build_with_streaming_server(&server) + .await + .expect("build streaming Codex test session") + .codex; submit_user_input(&codex, "first prompt").await; @@ -2390,6 +2399,7 @@ async fn steered_user_input_waits_for_model_continuation_after_mid_turn_compact( let codex = test_codex() .with_model("gpt-5.4") .with_config(|config| { + config.features.disable(Feature::InstantInterrupt).unwrap(); config.model_provider.name = "OpenAI (test)".to_string(); config.model_provider.supports_websockets = false; config.model_auto_compact_token_limit = Some(200); @@ -2475,6 +2485,7 @@ async fn steered_user_input_follows_compact_when_only_the_steer_needs_follow_up( let codex = test_codex() .with_model("gpt-5.4") .with_config(|config| { + config.features.disable(Feature::InstantInterrupt).unwrap(); config.model_provider.name = "OpenAI (test)".to_string(); config.model_provider.supports_websockets = false; config.model_auto_compact_token_limit = Some(200); @@ -2592,6 +2603,7 @@ async fn steered_user_input_waits_when_tool_output_triggers_compact_before_next_ let test = test_codex() .with_model("gpt-5.4") .with_config(|config| { + config.features.disable(Feature::InstantInterrupt).unwrap(); config.model_provider.name = "OpenAI (test)".to_string(); config.model_provider.supports_websockets = false; config.model_auto_compact_token_limit = Some(200); diff --git a/codex-rs/core/tests/suite/scenarios_guardian_heartbeat.rs b/codex-rs/core/tests/suite/scenarios_guardian_heartbeat.rs index b6f8bf894c..3a8a429f51 100644 --- a/codex-rs/core/tests/suite/scenarios_guardian_heartbeat.rs +++ b/codex-rs/core/tests/suite/scenarios_guardian_heartbeat.rs @@ -58,6 +58,8 @@ async fn heartbeat_repetitions_keep_later_human_authorization() -> anyhow::Resul .features .disable(Feature::EnableRequestCompression) .unwrap(); + // Keep the acknowledgement complete before the next heartbeat is processed. + config.features.disable(Feature::InstantInterrupt).unwrap(); config.approvals_reviewer = ApprovalsReviewer::AutoReview; config.permissions.approval_policy = Constrained::allow_any(AskForApproval::OnRequest); config diff --git a/codex-rs/features/src/lib.rs b/codex-rs/features/src/lib.rs index 44d86eb43f..c68bc3bb74 100644 --- a/codex-rs/features/src/lib.rs +++ b/codex-rs/features/src/lib.rs @@ -1159,7 +1159,7 @@ pub const FEATURES: &[FeatureSpec] = &[ id: Feature::InstantInterrupt, key: "instant_interrupt", stage: Stage::UnderDevelopment, - default_enabled: false, + default_enabled: true, }, FeatureSpec { id: Feature::CodeModeOnly, diff --git a/codex-rs/features/src/tests.rs b/codex-rs/features/src/tests.rs index 30555b50ad..5891dc0c9b 100644 --- a/codex-rs/features/src/tests.rs +++ b/codex-rs/features/src/tests.rs @@ -32,7 +32,8 @@ fn sleep_tool_config_rejects_unknown_mode() { #[test] fn under_development_features_are_disabled_by_default() { for spec in crate::FEATURES { - if matches!(spec.stage, Stage::UnderDevelopment) { + // Instant interrupts are enabled by default while retaining their development stage. + if matches!(spec.stage, Stage::UnderDevelopment) && spec.id != Feature::InstantInterrupt { assert_eq!( spec.default_enabled, false, "feature `{}` is under development and must be disabled by default", @@ -121,7 +122,8 @@ fn codex_apps_mcp_protocol_can_be_enabled_independently_of_generic_mcp() { #[test] fn default_enabled_features_are_stable() { for spec in crate::FEATURES { - if spec.default_enabled { + // Instant interrupts are enabled by default while retaining their development stage. + if spec.default_enabled && spec.id != Feature::InstantInterrupt { assert!( matches!(spec.stage, Stage::Stable | Stage::Removed), "feature `{}` is enabled by default but is not stable/removed ({:?})", diff --git a/codex-rs/tui/src/app/tests/safety_buffering.rs b/codex-rs/tui/src/app/tests/safety_buffering.rs index 1d72fae291..71a8f2b5bb 100644 --- a/codex-rs/tui/src/app/tests/safety_buffering.rs +++ b/codex-rs/tui/src/app/tests/safety_buffering.rs @@ -540,6 +540,12 @@ async fn run_safety_retry( let model_catalog_path = codex_home.path().join("models.json"); std::fs::write(&model_catalog_path, serde_json::to_vec(&model_catalog)?)?; let model_catalog_path = toml::Value::String(model_catalog_path.display().to_string()); + // The committed-steer case counts usage from the response that finishes before steering. + let instant_interrupt_config = if committed_steer.is_some() { + "instant_interrupt = false" + } else { + "" + }; std::fs::write( codex_home.path().join("config.toml"), format!( @@ -557,6 +563,7 @@ stream_max_retries = 0 [features] goals = true +{instant_interrupt_config} "#, server.uri() ), diff --git a/sdk/python/tests/test_app_server_turn_controls.py b/sdk/python/tests/test_app_server_turn_controls.py index dd994b88f9..ba85c79624 100644 --- a/sdk/python/tests/test_app_server_turn_controls.py +++ b/sdk/python/tests/test_app_server_turn_controls.py @@ -20,7 +20,7 @@ def test_turn_steer_adds_follow_up_input(tmp_path) -> None: ) with Codex(config=harness.app_server_config()) as codex: - thread = codex.thread_start() + thread = codex.thread_start(config={"features.instant_interrupt": False}) turn = thread.turn("Start a steerable turn.") harness.responses.wait_for_requests(1) steer = turn.steer("Use this steering input.")