# How the Codex-CC Plugin Intercepts Claude Code Sessions Using SessionStart and SessionEnd Hooks

> Learn how the Codex-CC plugin intercepts Claude Code sessions using SessionStart and SessionEnd hooks to capture metadata and clean up resources.

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

---

**The Codex-CC plugin intercepts Claude Code sessions via two lifecycle hooks declared in [`hooks.json`](https://github.com/openai/codex-plugin-cc/blob/main/hooks.json) and implemented in `session-lifecycle-hook.mjs`, which capture session metadata at startup and clean up resources at termination.**

The **openai/codex-plugin-cc** repository provides a plugin architecture that extends Claude Code with external tooling capabilities. A core mechanism enabling this integration is the session lifecycle interception system, which allows the plugin to monitor and respond to Claude Code session boundaries through declarative hooks.

## Hook Registration in hooks.json

The plugin's entry point for session interception is **[`plugins/codex/hooks/hooks.json`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/hooks/hooks.json)**. This JSON configuration file declares which lifecycle events the plugin handles and maps them to their implementations.

According to the repository structure, this file contains the hook definitions:

```json
{
  "sessionStart": "plugins/codex/scripts/session-lifecycle-hook.mjs",
  "sessionEnd": "plugins/codex/scripts/session-lifecycle-hook.mjs"
}

```

Both hooks point to the same module, which exports differentiated handlers for each event type. This centralization simplifies maintenance while keeping the hook-to-handler mapping explicit and version-controllable.

## Session Lifecycle Implementation

The **`plugins/codex/scripts/session-lifecycle-hook.mjs`** module contains the actual interception logic. It imports utilities from **`claude-session-transfer.mjs`** to bridge Claude Code's internal session representation with the plugin's state management system.

### SessionStart Handler

When Claude Code initializes a new session, the `sessionStart` hook executes immediately. The handler:

1. Extracts the **session identifier**, **workspace directory**, and **environment variables** from the event payload
2. Invokes `transferSession()` from the session transfer library to normalize the data
3. Persists this metadata via **`state.set()`** for cross-command accessibility

```javascript
import { transferSession } from "./lib/claude-session-transfer.mjs";
import { state } from "./lib/state.mjs";

export async function onSessionStart(event) {
  const { sessionId, workspace } = await transferSession(event);
  await state.set("currentSession", { sessionId, workspace });
}

```

This persistence layer enables subsequent plugin commands—such as `codex review` or `codex result`—to retrieve the active session context without re-initializing connections to Claude Code.

### SessionEnd Handler

When the Claude Code session terminates, the `sessionEnd` hook triggers cleanup operations:

```javascript
import { state } from "./lib/state.mjs";

export async function onSessionEnd() {
  await state.delete("currentSession");
  // terminate background jobs, flush temporary files, etc.
}

```

The handler:

- Removes the stored session entry via **`state.delete()`**
- Releases any broker-registered resources
- Prevents orphaned background processes from persisting across sessions

## Broker Integration Architecture

The lifecycle hooks do not operate in isolation. They register with the **App-Server Broker** implemented in **`plugins/codex/scripts/app-server-broker.mjs`**, which serves as the event routing layer between Claude Code and installed plugins.

This broker architecture provides two critical capabilities:

- **Real-time event forwarding** — Claude Code lifecycle events reach the plugin without core code modification
- **Isolation boundary** — Plugin failures in hook handlers do not crash the host Claude Code process

## Session State Persistence

The **`plugins/codex/scripts/lib/state.mjs`** module provides the storage backend for session metadata. By using this abstraction rather than in-memory variables, the plugin:

- Survives individual command invocations within the same session
- Enables inspection of session state for debugging
- Supports future extensions like session serialization or replay

## Key Files and Responsibilities

| Component | Path | Function |
|-----------|------|----------|
| Hook declarations | [`plugins/codex/hooks/hooks.json`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/hooks/hooks.json) | Maps lifecycle events to handler modules |
| Lifecycle handlers | `plugins/codex/scripts/session-lifecycle-hook.mjs` | Implements `onSessionStart` and `onSessionEnd` |
| Session transfer | `plugins/codex/scripts/lib/claude-session-transfer.mjs` | Normalizes Claude-specific session data |
| State management | `plugins/codex/scripts/lib/state.mjs` | Persistent key-value storage for session metadata |
| Event broker | `plugins/codex/scripts/app-server-broker.mjs` | Routes Claude Code events to registered plugins |

## Practical Impact of Session Interception

The SessionStart and SessionEnd hooks enable several plugin capabilities that would otherwise require deep Claude Code integration:

- **Workspace persistence** — Commands executed across multiple invocations share the same session context and working directory
- **Metadata propagation** — Session identifiers flow automatically to render pipelines and job tracking systems
- **Resource hygiene** — Guaranteed cleanup prevents accumulation of temporary files and zombie processes
- **Cross-session isolation** — Complete state reset between sessions eliminates data leakage risks

## Summary

- The **Codex-CC plugin** intercepts Claude Code sessions through declarative hooks configured in **[`hooks.json`](https://github.com/openai/codex-plugin-cc/blob/main/hooks.json)**
- **`session-lifecycle-hook.mjs`** handles both startup and shutdown events via differentiated exports
- **`claude-session-transfer.mjs`** bridges Claude's internal session representation with plugin-compatible data structures
- **`state.mjs`** provides persistent storage that survives individual command executions within a session
- The **App-Server Broker** routes events safely without modifying Claude Code core functionality
- Cleanup in **`onSessionEnd`** ensures resources are released and session isolation is maintained

## Frequently Asked Questions

### How does the plugin know when a Claude Code session starts?

The **`sessionStart`** hook declared in **[`hooks.json`](https://github.com/openai/codex-plugin-cc/blob/main/hooks.json)** registers a callback with the App-Server Broker. When Claude Code initializes a new session, the broker invokes this callback with session metadata including the workspace path and environment variables.

### What happens if the session end hook fails to execute?

The broker architecture isolates plugin failures from the host process. If **`onSessionEnd`** throws or hangs, Claude Code still terminates normally, though temporary files or background jobs may persist. The plugin implements defensive cleanup on subsequent session starts to handle such edge cases.

### Can multiple plugins register the same lifecycle hooks?

Yes. The broker supports multiple plugin registrations for identical hooks. Each registered handler executes independently, and failures in one plugin do not cascade to others. Execution order follows plugin load sequence unless explicitly prioritized.

### Where is session data stored between commands?

Session metadata persists in the **state module** at **`plugins/codex/scripts/lib/state.mjs`**. This abstraction uses a backing store (typically filesystem-based) that survives process boundaries between individual Codex CLI invocations within the same Claude Code session.