How the Codex Plugin Handles Progress Reporting for Long-Running Tasks

The Codex plugin creates a progress reporter that captures incremental SDK events, writes them to a log file and optionally stderr, then surfaces the most recent lines when users run the status command.

The openai/codex-plugin-cc repository implements a lightweight, log-driven progress reporting system for long-running Codex tasks. Instead of requiring complex state polling or in-memory tracking, the plugin persists progress events to the filesystem and infers job phases from log content. This design ensures users receive real-time feedback even during extended operations.

Creating the Progress Reporter

When a job starts, the createProgressReporter function in plugins/codex/scripts/lib/tracked-jobs.mjs constructs a callback that normalizes incoming events. The reporter supports three output channels: it can echo messages to stderr, append them to a designated log file, and forward events to an optional onEvent hook for custom processing.

The reporter accepts a configuration object controlling verbosity and destination:

import { createProgressReporter, runTrackedJob } from './lib/tracked-jobs.mjs';

const progress = createProgressReporter({
  stderr: true,          // Mirror progress to console
  logFile: '/tmp/job.log',
  onEvent: (ev) => console.log('Progress event:', ev)
});

await runTrackedJob(job, () => runner(progress), { logFile: '/tmp/job.log' });

Capturing Events During Execution

The reporter integrates with the Codex runtime through runTrackedJob, which passes the callback directly to the SDK-level task runner. Every progress event emitted by the Codex engine triggers the reporter, ensuring no incremental updates are lost. The wiring occurs in plugins/codex/scripts/codex-companion.mjs, where the plugin initializes the reporter before spawning the job process.

This architecture decouples the progress consumer from the execution context. Because events write immediately to disk, the system remains resilient to crashes or disconnections—the log file serves as the persistent source of truth.

Storing Progress Previews and Inferring Phases

While the job executes, the plugin periodically reads the log file via readJobProgressPreview in plugins/codex/scripts/lib/job-control.mjs. This function extracts the most recent lines and stores them in job.progressPreview for quick access.

For legacy job formats, the plugin analyzes preview content using inferLegacyJobPhase to determine the current operational phase. The function examines log line patterns to classify status as starting, investigating, verifying, editing, or finalizing:

import { readJobProgressPreview } from './lib/job-control.mjs';

const preview = readJobProgressPreview('/tmp/job.log', 10);
preview.forEach(line => console.log('▶', line));

Rendering Status to Users

When users execute /codex:status, the render function in plugins/codex/scripts/lib/render.mjs formats the progress preview under a "Progress:" heading. The rendering pipeline first builds a status snapshot via buildStatusSnapshot, which aggregates the job state and preview lines, then outputs a human-readable summary.

You can programmatically access this rendering pipeline:

import { buildStatusSnapshot } from './lib/job-control.mjs';
import { render } from './lib/render.mjs';

const snapshot = buildStatusSnapshot(process.cwd(), { maxProgressLines: 5 });
console.log(render(snapshot));

Summary

  • Log-driven architecture: Progress events persist immediately to disk via createProgressReporter, ensuring durability across process restarts.
  • Multi-channel output: The reporter simultaneously writes to log files, stderr, and optional event hooks defined in tracked-jobs.mjs.
  • Phase inference: Legacy jobs benefit from automatic phase detection through inferLegacyJobPhase in job-control.mjs.
  • Simple status rendering: The render function in render.mjs displays concise progress previews without requiring users to parse raw logs manually.

Frequently Asked Questions

Where is the progress reporter initialized in the codebase?

The progress reporter initializes in plugins/codex/scripts/codex-companion.mjs between lines 580-660, where createProgressReporter builds the callback and passes it to runTrackedJob for the duration of the Codex execution.

Can I customize how many lines appear in the status output?

Yes. The buildStatusSnapshot function accepts a maxProgressLines option that controls how many recent log lines appear in the rendered status. The default behavior captures the tail of the log file to provide context without overwhelming the terminal.

Does the plugin support real-time streaming without polling?

The plugin writes events synchronously to the log file as they occur, but status rendering requires polling the filesystem via readJobProgressPreview. There is no persistent WebSocket or socket connection; instead, the design favors simple file I/O that works across different execution environments and survives network interruptions.

What happens to progress logs after a job completes?

The log file specified during createProgressReporter initialization persists on disk after completion, serving as an audit trail. The plugin does not automatically truncate these files, allowing post-hoc analysis of the full progress history for debugging or compliance purposes.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →