diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index 8cbf20f5c6..f78a98dde3 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -1317,7 +1317,11 @@ impl ModelClient { fn prepare_response_items_for_request(&self, input: &mut [ResponseItem]) { for item in input { - if item.id().is_some_and(|id| !id.is_prefixed()) { + // A provider can echo a bare reserved prefix or return an opaque ID. + if item + .id() + .is_some_and(|id| id.allocation().is_none() && !id.is_prefixed()) + { item.set_id(/*new_id*/ None); } if !self.state.content_item_kinds_enabled { @@ -2744,9 +2748,10 @@ fn map_response_stream( let routing_affinity = api_stream.routing_affinity.take(); let upstream_request_id = api_stream.upstream_request_id.take(); let interrupt = api_stream.interrupt.take(); + let events = crate::model_request::intercept_stream(Box::pin(api_stream), interceptors); let mut stream = map_response_events( upstream_request_id, - crate::model_request::intercept_stream(Box::pin(api_stream), interceptors), + events, session_telemetry, inference_trace_attempt, client, diff --git a/codex-rs/protocol/src/response_item_id.rs b/codex-rs/protocol/src/response_item_id.rs index 289091e486..d65a7061c0 100644 --- a/codex-rs/protocol/src/response_item_id.rs +++ b/codex-rs/protocol/src/response_item_id.rs @@ -86,6 +86,28 @@ impl ResponseItemId { } } + /// Attach the request's reservation, preserving a matching suffix or the provider's opaque ID. + pub fn with_generation_prefix(self, reservation: &Self) -> Self { + let Some(allocation) = reservation.allocation() else { + return self; + }; + let mut allocation = allocation.clone(); + // Responses may prepend an item kind, as in `msg_item042_0`. + let (kind, handle) = match self.split_once('_') { + Some((kind, handle)) if !kind.is_empty() && kind != reservation.as_str() => { + (Some(kind), handle) + } + _ => (None, self.as_str()), + }; + if let Some(suffix) = handle.strip_prefix(reservation.as_str()) + && suffix.starts_with('_') + { + allocation.prefix = kind.map(str::to_owned); + allocation.suffix = Some(suffix.to_owned()); + } + self.with_allocation(allocation) + } + pub fn allocation(&self) -> Option<&ResponseItemIdAllocation> { match self { Self::Text(_) => None,