mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
48c16b8bcb
## Why `try_start_turn_if_idle` is the core helper for starting injected input only when the session is actually idle. It should stay focused on generic turn-lifecycle safety. The previous `ModeKind::Plan` guard mixed caller policy into that helper: Plan mode may choose not to auto-start some extension work, but that decision belongs at the extension or caller boundary rather than in the session injection primitive. ## What changed - Removed the `ModeKind::Plan` early return from `Session::try_start_turn_if_idle`. - Removed the now-unused `ModeKind` import from `core/src/session/inject.rs`. ## Testing Not run locally.
124 lines
4.3 KiB
Rust
124 lines
4.3 KiB
Rust
use super::input_queue::TurnInput;
|
|
use super::session::Session;
|
|
use super::turn_context::TurnContext;
|
|
use crate::state::ActiveTurn;
|
|
use crate::state::TurnState;
|
|
use crate::tasks::RegularTask;
|
|
use codex_protocol::models::ResponseItem;
|
|
use std::sync::Arc;
|
|
|
|
impl Session {
|
|
/// Returns the input if there is no active turn to inject into.
|
|
#[expect(
|
|
clippy::await_holding_invalid_type,
|
|
reason = "active turn checks and turn state updates must remain atomic"
|
|
)]
|
|
pub async fn inject_if_running(
|
|
&self,
|
|
input: Vec<ResponseItem>,
|
|
) -> Result<(), Vec<ResponseItem>> {
|
|
let mut active = self.active_turn.lock().await;
|
|
match active.as_mut() {
|
|
Some(active_turn) => {
|
|
self.input_queue
|
|
.extend_pending_input_for_turn_state(
|
|
active_turn.turn_state.as_ref(),
|
|
input.into_iter().map(TurnInput::ResponseItem).collect(),
|
|
)
|
|
.await;
|
|
Ok(())
|
|
}
|
|
None => Err(input),
|
|
}
|
|
}
|
|
|
|
/// Starts a regular turn with the provided items only if the session is idle.
|
|
pub(crate) async fn try_start_turn_if_idle(
|
|
self: &Arc<Self>,
|
|
input: Vec<ResponseItem>,
|
|
) -> Result<(), Vec<ResponseItem>> {
|
|
if input.is_empty() {
|
|
return Ok(());
|
|
}
|
|
if self.input_queue.has_trigger_turn_mailbox_items().await {
|
|
return Err(input);
|
|
}
|
|
|
|
let turn_state = {
|
|
let mut active_turn = self.active_turn.lock().await;
|
|
if active_turn.is_some() {
|
|
return Err(input);
|
|
}
|
|
let active_turn = active_turn.get_or_insert_with(ActiveTurn::default);
|
|
Arc::clone(&active_turn.turn_state)
|
|
};
|
|
|
|
if self.input_queue.has_trigger_turn_mailbox_items().await {
|
|
self.clear_reserved_idle_turn(&turn_state).await;
|
|
self.maybe_start_turn_for_pending_work().await;
|
|
return Err(input);
|
|
}
|
|
|
|
let turn_context = self
|
|
.new_default_turn_with_sub_id(uuid::Uuid::new_v4().to_string())
|
|
.await;
|
|
self.maybe_emit_unknown_model_warning_for_turn(turn_context.as_ref())
|
|
.await;
|
|
if self.input_queue.has_trigger_turn_mailbox_items().await {
|
|
self.clear_reserved_idle_turn(&turn_state).await;
|
|
self.maybe_start_turn_for_pending_work().await;
|
|
return Err(input);
|
|
}
|
|
let still_reserved = {
|
|
let active_turn = self.active_turn.lock().await;
|
|
active_turn.as_ref().is_some_and(|active_turn| {
|
|
active_turn.task.is_none() && Arc::ptr_eq(&active_turn.turn_state, &turn_state)
|
|
})
|
|
};
|
|
if !still_reserved {
|
|
self.clear_reserved_idle_turn(&turn_state).await;
|
|
return Err(input);
|
|
}
|
|
|
|
self.input_queue
|
|
.extend_pending_input_for_turn_state(
|
|
turn_state.as_ref(),
|
|
input.into_iter().map(TurnInput::ResponseItem).collect(),
|
|
)
|
|
.await;
|
|
self.start_task(turn_context, Vec::new(), RegularTask::new())
|
|
.await;
|
|
Ok(())
|
|
}
|
|
|
|
async fn clear_reserved_idle_turn(&self, turn_state: &Arc<tokio::sync::Mutex<TurnState>>) {
|
|
let mut active_turn_guard = self.active_turn.lock().await;
|
|
if let Some(active_turn) = active_turn_guard.as_ref()
|
|
&& active_turn.task.is_none()
|
|
&& Arc::ptr_eq(&active_turn.turn_state, turn_state)
|
|
{
|
|
*active_turn_guard = None;
|
|
}
|
|
}
|
|
|
|
/// Injects items into active work, or records them without starting a turn.
|
|
pub(crate) async fn inject_no_new_turn(
|
|
&self,
|
|
items: Vec<ResponseItem>,
|
|
current_turn_context: Option<&TurnContext>,
|
|
) {
|
|
let Err(items) = self.inject_if_running(items).await else {
|
|
return;
|
|
};
|
|
let default_turn_context;
|
|
let turn_context = match current_turn_context {
|
|
Some(turn_context) => turn_context,
|
|
None => {
|
|
default_turn_context = self.new_default_turn().await;
|
|
default_turn_context.as_ref()
|
|
}
|
|
};
|
|
self.record_conversation_items(turn_context, &items).await;
|
|
}
|
|
}
|