diff --git a/plugins/codex/agents/codex-rescue.md b/plugins/codex/agents/codex-rescue.md index 7009ec86a..153aa855a 100644 --- a/plugins/codex/agents/codex-rescue.md +++ b/plugins/codex/agents/codex-rescue.md @@ -41,6 +41,14 @@ Forwarding rules: - Return the stdout of the `codex-companion` command exactly as-is. - If the Bash call fails or Codex cannot be invoked, return nothing. +Prompt assembly and background handling: + +- Pass the task prompt inline, as the last arguments and after a `--` delimiter: `task [runtime options] -- ''`. Without `--`, option-like text inside the prompt such as `--write` is parsed as a runtime flag and stripped from the prompt. +- Single-quote the prompt so Bash performs no expansion, and escape every embedded single quote as `'\''` (so `don't stop` is passed as `'don'\''t stop'`). Never wrap the prompt in double quotes: `$(...)`, backticks, `$VAR`, and a bare `"` would be expanded or would terminate the argument locally before the companion receives the text. +- Do not write prompt files to disk using `node`, `fs`, shell heredocs, or any other interpreter in order to consume them with `--prompt-file`. +- Do not poll, `pgrep`, `watch`, `tail` logs, or run wait loops for a background task. When the call uses `--background`, return the printed job ID and the suggested `/codex:status ` command exactly as output and stop. +- A foreground call that the Bash harness moves to the background after its timeout never prints a companion job ID, and the harness identifier is not one. Return whatever the harness printed as-is; do not invent a job ID or suggest a `/codex:status` command for it. + Response style: - Do not add commentary before or after the forwarded `codex-companion` output. diff --git a/plugins/codex/skills/codex-cli-runtime/SKILL.md b/plugins/codex/skills/codex-cli-runtime/SKILL.md index 0e91bfb50..ad7874c9b 100644 --- a/plugins/codex/skills/codex-cli-runtime/SKILL.md +++ b/plugins/codex/skills/codex-cli-runtime/SKILL.md @@ -35,8 +35,15 @@ Command selection: - `--effort`: accepted values are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`. - `task --resume-last`: internal helper for "keep going", "resume", "apply the top fix", or "dig deeper" after a previous rescue run. +Prompt and background handling: +- Pass the prompt inline, as the last arguments and after a `--` delimiter: `task [runtime options] -- ''`. Without `--`, option-like text inside the prompt such as `--write` is parsed as a runtime flag and stripped from the prompt. +- Single-quote the prompt so Bash performs no expansion, and escape every embedded single quote as `'\''` (so `don't stop` is passed as `'don'\''t stop'`). Never wrap the prompt in double quotes: `$(...)`, backticks, `$VAR`, and a bare `"` would be expanded or would terminate the argument locally before the companion receives the text. +- Do not write prompt files to disk with `node`, `fs`, shell heredocs, or any other interpreter in order to consume them with `--prompt-file`. +- Do not poll, `pgrep`, `watch`, `tail` logs, or run wait loops for a background task. When `task` is invoked with `--background`, return the job ID and suggested `/codex:status ` command exactly as output and stop. +- A foreground call that the Bash harness moves to the background never prints a companion job ID, and the harness identifier is not one. Return whatever the harness printed as-is; do not invent a job ID or suggest a `/codex:status` command for it. + Safety rules: -- Default to write-capable Codex work in `codex:codex-rescue` unless the user explicitly asks for read-only behavior. +- Default to write-capable Codex work in `codex:codex-rescue` unless the user explicitly asks for read-only behavior or only wants review, diagnosis, or research without edits. - Preserve the user's task text as-is apart from stripping routing flags. - Do not inspect the repository, read files, grep, monitor progress, poll status, fetch results, cancel jobs, summarize output, or do any follow-up work of your own. - Return the stdout of the `task` command exactly as-is. diff --git a/tests/commands.test.mjs b/tests/commands.test.mjs index c34b06059..c47d29423 100644 --- a/tests/commands.test.mjs +++ b/tests/commands.test.mjs @@ -187,6 +187,25 @@ test("transfer, result, and cancel commands are exposed as deterministic runtime assert.match(resultHandling, /if Codex was never successfully invoked, do not generate a substitute answer at all/i); }); +test("rescue agent forbids unsafe prompt writes, wait loops, and harness job-ID assumptions", () => { + const agent = read("agents/codex-rescue.md"); + const runtimeSkill = read("skills/codex-cli-runtime/SKILL.md"); + + assert.match(agent, /Do not write prompt files to disk/i); + assert.match(agent, /node[^\n]*fs/i); + assert.match(agent, /Do not poll.*pgrep/i); + assert.match(agent, /return the printed job ID/i); + assert.match(agent, /task \[runtime options\] -- ''/); + assert.match(agent, /escape every embedded single quote as `'\\''`.*Never wrap the prompt in double quotes/i); + assert.match(agent, /do not invent a job ID or suggest a `\/codex:status` command for it/i); + assert.match(runtimeSkill, /Do not write prompt files to disk/i); + assert.match(runtimeSkill, /Do not poll.*pgrep/i); + assert.match(runtimeSkill, /return the job ID and suggested/i); + assert.match(runtimeSkill, /task \[runtime options\] -- ''/); + assert.match(runtimeSkill, /escape every embedded single quote as `'\\''`.*Never wrap the prompt in double quotes/i); + assert.match(runtimeSkill, /do not invent a job ID or suggest a `\/codex:status` command for it/i); +}); + test("internal docs use task terminology for rescue runs", () => { const runtimeSkill = read("skills/codex-cli-runtime/SKILL.md"); const promptingSkill = read("skills/gpt-5-4-prompting/SKILL.md");