mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Preserve turn attribution across recovery and compaction (#51402)
## Why Recovering an interrupted turn replaced its original trigger with `retry` and restored only its root lineage from model context. Turns suspended before that context was written could lose their attribution entirely. ## What changed - Persist regular-turn attribution in `TurnStartedEvent` and compaction resume metadata, including the trigger, parent and root turn IDs, and initiating agent path. - Restore matching attribution during recovery, keep unknown triggers absent, and retain the model-context root fallback for older records. - Reconstruct attribution from surviving turns across rollback and late completion or abort events, and clear inherited attribution when spawning a forked agent. ## Testing Add coverage for repeated cold recovery, suspension before context persistence, legacy rollouts, fresh attribution on subsequent turns, compaction checkpoints, rollback, and late terminal events. GitOrigin-RevId: 85b5763e8fbd48a1ea2c07cfda8196e63c2cd33a
This commit is contained in:
@@ -2156,6 +2156,11 @@ pub struct TurnCompleteEvent {
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
|
||||
pub struct TurnStartedEvent {
|
||||
/// Provenance of a regular turn, persisted before startup work can be suspended.
|
||||
/// Absent on older records and non-regular tasks.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub turn_attribution: Option<crate::turn_input::TurnAttribution>,
|
||||
pub turn_id: String,
|
||||
/// ID of the originating turn in the root thread; equals `turn_id` for root turns.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
@@ -3280,8 +3285,7 @@ pub struct TurnContextNetworkItem {
|
||||
pub struct TurnContextItem {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub turn_id: Option<String>,
|
||||
/// Root turn that owns this subagent turn's attribution.
|
||||
/// Only set for subagent turns; persisted so resume keeps the scope frozen at turn start.
|
||||
/// Root turn that owns this turn's attribution, retained across recovery.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub root_turn_id: Option<String>,
|
||||
/// Plugin selection captured for this turn. Absent in older histories.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Turn-input request and result types shared by Core's submission APIs.
|
||||
|
||||
use crate::AgentPath;
|
||||
use crate::models::ResponseItem;
|
||||
use crate::protocol::AdditionalContextEntry;
|
||||
use crate::protocol::InterAgentCommunication;
|
||||
@@ -52,6 +53,29 @@ pub struct TurnInputRequest {
|
||||
pub trace: Option<W3cTraceContext>,
|
||||
}
|
||||
|
||||
/// Attribution retained across recovery, sleep, and model-context compaction.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
|
||||
pub struct TurnAttribution {
|
||||
pub turn_id: String,
|
||||
pub turn_trigger: Option<String>,
|
||||
pub parent_turn_id: Option<String>,
|
||||
pub initiating_agent_path: Option<AgentPath>,
|
||||
pub root_turn_id: Option<String>,
|
||||
}
|
||||
|
||||
impl TurnAttribution {
|
||||
/// Restores provenance without changing the thread's execution settings.
|
||||
pub fn start_options(&self) -> TurnStartOptions {
|
||||
TurnStartOptions {
|
||||
turn_trigger: self.turn_trigger.clone(),
|
||||
parent_turn_id: self.parent_turn_id.clone(),
|
||||
initiating_agent_path: self.initiating_agent_path.clone(),
|
||||
root_turn_id: self.root_turn_id.clone(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Request to resume sampling for an interrupted regular turn.
|
||||
///
|
||||
/// Sampling restarts under `turn_id`, which must be the ID already recorded
|
||||
@@ -171,6 +195,8 @@ pub struct TurnStartOptions {
|
||||
pub service_tier: Option<String>,
|
||||
/// Parent turn lineage recorded if this request starts a new turn.
|
||||
pub parent_turn_id: Option<String>,
|
||||
/// Agent owning the parent turn, retained when recovering delegated work.
|
||||
pub initiating_agent_path: Option<AgentPath>,
|
||||
/// Causal root turn lineage recorded if this request starts a new turn.
|
||||
pub root_turn_id: Option<String>,
|
||||
/// Explicit cyber treatment for this turn. Omission preserves the backend's
|
||||
|
||||
Reference in New Issue
Block a user