mirror of
https://github.com/openai/codex.git
synced 2026-10-19 08:16:48 +00:00
Enable instant interrupts by default (#51812)
## What changed
Enable `instant_interrupt` by default so new user input can preempt responses and yield foreground code-mode observations. Keep the feature marked as under development and update feature-policy tests to allow this exception.
Explicitly disable instant interrupts in core, TUI, and Python SDK tests that depend on responses completing before steering or processing subsequent input.
GitOrigin-RevId: a8cab2d6c445bfdbd10b0b2abcf7893d32ee8595
(cherry picked from commit 406b0c4446)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 ({:?})",
|
||||
|
||||
@@ -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()
|
||||
),
|
||||
|
||||
@@ -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.")
|
||||
|
||||
Reference in New Issue
Block a user