TurnCaptureState: How Codex Tracks Turn Progress in the OpenAI Plugin
TurnCaptureState is the core asynchronous data structure that the OpenAI Codex plugin uses to monitor and record the lifecycle of a single interaction round (turn), tracking thread hierarchies, buffering notifications, and aggregating artifacts until the turn reaches completion.
In the openai/codex-plugin-cc repository, the Codex plugin relies on TurnCaptureState to maintain a reliable, real-time view of conversation progress. Defined in plugins/codex/scripts/lib/codex.mjs, this state object captures everything from sub-agent activity to file modifications, enabling UI progress reporting and post-turn analysis.
Core Architecture of TurnCaptureState
State Properties and Thread Management
The TurnCaptureState object maintains several critical properties to track Codex turn progress across potentially complex thread hierarchies:
- threadId / rootThreadId: Identifies the main conversation thread the turn belongs to.
- threadIds: A Set containing all thread IDs, including sub-threads created during the turn.
- threadTurnIds: A Map linking each thread ID to its most recent turn ID.
- turnId: The unique identifier for the current turn, populated when the turn starts.
- bufferedNotifications: An array holding raw Codex notifications awaiting processing.
- completion: A Promise that resolves when the turn finishes, delivering the final state.
- finalTurn: The completed Turn object returned by Codex with status
completed. - completed / finalAnswerSeen: Boolean flags indicating explicit completion and final answer detection.
- pendingCollaborations / activeSubagentTurns: Track ongoing tool calls and sub-agent turns to prevent premature finalization.
- reasoningSummary, messages, fileChanges, commandExecutions: Arrays accumulating high-level artifacts generated during the turn.
- onProgress: Optional callback function for emitting progress updates to the UI.
State Initialization
The state is instantiated via createTurnCaptureState(threadId, options) at lines 302-337 of codex.mjs. This factory function initializes the tracking structures and sets up the completion promise that callers await to determine when a turn has fully concluded.
How TurnCaptureState Tracks Codex Turn Progress
Thread Registration and Turn Identification
As Codex emits notifications such as turn/started and thread/started, the plugin invokes registerThread to update threadTurnIds and expand the threadIds Set. The belongsToTurn(state, message) function filters incoming messages, ensuring only relevant notifications from the current turn are captured while ignoring stale or unrelated thread activity.
Recording Artifacts and Notifications
The recordItem function processes incoming Codex events, extracting command executions, file changes, reasoning snippets, and assistant messages. These are appended to their respective arrays within the state object. Raw notifications accumulate in bufferedNotifications until they can be processed, ensuring no data loss during high-throughput turns.
Completion Detection and Finalization
Two mechanisms handle turn finalization:
- Explicit Completion: When Codex sends a
turn/completednotification,completeTurn(state, turn)marks the turn as finished, sets thecompletedflag, and resolves thecompletionpromise with the final state. - Inferred Completion: If the final answer appears but no explicit completion signal arrives,
scheduleInferredCompletion(state)sets a short timeout to infer completion, preventing the state from hanging indefinitely on stalled sub-agents.
Implementation in the Source Code
The turn-tracking logic resides primarily in plugins/codex/scripts/lib/codex.mjs, which defines TurnCaptureState and implements applyTurnNotification, recordItem, and the completion handlers. For persistence across process restarts, plugins/codex/scripts/lib/state.mjs provides serialization utilities. The test suite in tests/state.test.mjs and tests/runtime.test.mjs validates the capture workflow and finalization logic.
Practical Usage Examples
Capturing a Turn and Monitoring Completion
import { captureTurn } from "./codex.mjs";
const threadId = "thr_1";
const startRequest = { /* initial prompt parameters */ };
const turnState = await captureTurn(client, threadId, startRequest, {
onProgress: (msg) => console.log("[Progress]", msg)
});
console.log("Turn completed:", turnState.finalTurn);
console.log("Reasoning summary:", turnState.reasoningSummary);
Inspecting Captured Artifacts After Completion
console.log("Messages:", turnState.messages.map(m => m.text));
console.log("Edited files:", turnState.fileChanges.map(fc => fc.changes.map(c => c.path)));
console.log("Commands run:", turnState.commandExecutions.map(c => ({
cmd: c.command,
status: c.status,
exit: c.exitCode
})));
Implementing a Custom Progress Reporter
function progressReporter(update) {
if (typeof update === "string") {
console.log(update);
} else {
console.log(`${update.phase}: ${update.message}`);
}
}
await captureTurn(client, threadId, startRequest, { onProgress: progressReporter });
Summary
- TurnCaptureState is the central data structure in
openai/codex-plugin-ccfor tracking a single Codex interaction round from start to finish. - It maintains thread hierarchies via
threadIdsandthreadTurnIds, ensuring accurate attribution of messages across sub-threads and sub-agents. - The state aggregates artifacts including reasoning summaries, file changes, and command executions through the
recordItemfunction. - Completion is handled through both explicit signals (
completeTurn) and inferred timeouts (scheduleInferredCompletion), with thecompletionPromise providing asynchronous notification of turn finalization. - Located in
plugins/codex/scripts/lib/codex.mjs, this architecture enables reliable UI progress bars, sub-agent coordination, and post-turn analysis.
Frequently Asked Questions
What triggers the completion of a TurnCaptureState?
The completion Promise resolves when either the completeTurn function receives an explicit turn/completed notification from Codex, or when scheduleInferredCompletion detects a final answer without an explicit completion signal and triggers a timeout-based inference. This dual mechanism ensures the state finalizes even if the Codex API omits formal completion messages.
How does TurnCaptureState handle sub-agent turns and collaborations?
The state tracks active sub-agents through the activeSubagentTurns and pendingCollaborations counters. These prevent premature completion by ensuring the completion Promise only resolves after all spawned sub-threads and tool invocations have reported their status, maintaining consistency across complex multi-turn workflows.
Can TurnCaptureState persist across process restarts?
Yes, while the primary implementation in codex.mjs manages runtime state, the companion module plugins/codex/scripts/lib/state.mjs provides utilities for serializing and restoring turn state. This allows long-running Codex interactions to survive process interruptions and resume tracking from their last known position.
Where is TurnCaptureState defined in the codebase?
The TurnCaptureState interface and its factory function createTurnCaptureState are defined in plugins/codex/scripts/lib/codex.mjs at lines 302-337. All notification processing logic, including registerThread and recordItem, resides in the same file, making it the canonical reference for turn-tracking implementation details.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →