openai / openai/codex-plugin-cc

task: add `--resume-thread <id>` — jobs killed by a usage limit cannot be resumed from the plugin

Open
#700 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
33.3k
Forks
2.3k
PR merge metrics
No merged PRs in 30d

Description

Summary

task can only resume the latest thread (--resume-last), and a background job killed by a usage limit is left as failed with no way to continue it from the plugin — even though /codex:result prints the thread's Codex session ID and the runtime already supports resumeThreadId.

Scenario (plugin 1.0.6, codex-cli 0.151.0, Linux)

  1. Launch five /codex:rescue --background audits in parallel (read-only, --effort high).
  2. The account's 5-hour window runs out mid-run. All five jobs end with:
    [codex] Codex error: You've hit your usage limit. ... try again at 3:40 PM.
    [codex] Turn failed.
    
    Each had already read a large part of the repo (the expensive part).
  3. After the reset, there is no plugin way to continue them: --resume-last only targets the newest thread, and /codex:result just prints Resume in Codex: codex resume <id> — i.e. it hands the id back and sends the user to the terminal.

Workaround that works today: codex exec --sandbox read-only resume <id> "<continue prompt>" per thread.

Proposed fix (small)

Add --resume-thread <id> to task, mutually exclusive with --resume-last / --fresh, and thread it through buildTaskRequestexecuteTaskRun so both the foreground and background paths honour it. Patch against 1.0.6 (companion script + the codex-cli-runtime skill so the rescue subagent knows the flag) is below — ~40 lines, no behaviour change for existing flags.

A natural follow-up: when a job fails on a usage-limit error (the message carries the reset time), keep the thread id in the job record and let /codex:status offer a one-liner to resume it after the reset, instead of marking it terminally failed.

Patch

--- a/scripts/codex-companion.mjs
+++ b/scripts/codex-companion.mjs
@@ -79,7 +79,7 @@
       "  node scripts/codex-companion.mjs setup [--enable-review-gate|--disable-review-gate] [--json]",
       "  node scripts/codex-companion.mjs review [--wait|--background] [--base <ref>] [--scope <auto|working-tree|branch>]",
       "  node scripts/codex-companion.mjs adversarial-review [--wait|--background] [--base <ref>] [--scope <auto|working-tree|branch>] [focus text]",
-      "  node scripts/codex-companion.mjs task [--background] [--write] [--resume-last|--resume|--fresh] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh>] [prompt]",
+      "  node scripts/codex-companion.mjs task [--background] [--write] [--resume-last|--resume|--resume-thread <id>|--fresh] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh>] [prompt]",
       "  node scripts/codex-companion.mjs transfer [--source <claude-jsonl>] [--json]",
       "  node scripts/codex-companion.mjs status [job-id] [--all] [--json]",
       "  node scripts/codex-companion.mjs result [job-id] [--json]",
@@ -464,11 +464,14 @@
 
   const taskMetadata = buildTaskRunMetadata({
     prompt: request.prompt,
-    resumeLast: request.resumeLast
+    resumeLast: request.resumeLast || Boolean(request.resumeThread)
   });
 
   let resumeThreadId = null;
-  if (request.resumeLast) {
+  if (request.resumeThread) {
+    // Explicit thread id (e.g. a job cut by a usage limit): skip the latest-thread lookup.
+    resumeThreadId = String(request.resumeThread).trim();
+  } else if (request.resumeLast) {
     const latestThread = await resolveLatestTrackedTaskThread(workspaceRoot, {
       excludeJobId: request.jobId
     });
@@ -479,7 +482,7 @@
   }
 
   if (!request.prompt && !resumeThreadId) {
-    throw new Error("Provide a prompt, a prompt file, piped stdin, or use --resume-last.");
+    throw new Error("Provide a prompt, a prompt file, piped stdin, or use --resume-last / --resume-thread <id>.");
   }
 
   const result = await runAppServerTurn(workspaceRoot, {
@@ -601,7 +604,7 @@
   });
 }
 
-function buildTaskRequest({ cwd, model, effort, prompt, write, resumeLast, jobId }) {
+function buildTaskRequest({ cwd, model, effort, prompt, write, resumeLast, resumeThread = null, jobId }) {
   return {
     cwd,
     model,
@@ -609,6 +612,7 @@
     prompt,
     write,
     resumeLast,
+    resumeThread,
     jobId
   };
 }
@@ -649,9 +653,9 @@
   return positionalPrompt || readStdinIfPiped();
 }
 
-function requireTaskRequest(prompt, resumeLast) {
-  if (!prompt && !resumeLast) {
-    throw new Error("Provide a prompt, a prompt file, piped stdin, or use --resume-last.");
+function requireTaskRequest(prompt, resumeLast, resumeThread = null) {
+  if (!prompt && !resumeLast && !resumeThread) {
+    throw new Error("Provide a prompt, a prompt file, piped stdin, or use --resume-last / --resume-thread <id>.");
   }
 }
 
@@ -761,7 +765,7 @@
 
 async function handleTask(argv) {
   const { options, positionals } = parseCommandInput(argv, {
-    valueOptions: ["model", "effort", "cwd", "prompt-file"],
+    valueOptions: ["model", "effort", "cwd", "prompt-file", "resume-thread"],
     booleanOptions: ["json", "write", "resume-last", "resume", "fresh", "background"],
     aliasMap: {
       m: "model"
@@ -775,19 +779,23 @@
   const prompt = readTaskPrompt(cwd, options, positionals);
 
   const resumeLast = Boolean(options["resume-last"] || options.resume);
+  const resumeThread = options["resume-thread"] ? String(options["resume-thread"]).trim() : null;
   const fresh = Boolean(options.fresh);
-  if (resumeLast && fresh) {
-    throw new Error("Choose either --resume/--resume-last or --fresh.");
+  if ((resumeLast || resumeThread) && fresh) {
+    throw new Error("Choose either --resume/--resume-last/--resume-thread or --fresh.");
+  }
+  if (resumeLast && resumeThread) {
+    throw new Error("Choose either --resume-last or --resume-thread <id>, not both.");
   }
   const write = Boolean(options.write);
   const taskMetadata = buildTaskRunMetadata({
     prompt,
-    resumeLast
+    resumeLast: resumeLast || Boolean(resumeThread)
   });
 
   if (options.background) {
     ensureCodexAvailable(cwd);
-    requireTaskRequest(prompt, resumeLast);
+    requireTaskRequest(prompt, resumeLast, resumeThread);
 
     const job = buildTaskJob(workspaceRoot, taskMetadata, write);
     const request = buildTaskRequest({
@@ -797,6 +805,7 @@
       prompt,
       write,
       resumeLast,
+      resumeThread,
       jobId: job.id
     });
     const { payload } = enqueueBackgroundTask(cwd, job, request);
@@ -815,6 +824,7 @@
         prompt,
         write,
         resumeLast,
+        resumeThread,
         jobId: job.id,
         onProgress: progress
       }),
--- a/plugins/codex/skills/codex-cli-runtime/SKILL.md
+++ b/plugins/codex/skills/codex-cli-runtime/SKILL.md
@@ -30,6 +30,7 @@
 - If the forwarded request includes `--effort`, pass it through to `task`.
 - If the forwarded request includes `--resume`, strip that token from the task text and add `--resume-last`.
 - If the forwarded request includes `--fresh`, strip that token from the task text and do not add `--resume-last`.
+- If the forwarded request includes `--resume-thread <id>` (a Codex session/thread UUID, e.g. from a `/codex:result` line "Codex session ID:" of a job that failed on a usage limit), strip both tokens from the task text and pass `--resume-thread <id>` to `task`. This resumes THAT thread with its context instead of the latest one.
 - `--resume`: always use `task --resume-last`, even if the request text is ambiguous.
 - `--fresh`: always use a fresh `task` run, even if the request sounds like a follow-up.
 - `--effort`: accepted values are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in scripts/codex-companion.mjs at handleTask, buildTaskRequest, and executeTaskRun, then review plugins/codex/skills/codex-cli-runtime/SKILL.md. Verify that --resume-thread accepts an explicit session ID, works in foreground and background runs, and is rejected with --resume-last or --fresh; update the runtime guidance accordingly.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
cli, documentation
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.