mirror of
https://github.com/openai/codex.git
synced 2026-10-24 18:18:41 +00:00
Add opt-in code-mode yielding on new user input (#48135)
## Why User input queued during a long-running code-mode `exec` or `wait` can remain pending until the call returns. Allow these calls to yield early so Codex can receive the new input while the cell continues running. ## What changed - Add the `instant_interrupt` feature flag, disabled by default. - Watch queued user input for each sampling request and pass a shared preemption signal to code-mode `exec` and `wait` calls, including calls received later in the same response. - Yield running cell IDs without stopping the cells, allowing subsequent `wait` calls to collect their results. ## Testing Add integration coverage for enabled and disabled behavior, repeated steering during `exec` and `wait`, input deferred during compaction, and later calls in the same response. Verify that direct tool results and queued user messages are preserved, and add a scenario snapshot showing continuation after a cell yields. GitOrigin-RevId: e33466a980d98e113bfb6c6a45c59de0cc7b3c1d
This commit is contained in:
@@ -14,6 +14,8 @@ use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::sync::watch;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tokio_util::task::AbortOnDropHandle;
|
||||
|
||||
static PENDING_MAILBOX_MESSAGES: Gauge = Gauge::new("core.mailbox.pending");
|
||||
|
||||
@@ -215,6 +217,29 @@ impl InputQueue {
|
||||
})
|
||||
}
|
||||
|
||||
/// Signal once a user message is queued for this sampling request.
|
||||
pub(crate) async fn watch_user_input(
|
||||
&self,
|
||||
active_turn: &Mutex<Option<ActiveTurn>>,
|
||||
sub_id: &str,
|
||||
interrupt: CancellationToken,
|
||||
) -> Option<AbortOnDropHandle<()>> {
|
||||
let turn_state = self.turn_state_for_sub_id(active_turn, sub_id).await?;
|
||||
// Subscribe before inspecting the queue so an arrival cannot be missed.
|
||||
let mut activity = self.activity_tx.subscribe();
|
||||
Some(AbortOnDropHandle::new(tokio::spawn(async move {
|
||||
loop {
|
||||
if turn_state.lock().await.pending_input.has_user_input() {
|
||||
interrupt.cancel();
|
||||
return;
|
||||
}
|
||||
if activity.changed().await.is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
/// Clear any pending waiters and input buffered for the current turn.
|
||||
pub(crate) async fn clear_pending(&self, active_turn: &ActiveTurn) {
|
||||
let mut turn_state = active_turn.turn_state.lock().await;
|
||||
@@ -363,6 +388,12 @@ impl InputQueue {
|
||||
}
|
||||
|
||||
impl TurnInputQueue {
|
||||
fn has_user_input(&self) -> bool {
|
||||
self.items
|
||||
.iter()
|
||||
.any(|input| matches!(input, TurnInput::UserInput { .. }))
|
||||
}
|
||||
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.items.is_empty()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user