Skip to main content

codex_rollout_trace/model/
runtime.rs

1use serde::Deserialize;
2use serde::Serialize;
3
4use crate::payload::RawPayloadId;
5use crate::raw_event::RawEventSeq;
6
7use super::AgentPath;
8use super::AgentThreadId;
9use super::CodeCellId;
10use super::CodeModeRuntimeToolId;
11use super::CodexTurnId;
12use super::CompactionId;
13use super::CompactionRequestId;
14use super::ConversationItemId;
15use super::EdgeId;
16use super::McpCallId;
17use super::ModelVisibleCallId;
18use super::TerminalId;
19use super::TerminalOperationId;
20use super::ToolCallId;
21use super::session::ExecutionWindow;
22
23/// Runtime/debug object for one model-authored `exec` cell.
24///
25/// The JavaScript source and custom-tool outputs are still conversation items;
26/// this object tracks the code-mode runtime boundary and nested runtime work.
27#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
28pub struct CodeCell {
29    /// Reducer-owned graph id derived from the model-visible `exec` call id.
30    /// Runtime cell ids are stored separately because they are only handles for
31    /// later waits and nested code-mode tools.
32    pub code_cell_id: CodeCellId,
33    pub model_visible_call_id: ModelVisibleCallId,
34    pub thread_id: AgentThreadId,
35    pub codex_turn_id: CodexTurnId,
36    /// Conversation item containing the model-authored JavaScript.
37    pub source_item_id: ConversationItemId,
38    pub output_item_ids: Vec<ConversationItemId>,
39    /// Raw code-mode runtime/session id, useful when matching runtime payloads.
40    pub runtime_cell_id: Option<String>,
41    /// Full JS-cell runtime window; yielded cells can outlive the initial custom call.
42    pub execution: ExecutionWindow,
43    pub runtime_status: CodeCellRuntimeStatus,
44    pub initial_response_at_unix_ms: Option<i64>,
45    pub initial_response_seq: Option<RawEventSeq>,
46    pub yielded_at_unix_ms: Option<i64>,
47    pub yielded_seq: Option<RawEventSeq>,
48    pub source_js: String,
49    pub nested_tool_call_ids: Vec<ToolCallId>,
50    pub wait_tool_call_ids: Vec<ToolCallId>,
51}
52
53/// Code-mode runtime lifecycle.
54#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
55#[serde(rename_all = "snake_case")]
56pub enum CodeCellRuntimeStatus {
57    /// The `exec` request has been accepted but the runtime has not yet started user code.
58    Starting,
59    /// Runtime is executing JavaScript and has not yet yielded or terminated.
60    Running,
61    /// Initial `exec` returned while JavaScript kept running in the background.
62    Yielded,
63    /// Runtime reached a normal terminal result.
64    Completed,
65    /// Runtime reached an error terminal result.
66    Failed,
67    /// Runtime was explicitly terminated.
68    Terminated,
69}
70
71/// Installed conversation-history replacement boundary.
72///
73/// Duration-bearing upstream requests live in `CompactionRequest`. This object
74/// is the checkpoint where replacement history became the live thread history.
75/// The boundary marker and the model-visible summary are separate conversation
76/// items: the marker says where history was replaced, while the summary is part
77/// of `replacement_item_ids` when the compact endpoint returned one.
78#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
79pub struct Compaction {
80    pub compaction_id: CompactionId,
81    pub thread_id: AgentThreadId,
82    pub codex_turn_id: CodexTurnId,
83    pub installed_at_unix_ms: i64,
84    /// Structural conversation item marking where pre-compaction history ended.
85    pub marker_item_id: ConversationItemId,
86    /// Upstream compaction request attempts that contributed to this checkpoint.
87    pub request_ids: Vec<CompactionRequestId>,
88    /// Logical conversation items present immediately before replacement.
89    pub input_item_ids: Vec<ConversationItemId>,
90    /// Replacement conversation items installed by the checkpoint.
91    pub replacement_item_ids: Vec<ConversationItemId>,
92}
93
94/// One upstream remote request made while computing a compaction checkpoint.
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub struct CompactionRequest {
97    pub compaction_request_id: CompactionRequestId,
98    pub compaction_id: CompactionId,
99    pub thread_id: AgentThreadId,
100    pub codex_turn_id: CodexTurnId,
101    pub execution: ExecutionWindow,
102    pub model: String,
103    pub provider_name: String,
104    pub raw_request_payload_id: RawPayloadId,
105    /// Full compaction response payload. `None` while running or after pre-response failures.
106    pub raw_response_payload_id: Option<RawPayloadId>,
107}
108
109/// Runtime operation requested by the model, a JS code cell, or Codex itself.
110///
111/// A `ToolCall` is not a chat transcript row. Model-visible call/output items
112/// link to it through `model_visible_*_item_ids`; runtime-only tools can have
113/// empty model-visible lists.
114#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
115pub struct ToolCall {
116    pub tool_call_id: ToolCallId,
117    /// Globally unique MCP execution ID, when this tool reached an MCP backend.
118    pub mcp_call_id: Option<McpCallId>,
119    /// Model-visible protocol call ID, if the model directly requested this tool.
120    pub model_visible_call_id: Option<ModelVisibleCallId>,
121    /// Code-mode runtime's internal tool invocation ID, if this call came from JS.
122    pub code_mode_runtime_tool_id: Option<CodeModeRuntimeToolId>,
123    pub thread_id: AgentThreadId,
124    /// Runtime activation that started the tool. Background work may outlive this turn.
125    pub started_by_codex_turn_id: Option<CodexTurnId>,
126    pub execution: ExecutionWindow,
127    pub requester: ToolCallRequester,
128    pub kind: ToolCallKind,
129    pub model_visible_call_item_ids: Vec<ConversationItemId>,
130    pub model_visible_output_item_ids: Vec<ConversationItemId>,
131    /// Terminal operation started by this tool, when the tool touched a terminal.
132    pub terminal_operation_id: Option<TerminalOperationId>,
133    pub summary: ToolCallSummary,
134    /// Original invocation at the Codex tool boundary.
135    ///
136    /// Direct model tools store the model's function/custom call payload here.
137    /// Code-mode nested tools store the JSON call made by model-authored JS.
138    /// Runtime protocol events are deliberately kept separate below because
139    /// they describe how Codex executed the request, not what the caller sent.
140    pub raw_invocation_payload_id: Option<RawPayloadId>,
141    /// Result returned to the immediate requester.
142    ///
143    /// For direct tools this is the tool output item returned to the model; for
144    /// code-mode nested tools this is the value returned to JavaScript.
145    pub raw_result_payload_id: Option<RawPayloadId>,
146    /// Runtime/protocol payloads observed while executing the tool.
147    ///
148    /// Examples include exec begin/end, patch begin/end, and MCP begin/end
149    /// events. Reducers can use these to build richer runtime objects such as
150    /// terminal operations without overwriting the canonical invocation/result.
151    pub raw_runtime_payload_ids: Vec<RawPayloadId>,
152}
153
154/// Requester of a runtime tool.
155#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
156#[serde(rename_all = "snake_case", tag = "type")]
157pub enum ToolCallRequester {
158    Model,
159    /// Model-authored JavaScript requested the tool through code-mode.
160    CodeCell {
161        code_cell_id: CodeCellId,
162    },
163}
164
165/// Runtime tool category.
166#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
167#[serde(rename_all = "snake_case", tag = "type")]
168pub enum ToolCallKind {
169    ExecCommand,
170    WriteStdin,
171    ApplyPatch,
172    Mcp {
173        server: String,
174        tool: String,
175    },
176    Web,
177    ImageGeneration,
178    SpawnAgent,
179    AssignAgentTask,
180    SendMessage,
181    /// Multi-agent wait operation. Code-mode wait is modeled separately.
182    WaitAgent,
183    CloseAgent,
184    Other {
185        name: String,
186    },
187}
188
189/// Bounded card/list summary for a tool call.
190#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
191#[serde(rename_all = "snake_case", tag = "type")]
192pub enum ToolCallSummary {
193    /// Tool is summarized by its terminal operation.
194    Terminal { operation_id: TerminalOperationId },
195    Agent {
196        target_agent_path: AgentPath,
197        /// Task name/path segment when the operation creates or targets a task.
198        task_name: Option<String>,
199        message_preview: String,
200    },
201    WaitAgent {
202        /// Wait target, when narrower than "any child".
203        target_agent_path: Option<AgentPath>,
204        timeout_ms: Option<u64>,
205    },
206    Generic {
207        label: String,
208        input_preview: Option<String>,
209        output_preview: Option<String>,
210    },
211}
212
213/// Reusable terminal process/session returned by the runtime.
214#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
215pub struct TerminalSession {
216    pub terminal_id: TerminalId,
217    pub thread_id: AgentThreadId,
218    pub created_by_operation_id: TerminalOperationId,
219    pub operation_ids: Vec<TerminalOperationId>,
220    /// Terminal lifetime. This can outlive the operation that created it.
221    pub execution: ExecutionWindow,
222}
223
224/// One command/write/poll operation against a terminal session.
225#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
226pub struct TerminalOperation {
227    pub operation_id: TerminalOperationId,
228    /// Runtime terminal/process ID. `None` is legal only while the operation that creates it is starting.
229    pub terminal_id: Option<TerminalId>,
230    pub tool_call_id: ToolCallId,
231    pub kind: TerminalOperationKind,
232    /// Operation execution window. This is not necessarily the terminal session lifetime.
233    pub execution: ExecutionWindow,
234    pub request: TerminalRequest,
235    /// Runtime-observed terminal result. Model-visible output links through observations.
236    pub result: Option<TerminalResult>,
237    pub model_observations: Vec<TerminalModelObservation>,
238    pub raw_payload_ids: Vec<RawPayloadId>,
239}
240
241/// Terminal operation category.
242#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
243#[serde(rename_all = "snake_case")]
244pub enum TerminalOperationKind {
245    ExecCommand,
246    WriteStdin,
247}
248
249/// Terminal request summary.
250#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
251#[serde(rename_all = "snake_case", tag = "type")]
252pub enum TerminalRequest {
253    ExecCommand {
254        command: Vec<String>,
255        display_command: String,
256        cwd: String,
257        yield_time_ms: Option<u64>,
258        max_output_tokens: Option<usize>,
259    },
260    /// Request to interact with an existing terminal.
261    WriteStdin {
262        /// Bytes/text sent to stdin. Empty string means poll/read without writing bytes.
263        stdin: String,
264        yield_time_ms: Option<u64>,
265        max_output_tokens: Option<usize>,
266    },
267}
268
269/// Terminal result observed by the runtime.
270///
271/// This is debugger/runtime output. It is not proof that the model saw the same
272/// bytes; link model-visible call/output items through `TerminalModelObservation`.
273#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
274pub struct TerminalResult {
275    /// Process exit code. `None` if the process is still running or no exit status was produced.
276    pub exit_code: Option<i32>,
277    pub stdout: String,
278    pub stderr: String,
279    /// Tool runtime's formatted caller-facing output, when present.
280    pub formatted_output: Option<String>,
281    /// Token count before truncation, when the tool runtime reported it.
282    pub original_token_count: Option<usize>,
283    /// Streaming chunk ID, when this result was assembled from chunked terminal output.
284    pub chunk_id: Option<String>,
285}
286
287/// Conversation items that observed a terminal operation.
288#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
289pub struct TerminalModelObservation {
290    pub call_item_ids: Vec<ConversationItemId>,
291    pub output_item_ids: Vec<ConversationItemId>,
292    pub source: TerminalObservationSource,
293}
294
295/// Source of model-visible terminal observation.
296#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
297#[serde(rename_all = "snake_case")]
298pub enum TerminalObservationSource {
299    DirectToolCall,
300    CodeCellOutput,
301}
302
303/// Directed information-flow relationship between trace objects.
304#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
305pub struct InteractionEdge {
306    pub edge_id: EdgeId,
307    pub kind: InteractionEdgeKind,
308    pub source: TraceAnchor,
309    pub target: TraceAnchor,
310    pub started_at_unix_ms: i64,
311    pub ended_at_unix_ms: Option<i64>,
312    pub carried_item_ids: Vec<ConversationItemId>,
313    pub carried_raw_payload_ids: Vec<RawPayloadId>,
314}
315
316/// Information-flow edge category.
317#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
318#[serde(rename_all = "snake_case")]
319pub enum InteractionEdgeKind {
320    SpawnAgent,
321    AssignAgentTask,
322    SendMessage,
323    AgentResult,
324    CloseAgent,
325}
326
327/// Typed pointer to one stable reduced-trace object.
328#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
329#[serde(rename_all = "snake_case", tag = "type")]
330pub enum TraceAnchor {
331    ConversationItem { item_id: ConversationItemId },
332    ToolCall { tool_call_id: ToolCallId },
333    Thread { thread_id: AgentThreadId },
334}