mirror of
https://github.com/openai/codex.git
synced 2026-10-27 18:48:37 +00:00
Add typed trace state model
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 },
|
||||
}
|
||||
@@ -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>,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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;
|
||||
+823
-764
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user