How the Codex Plugin Implements Progress Reporting for Long-Running Operations
The Codex plugin implements progress reporting by creating a progress reporter callback that captures SDK-level events, writes them to a log file, and exposes a live preview via the /codex:status command.
The openai/codex-plugin-cc repository provides a lightweight, log-driven mechanism for tracking long-running Codex tasks. By normalizing incremental events and storing them in a persistent log, the plugin enables users to monitor job status without polling internal state manually.
Creating the Progress Reporter
The createProgressReporter Function
Progress tracking begins with the createProgressReporter function in plugins/codex/scripts/lib/tracked-jobs.mjs. This factory builds a callback that normalizes each incoming event, optionally echoes messages to stderr, appends them to a designated log file, and forwards the event to an optional onEvent hook.
The reporter configuration accepts three key parameters:
stderr– Whentrue, echoes progress messages to the consolelogFile– Path to the file where events are persistedonEvent– Optional callback for custom event handling
// Creating a progress reporter when launching a job
import { createProgressReporter, runTrackedJob } from './lib/tracked-jobs.mjs';
const progress = createProgressReporter({
stderr: true, // also echo to console
logFile: '/tmp/job.log',
onEvent: (ev) => console.log('Progress event:', ev)
});
await runTrackedJob(job, () => runner(progress), { logFile: '/tmp/job.log' });
Running the Job with Progress Tracking
Integrating with runTrackedJob
The reporter instance is passed to the Codex runtime through runTrackedJob, ensuring every SDK-level progress event is captured during execution. This integration happens in plugins/codex/scripts/codex-companion.mjs (lines 580-660), where the plugin wires the reporter to the running Codex job.
The runTrackedJob function accepts the job definition, a runner function that receives the progress callback, and options specifying the log file path. This architecture decouples the progress collection from the job execution logic while maintaining a continuous stream of status updates.
Storing and Reading Progress Previews
The readJobProgressPreview Function
While the job runs, the plugin periodically reads the log file using readJobProgressPreview from plugins/codex/scripts/lib/job-control.mjs. This function extracts the most recent lines and stores them in job.progressPreview, creating a window into the current activity without loading the entire log history.
// Manually reading the preview (what the renderer does)
import { readJobProgressPreview } from './lib/job-control.mjs';
const preview = readJobProgressPreview('/tmp/job.log', 10);
preview.forEach(line => console.log('▶', line));
The function accepts the log file path and a line count limit, returning an array of the most recent progress entries. This preview is updated throughout the job lifecycle, providing near real-time visibility into long-running operations.
Inferring Job Phases
Legacy Phase Detection with inferLegacyJobPhase
For legacy jobs, the plugin analyzes preview lines using inferLegacyJobPhase (defined in plugins/codex/scripts/lib/job-control.mjs, lines 9-20). This heuristic examines log content to guess the current phase—such as starting, investigating, verifying, editing, or finalizing—based on patterns in the progress output.
This inference layer allows older Codex tasks to participate in the progress reporting system without requiring explicit phase annotations from the SDK.
Rendering Progress to the User
The /codex:status Command Implementation
When users run /codex:status, the render function in plugins/codex/scripts/lib/render.mjs (lines 158-162) displays the progress preview under a "Progress:" heading. The rendering process uses buildStatusSnapshot to aggregate job data, including the preview lines, before formatting them for the terminal.
// How the status command shows the preview
import { buildStatusSnapshot } from './lib/job-control.mjs';
import { render } from './lib/render.mjs';
const snapshot = buildStatusSnapshot(process.cwd(), { maxProgressLines: 5 });
console.log(render(snapshot));
The maxProgressLines option controls how many recent log entries appear in the status output, keeping the display concise while providing sufficient context for understanding current job activity.
Summary
The Codex plugin progress reporting system combines several components to provide lightweight, real-time feedback:
- Event Capture: The
createProgressReporterfunction inplugins/codex/scripts/lib/tracked-jobs.mjsbuilds callbacks that normalize and persist SDK events - Log Persistence: All progress events append to a dedicated log file, creating a durable history of job execution
- Preview Generation:
readJobProgressPreviewextracts recent lines from the log to populatejob.progressPreview - Phase Inference:
inferLegacyJobPhaseanalyzes log patterns to determine the current job phase for legacy tasks - Status Rendering: The
renderfunction inplugins/codex/scripts/lib/render.mjsdisplays the preview when users invoke/codex:status
Frequently Asked Questions
How does the Codex plugin handle progress events from the SDK?
The plugin intercepts SDK-level progress events through a reporter callback created by createProgressReporter. This callback normalizes each event, writes it to a log file, optionally echoes it to stderr, and forwards it to any custom onEvent hook. The reporter is passed directly to the Codex runtime via runTrackedJob, ensuring all progress updates are captured during execution.
Where does the plugin store progress information during job execution?
Progress information is stored in two locations: a persistent log file specified during reporter creation (typically /tmp/job.log or similar) and the job.progressPreview property. The readJobProgressPreview function periodically reads the log file and updates the preview array with the most recent lines, allowing the status command to display current activity without reading the entire log history.
Can I customize how progress events are processed or displayed?
Yes. The createProgressReporter function accepts an onEvent callback parameter that receives every normalized event before it is written to the log. You can implement custom filtering, transformation, or external notification logic in this hook. Additionally, when calling buildStatusSnapshot, you can specify maxProgressLines to control how many log entries appear in the /codex:status output.
How does the plugin determine which phase a legacy job is currently in?
The inferLegacyJobPhase function in plugins/codex/scripts/lib/job-control.mjs analyzes the content of job.progressPreview lines to heuristically determine the current phase. It looks for patterns indicating specific stages—such as "starting," "investigating," "verifying," "editing," or "finalizing"—allowing older Codex tasks to report meaningful progress status without native phase support from the SDK.
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 →