Codex Companion Lifecycle Hooks Explained: When SessionStart, SessionEnd, and Stop Fire

Codex Companion registers three optional lifecycle hooks—SessionStart, SessionEnd, and Stop—in plugins/codex/hooks/hooks.json that fire on session creation, session termination, and explicit user cancellation respectively.

The openai/codex-plugin-cc repository implements a robust hook system for managing Codex session lifecycles. Understanding when these hooks fire and what they execute is essential for customizing behavior or debugging session management issues.

Hook Registration in hooks.json

All three hooks are defined in plugins/codex/hooks/hooks.json. Each entry specifies the Node.js command to run and a timeout value that prevents indefinite hanging.

// plugins/codex/hooks/hooks.json (excerpt)
{
  "SessionStart": {
    "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/session-lifecycle-hook.mjs\" SessionStart",
    "timeout": 5000
  },
  "SessionEnd": {
    "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/session-lifecycle-hook.mjs\" SessionEnd",
    "timeout": 5000
  },
  "Stop": {
    "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/stop-review-gate-hook.mjs\"",
    "timeout": 900000
  }
}

These hooks are optional—the platform only executes them if the entries exist in hooks.json. By default, all three are configured and active.

SessionStart Hook: Fires Immediately After Session Creation

The SessionStart hook triggers immediately after a new Codex session initializes.

What It Does

In plugins/codex/scripts/session-lifecycle-hook.mjs, the handleSessionStart function performs essential setup:

// session-lifecycle-hook.mjs lines 20-30
async function handleSessionStart(sessionId, transcriptPath, pluginData) {
  await appendEnvVar('CODEX_SESSION_ID', sessionId);
  await appendEnvVar('CODEX_TRANSCRIPT_PATH', transcriptPath);
  await appendEnvVar('CODEX_PLUGIN_DATA', JSON.stringify(pluginData));
}

This stores three critical values in the environment:

  • CODEX_SESSION_ID – unique identifier for the session
  • CODEX_TRANSCRIPT_PATH – location of the conversation transcript
  • CODEX_PLUGIN_DATA – serialized plugin configuration

The 5-second timeout ensures rapid failure if environment setup hangs.

SessionEnd Hook: Fires When Sessions Terminate

The SessionEnd hook executes whenever a Codex session ends—whether through normal completion or user cancellation.

What It Does

The handleSessionEnd function in session-lifecycle-hook.mjs (lines 83-114) orchestrates comprehensive cleanup:

Cleanup Action Function Called
Shut down broker endpoint sendBrokerShutdown()
Remove queued and running jobs cleanupSessionJobs()
Tear down broker session teardownBrokerSession()
// Example: Manual SessionEnd invocation (matches platform behavior)
const { execSync } = require('child_process');

execSync('node plugins/codex/scripts/session-lifecycle-hook.mjs SessionEnd', {
  env: { CLAUDE_PLUGIN_ROOT: process.cwd() },
  timeout: 5000,
});

This ensures no orphaned processes or dangling job queues persist after a session closes.

Stop Hook: Fires on Explicit User Cancellation

The Stop hook differs from SessionEnd—it specifically handles the review-gate stop command rather than general session termination.

When It Fires

The Stop hook triggers when a user explicitly clicks "Stop" in the UI to abort a running review-gate process. This can occur independent of normal session lifecycle events.

What It Does

The stop-review-gate-hook.mjs script executes the stop-review-gate workflow:

// Example: Manual Stop hook invocation
execSync('node plugins/codex/scripts/stop-review-gate-hook.mjs', {
  env: { CLAUDE_PLUGIN_ROOT: process.cwd() },
  timeout: 900000, // 15 minutes
});

The dramatically longer timeout (900 seconds vs. 5 seconds) accommodates the complexity of aborting pending review checks and cleaning up associated state without leaving partially processed gates.

Timeout Values and Reliability

Hook Timeout Rationale
SessionStart 5 seconds Environment setup should be near-instant
SessionEnd 5 seconds Cleanup operations are lightweight
Stop 15 minutes Review-gate abortion may require negotiating with external systems

These timeouts guarantee that hook failures don't indefinitely block the main Codex process.

Key Implementation Files

File Path Purpose
plugins/codex/hooks/hooks.json Hook definitions and command mappings
plugins/codex/scripts/session-lifecycle-hook.mjs SessionStart and SessionEnd handlers
plugins/codex/scripts/stop-review-gate-hook.mjs Stop hook implementation

Summary

  • SessionStart fires after session creation, storing session metadata via handleSessionStart in session-lifecycle-hook.mjs
  • SessionEnd fires on any session termination, cleaning up brokers and jobs via handleSessionEnd in the same file
  • Stop fires only on explicit review-gate cancellation, running stop-review-gate-hook.mjs with a 15-minute timeout
  • All hooks are optional and configured in plugins/codex/hooks/hooks.json with appropriate timeouts

Frequently Asked Questions

What happens if a hook times out?

The platform aborts the hook execution and continues. The main Codex session proceeds regardless of hook success or failure, ensuring reliability isn't compromised by plugin issues.

Can I disable specific hooks?

Yes. Remove the corresponding entry from plugins/codex/hooks/hooks.json. The platform checks for hook existence before execution, so missing entries are silently skipped.

How does SessionEnd differ from Stop?

SessionEnd runs for every session closure—normal or abnormal—and performs general resource cleanup. Stop only runs when the user explicitly cancels a review-gate process and handles aborting that specific workflow.

Where does CLAUDE_PLUGIN_ROOT come from?

The environment variable is set by the hosting platform to the absolute path of the codex-plugin-cc installation. Hook commands use it to locate scripts reliably regardless of working directory.

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 →