How Thread Persistence and Resume Functionality Work in Codex
Codex enables durable conversational state by storing threads on the server with the ephemeral flag set to false, allowing users to resume any session using --resume-last or explicit codex resume <thread-id> commands.
The openai/codex-plugin-cc repository implements a persistent thread model that lets developers start long-running tasks, exit the CLI, and later resume exactly where they left off. This article explains the underlying mechanics of thread persistence and resume functionality in Codex, referencing the actual source implementation in the companion scripts.
Starting a Persistent Thread
When you initiate a task that needs to survive beyond the current CLI session, the companion script invokes runAppServerTurn with persistThread: true. According to the source code in codex-companion.mjs, this flag determines whether the thread survives after the turn completes.
The startThread function in plugins/codex/scripts/lib/codex.mjs handles the actual creation. It sets the ephemeral flag to false when persistence is requested, ensuring the thread is stored on the Codex server rather than discarded:
// plugins/codex/scripts/lib/codex.mjs
const response = await client.request("thread/start", buildThreadParams(cwd, {
model,
sandbox,
// Non-ephemeral when persisting
ephemeral: options.persistThread ? false : true,
threadName: options.persistThread ? options.threadName : options.threadName ?? null
}));
A thread ID is generated server-side, and if a custom name is provided, the script calls thread/name/set to store it. This ID serves as the durable reference for all future resume operations.
Resuming a Saved Thread
Codex provides two primary mechanisms for resuming conversations: automatic resolution of the latest thread or explicit resumption via thread ID.
Automatic Resolution with --resume-last
When you pass the --resume-last flag (or --resume), the companion script calls resolveLatestTrackedTaskThread to locate the most recent tracked task. It extracts the id from that task record and passes it as resumeThreadId to runAppServerTurn.
The Resume Request Flow
Inside runAppServerTurn, the presence of options.resumeThreadId triggers a call to resumeThread, which sends a thread/resume request built by buildResumeParams:
// plugins/codex/scripts/lib/codex.mjs
async function resumeThread(client, threadId, cwd, options = {}) {
return client.request("thread/resume", buildResumeParams(threadId, cwd, options));
}
This reattaches your CLI session to the existing server-side thread, preserving all previous context, file states, and conversation history.
Generating Resume Commands in Output
Every task result rendered to your terminal includes a copy-pasteable resume command. The rendering helpers in plugins/codex/scripts/lib/render.mjs extract the threadId from either a stored job or the current job object, then compute the resume instruction:
// plugins/codex/scripts/lib/render.mjs
const threadId = storedJob?.threadId ?? job.threadId ?? null;
const resumeCommand = threadId ? `codex resume ${threadId}` : null;
// ...
lines.push(`Codex session ID: ${threadId}`);
lines.push(`Resume in Codex: ${resumeCommand}`);
This ensures you can always recover your session even if you did not note the thread ID manually.
Practical CLI Workflow
The persistence system supports three common developer workflows:
-
Start a persistent task: Creates a non-ephemeral thread and prints the resume command.
codex task --write "Implement feature X" # Output includes: # Codex session ID: thr_abc123 # Resume in Codex: codex resume thr_abc123 -
Resume the latest task automatically: The CLI auto-detects the most recent thread and continues the conversation.
codex task --resume-last "Refactor to use async I/O" -
Manually resume a specific thread: Use the exact command printed in step 1 to re-enter any previous session.
codex resume thr_abc123
Summary
- Thread persistence is controlled by the
ephemeralflag inbuildThreadParams; setting it tofalsestores the thread on Codex's servers. - Resume functionality relies on
thread/resumerequests sent viaresumeThreadincodex.mjs, using IDs resolved either automatically (--resume-last) or manually. - Resume commands are dynamically generated in
render.mjsand appended to every task output, ensuring users can always recover their session. - The implementation spans
codex-companion.mjs(CLI handling),codex.mjs(core logic), andrender.mjs(output formatting), with comprehensive test coverage intests/runtime.test.mjs.
Frequently Asked Questions
How does Codex store thread state between CLI invocations?
Codex stores the conversation state on its servers, not locally. When persistThread: true is passed to startThread, the ephemeral parameter is set to false in the thread/start request. This instructs the Codex backend to retain the thread indefinitely, allowing it to be retrieved later via the thread/resume endpoint using the unique thread ID.
What is the difference between --resume-last and a manual codex resume command?
The --resume-last flag triggers automatic resolution via resolveLatestTrackedTaskThread, which scans tracked tasks to find the most recent thread ID and immediately resumes it. In contrast, codex resume <thread-id> requires you to provide the specific thread ID manually, which is useful when you want to return to an older conversation rather than the most recent one.
Where in the codebase is the "Resume in Codex" output generated?
The resume instruction is constructed in plugins/codex/scripts/lib/render.mjs within the result rendering helpers (such as renderStoredJobResult and renderTaskResult). These functions extract the threadId from the job object, construct the command string `codex resume ${threadId}`, and append it to the terminal output alongside the session ID.
Can I resume a thread after completely closing my terminal?
Yes. Because persistent threads are stored server-side with ephemeral: false, they survive terminal closures, system restarts, and network changes. As long as you have the thread ID (from previous output or the codex resume command shown in your history), you can reconnect to the exact same conversation state from any new terminal session.
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 →