# How the Codex Plugin Spawns Detached Background Workers for Long-Running Tasks

> Discover how the Codex plugin spawns detached background workers using Node.js child_process. Learn how to run long-running tasks independently of the parent CLI.

- Repository: [OpenAI/codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
- Tags: internals
- Published: 2026-07-28

---

**The Codex plugin launches detached background workers using Node.js `child_process.spawn` with `detached: true`, `stdio: "ignore"`, and `windowsHide: true`, followed by `child.unref()` to allow the parent CLI to exit while the worker continues running.**

The `openai/codex-plugin-cc` repository implements reliable background task execution through process detachment. When users invoke `codex task … --background`, the system must create persistent workers that survive the original CLI session. Understanding how the Codex plugin spawns detached background workers reveals a carefully designed architecture using Node.js process management primitives.

## The Core spawnDetachedTaskWorker Implementation

In `plugins/codex/scripts/codex-companion.mjs`, the `spawnDetachedTaskWorker` function handles the actual subprocess creation. This utility constructs the worker command and configures spawn options to ensure complete independence from the parent process.

The function executes the same script (`codex-companion.mjs`) with the `task-worker` subcommand, passing the job ID and working directory:

```javascript
function spawnDetachedTaskWorker(cwd, jobId) {
  const scriptPath = path.join(ROOT_DIR, "scripts", "codex-companion.mjs");
  const child = spawn(process.execPath, [scriptPath, "task-worker", "--cwd", cwd, "--job-id", jobId], {
    cwd,
    env: process.env,
    detached: true,
    stdio: "ignore",
    windowsHide: true
  });
  child.unref();
  return child;
}

```

After spawning, the function immediately returns the child process object, allowing the caller to extract the PID for tracking purposes.

## Process Isolation and Detachment Options

The Codex plugin uses specific Node.js spawn options to guarantee the worker can operate autonomously.

### Independent Process Groups

Setting `detached: true` creates a new process group on POSIX systems, preventing `SIGINT` signals from propagating to the child when the parent terminates. This enables the background worker to continue execution even if the user closes the terminal or exits the CLI.

### Standard I/O Disconnection

The `stdio: "ignore"` configuration disconnects all standard streams (stdin, stdout, stderr) between parent and child. Without this setting, the parent process would maintain open file descriptors, potentially blocking exit until the child terminates.

### Cross-Platform Window Management

On Windows systems, the `windowsHide: true` flag suppresses the creation of console windows for background tasks. This provides a seamless experience where detached workers run silently without flashing terminal windows.

## Recording and Tracking Background Jobs

Once spawned, the worker's PID must persist for lifecycle management. The `enqueueBackgroundTask` function in `codex-companion.mjs` writes a JSON job file containing the process identifier:

```javascript
function enqueueBackgroundTask(cwd, job, request) {
  const child = spawnDetachedTaskWorker(cwd, job.id);
  const queuedRecord = {
    ...job,
    status: "queued",
    phase: "queued",
    pid: child.pid ?? null,
    logFile,
    request
  };
  writeJobFile(job.workspaceRoot, job.id, queuedRecord);
  upsertJob(job.workspaceRoot, queuedRecord);
  return { payload: { jobId: job.id, status: "queued", title: job.title, summary: job.summary, logFile } };
}

```

This record enables the `job-control` module to query status and terminate specific workers by their stored PID.

## Cross-Platform Termination Logic

Terminating detached workers requires platform-specific handling implemented in `plugins/codex/scripts/lib/process.mjs`. The `terminateProcessTree` function delivers signals appropriately for each operating system.

On POSIX systems, the code sends `SIGTERM` to the negative PID (process group) using `process.kill(-pid, "SIGTERM")`, targeting all child processes simultaneously. On Windows, it executes `taskkill` with the `/T` flag to terminate the entire process tree.

```javascript
import { terminateProcessTree } from "./process.mjs";

function cancelJob(job) {
  if (job.pid) {
    const result = terminateProcessTree(job.pid);
    // result = { attempted: true, delivered: true/false, method: "..."}
  }
}

```

This approach ensures clean shutdown regardless of whether the worker spawned additional subprocesses.

## Summary

- The `spawnDetachedTaskWorker` function in `codex-companion.mjs` creates background workers using `child_process.spawn` with `detached: true` and `stdio: "ignore"`.
- Calling `child.unref()` immediately after spawning allows the Node.js event loop to exit without waiting for the background task.
- Job records store the worker PID (`child.pid`) to enable later status checks and termination via `terminateProcessTree`.
- The termination utility in `process.mjs` handles cross-platform cleanup using `SIGTERM` on POSIX and `taskkill` on Windows.
- Similar detachment patterns appear in `app-server.mjs` for spawning detached application server processes.

## Frequently Asked Questions

### What is the purpose of `child.unref()` in the Codex plugin?

The `child.unref()` method tells the Node.js event loop that the parent process should not remain active solely to keep the child process running. According to the `openai/codex-plugin-cc` source code, this call immediately follows the spawn operation in `spawnDetachedTaskWorker`, ensuring the CLI can return control to the user while the background worker continues independently.

### How does the Codex plugin handle process termination on Windows?

On Windows, the plugin uses the `windowsHide: true` spawn option to prevent console windows from appearing, and relies on the `terminateProcessTree` function in `process.mjs` to execute `taskkill` with the `/T` flag. This kills the entire process tree, matching the POSIX behavior of sending signals to process groups.

### Where is the background worker PID stored in the Codex plugin?

The PID is stored in the JSON job file created by `enqueueBackgroundTask` within `codex-companion.mjs`. The record includes `pid: child.pid ?? null`, allowing the job control system to reference the specific process ID for status queries or cancellation requests later.

### Can detached Codex workers survive if the parent CLI process crashes?

Yes. Because the plugin uses `detached: true` when spawning, the child process runs in its own process group and is not dependent on the parent's lifecycle. The `stdio: "ignore"` configuration further ensures no open file descriptors tie the processes together, making the workers resilient to parent process termination.