//! Adapter between core tool dispatch objects and rollout-trace events.//!//! `codex-rollout-trace` owns the event schema and writer behavior. This module//! keeps the core-specific mapping from registry invocations/results out of the//! registry control flow.use crate::function_tool::FunctionCallError;use crate::tools::context::ToolCallSource;use crate::tools::context::ToolInvocation;use crate::tools::context::ToolOutput;use crate::tools::context::ToolPayload;use codex_rollout_trace::ExecutionStatus;use codex_rollout_trace::ToolDispatchInvocation;use codex_rollout_trace::ToolDispatchPayload;use codex_rollout_trace::ToolDispatchRequester;use codex_rollout_trace::ToolDispatchResult;use codex_rollout_trace::ToolDispatchTraceContext;/// Keeps registry early-return paths paired with trace end events.pub(crate) struct ToolDispatchTrace { context: ToolDispatchTraceContext,}impl ToolDispatchTrace { pub(crate) fn start(invocation: &ToolInvocation) -> Self { let context = invocation .session .services .rollout_thread_trace .start_tool_dispatch_trace(|| tool_dispatch_invocation(invocation)); Self { context } } pub(crate) fn record_completed( &self, invocation: &ToolInvocation, call_id: &str, payload: &ToolPayload, result: &dyn ToolOutput, ) { if !self.context.is_enabled() { return; } let Some(result_payload) = tool_dispatch_result(invocation, call_id, payload, result) else { return; }; let status = if result.success_for_logging() { ExecutionStatus::Completed } else { ExecutionStatus::Failed }; self.context.record_completed(status, result_payload); } pub(crate) fn record_failed(&self, error: &FunctionCallError) { self.context.record_failed(error); }}fn tool_dispatch_invocation(invocation: &ToolInvocation) -> Option<ToolDispatchInvocation> { let requester = match &invocation.source { ToolCallSource::Direct => ToolDispatchRequester::Model { model_visible_call_id: invocation.call_id.clone(), }, ToolCallSource::CodeMode { cell_id, runtime_tool_call_id, } => ToolDispatchRequester::CodeCell { runtime_cell_id: cell_id.clone(), runtime_tool_call_id: runtime_tool_call_id.clone(), }, }; Some(ToolDispatchInvocation { thread_id: invocation.session.thread_id.to_string(), codex_turn_id: invocation.turn.sub_id.clone(), tool_call_id: invocation.call_id.clone(), tool_name: invocation.tool_name.name.clone(), tool_namespace: invocation.tool_name.namespace.clone(), requester, payload: tool_dispatch_payload(&invocation.payload), })}fn tool_dispatch_result( invocation: &ToolInvocation, call_id: &str, payload: &ToolPayload, result: &dyn ToolOutput,) -> Option<ToolDispatchResult> { match invocation.source { ToolCallSource::Direct => Some(ToolDispatchResult::DirectResponse { response_item: result.to_response_item(call_id, payload), }), ToolCallSource::CodeMode { .. } => Some(ToolDispatchResult::CodeModeResponse { value: result.code_mode_result(payload), }), }}fn tool_dispatch_payload(payload: &ToolPayload) -> ToolDispatchPayload { match payload { ToolPayload::Function { arguments } => ToolDispatchPayload::Function { arguments: arguments.clone(), }, ToolPayload::ToolSearch { arguments } => ToolDispatchPayload::ToolSearch { arguments: arguments.clone(), }, ToolPayload::Custom { input } => ToolDispatchPayload::Custom { input: input.clone(), }, }}#[cfg(test)]#[path = "tool_dispatch_trace_tests.rs"]mod tests;