How the Codex Plugin Handles Session Filtering for Job Visibility
The Codex plugin isolates jobs to the current Claude session by storing the session ID in CODEX_COMPANION_SESSION_ID, tagging every job with this ID, and filtering all operations—status checks, cancellations, and review gates—against this value.
The openai/codex-plugin-cc repository implements strict session filtering for job visibility to prevent cross-session interference. When you run Claude Code with the Codex plugin enabled, the system ensures that commands like codex:status and codex:cancel only surface or affect jobs spawned during your current Claude session. This isolation relies on a coordinated chain of environment propagation, job metadata tagging, and filtered queries.
Propagating Session IDs via Environment Variables
When a Claude session initializes, the plugin captures the unique session identifier and exposes it to downstream processes. This mechanism forms the foundation of the visibility boundary.
Capturing the Session Identifier
The session-lifecycle-hook runs automatically when Claude starts. In plugins/codex/scripts/session-lifecycle-hook.mjs (lines 20-23), the hook retrieves the Claude session ID and exports it as an environment variable:
process.env.CODEX_COMPANION_SESSION_ID = claudeSessionId;
This variable persists for the duration of the session and serves as the single source of truth for all session-based filtering.
Tagging Jobs with Session Metadata
Every Codex job created through the plugin carries an immutable sessionId field that links it to its originating Claude session. This attribution occurs at job creation time.
The Tracked Jobs Utility
In plugins/codex/scripts/lib/tracked-jobs.mjs (lines 62-66), the job creation logic reads the CODEX_COMPANION_SESSION_ID environment variable and embeds it into the job record:
const job = {
id: generateId(),
sessionId: process.env.CODEX_COMPANION_SESSION_ID,
// ... other fields
};
Once persisted, this sessionId field acts as the foreign key for all session-scoped queries.
Filtering Operations by Current Session
With jobs properly tagged, the plugin filters every user-facing operation against the current value of CODEX_COMPANION_SESSION_ID. This ensures that commands executed in one Claude window cannot see or manipulate jobs from another.
Status and Cancel Command Isolation
The status command queries the in-memory job registry and returns only records where job.sessionId matches the current environment variable. As demonstrated in tests/runtime.test.mjs (lines 1159-1163), the test suite validates that "status without a job id only shows jobs from the current Claude session."
Similarly, the cancel command ignores active jobs from other sessions unless you explicitly provide a job ID. The test at lines 1637-1658 confirms that cross-session cancellation attempts return "no active jobs" messages scoped to the current session.
Review Gate Session Validation
The stop-review-gate hook enforces session isolation before allowing a review gate to close. In plugins/codex/scripts/stop-review-gate-hook.mjs (lines 45-47), the logic filters pending jobs by comparing each job's sessionId to CODEX_COMPANION_SESSION_ID:
const pendingJobs = state.jobs.filter(job =>
job.sessionId === process.env.CODEX_COMPANION_SESSION_ID
);
If no pending jobs match the current session, the gate closes automatically.
Cleaning Up Sessions on Exit
When a Claude session terminates, the lifecycle hook removes all associated jobs to prevent stale entries from persisting into future sessions.
The Cleanup Routine
In plugins/codex/scripts/session-lifecycle-hook.mjs (lines 42-75), the session-end handler iterates over the global state.jobs array, identifies records matching the ending session's ID, and terminates any still-running processes before removing them from state:
for (const job of state.jobs) {
if (job.sessionId === endingSessionId) {
await terminateProcess(job);
state.jobs.delete(job.id);
}
}
Practical Example of Session Isolation
The following workflow demonstrates how session filtering manifests in practice:
# Claude session "thr_1" starts: CODEX_COMPANION_SESSION_ID is set automatically
# Run a job in this session
$ codex:run analyze-code.js
# Job record stored with sessionId: "thr_1"
# Check status—only thr_1 jobs appear
$ codex:status
# → Active Codex job: thr_1-abc...
# From a different Claude session, same commands return empty
$ codex:status
# → No active Codex jobs for this session.
# Session end cleanup removes thr_1 jobs automatically
Summary
- Environment propagation: The session-lifecycle-hook sets
CODEX_COMPANION_SESSION_IDon startup, creating a visibility boundary for the entire session. - Job attribution: The tracked-jobs utility tags every new job with the current session ID via the
sessionIdfield. - Filtered queries: Status, cancel, and review-gate operations in
runtime.test.mjsandstop-review-gate-hook.mjscompare job metadata against the active session ID to exclude cross-session entries. - Automatic cleanup: On session exit,
session-lifecycle-hook.mjspurges all jobs associated with the terminating session, preventing stale job accumulation.
Frequently Asked Questions
How does the Codex plugin prevent jobs from one Claude session appearing in another?
The plugin stores the Claude session ID in the CODEX_COMPANION_SESSION_ID environment variable and tags every Codex job with this value in the sessionId field. When listing or acting on jobs, the plugin filters the results to only include records where job.sessionId matches the current environment variable, effectively hiding jobs from other sessions.
What happens to Codex jobs when a Claude session ends?
When the session ends, the session-lifecycle-hook iterates through all tracked jobs in state.jobs and removes those where sessionId matches the ending session. It also terminates any still-running processes associated with those jobs, ensuring no orphaned tasks persist into future sessions.
Can I view or cancel jobs from a different Claude session using the Codex plugin?
By default, the codex:status and codex:cancel commands filter by the current session ID as implemented in the runtime tests. While you may target specific jobs by explicit ID, the plugin architecture discourages cross-session manipulation to maintain clean isolation boundaries and prevent accidental interference.
Which source files handle the session filtering logic?
Session filtering spans four key files: plugins/codex/scripts/session-lifecycle-hook.mjs manages the environment variable and cleanup, plugins/codex/scripts/lib/tracked-jobs.mjs handles job tagging, plugins/codex/scripts/stop-review-gate-hook.mjs validates review gates against session IDs, and tests/runtime.test.mjs contains the test coverage verifying these behaviors.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →