release: v1.1.0 — cbepx fork of codex-plugin-cc - #1
Merged
Conversation
The stop-time review gate has no built-in bound: it keeps blocking the stop while Codex returns BLOCK, which can create a long-running Claude/Codex loop (as the README itself warns). This adds an opt-in cap via the CODEX_REVIEW_GATE_MAX_ROUNDS env var. When set to a positive integer, the gate allows the stop after that many consecutive blocked rounds in a session. Unset/0 keeps the previous unbounded behavior. Rounds are counted per session via stop_hook_active and persisted in the existing per-workspace state config.
… them
handleServerRequest rejected every server->client request with -32601
"Unsupported server request". Connectors surfaced as `codex_apps` (e.g.
ChatGPT connectors) request the operator's consent via an
`mcpServer/elicitation/request`, delivered as a server->client request.
Because this client runs Codex non-interactively, that request was never
answered: the background runner returned "user rejected MCP tool call" and
`codex exec` / `codex mcp-server` hung on it.
Answer the elicitation with { action: "accept" } so connectors the operator
has already enabled can run; unknown server requests still get -32601. Adds
a unit test and exports AppServerClientBase for it.
Fixes openai#499
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reject unrecognized --options instead of swallowing them into the prompt, and print usage for --help/-h without dispatching a Codex thread. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the rescue subagent's sole task invocation in the foreground so it returns completed stdout. Explicit background mode remains owned by the outer rescue command, which backgrounds the entire subagent.
`--effort max` and `--effort ultra` were rejected by the companion even
though Codex supports both. GPT-5.6 models advertise them as reasoning
levels, so the plugin was the only thing blocking them:
$ codex debug models
gpt-5.6-sol efforts=[low,medium,high,xhigh,max,ultra]
gpt-5.6-terra efforts=[low,medium,high,xhigh,max,ultra]
gpt-5.6-luna efforts=[low,medium,high,xhigh,max]
gpt-5.5 efforts=[low,medium,high,xhigh]
Codex's own docs string agrees: "GPT-5.6 supports none, low, medium,
high, xhigh, and max."
The app-server protocol does not model effort as a closed enum. In the
generated types, `TurnStartParams.effort` is a `ReasoningEffort`, and
`ReasoningEffort` is `string` — Codex validates the value against the
reasoning levels the selected model advertises. Because the companion
kept its own hardcoded list, it fell behind: it still accepts `none`
and `minimal`, which no model in the current catalog advertises, while
rejecting `max` and `ultra`, which the current models do.
Verified end to end against a real Codex run:
$ node codex-companion.mjs task --model gpt-5.6-luna --effort max \
"Reply with exactly: OK"
[codex] Turn completed.
OK
Per-model validation stays with Codex, which is where the model catalog
lives; the companion only rejects values Codex has no variant for.
`handleReviewCommand` accepted `--model`/`-m` but forwarded the raw string to `executeReviewRun`, so `normalizeRequestedModel()` — the only thing that maps `MODEL_ALIASES` — never ran on the review path. `--model spark` reached `thread/start` as the literal `spark` and came back as: The 'spark' model is not supported when using Codex with a ChatGPT account. which names the account as the cause when the account is fine and `spark` was never a model id. The alias is documented for the runtime in skills/codex-cli-runtime/SKILL.md and agents/codex-rescue.md with nothing marking it task-only. Normalize in `handleReviewCommand` the way `handleTask` already does, so both command families resolve aliases identically. Tests: the fake app-server now records the model it receives on `thread/start` (it only recorded `turn/start`, so nothing could observe the review path), plus a regression test per command mirroring the existing task-path one. Both fail on main with `actual: 'spark', expected: 'gpt-5.3-codex-spark'`. Fixes openai#687 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QdCdeAZa2tCJjFq69meyK9
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # tests/runtime.test.mjs
# Conflicts: # tests/fake-codex-fixture.mjs
…es, verification) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # tests/fake-codex-fixture.mjs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tached rescue agent Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…5.6 model aliases Model and reasoning effort are sent via thread/start.config (ReviewStartParams has no such fields; thread/start.model is unreliable). Closes upstream openai#476 openai#651 openai#468 openai#408 for this fork. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Form-mode elicitations require structured content; accepting with content: null lets the tool call proceed without the values it asked for. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pt-5.6 aliases, agent-compat hint Claude Code >=2.1.232 runs every Agent subagent in the background, so the default rescue path now drives the companion directly: task --background, status --wait slices, result. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hread/resume A broker-backed app-server keeps the thread loaded, so thread/resume is a hot rejoin and its config/approvalPolicy/sandbox overrides are ignored (app-server 0.150.1). Resume now spawns its own session. A top-level thread/resume.model sets has_model_resume_override and blocks restoring the persisted model/provider/effort, so it is dropped; model and effort ride on turn/start. Also restores the tsc gate broken by the buildThreadConfig JSDoc. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n skill/agent with detached flow, never default to --write Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…in, never inside shell strings Claude Code substitutes $ARGUMENTS (and a rescue request's text) into the command body before bash runs it, so any $(...) or backtick the user typed executed on the host shell, outside Codex's sandbox. Every command body now feeds the raw argument string through a quoted heredoc on stdin, and the companion's new global --args-stdin flag tokenizes it with the shell-like splitter normalizeArgv already used — no shell involved. Rescue job ids are validated against ^[A-Za-z0-9_-]+$ before use. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…protocol error `--write` runs use approvalPolicy "on-request", so app-server sends approval requests back to the client; answering them with -32601 made the turn error out or hang. This client is non-interactive, so each approval now gets its own type's refusal variant from the generated protocol types: execCommandApproval and applyPatchApproval take a ReviewDecision denial with a reason string, item/commandExecution and item/fileChange take the "decline" enum (no reason field exists), and item/permissions has no refusal variant at all — granting nothing for the turn is its fail-closed answer. Unknown methods still get -32601. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…client Accepting a url-mode elicitation fabricates operator consent: it tells the MCP server that an out-of-band authorization succeeded when nobody completed it. No mode may be accepted here — there is no operator. Every elicitation, in any mode (url, form, openai/form, missing, unknown), is now declined; url/form flows must be completed in an interactive Codex session. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…config values out of job records enqueueBackgroundTask spawned the detached worker and only then wrote the job record, so a worker that started fast read a missing record and exited while the parent reported `queued`. The record (and the new private payload file) are now written first; if the spawn fails the job is marked `failed` with the error text. Nothing writes the record after the spawn: the worker owns it and stores its own pid, so a parent-side pid patch would only race it. buildTaskRequest's full `config` map reached the job record, which `status --json` and `result --json` echo back, exposing `--config` auth headers. The worker now reads the unredacted request from a one-shot `jobs/<id>.request.json` (mode 0600, deleted once read, record used as fallback), while the record keeps `config` with key|token|secret|auth|password values replaced by "[redacted]". Pruned jobs drop their payload file with the rest of their artifacts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…an active job
Two concurrent turns on one Codex thread interleave their history. The
resume-candidate lookup is scoped to the current Claude session, so a job from
another session could not block a resume; runAppServerTurn now checks the thread
itself, where every resume passes, and refuses while a queued or running job
holds it.
/codex:rescue also stopped choosing --resume-last on its own: when
task-resume-candidate reports a resumable thread it asks via AskUserQuestion
("Continue current Codex thread" / "Start a new Codex thread") first, so a
request is never silently appended to an unrelated earlier thread. Its
allowed-tools becomes `Bash, AskUserQuestion, Agent` — the body is
multi-command shell, not just `node`.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The README install section still pointed at the upstream marketplace and plugin id (openai/codex-plugin-cc, codex@openai-codex); it now installs the fork (CBEPX/codex-plugin-cc, codex@cbepx), with the "Upstream:" attribution the only remaining upstream mention. package-lock.json still carried the upstream package name, so `bump-version --check` now also pins the lockfile `name` and `packages[""].name` to package.json's name, and the lockfile was regenerated. CHANGELOG: openai#547 reworded (unknown flags are CLI errors; --help prints usage and exits 0) plus entries for the argument boundary, approval denial and enqueue ordering/redaction. The rescue agent returns "the `result` stdout" rather than "the stdout of the codex-companion command", and both rescue shell blocks trap EXIT to clean up their mktemp files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eredoc delimiters; unlink payload on spawn failure R1: routing the whole rescue request through --args-stdin fed the prose to splitRawArgumentString, which strips quotes as grouping, consumes backslashes as escapes and splits on newlines — a stack trace, a regex (\d+ -> d+), a Windows path or a fenced code block reached Codex mangled. The launch step now uses two channels in the same Bash call: the prose is written byte-exact to $PROMPT by its own quoted heredoc and passed as --prompt-file, while --args-stdin carries only the runtime flags. readTaskPrompt already prefers --prompt-file over stdin, so the two compose without a companion change (covered by a new test). R2: a payload line equal to the fixed CODEX_ARGS delimiter would close the heredoc early and run the rest on the host shell. Both rescue bodies now require a fresh random suffix on both delimiters per call. The seven flag-only command bodies keep the fixed delimiter. R3: the spawn-failure branch of enqueueBackgroundTask left jobs/<id>.request.json (0600, possibly holding --config secrets) on disk until prune; it is unlinked there via a new removeJobRequestFile helper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1.1.0 — 2026-08-27
Fork of openai/codex-plugin-cc 1.0.6 (
db52e28). Marketplacecbepx, plugin name unchanged (codex).Merged from upstream pull requests
maxandultrareasoning effortsreview/adversarial-reviewon-requestapproval policy for--writetask runsstatus/result/cancel/transfercommands (pass permission classifiers)--helpprints usage and exits 0CLAUDE_ENV_FILEexports; fix: fail closed on malformed stop hook input openai/codex-plugin-cc#682 stop gate fails closed on malformed input; Add optional CODEX_REVIEW_GATE_MAX_ROUNDS cap to the stop review gate openai/codex-plugin-cc#396CODEX_REVIEW_GATE_MAX_ROUNDSFork changes
--args-stdin) instead of a shell string: Claude Code substitutes$ARGUMENTSbefore bash runs, so$(...)/backticks in a prompt used to execute on the host shell, outside Codex's sandbox. Rescue job ids are validated before use./codex:rescuekeeps the two channels separate — the request prose goes to--prompt-filethrough its own quoted heredoc so quotes, backslashes and newlines survive byte-exact, while--args-stdincarries only runtime flags — and randomizes both heredoc delimiters per call; the other seven command bodies keep the fixedCODEX_ARGSdelimiter because their payload is only flags and job ids.execCommandApproval,applyPatchApproval,item/commandExecution/requestApproval,item/fileChange/requestApproval,item/permissions/requestApproval) are answered with each type's refusal variant instead of a-32601protocol error that made--writeturns fail or hang.queued), and the worker reads the full request — including--configvalues — from a private one-shotjobs/<id>.request.json(mode 0600); the job recordstatus/resultecho keeps secret-looking config values redacted.thread/start.config(model,review_modelfor native review,model_reasoning_effort); generic--configpairs are applied first, dedicated flags override them;--effortnow works onreviewandadversarial-review.--config key=valueontask,review,adversarial-reviewforwards anyconfig.tomloverride to the thread (values are JSON-parsed; quote a literal string as'"true"'). Prompt-taking commands stop option parsing at the first positional, so prompt text likels -Ris never mis-parsed.--resume-lastopens a fresh app-server session (cold resume) so--config, sandbox and approval policy take effect, and never sendsmodelonthread/resume(it would drop the persisted model); the resumed turn's model/effort ride onturn/start./codex:rescueis synchronous by default without theAgenttool:task --background→status --waitin ≤9-minute slices (job id carried literally between Bash calls) →result; launch failures stop immediately with visible stderr;Agentonly for--background;--writeis never added unless the user explicitly asked Codex to modify files./codex:rescueasks before continuing an existing Codex thread (Continue current Codex thread/Start a new Codex thread) instead of resuming silently; itsallowed-toolsis nowBash, AskUserQuestion, Agentbecause the body is multi-command shell.sol,luna,terra,mini(plusspark); rescue agent has no pinnedmodel:; runtime skill mentions$agent-compat:skill-routerfor uncommon domains.spawnSyncusesSIGKILLand a 16 MiB buffer.tests/test-env.mjs); CI on push;npm run buildtype-checks the JSDoc.1.0.6 and earlier
See upstream releases: https://github.com/openai/codex-plugin-cc/releases
Reviews: per-task spec+quality gates (Claude), plan + merge-batch + Task 7 + whole-branch adversarial reviews (Codex gpt-5.6-sol), final whole-branch review (Claude opus); all findings fixed or ruled in the SDD ledger. Tests 91 → 142,
npm run buildandclaude plugin validate --strictpass.🤖 Generated with Claude Code