Add typed trace state model

This commit is contained in:
Albin Cassirer
2026-04-20 17:04:16 -07:00
parent 066f172cd6
commit a02e74985f
8 changed files with 1621 additions and 764 deletions
+3
View File
@@ -5,6 +5,9 @@
//! `state.json` shape consumed by the standalone trace viewer.
mod local;
mod model;
mod payload;
mod raw_event;
mod reduce;
pub use local::CODEX_TRACE_ROOT_ENV;
+176
View File
@@ -0,0 +1,176 @@
use serde::Deserialize;
use serde::Serialize;
use crate::payload::RawPayloadId;
use super::AgentThreadId;
use super::CodeCellId;
use super::CodexTurnId;
use super::CompactionId;
use super::ConversationItemId;
use super::EdgeId;
use super::InferenceCallId;
use super::ModelVisibleCallId;
use super::ToolCallId;
use super::session::ExecutionWindow;
/// One logical transcript item or transcript boundary.
///
/// The reducer builds conversation items primarily from inference request and
/// response payloads. Runtime objects can be listed in `produced_by`, but they
/// must not rewrite what the item body says the model saw. Structural items,
/// such as compaction markers, live in the same ordered list so conversation
/// views can show where the live history changed.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct ConversationItem {
pub(crate) item_id: ConversationItemId,
pub(crate) thread_id: AgentThreadId,
/// Runtime activation that first introduced this item locally, when known.
pub(crate) codex_turn_id: Option<CodexTurnId>,
pub(crate) first_seen_at_unix_ms: i64,
pub(crate) role: ConversationRole,
/// Codex channel for assistant/tool content, when the item is channel-specific.
pub(crate) channel: Option<ConversationChannel>,
pub(crate) kind: ConversationItemKind,
pub(crate) body: ConversationBody,
/// Protocol/model `call_id` for function/custom tool call and output items.
pub(crate) call_id: Option<ModelVisibleCallId>,
/// Runtime or control-plane objects that caused this conversation item to exist.
pub(crate) produced_by: Vec<ProducerRef>,
}
/// Model-visible role assigned to a conversation item.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ConversationRole {
System,
Developer,
User,
Assistant,
Tool,
}
/// Codex channel for model-visible content.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ConversationChannel {
Analysis,
Commentary,
Final,
/// Remote compaction summaries are reintroduced as assistant summary-channel content.
Summary,
}
/// Responses item category after normalization into the reduced transcript.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ConversationItemKind {
Message,
Reasoning,
FunctionCall,
FunctionCallOutput,
CustomToolCall,
CustomToolCallOutput,
/// Structural marker inserted where live history was replaced by compaction.
CompactionMarker,
}
/// Ordered content parts for a reduced conversation item.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct ConversationBody {
/// Renderable model-visible parts. Raw payload refs are used when the bytes
/// are too large or too structured for the normal conversation path.
pub(crate) parts: Vec<ConversationPart>,
}
/// One model-visible part inside a conversation item.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub(crate) enum ConversationPart {
Text {
text: String,
},
/// A model-provided summary of content whose full form may also be present.
///
/// Reasoning summaries are not interchangeable with raw reasoning text:
/// both can be present in one payload, and replay/debug tooling needs to
/// preserve which representation the model actually returned.
Summary {
text: String,
},
/// Opaque model-visible content that is intentionally not decoded here.
///
/// Reasoning can be carried as `encrypted_content` with no readable text.
/// Keeping that blob inline makes it part of item identity, unlike a raw
/// payload reference whose ID changes every time the same item is replayed
/// in a later inference request.
Encoded {
label: String,
value: String,
},
/// Small JSON-ish body represented by a summary plus a raw ref.
Json {
summary: String,
raw_payload_id: RawPayloadId,
},
Code {
language: String,
source: String,
},
/// Large or uncommon payload that should be lazy-loaded from details UI.
PayloadRef {
label: String,
raw_payload_id: RawPayloadId,
},
}
/// Explanation for where a conversation item came from.
///
/// This is deliberately plural at the call site: a function output can be both
/// model-visible conversation and the product of a runtime tool call.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub(crate) enum ProducerRef {
UserInput,
Inference { inference_call_id: InferenceCallId },
Tool { tool_call_id: ToolCallId },
CodeCell { code_cell_id: CodeCellId },
InteractionEdge { edge_id: EdgeId },
Compaction { compaction_id: CompactionId },
Harness,
}
/// One outbound inference request and its response metadata.
///
/// Full upstream request/response bodies live behind raw payload refs. The
/// request/response item ID lists are the reduced, model-visible snapshot.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct InferenceCall {
pub(crate) inference_call_id: InferenceCallId,
pub(crate) thread_id: AgentThreadId,
pub(crate) codex_turn_id: CodexTurnId,
pub(crate) execution: ExecutionWindow,
pub(crate) model: String,
pub(crate) provider_name: String,
/// Upstream request ID returned by HTTP/proxy/engine infrastructure.
pub(crate) upstream_request_id: Option<String>,
/// Complete ordered input snapshot sent with this request.
pub(crate) request_item_ids: Vec<ConversationItemId>,
/// Ordered output items produced by this response.
pub(crate) response_item_ids: Vec<ConversationItemId>,
/// Runtime tool calls whose model-visible call item came from this response.
pub(crate) tool_call_ids_started_by_response: Vec<ToolCallId>,
pub(crate) usage: Option<TokenUsage>,
pub(crate) raw_request_payload_id: RawPayloadId,
/// Full upstream response payload. `None` while running or after pre-stream failures.
pub(crate) raw_response_payload_id: Option<RawPayloadId>,
}
/// Token usage summary for one inference call.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TokenUsage {
pub(crate) input_tokens: u64,
pub(crate) cached_input_tokens: u64,
pub(crate) output_tokens: u64,
pub(crate) reasoning_output_tokens: u64,
}
+122
View File
@@ -0,0 +1,122 @@
//! Reduced rollout trace model.
//!
//! These types describe the deterministic replay output. They intentionally
//! separate model-visible conversation from runtime/debug objects.
use std::collections::BTreeMap;
use serde::Deserialize;
use serde::Serialize;
use crate::payload::RawPayloadId;
use crate::payload::RawPayloadRef;
mod conversation;
mod runtime;
mod session;
pub(crate) use conversation::*;
pub(crate) use runtime::*;
pub(crate) use session::*;
/// Codex conversation/session UUID.
pub(crate) type AgentThreadId = String;
/// Stable multi-agent routing path such as `/root` or `/root/search_docs`.
pub(crate) type AgentPath = String;
/// Runtime submission/activation UUID. This is not a chat turn.
pub(crate) type CodexTurnId = String;
/// Reduced transcript item ID assigned by the trace reducer.
pub(crate) type ConversationItemId = String;
/// Local ID for one outbound upstream inference request.
pub(crate) type InferenceCallId = String;
/// Reducer-owned ID for one runtime tool-call object.
pub(crate) type ToolCallId = String;
/// Responses `call_id` / custom-tool call ID visible in inference payloads.
pub(crate) type ModelVisibleCallId = String;
/// Tool invocation ID assigned inside the code-mode JavaScript runtime.
pub(crate) type CodeModeRuntimeToolId = String;
/// Reducer-owned ID for one model-authored `exec` JavaScript cell.
pub(crate) type CodeCellId = String;
/// Process/session ID returned by Codex's terminal runtime.
pub(crate) type TerminalId = String;
/// Reducer-owned ID for one command/write/poll operation against a terminal.
pub(crate) type TerminalOperationId = String;
/// Reducer-owned ID for one installed conversation-history checkpoint.
pub(crate) type CompactionId = String;
/// Reducer-owned ID for one upstream request that computes a compaction.
pub(crate) type CompactionRequestId = String;
/// Reducer-owned ID for one information-flow edge.
pub(crate) type EdgeId = String;
/// Reducer-owned ID for request/log correlation metadata.
#[allow(dead_code)]
pub(crate) type CorrelationId = String;
/// Canonical reduced graph for one Codex rollout.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct RolloutTrace {
pub(crate) schema_version: u32,
/// Unique identity for this trace capture.
///
/// `rollout_id` names the Codex rollout/session being observed. `trace_id`
/// names the diagnostic artifact produced for that rollout, which keeps
/// storage/replay identity separate from the product-level session identity.
pub(crate) trace_id: String,
/// CLI-visible rollout/run identity. Higher-level experiment/sample IDs wrap this object.
pub(crate) rollout_id: String,
pub(crate) started_at_unix_ms: i64,
/// Wall-clock timestamp for terminal rollout status. `None` means running or partial trace.
pub(crate) ended_at_unix_ms: Option<i64>,
pub(crate) status: RolloutStatus,
pub(crate) root_thread_id: AgentThreadId,
pub(crate) threads: BTreeMap<AgentThreadId, AgentThread>,
pub(crate) codex_turns: BTreeMap<CodexTurnId, CodexTurn>,
pub(crate) conversation_items: BTreeMap<ConversationItemId, ConversationItem>,
pub(crate) inference_calls: BTreeMap<InferenceCallId, InferenceCall>,
/// Model-authored `exec` JavaScript cells keyed by reducer-owned cell ID.
pub(crate) code_cells: BTreeMap<CodeCellId, CodeCell>,
pub(crate) tool_calls: BTreeMap<ToolCallId, ToolCall>,
/// Terminal runtime sessions keyed by process/session ID returned by the runtime.
pub(crate) terminal_sessions: BTreeMap<TerminalId, TerminalSession>,
/// Commands/writes/polls against terminals keyed by reducer-owned operation ID.
pub(crate) terminal_operations: BTreeMap<TerminalOperationId, TerminalOperation>,
/// Installed compaction checkpoints keyed by checkpoint ID.
pub(crate) compactions: BTreeMap<CompactionId, Compaction>,
/// Upstream remote compaction calls keyed by local request ID.
pub(crate) compaction_requests: BTreeMap<CompactionRequestId, CompactionRequest>,
/// Information-flow edges between threads, cells, tools, and runtime resources.
pub(crate) interaction_edges: BTreeMap<EdgeId, InteractionEdge>,
/// Raw JSON payloads keyed by raw-payload ID. Most point at files outside this object.
pub(crate) raw_payloads: BTreeMap<RawPayloadId, RawPayloadRef>,
}
impl RolloutTrace {
/// Builds an empty reduced trace that a reducer can populate.
pub(crate) fn new(
schema_version: u32,
trace_id: String,
rollout_id: String,
root_thread_id: AgentThreadId,
started_at_unix_ms: i64,
) -> Self {
Self {
schema_version,
trace_id,
rollout_id,
started_at_unix_ms,
ended_at_unix_ms: None,
status: RolloutStatus::Running,
root_thread_id,
threads: BTreeMap::new(),
codex_turns: BTreeMap::new(),
conversation_items: BTreeMap::new(),
inference_calls: BTreeMap::new(),
code_cells: BTreeMap::new(),
tool_calls: BTreeMap::new(),
terminal_sessions: BTreeMap::new(),
terminal_operations: BTreeMap::new(),
compactions: BTreeMap::new(),
compaction_requests: BTreeMap::new(),
interaction_edges: BTreeMap::new(),
raw_payloads: BTreeMap::new(),
}
}
}
+331
View File
@@ -0,0 +1,331 @@
use serde::Deserialize;
use serde::Serialize;
use crate::payload::RawPayloadId;
use crate::raw_event::RawEventSeq;
use super::AgentPath;
use super::AgentThreadId;
use super::CodeCellId;
use super::CodeModeRuntimeToolId;
use super::CodexTurnId;
use super::CompactionId;
use super::CompactionRequestId;
use super::ConversationItemId;
use super::EdgeId;
use super::ModelVisibleCallId;
use super::TerminalId;
use super::TerminalOperationId;
use super::ToolCallId;
use super::session::ExecutionWindow;
/// Runtime/debug object for one model-authored `exec` cell.
///
/// The JavaScript source and custom-tool outputs are still conversation items;
/// this object tracks the code-mode runtime boundary and nested runtime work.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct CodeCell {
/// Reducer-owned graph id derived from the model-visible `exec` call id.
/// Runtime cell ids are stored separately because they are only handles for
/// later waits and nested code-mode tools.
pub(crate) code_cell_id: CodeCellId,
pub(crate) model_visible_call_id: ModelVisibleCallId,
pub(crate) thread_id: AgentThreadId,
pub(crate) codex_turn_id: CodexTurnId,
/// Conversation item containing the model-authored JavaScript.
pub(crate) source_item_id: ConversationItemId,
pub(crate) output_item_ids: Vec<ConversationItemId>,
/// Raw code-mode runtime/session id, useful when matching runtime payloads.
pub(crate) runtime_cell_id: Option<String>,
/// Full JS-cell runtime window; yielded cells can outlive the initial custom call.
pub(crate) execution: ExecutionWindow,
pub(crate) runtime_status: CodeCellRuntimeStatus,
pub(crate) initial_response_at_unix_ms: Option<i64>,
pub(crate) initial_response_seq: Option<RawEventSeq>,
pub(crate) yielded_at_unix_ms: Option<i64>,
pub(crate) yielded_seq: Option<RawEventSeq>,
pub(crate) source_js: String,
pub(crate) nested_tool_call_ids: Vec<ToolCallId>,
pub(crate) wait_tool_call_ids: Vec<ToolCallId>,
}
/// Code-mode runtime lifecycle.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum CodeCellRuntimeStatus {
/// The `exec` request has been accepted but the runtime has not yet started user code.
Starting,
/// Runtime is executing JavaScript and has not yet yielded or terminated.
Running,
/// Initial `exec` returned while JavaScript kept running in the background.
Yielded,
/// Runtime reached a normal terminal result.
Completed,
/// Runtime reached an error terminal result.
Failed,
/// Runtime was explicitly terminated.
Terminated,
}
/// Installed conversation-history replacement boundary.
///
/// Duration-bearing upstream requests live in `CompactionRequest`. This object
/// is the checkpoint where replacement history became the live thread history.
/// The boundary marker and the model-visible summary are separate conversation
/// items: the marker says where history was replaced, while the summary is part
/// of `replacement_item_ids` when the compact endpoint returned one.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct Compaction {
pub(crate) compaction_id: CompactionId,
pub(crate) thread_id: AgentThreadId,
pub(crate) codex_turn_id: CodexTurnId,
pub(crate) installed_at_unix_ms: i64,
/// Structural conversation item marking where pre-compaction history ended.
pub(crate) marker_item_id: ConversationItemId,
/// Upstream compaction request attempts that contributed to this checkpoint.
pub(crate) request_ids: Vec<CompactionRequestId>,
/// Logical conversation items present immediately before replacement.
pub(crate) input_item_ids: Vec<ConversationItemId>,
/// Replacement conversation items installed by the checkpoint.
pub(crate) replacement_item_ids: Vec<ConversationItemId>,
}
/// One upstream remote request made while computing a compaction checkpoint.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct CompactionRequest {
pub(crate) compaction_request_id: CompactionRequestId,
pub(crate) compaction_id: CompactionId,
pub(crate) thread_id: AgentThreadId,
pub(crate) codex_turn_id: CodexTurnId,
pub(crate) execution: ExecutionWindow,
pub(crate) model: String,
pub(crate) provider_name: String,
pub(crate) raw_request_payload_id: RawPayloadId,
/// Full compaction response payload. `None` while running or after pre-response failures.
pub(crate) raw_response_payload_id: Option<RawPayloadId>,
}
/// Runtime operation requested by the model, a JS code cell, or Codex itself.
///
/// A `ToolCall` is not a chat transcript row. Model-visible call/output items
/// link to it through `model_visible_*_item_ids`; runtime-only tools can have
/// empty model-visible lists.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct ToolCall {
pub(crate) tool_call_id: ToolCallId,
/// Model-visible protocol call ID, if the model directly requested this tool.
pub(crate) model_visible_call_id: Option<ModelVisibleCallId>,
/// Code-mode runtime's internal tool invocation ID, if this call came from JS.
pub(crate) code_mode_runtime_tool_id: Option<CodeModeRuntimeToolId>,
pub(crate) thread_id: AgentThreadId,
/// Runtime activation that started the tool. Background work may outlive this turn.
pub(crate) started_by_codex_turn_id: Option<CodexTurnId>,
pub(crate) execution: ExecutionWindow,
pub(crate) requester: ToolCallRequester,
pub(crate) kind: ToolCallKind,
pub(crate) model_visible_call_item_ids: Vec<ConversationItemId>,
pub(crate) model_visible_output_item_ids: Vec<ConversationItemId>,
/// Terminal operation started by this tool, when the tool touched a terminal.
pub(crate) terminal_operation_id: Option<TerminalOperationId>,
pub(crate) summary: ToolCallSummary,
/// Original invocation at the Codex tool boundary.
///
/// Direct model tools store the model's function/custom call payload here.
/// Code-mode nested tools store the JSON call made by model-authored JS.
/// Runtime protocol events are deliberately kept separate below because
/// they describe how Codex executed the request, not what the caller sent.
pub(crate) raw_invocation_payload_id: Option<RawPayloadId>,
/// Result returned to the immediate requester.
///
/// For direct tools this is the tool output item returned to the model; for
/// code-mode nested tools this is the value returned to JavaScript.
pub(crate) raw_result_payload_id: Option<RawPayloadId>,
/// Runtime/protocol payloads observed while executing the tool.
///
/// Examples include exec begin/end, patch begin/end, and MCP begin/end
/// events. Reducers can use these to build richer runtime objects such as
/// terminal operations without overwriting the canonical invocation/result.
pub(crate) raw_runtime_payload_ids: Vec<RawPayloadId>,
}
/// Requester of a runtime tool.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub(crate) enum ToolCallRequester {
Model,
/// Model-authored JavaScript requested the tool through code-mode.
CodeCell {
code_cell_id: CodeCellId,
},
}
/// Runtime tool category.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub(crate) enum ToolCallKind {
ExecCommand,
WriteStdin,
ApplyPatch,
Mcp {
server: String,
tool: String,
},
Web,
ImageGeneration,
SpawnAgent,
AssignAgentTask,
SendMessage,
/// Multi-agent wait operation. Code-mode wait is modeled separately.
WaitAgent,
CloseAgent,
Other {
name: String,
},
}
/// Bounded card/list summary for a tool call.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub(crate) enum ToolCallSummary {
/// Tool is summarized by its terminal operation.
Terminal { operation_id: TerminalOperationId },
Agent {
target_agent_path: AgentPath,
/// Task name/path segment when the operation creates or targets a task.
task_name: Option<String>,
message_preview: String,
},
WaitAgent {
/// Wait target, when narrower than "any child".
target_agent_path: Option<AgentPath>,
timeout_ms: Option<u64>,
},
Generic {
label: String,
input_preview: Option<String>,
output_preview: Option<String>,
},
}
/// Reusable terminal process/session returned by the runtime.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct TerminalSession {
pub(crate) terminal_id: TerminalId,
pub(crate) thread_id: AgentThreadId,
pub(crate) created_by_operation_id: TerminalOperationId,
pub(crate) operation_ids: Vec<TerminalOperationId>,
/// Terminal lifetime. This can outlive the operation that created it.
pub(crate) execution: ExecutionWindow,
}
/// One command/write/poll operation against a terminal session.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct TerminalOperation {
pub(crate) operation_id: TerminalOperationId,
/// Runtime terminal/process ID. `None` is legal only while the operation that creates it is starting.
pub(crate) terminal_id: Option<TerminalId>,
pub(crate) tool_call_id: ToolCallId,
pub(crate) kind: TerminalOperationKind,
/// Operation execution window. This is not necessarily the terminal session lifetime.
pub(crate) execution: ExecutionWindow,
pub(crate) request: TerminalRequest,
/// Runtime-observed terminal result. Model-visible output links through observations.
pub(crate) result: Option<TerminalResult>,
pub(crate) model_observations: Vec<TerminalModelObservation>,
pub(crate) raw_payload_ids: Vec<RawPayloadId>,
}
/// Terminal operation category.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum TerminalOperationKind {
ExecCommand,
WriteStdin,
}
/// Terminal request summary.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub(crate) enum TerminalRequest {
ExecCommand {
command: Vec<String>,
display_command: String,
cwd: String,
yield_time_ms: Option<u64>,
max_output_tokens: Option<usize>,
},
/// Request to interact with an existing terminal.
WriteStdin {
/// Bytes/text sent to stdin. Empty string means poll/read without writing bytes.
stdin: String,
yield_time_ms: Option<u64>,
max_output_tokens: Option<usize>,
},
}
/// Terminal result observed by the runtime.
///
/// This is debugger/runtime output. It is not proof that the model saw the same
/// bytes; link model-visible call/output items through `TerminalModelObservation`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TerminalResult {
/// Process exit code. `None` if the process is still running or no exit status was produced.
pub(crate) exit_code: Option<i32>,
pub(crate) stdout: String,
pub(crate) stderr: String,
/// Tool runtime's formatted caller-facing output, when present.
pub(crate) formatted_output: Option<String>,
/// Token count before truncation, when the tool runtime reported it.
pub(crate) original_token_count: Option<usize>,
/// Streaming chunk ID, when this result was assembled from chunked terminal output.
pub(crate) chunk_id: Option<String>,
}
/// Conversation items that observed a terminal operation.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TerminalModelObservation {
pub(crate) call_item_ids: Vec<ConversationItemId>,
pub(crate) output_item_ids: Vec<ConversationItemId>,
pub(crate) source: TerminalObservationSource,
}
/// Source of model-visible terminal observation.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum TerminalObservationSource {
DirectToolCall,
CodeCellOutput,
}
/// Directed information-flow relationship between trace objects.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct InteractionEdge {
pub(crate) edge_id: EdgeId,
pub(crate) kind: InteractionEdgeKind,
pub(crate) source: TraceAnchor,
pub(crate) target: TraceAnchor,
pub(crate) started_at_unix_ms: i64,
pub(crate) ended_at_unix_ms: Option<i64>,
pub(crate) carried_item_ids: Vec<ConversationItemId>,
pub(crate) carried_raw_payload_ids: Vec<RawPayloadId>,
}
/// Information-flow edge category.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum InteractionEdgeKind {
SpawnAgent,
AssignAgentTask,
SendMessage,
AgentResult,
CloseAgent,
}
/// Typed pointer to one stable reduced-trace object.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub(crate) enum TraceAnchor {
ConversationItem { item_id: ConversationItemId },
ToolCall { tool_call_id: ToolCallId },
Thread { thread_id: AgentThreadId },
}
+110
View File
@@ -0,0 +1,110 @@
use serde::Deserialize;
use serde::Serialize;
use crate::raw_event::RawEventSeq;
use super::AgentPath;
use super::AgentThreadId;
use super::CodexTurnId;
use super::ConversationItemId;
use super::EdgeId;
/// Coarse terminal status for the rollout.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum RolloutStatus {
/// Writer has not seen a terminal rollout event.
Running,
/// Rollout ended normally.
Completed,
/// Rollout ended because an operation failed.
Failed,
/// Rollout was cancelled or otherwise stopped before normal completion.
Aborted,
}
/// One Codex thread/session participating in the rollout.
///
/// Threads are agents in the multi-agent sense, but the root interactive
/// session is represented by the same object. Runtime objects live in top-level
/// maps and point back to their owning thread; only transcript order is stored
/// here because compaction/reconciliation makes it semantic.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct AgentThread {
pub(crate) thread_id: AgentThreadId,
/// Stable routing identity. Viewer/search should prefer this over nickname.
pub(crate) agent_path: AgentPath,
/// Presentation hint. It can collide and must not be used as identity.
pub(crate) nickname: Option<String>,
pub(crate) origin: AgentOrigin,
/// Session lifecycle for this thread.
///
/// Child threads can end independently from the root rollout, for example
/// after a parent calls `close_agent`. Keeping this on the thread prevents
/// those shutdowns from being mistaken for whole-rollout completion.
pub(crate) execution: ExecutionWindow,
/// Configured model presentation hint. Individual inference calls carry the actual upstream model.
pub(crate) default_model: Option<String>,
/// Logical conversation items first observed for this thread, in transcript order.
pub(crate) conversation_item_ids: Vec<ConversationItemId>,
}
/// Provenance for a traced Codex thread.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub(crate) enum AgentOrigin {
Root,
Spawned {
parent_thread_id: AgentThreadId,
/// Interaction edge that carried the spawn task.
spawn_edge_id: EdgeId,
/// Stable path segment/task name selected by the parent/tool call.
task_name: String,
/// Selected agent role/type, for example `worker` or `explorer`.
agent_role: String,
},
}
/// Runtime interval for a typed trace object.
///
/// Wall-clock timestamps are for display and latency. Sequence numbers are the
/// causal ordering primitive and should be used to pair observations or break
/// same-millisecond ties.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct ExecutionWindow {
pub(crate) started_at_unix_ms: i64,
pub(crate) started_seq: RawEventSeq,
pub(crate) ended_at_unix_ms: Option<i64>,
pub(crate) ended_seq: Option<RawEventSeq>,
pub(crate) status: ExecutionStatus,
}
/// Coarse lifecycle status for a runtime object.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ExecutionStatus {
/// Object is still live or the trace ended before its terminal event.
Running,
/// Object completed successfully.
Completed,
/// Object reached an error state.
Failed,
/// Object was cancelled by user/policy/runtime before completion.
Cancelled,
/// Object was aborted when its owner/runtime stopped.
Aborted,
}
/// One activation of the Codex runtime for one thread.
///
/// A Codex turn groups protocol/runtime work for one thread activation.
/// It is not a user/assistant message pair; conversation belongs in
/// `ConversationItem`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct CodexTurn {
pub(crate) codex_turn_id: CodexTurnId,
pub(crate) thread_id: AgentThreadId,
pub(crate) execution: ExecutionWindow,
/// Conversation items that directly triggered this activation, when known.
pub(crate) input_item_ids: Vec<ConversationItemId>,
}
+49
View File
@@ -0,0 +1,49 @@
//! References to heavyweight trace payloads stored outside the reduced graph.
use serde::Deserialize;
use serde::Serialize;
/// Stable identifier for one raw payload inside a rollout bundle.
pub(crate) type RawPayloadId = String;
/// Reference to a raw request/response/log payload.
///
/// `RolloutTrace` stores these references so normal timeline and conversation
/// rendering does not require the browser or reducer output to inline every
/// upstream request, tool response, or terminal log.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct RawPayloadRef {
pub(crate) raw_payload_id: RawPayloadId,
/// Payload role. This lets details UI choose syntax highlighting and labels
/// without opening the payload file first.
pub(crate) kind: RawPayloadKind,
/// Path relative to the trace bundle root.
///
/// The writer always materializes payloads as bundle-local files. Keeping
/// this as a plain path avoids exposing storage modes we do not produce.
pub(crate) path: String,
}
/// Coarse role of a raw payload.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type", content = "value")]
pub(crate) enum RawPayloadKind {
InferenceRequest,
/// Full upstream inference response or non-delta response stream summary.
InferenceResponse,
CompactionRequest,
/// Trace-only checkpoint captured when processed replacement history is installed.
CompactionCheckpoint,
CompactionResponse,
ToolInvocation,
ToolResult,
/// Raw runtime/protocol observation for an executing tool.
ToolRuntimeEvent,
/// Raw terminal runtime event or stream shard.
TerminalRuntimeEvent,
ProtocolEvent,
/// One-shot metadata captured when a Codex session/thread starts.
SessionMetadata,
/// Runtime notification payload carried when a child agent reports back to its parent.
AgentResult,
}
+7
View File
@@ -0,0 +1,7 @@
//! Shared raw-event primitives used by the reduced state model.
//!
//! The local OTEL/tracing wire format remains intentionally untyped. This
//! module only gives `state.json` objects a documented sequence-number type.
/// Monotonic order assigned while replaying one trace event log.
pub(crate) type RawEventSeq = u64;
File diff suppressed because it is too large Load Diff