From 7f9f93e057d7c73a84ea071aabcc88ddfe743150 Mon Sep 17 00:00:00 2001 From: Owen Lin Date: Thu, 19 Mar 2026 11:38:47 -0700 Subject: [PATCH 1/2] feat(tracing): tag app-server turn spans with turn_id --- codex-rs/app-server/src/app_server_tracing.rs | 1 + .../app-server/src/codex_message_processor.rs | 3 +++ .../src/message_processor/tracing_tests.rs | 6 +++++- codex-rs/app-server/src/outgoing_message.rs | 15 +++++++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/codex-rs/app-server/src/app_server_tracing.rs b/codex-rs/app-server/src/app_server_tracing.rs index 0bb38d5fba5..26fe8ca9997 100644 --- a/codex-rs/app-server/src/app_server_tracing.rs +++ b/codex-rs/app-server/src/app_server_tracing.rs @@ -107,6 +107,7 @@ fn app_server_request_span_template( app_server.api_version = "v2", app_server.client_name = field::Empty, app_server.client_version = field::Empty, + turn.id = field::Empty, ) } diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index b6ece83ddf6..c97268eb681 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -6024,6 +6024,9 @@ impl CodexMessageProcessor { match turn_id { Ok(turn_id) => { + self.outgoing + .record_request_turn_id(&request_id, &turn_id) + .await; let turn = Turn { id: turn_id.clone(), items: vec![], diff --git a/codex-rs/app-server/src/message_processor/tracing_tests.rs b/codex-rs/app-server/src/message_processor/tracing_tests.rs index e39484cedbc..58499745bd5 100644 --- a/codex-rs/app-server/src/message_processor/tracing_tests.rs +++ b/codex-rs/app-server/src/message_processor/tracing_tests.rs @@ -580,7 +580,7 @@ async fn turn_start_jsonrpc_span_parents_core_turn_spans() -> Result<()> { parent_span_id: remote_parent_span_id, context: remote_trace, } = RemoteTrace::new("00000000000000000000000000000077", "0000000000000088"); - let _: TurnStartResponse = harness + let turn_start_response: TurnStartResponse = harness .request( ClientRequest::TurnStart { request_id: RequestId::Integer(3), @@ -628,6 +628,10 @@ async fn turn_start_jsonrpc_span_parents_core_turn_spans() -> Result<()> { assert_eq!(server_request_span.parent_span_id, remote_parent_span_id); assert!(server_request_span.parent_span_is_remote); assert_eq!(server_request_span.span_context.trace_id(), remote_trace_id); + assert_eq!( + span_attr(server_request_span, "turn.id"), + Some(turn_start_response.turn.id.as_str()) + ); assert_span_descends_from(&spans, core_turn_span, server_request_span); harness.shutdown().await; diff --git a/codex-rs/app-server/src/outgoing_message.rs b/codex-rs/app-server/src/outgoing_message.rs index 2ab8fb04bd6..67761525b36 100644 --- a/codex-rs/app-server/src/outgoing_message.rs +++ b/codex-rs/app-server/src/outgoing_message.rs @@ -75,6 +75,10 @@ impl RequestContext { pub(crate) fn span(&self) -> Span { self.span.clone() } + + fn record_turn_id(&self, turn_id: &str) { + self.span.record("turn.id", turn_id); + } } #[derive(Debug, Clone)] @@ -217,6 +221,17 @@ impl OutgoingMessageSender { .and_then(RequestContext::request_trace) } + pub(crate) async fn record_request_turn_id( + &self, + request_id: &ConnectionRequestId, + turn_id: &str, + ) { + let request_contexts = self.request_contexts.lock().await; + if let Some(request_context) = request_contexts.get(request_id) { + request_context.record_turn_id(turn_id); + } + } + async fn take_request_context( &self, request_id: &ConnectionRequestId, From 8e042a093cceac78a48d1d8cabd6f6247e51532f Mon Sep 17 00:00:00 2001 From: Owen Lin Date: Thu, 19 Mar 2026 11:46:29 -0700 Subject: [PATCH 2/2] also record turn id for turn/steer and turn/interrupt --- codex-rs/app-server/src/codex_message_processor.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index c97268eb681..13e863b7fc5 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -6079,6 +6079,9 @@ impl CodexMessageProcessor { .await; return; } + self.outgoing + .record_request_turn_id(&request_id, ¶ms.expected_turn_id) + .await; if let Err(error) = Self::validate_v2_input_limit(¶ms.input) { self.outgoing.send_error(request_id, error).await; return; @@ -6559,7 +6562,10 @@ impl CodexMessageProcessor { request_id: ConnectionRequestId, params: TurnInterruptParams, ) { - let TurnInterruptParams { thread_id, .. } = params; + let TurnInterruptParams { thread_id, turn_id } = params; + self.outgoing + .record_request_turn_id(&request_id, &turn_id) + .await; let (thread_uuid, thread) = match self.load_thread(&thread_id).await { Ok(v) => v,