mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Preserve turn attribution when queued mail wakes durable sleep (#51419)
## Why Queue-only agent mail can wake a sleeping session without starting a new task. The wake turn needs to retain the previous turn's attribution so delegated work reports completion to the initiating agent, including after a reload. ## What changed Capture the previous turn's attribution while reserving the pending-work turn. Reuse its trigger, parent and root turn IDs, and initiating agent path for queue-only wakes. Mail that explicitly starts a new task continues to supply its own attribution. ## Testing Extend regression coverage for live and reloaded sleep wakes, delegated and unknown attribution, settings changes while asleep, and subsequent task-triggering mail. Verify that a reloaded sleeping worker routes completion to the initiating turn. GitOrigin-RevId: 28e38474028bdbf35a0538c1202165f056bb3dc3
This commit is contained in:
@@ -657,6 +657,36 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::await_holding_invalid_type,
|
||||
reason = "The admission lock keeps the previous turn stable while capturing its attribution."
|
||||
)]
|
||||
pub(crate) async fn reserve_pending_work_turn(
|
||||
&self,
|
||||
) -> Option<(Arc<tokio::sync::Mutex<TurnState>>, TurnStartOptions)> {
|
||||
let mut active_turn = self.active_turn.lock().await;
|
||||
if active_turn.is_some() {
|
||||
return None;
|
||||
}
|
||||
let needs_new_turn = self.input_queue.has_trigger_turn_mailbox_items().await;
|
||||
if !needs_new_turn && !self.has_outstanding_durable_sleep() {
|
||||
return None;
|
||||
}
|
||||
let active_turn = active_turn.get_or_insert_with(ActiveTurn::default);
|
||||
let previous_options = if needs_new_turn {
|
||||
Default::default()
|
||||
} else {
|
||||
self.state
|
||||
.lock()
|
||||
.await
|
||||
.turn_attribution
|
||||
.as_ref()
|
||||
.map(codex_history::TurnAttribution::start_options)
|
||||
.unwrap_or_default()
|
||||
};
|
||||
Some((Arc::clone(&active_turn.turn_state), previous_options))
|
||||
}
|
||||
|
||||
pub(crate) async fn route_realtime_text_input(
|
||||
self: &Arc<Self>,
|
||||
text: String,
|
||||
|
||||
@@ -457,20 +457,12 @@ impl Session {
|
||||
self: &Arc<Self>,
|
||||
sub_id: String,
|
||||
) {
|
||||
if !self.input_queue.has_pending_mailbox_items().await
|
||||
|| (!self.input_queue.has_trigger_turn_mailbox_items().await
|
||||
&& !self.has_outstanding_durable_sleep())
|
||||
{
|
||||
if !self.input_queue.has_pending_mailbox_items().await {
|
||||
return;
|
||||
}
|
||||
|
||||
let turn_state = {
|
||||
let mut active_turn = self.active_turn.lock().await;
|
||||
if active_turn.is_some() {
|
||||
return;
|
||||
}
|
||||
let active_turn = active_turn.get_or_insert_with(ActiveTurn::default);
|
||||
Arc::clone(&active_turn.turn_state)
|
||||
let Some((turn_state, previous_options)) = self.reserve_pending_work_turn().await else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.services
|
||||
@@ -493,6 +485,7 @@ impl Session {
|
||||
|item| matches!(item, TurnInput::InterAgentCommunication(mail) if mail.trigger_turn),
|
||||
) {
|
||||
// Queue-only mail wakes durable sleep without selecting a new task's settings.
|
||||
start_options = previous_options;
|
||||
start_options.cyber_access_program = self
|
||||
.reference_context_item()
|
||||
.await
|
||||
@@ -511,14 +504,18 @@ impl Session {
|
||||
turn_context.turn_metadata_state.set_turn_trigger(trigger);
|
||||
}
|
||||
if let Some(id) = start_options.parent_turn_id {
|
||||
if let Some(initiating_agent_path) = input.iter().find_map(|item| {
|
||||
let TurnInput::InterAgentCommunication(communication) = item else {
|
||||
return None;
|
||||
};
|
||||
communication
|
||||
.trigger_turn
|
||||
.then(|| communication.author.clone())
|
||||
}) {
|
||||
if let Some(initiating_agent_path) = input
|
||||
.iter()
|
||||
.find_map(|item| {
|
||||
let TurnInput::InterAgentCommunication(communication) = item else {
|
||||
return None;
|
||||
};
|
||||
communication
|
||||
.trigger_turn
|
||||
.then(|| communication.author.clone())
|
||||
})
|
||||
.or(start_options.initiating_agent_path)
|
||||
{
|
||||
turn_context
|
||||
.turn_metadata_state
|
||||
.set_initiating_agent_path(initiating_agent_path);
|
||||
|
||||
Reference in New Issue
Block a user