Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,17 @@ 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 <threadId>`, and `--fresh`. If you omit `--resume` and `--fresh`, the plugin can offer to continue the latest rescue thread for this repo.

Use `--resume-id <threadId>` 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:

```bash
/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
Expand Down
28 changes: 19 additions & 9 deletions plugins/codex/scripts/codex-companion.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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 <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-id <threadId>|--fresh] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh>] [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]"
Expand Down Expand Up @@ -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
});
Expand Down Expand Up @@ -598,14 +600,15 @@ 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,
effort,
prompt,
write,
resumeLast,
resumeId,
jobId
};
}
Expand Down Expand Up @@ -731,7 +734,7 @@ async function handleReview(argv) {

async function handleTask(argv) {
const { options, positionals } = parseCommandInput(argv, {
valueOptions: ["model", "effort", "cwd", "prompt-file"],
valueOptions: ["model", "effort", "cwd", "prompt-file", "resume-id"],
booleanOptions: ["json", "write", "resume-last", "resume", "fresh", "background"],
aliasMap: {
m: "model"
Expand All @@ -744,20 +747,25 @@ async function handleTask(argv) {
const effort = normalizeReasoningEffort(options.effort);
const prompt = readTaskPrompt(cwd, options, positionals);

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 && fresh) {
throw new Error("Choose either --resume/--resume-last or --fresh.");
if ((resumeLast || resumeId) && fresh) {
throw new Error("Choose either --resume/--resume-last/--resume-id 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({
Expand All @@ -767,6 +775,7 @@ async function handleTask(argv) {
prompt,
write,
resumeLast,
resumeId,
jobId: job.id
});
const { payload } = enqueueBackgroundTask(cwd, job, request);
Expand All @@ -785,6 +794,7 @@ async function handleTask(argv) {
prompt,
write,
resumeLast,
resumeId,
jobId: job.id,
onProgress: progress
}),
Expand Down