# How Codex Plugin Uses Notification Handlers to Track Thread, Turn, and Item Lifecycle Events

> Explore how Codex plugin uses notification handlers like thread started and item completed to track lifecycle events. Learn about the custom handler on CodexAppServerClient.

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

---

**The openai/codex-plugin-cc repository utilizes a custom notification handler installed on the `CodexAppServerClient` to monitor lifecycle events through methods like `thread/started`, `turn/started`, and `item/completed`, processing them via `applyTurnNotification` in `plugins/codex/scripts/lib/codex.mjs`.**

The openai/codex-plugin-cc project provides a framework for integrating Codex into development workflows. Understanding how **notification handlers** track the progression of threads, turns, and items is essential for building reliable Codex-powered applications. The plugin intercepts server-sent notifications to maintain accurate state throughout the session lifecycle.

## Installing the Notification Handler During Turn Capture

The lifecycle tracking begins when `captureTurn` is invoked in `plugins/codex/scripts/lib/codex.mjs`. This function replaces the client's default `notificationHandler` with a custom implementation that routes events through `applyTurnNotification`.

Before installing the new handler, the code preserves the existing handler as `previousHandler`. This ensures that unrelated notifications can still propagate to other parts of the plugin after the current turn's processing completes.

### Handler Installation Pattern

When a turn starts, the plugin calls `client.setNotificationHandler(handler)`, which is defined in `plugins/codex/scripts/lib/app-server.mjs` (lines 76-78). After the turn completes, the original handler is restored using `client.setNotificationHandler(previousHandler ?? null)`.

## Lifecycle Events Processed by applyTurnNotification

The `applyTurnNotification` function (lines 92-154 in `codex.mjs`) processes seven distinct notification types that map to specific lifecycle stages:

- **`thread/started`** (lines 92-99): Triggered when a new thread is created. The handler records the thread ID and initializes tracking state.
- **`thread/name/updated`** (lines 100-104): Fires when the thread's display name changes, allowing the plugin to update thread metadata.
- **`turn/started`** (lines 105-122): Signals the beginning of a turn, whether on the main thread or within a sub-agent. This initializes turn-specific state objects.
- **`item/started`** (lines 123-130): Indicates that an individual item—such as a command execution, file change, or reasoning step—has begun processing.
- **`item/completed`** (lines 131-138): Marks the completion of the corresponding item, enabling progress tracking and result capture.
- **`error`** (lines 139-141): Handles error notifications from Codex, allowing the plugin to capture failure states.
- **`turn/completed`** (lines 142-154): Signifies that the entire turn has finished, triggering cleanup operations and state reset.

## Turn Ownership and Handler Delegation

Before processing any notification, the handler verifies that the message belongs to the current turn using the `belongsToTurn` predicate. If the notification relates to a different turn or external process, the handler delegates to `previousHandler`, ensuring that concurrent operations do not interfere with each other.

This delegation pattern allows multiple turns or plugin components to coexist without dropping notifications. The check happens at the entry point of the custom handler, maintaining clean separation of concerns.

## Implementing Custom Notification Handlers

Developers can extend the plugin's notification handling by wrapping the existing handler. The `notificationHandler` property on the client object accepts any function that processes Codex protocol messages.

```javascript
// Capture the original handler before installing custom logic
const previous = client.notificationHandler;

client.setNotificationHandler(msg => {
  // Preserve plugin functionality
  if (previous) previous(msg);
  
  // Add custom tracking for item starts
  if (msg.method === "item/started") {
    console.log("Custom tracking - item started:", msg.params.item);
  }
});

```

After the turn completes, restore the previous handler to prevent memory leaks and ensure proper cleanup:

```javascript
client.setNotificationHandler(previous ?? null);

```

The `captureTurn` function abstracts this pattern internally, providing an `onProgress` callback that receives processed events without manual handler management:

```javascript
await captureTurn(
  client,
  threadId,
  () => client.request("turn/start", { threadId, input: buildTurnInput(prompt) }),
  {
    onProgress: (update) => console.log("Progress:", update)
  }
);

```

## Key Source Files and Architecture

The notification handling system spans several modules in the `plugins/codex/scripts/lib/` directory:

- **`plugins/codex/scripts/lib/codex.mjs`**: Implements `captureTurn` and `applyTurnNotification`, containing the core lifecycle event processing logic.
- **`plugins/codex/scripts/lib/app-server.mjs`**: Defines `AppServerClientBase` with the `notificationHandler` property and `setNotificationHandler` method (lines 76-78).
- **`plugins/codex/scripts/lib/state.mjs`**: Maintains turn-capture state including `turnId`, `completed` flags, and `reasoningSummary` that the handler updates.
- **`plugins/codex/scripts/lib/process.mjs`**: Provides `terminateProcessTree`, invoked during session cleanup when turns complete.
- **`plugins/codex/scripts/session-lifecycle-hook.mjs`**: Demonstrates session-level event handling (`SessionStart`, `SessionEnd`) that complements the turn-level notifications.

## Summary

- The **notification handler** in openai/codex-plugin-cc intercepts Codex server notifications to track thread, turn, and item lifecycles.
- `captureTurn` in `codex.mjs` installs a custom handler that processes seven specific notification methods including `thread/started`, `turn/started`, and `item/completed`.
- The `applyTurnNotification` function routes events to appropriate state updates while filtering irrelevant notifications via `belongsToTurn`.
- `setNotificationHandler` in `app-server.mjs` manages handler registration, preserving previous handlers to maintain plugin extensibility.
- Developers can wrap handlers to add custom logic without disrupting the plugin's core lifecycle tracking.

## Frequently Asked Questions

### What specific notification methods does the Codex plugin handle?

The plugin processes seven notification methods in `applyTurnNotification`: `thread/started`, `thread/name/updated`, `turn/started`, `item/started`, `item/completed`, `error`, and `turn/completed`. Each maps to a specific lifecycle stage in the Codex session.

### How does the plugin prevent notification conflicts between concurrent turns?

The handler uses a `belongsToTurn` check to verify that incoming notifications relate to the current turn. If a notification belongs to a different turn, the handler delegates to `previousHandler`, ensuring that concurrent operations receive their appropriate messages without interference.

### Can I add custom logic to process Codex notifications without modifying the core plugin?

Yes. You can capture the existing `client.notificationHandler`, install a wrapper function that calls the original handler first, then add your custom processing. Always restore the original handler after your turn completes using `client.setNotificationHandler(previous ?? null)`.

### Where is the notification handler state stored in the client architecture?

The handler is stored as the `notificationHandler` property on the `CodexAppServerClient` instance, defined in `plugins/codex/scripts/lib/app-server.mjs`. The `setNotificationHandler` method provides the official interface for updating this property while maintaining proper state management.