From 04b059a3e9d2e172875e617f60337f8ca2ee3a14 Mon Sep 17 00:00:00 2001 From: hirotomoyamada Date: Wed, 15 Apr 2026 13:17:20 +0900 Subject: [PATCH 1/5] feat(task): add --resume option for explicit thread resumption Closes #230 --- package.json | 2 +- plugins/codex/scripts/codex-companion.mjs | 24 ++++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 2f8efc96..292041a1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@openai/codex-plugin-cc", - "version": "1.0.3", + "version": "1.1.0", "private": true, "type": "module", "description": "Use Codex from Claude Code to review code or delegate tasks.", diff --git a/plugins/codex/scripts/codex-companion.mjs b/plugins/codex/scripts/codex-companion.mjs index 35222fd5..d9ecb1b6 100644 --- a/plugins/codex/scripts/codex-companion.mjs +++ b/plugins/codex/scripts/codex-companion.mjs @@ -461,11 +461,13 @@ async function executeTaskRun(request) { const taskMetadata = buildTaskRunMetadata({ prompt: request.prompt, - resumeLast: request.resumeLast + resumeLast: request.resumeLast || Boolean(request.resumeId) }); let resumeThreadId = null; - if (request.resumeLast) { + if (request.resumeId) { + resumeThreadId = request.resumeId; + } else if (request.resumeLast) { const latestThread = await resolveLatestTrackedTaskThread(workspaceRoot, { excludeJobId: request.jobId }); @@ -598,7 +600,7 @@ function buildTaskJob(workspaceRoot, taskMetadata, write) { }); } -function buildTaskRequest({ cwd, model, effort, prompt, write, resumeLast, jobId }) { +function buildTaskRequest({ cwd, model, effort, prompt, write, resumeLast, resumeId, jobId }) { return { cwd, model, @@ -606,6 +608,7 @@ function buildTaskRequest({ cwd, model, effort, prompt, write, resumeLast, jobId prompt, write, resumeLast, + resumeId, jobId }; } @@ -731,8 +734,8 @@ async function handleReview(argv) { async function handleTask(argv) { const { options, positionals } = parseCommandInput(argv, { - valueOptions: ["model", "effort", "cwd", "prompt-file"], - booleanOptions: ["json", "write", "resume-last", "resume", "fresh", "background"], + valueOptions: ["model", "effort", "cwd", "prompt-file", "resume"], + booleanOptions: ["json", "write", "resume-last", "fresh", "background"], aliasMap: { m: "model" } @@ -744,20 +747,21 @@ async function handleTask(argv) { const effort = normalizeReasoningEffort(options.effort); const prompt = readTaskPrompt(cwd, options, positionals); - const resumeLast = Boolean(options["resume-last"] || options.resume); + const resumeId = typeof options.resume === "string" ? options.resume : null; + const resumeLast = Boolean(options["resume-last"]); const fresh = Boolean(options.fresh); - if (resumeLast && fresh) { + if ((resumeLast || resumeId) && fresh) { throw new Error("Choose either --resume/--resume-last or --fresh."); } const write = Boolean(options.write); const taskMetadata = buildTaskRunMetadata({ prompt, - resumeLast + resumeLast: resumeLast || Boolean(resumeId) }); if (options.background) { ensureCodexAvailable(cwd); - requireTaskRequest(prompt, resumeLast); + requireTaskRequest(prompt, resumeLast || Boolean(resumeId)); const job = buildTaskJob(workspaceRoot, taskMetadata, write); const request = buildTaskRequest({ @@ -767,6 +771,7 @@ async function handleTask(argv) { prompt, write, resumeLast, + resumeId, jobId: job.id }); const { payload } = enqueueBackgroundTask(cwd, job, request); @@ -785,6 +790,7 @@ async function handleTask(argv) { prompt, write, resumeLast, + resumeId, jobId: job.id, onProgress: progress }), From 76c0aae9f9ed921f3732de2d606b0c7b84a0ee4f Mon Sep 17 00:00:00 2001 From: hirotomoyamada Date: Wed, 15 Apr 2026 13:23:07 +0900 Subject: [PATCH 2/5] revert: remove version bump from feat/resume-thread-id --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 292041a1..2f8efc96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@openai/codex-plugin-cc", - "version": "1.1.0", + "version": "1.0.3", "private": true, "type": "module", "description": "Use Codex from Claude Code to review code or delegate tasks.", From 2ce86d41f835ea67227bd0cbe57aa3bf42611fec Mon Sep 17 00:00:00 2001 From: hirotomoyamada Date: Wed, 15 Apr 2026 13:42:07 +0900 Subject: [PATCH 3/5] fix(task): use --resume-id for explicit thread ID to preserve --resume boolean compat --- plugins/codex/scripts/codex-companion.mjs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/codex/scripts/codex-companion.mjs b/plugins/codex/scripts/codex-companion.mjs index d9ecb1b6..d4ad06cf 100644 --- a/plugins/codex/scripts/codex-companion.mjs +++ b/plugins/codex/scripts/codex-companion.mjs @@ -77,7 +77,7 @@ function printUsage() { " node scripts/codex-companion.mjs setup [--enable-review-gate|--disable-review-gate] [--json]", " node scripts/codex-companion.mjs review [--wait|--background] [--base ] [--scope ]", " node scripts/codex-companion.mjs adversarial-review [--wait|--background] [--base ] [--scope ] [focus text]", - " node scripts/codex-companion.mjs task [--background] [--write] [--resume-last|--resume|--fresh] [--model ] [--effort ] [prompt]", + " node scripts/codex-companion.mjs task [--background] [--write] [--resume-last|--resume|--resume-id |--fresh] [--model ] [--effort ] [prompt]", " node scripts/codex-companion.mjs status [job-id] [--all] [--json]", " node scripts/codex-companion.mjs result [job-id] [--json]", " node scripts/codex-companion.mjs cancel [job-id] [--json]" @@ -734,8 +734,8 @@ async function handleReview(argv) { async function handleTask(argv) { const { options, positionals } = parseCommandInput(argv, { - valueOptions: ["model", "effort", "cwd", "prompt-file", "resume"], - booleanOptions: ["json", "write", "resume-last", "fresh", "background"], + valueOptions: ["model", "effort", "cwd", "prompt-file", "resume-id"], + booleanOptions: ["json", "write", "resume-last", "resume", "fresh", "background"], aliasMap: { m: "model" } @@ -747,11 +747,11 @@ async function handleTask(argv) { const effort = normalizeReasoningEffort(options.effort); const prompt = readTaskPrompt(cwd, options, positionals); - const resumeId = typeof options.resume === "string" ? options.resume : null; - const resumeLast = Boolean(options["resume-last"]); + const resumeId = typeof options["resume-id"] === "string" ? options["resume-id"] : null; + const resumeLast = Boolean(options["resume-last"] || options.resume); const fresh = Boolean(options.fresh); if ((resumeLast || resumeId) && fresh) { - throw new Error("Choose either --resume/--resume-last or --fresh."); + throw new Error("Choose either --resume/--resume-last/--resume-id or --fresh."); } const write = Boolean(options.write); const taskMetadata = buildTaskRunMetadata({ From 943aab3340c08214274e96dbfa9caeb86ef90d61 Mon Sep 17 00:00:00 2001 From: hirotomoyamada Date: Wed, 15 Apr 2026 14:00:39 +0900 Subject: [PATCH 4/5] docs: add --resume-id option to rescue examples --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 458c39fb..acbc868b 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,9 @@ Use it when you want Codex to: > [!NOTE] > Depending on the task and the model you choose these tasks might take a long time and it's generally recommended to force the task to be in the background or move the agent to the background. -It supports `--background`, `--wait`, `--resume`, and `--fresh`. If you omit `--resume` and `--fresh`, the plugin can offer to continue the latest rescue thread for this repo. +It supports `--background`, `--wait`, `--resume`, `--resume-id `, and `--fresh`. If you omit `--resume` and `--fresh`, the plugin can offer to continue the latest rescue thread for this repo. + +Use `--resume-id ` to resume a specific thread by ID. This is useful when managing multiple concurrent threads (e.g., parallel PR reviews) where `--resume` would pick the wrong thread. Examples: @@ -145,6 +147,7 @@ Examples: /codex:rescue investigate why the tests started failing /codex:rescue fix the failing test with the smallest safe patch /codex:rescue --resume apply the top fix from the last run +/codex:rescue --resume-id thread_abc123 continue from the specific thread /codex:rescue --model gpt-5.4-mini --effort medium investigate the flaky integration test /codex:rescue --model spark fix the issue quickly /codex:rescue --background investigate the regression From dc006ef083bf3cf2db88ba744fc8bc9f923c33bc Mon Sep 17 00:00:00 2001 From: hirotomoyamada Date: Wed, 15 Apr 2026 14:41:21 +0900 Subject: [PATCH 5/5] fix(task): reject flag-like values for --resume-id --- plugins/codex/scripts/codex-companion.mjs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/codex/scripts/codex-companion.mjs b/plugins/codex/scripts/codex-companion.mjs index d4ad06cf..67289a54 100644 --- a/plugins/codex/scripts/codex-companion.mjs +++ b/plugins/codex/scripts/codex-companion.mjs @@ -747,7 +747,11 @@ async function handleTask(argv) { const effort = normalizeReasoningEffort(options.effort); const prompt = readTaskPrompt(cwd, options, positionals); - const resumeId = typeof options["resume-id"] === "string" ? options["resume-id"] : null; + const rawResumeId = typeof options["resume-id"] === "string" ? options["resume-id"] : null; + if (rawResumeId && rawResumeId.startsWith("-")) { + throw new Error(`Invalid --resume-id value: "${rawResumeId}". Provide a thread ID, not a flag.`); + } + const resumeId = rawResumeId; const resumeLast = Boolean(options["resume-last"] || options.resume); const fresh = Boolean(options.fresh); if ((resumeLast || resumeId) && fresh) {