Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
bd7b367
docs(plan): v1.3.0 implementation plan (lifecycle correctness)
CBEPX Sep 27, 2026
bc77209
fix(runtime): terminal error notifications, errorMessage on silent tu…
CBEPX Sep 27, 2026
d82fe1e
fix(runtime): retried errors leave no errorMessage; status prints Err…
CBEPX Sep 27, 2026
06102ce
fix(runtime): a subagent's terminal error does not fail the main turn
CBEPX Sep 27, 2026
73509cc
fix(runtime): gate turn notification buffering on turn start, not on …
CBEPX Sep 27, 2026
e06cadb
fix(broker): bound hung connects; status --wait exits 1 on timeout
CBEPX Sep 27, 2026
5f4085e
fix(broker): kill a live wedged broker on replace, retry the readines…
CBEPX Sep 27, 2026
cac1b2b
feat(stop-gate): pin model/effort, bound rounds by default, name sign…
CBEPX Sep 27, 2026
b756bdf
fix(transfer): resolve Claude transcripts under CLAUDE_CONFIG_DIR
CBEPX Sep 27, 2026
a9d7b2a
feat(models): resolve aliases and validate efforts against the Codex …
CBEPX Sep 27, 2026
aa3209e
fix(state): private per-user, per-plugin fallback state root; validat…
CBEPX Sep 27, 2026
98f8b85
fix(state): refuse a symlinked fallback state root
CBEPX Sep 27, 2026
59208db
fix(process): identity-checked kills and reaping on posix; identity i…
CBEPX Sep 27, 2026
69f958d
fix(broker): a freshly spawned broker that never becomes ready is kil…
CBEPX Sep 27, 2026
734f17a
release: prepare v1.3.0 (version bump, changelog, README)
CBEPX Sep 27, 2026
9237cd4
fix(codex): name the main turn from turn/started when turn/start carr…
CBEPX Sep 27, 2026
88853b3
fix(broker): never signal an exited fresh child or an unverified lega…
CBEPX Sep 27, 2026
41ca814
fix(cancel): keep the job running when the worker kill was refused
CBEPX Sep 27, 2026
6685f03
fix(state): bound lock identity probes by the acquisition deadline
CBEPX Sep 27, 2026
ec3e295
fix(setup): validate the gate effort against the gate model before an…
CBEPX Sep 27, 2026
c81c4b5
docs: v1.3.0 fix-wave wording (fallback root, cancel, alias resolutio…
CBEPX Sep 27, 2026
14378ed
test(cancel): give the queued-window worker a provable identity
CBEPX Sep 27, 2026
faa5cfb
fix(cancel): only acknowledge a delivered kill and keep the cancelled…
CBEPX Sep 27, 2026
fe8edf7
fix(broker): kill a fresh unready broker as a process group
CBEPX Sep 27, 2026
85fbc6a
fix(reaper): fail a legacy job whose pid now runs an unrelated process
CBEPX Sep 27, 2026
3cf11e1
fix(setup): validate the stored gate effort against a new gate model
CBEPX Sep 27, 2026
b20c7b3
docs: v1.3.0 changelog for undelivered cancel and legacy-job reconcil…
CBEPX Sep 27, 2026
4ccb675
test(cancel): describe the refused-cancel fixture as a foreign compan…
CBEPX Sep 27, 2026
04b901b
fix(process): re-verify ownership before signalling a pid that leads …
CBEPX Sep 27, 2026
1abc001
fix(process): read whole command lines, never a COLUMNS-truncated one…
CBEPX Sep 27, 2026
0b89636
fix(hook): SessionEnd keeps records of foreground workers it did not …
CBEPX Sep 27, 2026
2a3a428
fix(process): pass spawnSync only integer timeouts >= 1 (I1)
CBEPX Sep 27, 2026
eef98a7
fix(process): report a linux zombie's command line as <defunct> (I2)
CBEPX Sep 27, 2026
ca3f74d
fix(broker): tolerate a broker that already cleared its own record du…
CBEPX Sep 27, 2026
580a9d6
fix(broker): best-effort pid/log cleanup during teardown
CBEPX Sep 27, 2026
d966259
fix(runtime): replay buffered thread/started like the live handler so…
CBEPX Sep 27, 2026
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
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
},
"metadata": {
"description": "CBEPX fork of the OpenAI Codex plugin for Claude Code: max/ultra effort, per-thread config overrides, gpt-5.6 aliases, rescue agent fixes.",
"version": "1.2.1"
"version": "1.3.0"
},
"plugins": [
{
"name": "codex",
"description": "Use Codex from Claude Code to review code or delegate tasks.",
"version": "1.2.1",
"version": "1.3.0",
"author": {
"name": "OpenAI"
},
Expand Down
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# Changelog

## 1.3.0 — 2026-09-27

### Fixed
- Terminal `error` notifications end the turn as failed instead of hanging (#698, PR #710); an error with `willRetry: true` keeps the turn running; a subagent's terminal error no longer fails the main turn; `errorMessage`/summary on a silently failed turn reflect the real failure instead of raw output (#757, PR #763); `fileChange` items without `changes` no longer crash progress (#775); a `turn/start` response without `turn.id` no longer strands buffered notifications (#781).
- A broker connect that never resolves is now bounded, and a broker request falls back to a direct app-server on `ETIMEDOUT` (#773); `status <id> --wait` exits 1 and prints a timeout line on expiry (#774), in `--json` mode too.
- A live but wedged broker is killed before it is replaced (#753, #762, #782), its readiness probe is retried for 2 s before that (#768), and a stale or dead pid recorded from an earlier session is never signalled (#749); a freshly spawned broker that never becomes ready is killed as well.
- `/codex:transfer` honours `CLAUDE_CONFIG_DIR` when resolving Claude session transcripts (#721).
- The fallback state root is private (0700), per-user and per-plugin, and a symlinked root is refused (#521, #609); `broker.json` is validated before use.
- Identity-checked kills and reaping on posix (#743): job records and the pid sidecar carry `pidIdentity` (a JSON `{pid, identity}` sidecar; the legacy bare integer is still read), and `broker.json` carries `pidIdentity` too; when `cancel` refuses to signal a still-live pid it cannot verify, it reports `cancellation not confirmed: worker pid N left running (<reason>)`, exits 1 and leaves the job `running` (turn interrupt still sent).
- `cancel` reports `cancelled` only when its signal reached the worker (a pid that leads no process group is signalled directly); an undelivered kill of a live worker is reported as pending (`not-delivered`), and a worker that finishes after an acknowledged cancel no longer overwrites the `cancelled` record.
- A `running` job recorded without an identity (v1.2.x) whose pid now runs an unrelated, non-companion process is reconciled as failed instead of staying `running` forever; nothing is signalled.
- `SessionEnd` keeps the record (and its files) of a foreground job whose worker it could not stop — a refused or undelivered kill, or a job its time budget never reached — and logs `[codex] SessionEnd left <id> running: <reason>`; only a stopped, provably gone or pid-less job's record is removed.
- Subagent labels no longer depend on notification timing: buffered `thread/started` notifications are applied on replay.

### Added
- `setup --review-gate-model <model|inherit> --review-gate-effort <effort|inherit>` pins the stop-time review gate's model/effort independently of your Codex config (#769).
- The stop gate's block reason names the signal that killed the review task and always ends with the `/codex:setup --disable-review-gate` escape hatch (#589, #483).
- Model aliases resolve against the local Codex catalogue (`$CODEX_HOME/models_cache.json`, then `codex debug models --bundled`, hardcoded fallback last); adds the `astra` alias; `--effort` is validated per resolved model (#468, #703, #485).
- `CODEX_COMPANION_MODEL_CATALOG` overrides the catalogue source for tests.

### Changed
- `CODEX_REVIEW_GATE_MAX_ROUNDS` now defaults to 3 (was unbounded); set it to `0` explicitly to keep the pre-1.3.0 unbounded behavior (#548).
- `hooks.json` no longer carries a top-level `description` key (#459).
- `runCommand` reports `status: null`, not `0`, for a subprocess that timed out.

### Known limitations
- On Windows, kills issued from stored process records (cancel worker, `SessionEnd` cleanup, stale-broker replacement, broker teardown) are refused until process identity lands in v1.4.0; leaks are bounded by the broker idle timeout, and the turn interrupt is still sent regardless.
- Foreground job records written by v1.2.x (no `pidIdentity`) are not killed at `SessionEnd` after upgrading to v1.3.0 (one-off).
- When `CLAUDE_PLUGIN_DATA` is unset (inside Claude Code the SessionStart hook normally sets it), the fallback state root under `os.tmpdir()` hashes `CLAUDE_PLUGIN_ROOT`, whose path includes the plugin version: job and broker state is orphaned on every plugin update, not only when upgrading from v1.2.x.
- Darwin identity checks use `ps lstart`, which has 1 s resolution.

Ported with reference to upstream PRs by ALV0612, Soumya95, kevin9327, mzl9039, sylvesterkaczmarek, mittalpk, SomSamantray, weivwang.

## 1.2.1 — 2026-09-21

### Fork changes
Expand Down
33 changes: 24 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ Examples:
/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 --model gpt-5.6-terra --effort medium investigate the flaky integration test
/codex:rescue --model gpt-6-astra --effort medium investigate the flaky integration test
/codex:rescue --model spark fix the issue quickly
/codex:rescue --background investigate the regression
```
Expand All @@ -161,8 +161,8 @@ Ask Codex to redesign the database connection to be more resilient.
**Notes:**

- if you do not pass `--model` or `--effort`, Codex chooses its own defaults.
- `--effort` accepts `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, and `ultra`. Which of those a given model actually supports is decided by Codex, not by the plugin — run `codex debug models` to see the reasoning levels each model advertises.
- model aliases: `spark` -> `gpt-5.3-codex-spark`, `sol` -> `gpt-5.6-sol`, `luna` -> `gpt-5.6-luna`, `terra` -> `gpt-5.6-terra`, `mini` -> `gpt-5.4-mini`
- `--effort` accepts `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, and `ultra`. Which of those a given model supports comes from the local Codex model catalogue: when `--model` names a catalogued model, the plugin rejects an effort that model does not list, and otherwise leaves the check to Codex — run `codex debug models` to see the reasoning levels each model advertises.
- model aliases resolve against the local Codex model catalogue (`$CODEX_HOME/models_cache.json`, else `codex debug models --bundled`): an alias picks the listed model whose slug ends in `-<alias>`, lowest priority number first, newest family on ties; today `sol` -> `gpt-6-sol`, `astra` -> `gpt-6-astra`, `luna` -> `gpt-6-luna`, `terra` -> `gpt-5.6-terra`, `spark` -> `gpt-5.3-codex-spark`, `mini` -> `gpt-5.4-mini`; run `codex debug models` to see yours. An exact model slug passes through unchanged, and when the model is in the catalogue `--effort` is checked against the reasoning levels it lists
- `--config key=value` (repeatable, also on `/codex:review` and `/codex:adversarial-review`) forwards a `config.toml` override to the Codex thread, e.g. `--config model_provider=ollama`. On `--resume-last` the plugin opens a fresh app-server session (cold resume) so `--config` overrides, sandbox and approval policy take effect; model and effort for the resumed turn are sent on the turn, never on the resume request. In a `--background`/`--await` job record the config **keys** are recorded and the **values** are never stored (they read back as `[redacted]` in `status`/`result`): the real values live only in the job's private 0600 `jobs/<id>.request.json`, which the worker consumes and deletes.
- follow-up rescue requests can continue the latest Codex task in the repo
- under the hood, `/codex:rescue` and the `codex-rescue` agent are each a single `scripts/codex-companion.mjs task --await --prompt-stdin <flags>` call: `--await [--await-timeout-ms <ms>]` launches the same tracked background job as `--background`, then waits for it (default 540000 ms), and `--prompt-stdin` reads the prompt as stdin verbatim (so it cannot be combined with `--args-stdin`, `--prompt-file`, or prompt text on the command line). Exit code is 0 when the job completed, 1 when it failed or was cancelled, and 3 when the wait times out while the job is still queued or running — exit 3 prints a `Re-run: node "<abs>" result <id> --wait --timeout-ms 540000` hint, which is the only follow-up call the rescue flow makes.
Expand All @@ -185,7 +185,7 @@ Examples:
/codex:transfer --source ~/.claude/projects/-Users-me-repo/<session-id>.jsonl
```

The plugin's existing `SessionStart` hook supplies the current transcript path automatically; `--source` is available as a manual override. The transfer uses Codex's external-agent session importer, so it follows the same conversion rules as importing Claude history in the Codex App and creates visible turns that can be continued in the App or TUI. The source must be under `~/.claude/projects`, and older Codex versions that do not expose session import must be upgraded before using this command.
The plugin's existing `SessionStart` hook supplies the current transcript path automatically; `--source` is available as a manual override. The transfer uses Codex's external-agent session importer, so it follows the same conversion rules as importing Claude history in the Codex App and creates visible turns that can be continued in the App or TUI. The source must be under `~/.claude/projects`, and older Codex versions that do not expose session import must be upgraded before using this command. The transcript root honours `CLAUDE_CONFIG_DIR` when set, resolving to `<CLAUDE_CONFIG_DIR>/projects` instead of `~/.claude/projects`.

### `/codex:status`

Expand All @@ -204,6 +204,8 @@ Use it to:
- see the latest completed job
- confirm whether a task is still running

`status <id> --wait [--timeout-ms <ms>]` blocks until the job reaches a terminal status; it exits 1 when the wait times out while the job is still running (with `--json` too, whose snapshot carries `waitTimedOut: true`), and the text output ends with `Timed out after <N>s while the job was still running.`

### `/codex:result`

Shows the final stored Codex output for a finished job.
Expand Down Expand Up @@ -245,21 +247,30 @@ You can also use `/codex:setup` to manage the optional review gate.
/codex:setup --disable-review-gate
```

When the review gate is enabled, the plugin uses a `Stop` hook to run a targeted Codex review based on Claude's response. If that review finds issues, the stop is blocked so Claude can address them first.
When the review gate is enabled, the plugin uses a `Stop` hook to run a targeted Codex review based on Claude's response. If that review finds issues, the stop is blocked so Claude can address them first. When the review itself fails (timeout, killed by a signal, invalid output), the block reason says why and ends with `Disable with /codex:setup --disable-review-gate.`

To pin the model and reasoning effort the gate's review uses, independently of your Codex config:

```bash
/codex:setup --review-gate-model spark --review-gate-effort low
/codex:setup --review-gate-model inherit --review-gate-effort inherit
```

Model aliases (`spark`, `astra`, `sol`, `luna`, `terra`, `mini`) resolve the same way as for `/codex:rescue`; `inherit` clears the pin so the review uses your Codex config again.

> [!WARNING]
> The review gate can create a long-running Claude/Codex loop and may drain usage limits quickly. Only enable it when you plan to actively monitor the session.

#### Bounding the review gate

By default the gate keeps blocking the stop until Codex is satisfied, which is what can create the loop above. Set `CODEX_REVIEW_GATE_MAX_ROUNDS` to cap how many consecutive gate rounds run in a single session before the stop is allowed through:
By default the gate blocks at most 3 consecutive rounds in a single session, then lets the stop through. Set `CODEX_REVIEW_GATE_MAX_ROUNDS` to change that cap:

```bash
# allow at most 5 stop-gate review rounds per session, then let the stop proceed
export CODEX_REVIEW_GATE_MAX_ROUNDS=5
```

When unset or `0`, the gate is unbounded (the previous behavior). The count is per session, increments on each blocked round (tracked via `stop_hook_active`), and resets once a stop is allowed or a fresh user turn begins.
When unset, the cap is 3. Set it to `0` explicitly to keep the gate unbounded (the pre-1.3.0 behavior). The count is per session, increments on each blocked round (tracked via `stop_hook_active`), and resets once a stop is allowed or a fresh user turn begins.

## Typical Flows

Expand Down Expand Up @@ -295,10 +306,10 @@ The Codex plugin wraps the [Codex app server](https://developers.openai.com/code

### Common Configurations

If you want to change the default reasoning effort or the default model that gets used by the plugin, you can define that inside your user-level or project-level `config.toml`. For example to always use `gpt-5.6-terra` on `high` for a specific project you can add the following to a `.codex/config.toml` file at the root of the directory you started Claude in:
If you want to change the default reasoning effort or the default model that gets used by the plugin, you can define that inside your user-level or project-level `config.toml`. For example to always use `gpt-6-astra` on `high` for a specific project you can add the following to a `.codex/config.toml` file at the root of the directory you started Claude in:

```toml
model = "gpt-5.6-terra"
model = "gpt-6-astra"
model_reasoning_effort = "high"
```

Expand Down Expand Up @@ -364,3 +375,7 @@ goes through.
Yes. Because the plugin uses your local Codex CLI, your existing sign-in method and config still apply.

If you need to point the built-in OpenAI provider at a different endpoint, set `openai_base_url` in your [Codex config](https://developers.openai.com/codex/config-advanced/#config-and-state-locations).

### Windows

As of v1.3.0, kills issued from stored process records (`/codex:cancel`, `SessionEnd` cleanup, stale-broker replacement, broker teardown) are refused on Windows until process identity lands in v1.4.0. This bounds any leak by the broker idle timeout, and a turn interrupt is still sent regardless — it just cannot be followed by a forced kill on that platform yet.
Loading
Loading