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.
--- 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`.
Summary
taskcan only resume the latest thread (--resume-last), and a background job killed by a usage limit is left asfailedwith no way to continue it from the plugin — even though/codex:resultprints the thread'sCodex session IDand the runtime already supportsresumeThreadId.Scenario (plugin 1.0.6, codex-cli 0.151.0, Linux)
/codex:rescue --backgroundaudits in parallel (read-only,--effort high).--resume-lastonly targets the newest thread, and/codex:resultjust printsResume 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>totask, mutually exclusive with--resume-last/--fresh, and thread it throughbuildTaskRequest→executeTaskRunso both the foreground and background paths honour it. Patch against 1.0.6 (companion script + thecodex-cli-runtimeskill 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:statusoffer a one-liner to resume it after the reset, instead of marking it terminallyfailed.Patch