Summary
When /codex:adversarial-review (or the underlying node scripts/codex-companion.mjs adversarial-review …) is invoked with a focus text argument, the focus text is re-tokenized by normalizeArgv + splitRawArgumentString. Any literal --FLAG VALUE substring inside the focus text is then consumed by the option parser and forwarded as if the caller had set that flag — including --model X, which gets sent to the OpenAI API as the model selection.
The result is a 400 from the OpenAI API:
{"type":"error","status":400,"error":{"type":"invalid_request_error","message":"The 'pm_v6_2' model is not supported when using Codex with a ChatGPT account."}}
Reproduced on plugin version 1.0.3 and verified by reading origin/main (1.0.4) — the bug is still present upstream.
Reproduction
node ~/.claude/plugins/cache/openai-codex/codex/1.0.3/scripts/codex-companion.mjs \
adversarial-review "Review the build script. The build command is: build_v6_artifact.py --variant v6.2 --model pm_v6_2"
Expected: review runs, focus text is treated as opaque prose.
Actual: companion picks pm_v6_2 as the model, OpenAI rejects it as an unknown model name.
Any focus text that mentions a flag-with-value example (which is common for design reviews of CLI tools) hits this. Using a placeholder like <registry_name> also fails (The '<registry_name>' model is not supported).
Root cause
scripts/lib/args.mjs:
splitRawArgumentString(raw) does a shell-like tokenization of a single string into argv-style tokens.
scripts/codex-companion.mjs:
normalizeArgv(argv) (line 127-134) calls splitRawArgumentString when argv.length === 1.
main() (line 980-988) destructures: const [subcommand, ...argv] = process.argv.slice(2);. When the caller passes a single quoted focus argument, argv has exactly one element — normalizeArgv then re-tokenizes the user's whole focus prose.
handleReviewCommand (line 683-687) uses parseCommandInput with valueOptions: ["base", "scope", "model", "cwd"] and aliasMap { m: "model" }. Any --model XXX (or -m XXX) substring in the re-tokenized prose is consumed as the OpenAI model selection. The remaining tokens become positionals (focus text), so the visible failure mode is "review runs against the wrong model name" rather than "command rejected with a syntax error".
The same vulnerability applies to --base, --scope, --cwd, --effort, --prompt-file, etc., wherever the corresponding subcommand registers them as value options. --model happens to be the most visible one because the OpenAI API returns an explicit error.
Suggested fix directions
Several options, in increasing strictness:
- Stop re-tokenizing focus text. Treat positionals after the subcommand as opaque — don't run
splitRawArgumentString on them. Callers who want CLI options in the same call must pass them as separate argv elements. This is what most Unix tools do (grep "--FLAG value" does not parse the quoted string).
- Tokenize only the option-prefix portion of argv. Stop tokenizing as soon as the parser sees a non-option positional. Concretely: split the raw string, then on the first non-
- token, glue all remaining tokens back into a single positional. This preserves the node companion.mjs review --background "focus" ergonomics without leaking flags from inside "focus".
- Require an explicit
-- separator before focus text when the caller wants opaque content. Document node companion.mjs adversarial-review --background -- "focus text". This is the lowest-risk change but pushes burden onto callers.
- Reject
--model (and similar) embedded in focus text at the validateNativeReviewRequest / handleReviewCommand layer when both a positional --model token and a focus body are present. Hostile but explicit.
Option 1 or 2 seems right: focus text should never silently become CLI options.
Impact
- Affects any review where the focus text contains a CLI example involving
--model, --base, --scope, --effort etc. Very common when reviewing CLI tooling, model-deploy scripts, or anything that takes a --model flag.
- Workaround: rewrite the focus prose to avoid literal
--FLAG VALUE patterns. Discoverable only by reading the companion source.
Environment
- Plugin:
openai-codex v1.0.3 (also confirmed present on origin/main at v1.0.4)
- Claude Code on macOS (darwin 25.4.0)
- ChatGPT account auth (the surfaced error is account-tier specific, but the root cause is general)
Summary
When
/codex:adversarial-review(or the underlyingnode scripts/codex-companion.mjs adversarial-review …) is invoked with a focus text argument, the focus text is re-tokenized bynormalizeArgv+splitRawArgumentString. Any literal--FLAG VALUEsubstring inside the focus text is then consumed by the option parser and forwarded as if the caller had set that flag — including--model X, which gets sent to the OpenAI API as the model selection.The result is a 400 from the OpenAI API:
Reproduced on plugin version 1.0.3 and verified by reading
origin/main(1.0.4) — the bug is still present upstream.Reproduction
Expected: review runs, focus text is treated as opaque prose.
Actual: companion picks
pm_v6_2as the model, OpenAI rejects it as an unknown model name.Any focus text that mentions a flag-with-value example (which is common for design reviews of CLI tools) hits this. Using a placeholder like
<registry_name>also fails (The '<registry_name>' model is not supported).Root cause
scripts/lib/args.mjs:splitRawArgumentString(raw)does a shell-like tokenization of a single string into argv-style tokens.scripts/codex-companion.mjs:normalizeArgv(argv)(line 127-134) callssplitRawArgumentStringwhenargv.length === 1.main()(line 980-988) destructures:const [subcommand, ...argv] = process.argv.slice(2);. When the caller passes a single quoted focus argument,argvhas exactly one element —normalizeArgvthen re-tokenizes the user's whole focus prose.handleReviewCommand(line 683-687) usesparseCommandInputwithvalueOptions: ["base", "scope", "model", "cwd"]andaliasMap { m: "model" }. Any--model XXX(or-m XXX) substring in the re-tokenized prose is consumed as the OpenAI model selection. The remaining tokens become positionals (focus text), so the visible failure mode is "review runs against the wrong model name" rather than "command rejected with a syntax error".The same vulnerability applies to
--base,--scope,--cwd,--effort,--prompt-file, etc., wherever the corresponding subcommand registers them as value options.--modelhappens to be the most visible one because the OpenAI API returns an explicit error.Suggested fix directions
Several options, in increasing strictness:
splitRawArgumentStringon them. Callers who want CLI options in the same call must pass them as separate argv elements. This is what most Unix tools do (grep "--FLAG value"does not parse the quoted string).-token, glue all remaining tokens back into a single positional. This preserves thenode companion.mjs review --background "focus"ergonomics without leaking flags from inside"focus".--separator before focus text when the caller wants opaque content. Documentnode companion.mjs adversarial-review --background -- "focus text". This is the lowest-risk change but pushes burden onto callers.--model(and similar) embedded in focus text at thevalidateNativeReviewRequest/handleReviewCommandlayer when both a positional--modeltoken and a focus body are present. Hostile but explicit.Option 1 or 2 seems right: focus text should never silently become CLI options.
Impact
--model,--base,--scope,--effortetc. Very common when reviewing CLI tooling, model-deploy scripts, or anything that takes a--modelflag.--FLAG VALUEpatterns. Discoverable only by reading the companion source.Environment
openai-codexv1.0.3 (also confirmed present onorigin/mainat v1.0.4)