mirror of
https://github.com/openai/codex.git
synced 2026-10-30 19:18:49 +00:00
## Why Persisted turn-start events lack the originating root turn ID, so they cannot directly associate child turns with the root turn that initiated them. ## What changed - Add optional `root_turn_id` to `TurnStartedEvent`. Use the inherited root turn ID when available, otherwise the turn's own ID. - Centralize turn-start emission for regular turns, shell commands, and manual compaction so they record attribution consistently. - Carry attribution into `ThreadHistoryTurnChange` when processing turn-start events and retain it through completion in the history builder. - Keep older records compatible by defaulting missing attribution to `None`. ## Testing Add coverage for root and child turn attribution, persistence when tool collisions fail a turn before sampling, history change propagation, and deserialization of older turn-start records. GitOrigin-RevId: 54a80b828ca7d1c4c19f13d468c01d052e1b9130
85 lines
3.5 KiB
Rust
85 lines
3.5 KiB
Rust
use std::sync::Arc;
|
|
|
|
use crate::compact::InitialContextInjection;
|
|
use crate::context::world_state::WorldState;
|
|
use crate::hook_runtime::PostCompactHookOutcome;
|
|
use crate::hook_runtime::PreCompactHookOutcome;
|
|
use crate::hook_runtime::run_post_compact_hooks;
|
|
use crate::hook_runtime::run_pre_compact_hooks;
|
|
use crate::session::session::Session;
|
|
use crate::session::step_context::StepContext;
|
|
use crate::session::turn_context::TurnContext;
|
|
use codex_analytics::CompactionTrigger;
|
|
use codex_protocol::error::CodexErr;
|
|
use codex_protocol::error::Result as CodexResult;
|
|
use codex_protocol::items::ContextCompactionItem;
|
|
use codex_protocol::items::TurnItem;
|
|
use tokio_util::sync::CancellationToken;
|
|
|
|
/// Runs token-budget manual compaction as a normal compaction lifecycle.
|
|
///
|
|
/// Token-budget compaction skips model/server summarization and installs a fresh context window
|
|
/// instead. It is still modeled as compaction so compact hooks and `ContextCompaction` turn items
|
|
/// observe the same lifecycle as local or remote compaction.
|
|
pub(crate) async fn run_manual_compact_task(
|
|
sess: Arc<Session>,
|
|
turn_context: Arc<TurnContext>,
|
|
) -> CodexResult<()> {
|
|
sess.emit_turn_started(&turn_context).await;
|
|
|
|
// Manual compaction runs outside run_turn, so it captures its own current step.
|
|
let step_context = sess
|
|
.capture_step_context(Arc::clone(&turn_context), &CancellationToken::new())
|
|
.await?;
|
|
let world_state = Arc::new(sess.build_world_state_for_step(&step_context).await?);
|
|
run_compact_task_inner(&sess, &step_context, world_state, CompactionTrigger::Manual).await
|
|
}
|
|
|
|
/// Runs token-budget inline auto-compaction as a normal compaction lifecycle.
|
|
///
|
|
/// Token-budget compaction skips model/server summarization and installs a fresh context window
|
|
/// instead. It is still modeled as compaction so compact hooks and `ContextCompaction` turn items
|
|
/// observe the same lifecycle as local or remote compaction.
|
|
pub(crate) async fn run_inline_auto_compact_task(
|
|
sess: Arc<Session>,
|
|
step_context: Arc<StepContext>,
|
|
initial_context_injection: InitialContextInjection,
|
|
) -> CodexResult<()> {
|
|
let world_state = match initial_context_injection {
|
|
InitialContextInjection::BeforeLastUserMessage { world_state, .. } => world_state,
|
|
InitialContextInjection::DoNotInject => {
|
|
Arc::new(sess.build_world_state_for_step(&step_context).await?)
|
|
}
|
|
};
|
|
run_compact_task_inner(&sess, &step_context, world_state, CompactionTrigger::Auto).await
|
|
}
|
|
|
|
async fn run_compact_task_inner(
|
|
sess: &Arc<Session>,
|
|
step_context: &Arc<StepContext>,
|
|
world_state: Arc<WorldState>,
|
|
trigger: CompactionTrigger,
|
|
) -> CodexResult<()> {
|
|
let turn_context = &step_context.turn;
|
|
let pre_compact_outcome = run_pre_compact_hooks(sess, turn_context, trigger).await;
|
|
match pre_compact_outcome {
|
|
PreCompactHookOutcome::Continue => {}
|
|
PreCompactHookOutcome::Stopped => return Err(CodexErr::TurnAborted),
|
|
}
|
|
|
|
let compaction_item = TurnItem::ContextCompaction(ContextCompactionItem::new());
|
|
sess.emit_turn_item_started(turn_context, &compaction_item)
|
|
.await;
|
|
sess.start_new_context_window(step_context, world_state)
|
|
.await;
|
|
sess.emit_turn_item_completed(turn_context, compaction_item)
|
|
.await;
|
|
|
|
let post_compact_outcome = run_post_compact_hooks(sess, turn_context, trigger).await;
|
|
if let PostCompactHookOutcome::Stopped = post_compact_outcome {
|
|
return Err(CodexErr::TurnAborted);
|
|
}
|
|
|
|
Ok(())
|
|
}
|