codex-in-claude v0.20.0
A compatibility and diagnosis release. Nothing on the agent-visible surface changed: the result
fingerprint stays at codex-in-claude/0.1/schema-84 and RESULT_FORMAT at 10, and there is
no breaking change.
The tracked Codex version moves to 0.149, verified end to end and A/B'd against 0.148.0.
Two more ways a user's own Codex config can refuse to start are now diagnosed instead of surfacing
as a bare nonzero_exit: a setting 0.149 retired, and a value of the wrong variant or type.
Each is attributed to whoever sent the key — the plugin's own pins only when this run actually
emitted them. On WSL2, codex is resolved to the WSL-native install instead of a Windows-side
shim that Windows-PATH interop can put first on PATH, and a new CODEX_IN_CLAUDE_CODEX_BIN
override names a binary outright.
Added
user_config_rejectednow covers two more config-parse failures — a setting codex has
RETIRED (#542;0.149retired theuntrustedapproval policy and refuses to start when the
user's config still selects it) and an INVALID value, the wrong enum variant or the wrong TOML
type (#550; a plain typo inconfig.toml). Both are distinct grammars from the--strict-config
unknown-KEY one — the key exists and only its value is refused — and neither matched a drift
signature, so each surfaced as a barenonzero_exitwith the diagnosis lost. Neither needs a
-cpin or--strict-config, and both fire at the defaultinheritisolation, so they are the
failures a user meets on the first run after an upgrade or an edit. Captured verbatim from
codex-cli 0.149.1; each recognizer is anchored to the whole of stderr so a config-shaped pair
quoted ahead of a genuine auth/drift/rate-limit diagnostic cannot steal the classification. The
offending value is never echoed — it is free text the user typed into the wrong key, plausibly
a secret no pattern-based redactor recognizes — while what codex expected (the allowed
variants, or the type) is surfaced as the actionable content. Because these messages name no
file, an error under an operator--profilediscloses that the profile may be the source rather
than asserting the user's own config, and the repair guidance is written for a refused value
rather than inherited from the unknown-key prose. Attribution follows the--strict-config
discipline, keyed on who sent the rejected key on this run: one of the plugin's own pins —
emitted only onworkspace-writeruns and effort-carrying runs, and verified live to outrank a
bad file value entirely, so the refused value can only have been ours — iscli_contract_changed;
an operator passthrough key, or a dotted child of one (codex echoes a-c t={k=v}parent-table
assignment ast.k), isextra_args_rejected; anything else isuser_config_rejected. No new
error code, so noFINGERPRINTchange.CODEX_IN_CLAUDE_CODEX_BINnames thecodexbinary to invoke (#538). A non-empty value is
used exactly as given, with noPATHre-resolution, and must be an executable file on disk: a
missing path, a directory, or a file without the execute bit is reported bycodex_statusas
codex_found: falsewith areadiness_detailnaming the env var — never its value, which is
operator-controlled and unbounded — and by every paid run ascodex_not_found, zero spend,
rather than as aninternal_errorinviting a retry. Documented in the README's Configuration
table andCOMPATIBILITY.md.
Changed
- Tracked Codex version is now
0.149.SUPPORTED_VERSIONStracks(0, 149); a0.148CLI
now warns incodex_status(advisory only — it never blocks). Verified end to end against
codex-cli 0.149.1and A/B'd against a side-by-side0.148.0. No contract break: all 12
ALWAYS_SENDflags,--model, and all three sandbox values are present and unchanged.- Re-verified live, each with its own positive control: the
workspace-writenetwork-egress and
writable-roots pins (both still outrank the config file and--profile); the read boundary on
both tiers, with the write negative control proving the sandbox was in force;--ignore-rules;
model_reasoning_effort;--output-schemaconformance together with--output-last-message
receiving exactly the finalagent_message; and the--strict-configrejection grammar,
unchanged in both forms. - The implicit-context marker probe (
AGENTS.mdsources, both skills roots, all four variants)
produced a presence matrix identical to0.148.0— notable because0.149reworked skill
selection and carried an upstream change titled "Enforce filesystem permissions when loading
AGENTS.md". KNOWN_MODEL_SLUGSgainsgpt-reserve. A contemporaneous cache-free A/B had0.148.0fetch
the identical set, so this is a backend catalog move, not a client change. Its
visibility: "hide"is deliberately not filtered (#547).- The
0.148.0→0.149.1app-server schema diff leaves six of the seven consumed schemas
byte-identical;GetAccountRateLimitsResponsegains twoPlanTypeenum values (edu_plus,
edu_pro), absorbed becauseplanTypeis read as a bounded free-form string. - New
0.149surface is deliberately not adopted: theagentsandqueuesubcommands and
exec --thread-sourceare unused, and the retireduntrustedapproval value was never sent.
remote_plugin,view_image, andrecommended_pluginsall hold their recorded postures.
- Re-verified live, each with its own positive control: the
- The
remote_pluginguarantee now says which half of it is verified (#542, #548). The
mechanism half is re-verified and pinned in the live integration suite:--disablewins over
--enablein either order and over-c features.remote_plugin=true, and an unknown feature name
still fails loud. The tool-surface half — proving no connector tool is exposed — needs a machine
with a connector installed as a positive control, and none is; it is recorded as not exercised
rather than as a pass, andCOMPATIBILITY.mdtells the next agent to check for that control
first. Upstream0.149.0's "Remove the workspace settings gate for apps and plugins" touches only
theapp-serverandchatgptcrates, so it does not reach thecodex execpath this covers. collaborating-with-codexskill: the spend step describes only the livecodex_statusquota
read; the readiness gate,blockedrefusal, cap declaration, async preference, and composition
opt-in gate are now explicit Binding rules; the compoundDelegationandRetryrules are
split; the untrusted-workspace rationale moved to Data exposure; and the transfer reference notes
that a resumed thread runs under Codex's ownAGENTS.mdauto-loading and skill discovery,
outside the plugin's flags.
Fixed
- On WSL2,
codexsubprocesses resolve the WSL-native install instead of a Windows-side shim
(#537, #538). WSL2 — this project's documented way to run on Windows — forwards the Windows
PATHinto the WSLPATH, so the bare"codex"every spawn site used could resolve to a
Windows npm-global shim and fail confusingly (codex_statusreportedcodex_found: falsenext
to a fully populatedflags_warning). Every spawn site now goes throughbinpath.codex_bin(),
resolved once per process: under WSL2 ($WSL_DISTRO_NAME, ormicrosoftin/proc/version) it
probes$HOME/.local/bin/codex,/usr/local/bin/codex, then the npm global bin dir (from
npm prefix -g;npm bin -gwas removed in npm 9+) ahead ofshutil.which("codex"); on any
other host it goes straight toshutil.which, so a stale~/.local/bin/codexcannot shadow a
newer install there. The probe order lives inbinresolve.py, andcodex_status's "not found"
detail no longer claimsPATHis the only place checked. Thanks to @cbeaulieu-gt (#539).