Files
codex/codex-rs/core/src/tasks/lifecycle.rs
T
bkotsopoulos 810501d60d Add trace instrumentation for turn startup and response streaming (#56515)
## What changed

- Record time spent waiting for each stream event in `codex.response.receive_wait_ms` on the existing `handle_responses` span.
- Add trace spans for startup prewarm consumption, turn-start lifecycle callbacks, assistant text flushing, and response stream error handling.
- Include the turn ID and lifecycle phase on the turn-start span and the request type on the error-handling span.

GitOrigin-RevId: 7792508c830ef05b03aed01eb840ca7e6577ad0b
2026-10-23 16:09:06 +00:00

127 lines
4.8 KiB
Rust

use std::sync::Arc;
use codex_analytics::TurnAnalyticsMetadata;
use codex_extension_api::ExtensionData;
use codex_extension_api::ThreadIdleCause;
use codex_extension_api::TurnStartPhase;
use codex_protocol::error::CodexErrorDetails;
use codex_protocol::protocol::CodexErrorInfo;
use codex_protocol::protocol::TokenUsage;
use codex_protocol::protocol::TurnAbortReason;
use crate::session::session::Session;
use crate::session::turn_context::TurnContext;
impl Session {
#[tracing::instrument(level = "trace", skip_all, fields(turn_id = %turn_context.sub_id, phase = ?phase))]
pub(super) async fn emit_turn_start_lifecycle(
self: &Arc<Self>,
turn_context: &TurnContext,
token_usage_at_turn_start: Option<&TokenUsage>,
phase: TurnStartPhase,
) {
let metadata: Arc<dyn TurnAnalyticsMetadata> = turn_context.turn_metadata_state.clone();
turn_context.extension_data.insert(metadata);
turn_context
.extension_data
.insert(turn_context.session_telemetry.clone());
let collaboration_mode = turn_context.collaboration_mode();
for contributor in self.services.extensions.turn_lifecycle_contributors() {
if contributor.turn_start_phase(&self.services.thread_extension_data) != phase {
continue;
}
if phase == TurnStartPhase::RegularTaskStart
&& contributor.requires_mcp_runtime(&self.services.thread_extension_data)
{
self.refresh_mcp_if_dirty().await;
}
contributor
.on_turn_start(codex_extension_api::TurnStartInput {
turn_id: turn_context.sub_id.as_str(),
collaboration_mode: &collaboration_mode,
token_usage_at_turn_start,
session_store: &self.services.session_extension_data,
thread_store: &self.services.thread_extension_data,
turn_store: turn_context.extension_data.as_ref(),
})
.await;
}
}
pub(super) async fn emit_turn_stop_lifecycle(&self, turn_store: &ExtensionData) {
for contributor in self.services.extensions.turn_lifecycle_contributors() {
contributor
.on_turn_stop(codex_extension_api::TurnStopInput {
session_store: &self.services.session_extension_data,
thread_store: &self.services.thread_extension_data,
turn_store,
})
.await;
}
}
pub(crate) async fn emit_thread_idle_lifecycle_if_idle(&self, cause: ThreadIdleCause) {
let cause = {
let active_turn = self.active_turn.lock().await;
if active_turn.is_some() {
return;
}
if self.is_interrupted() {
ThreadIdleCause::Interrupted
} else {
cause
}
};
if self.input_queue.has_trigger_turn_mailbox_items().await {
return;
}
for contributor in self.services.extensions.thread_lifecycle_contributors() {
contributor
.on_thread_idle(codex_extension_api::ThreadIdleInput {
cause,
session_store: &self.services.session_extension_data,
thread_store: &self.services.thread_extension_data,
})
.await;
}
}
pub(super) async fn emit_turn_abort_lifecycle(
&self,
reason: TurnAbortReason,
turn_store: &ExtensionData,
) {
for contributor in self.services.extensions.turn_lifecycle_contributors() {
contributor
.on_turn_abort(codex_extension_api::TurnAbortInput {
reason: reason.clone(),
session_store: &self.services.session_extension_data,
thread_store: &self.services.thread_extension_data,
turn_store,
})
.await;
}
}
pub(crate) async fn emit_turn_error_lifecycle(
&self,
turn_context: &TurnContext,
error: CodexErrorInfo,
error_details: &CodexErrorDetails,
) {
for contributor in self.services.extensions.turn_lifecycle_contributors() {
contributor
.on_turn_error(codex_extension_api::TurnErrorInput {
turn_id: turn_context.sub_id.as_str(),
error: error.clone(),
error_details,
session_store: &self.services.session_extension_data,
thread_store: &self.services.thread_extension_data,
turn_store: turn_context.extension_data.as_ref(),
})
.await;
}
}
}