Releases: briandconnelly/codex-in-claude
Release list
codex-in-claude v0.22.0
An install-repair and hardening release. The headline is the dependency cap: fastmcp 4.0.0
published and every earlier release's uncapped fastmcp>=3.4 floor now resolves it and fails at
import, so fresh installs — including the .mcp.json plugin path — are broken until this release
supersedes them. Alongside the cap, the three free diagnostics gain the same envelope guarantees
as the paid tools, and the developer_instructions docs stop overstating what the parameter and
its fingerprint prove. The result fingerprint moves schema-85 → schema-87 (both hops
additive or wording-only); nothing is breaking. The fastmcp 4 port itself lands separately
(#570).
Fixed
-
Fresh installs no longer resolve the incompatible fastmcp 4 (#572): every released version
declaresfastmcp>=3.4with no upper bound, so once fastmcp 4.0.0 (on mcp 2.1.1) published, a
fresh install resolved it and failed at import (cannot import name 'McpError' from 'mcp') —
including the.mcp.jsonplugin path. The floor is now capped at<4. Already-published
versions cannot be repaired, only superseded by releasing this cap. The fastmcp 4 port itself is
#570. -
The three free diagnostics can no longer fail as a raw MCP protocol error (#541):
codex_status,codex_capabilitiesandcodex_modelsare synchronous, so the_guard
decorator — which wraps async tools — never covered them, and anything they raised escaped the
handler instead of returning the documented envelope. That was worst forcodex_status, whose
entire job is to answer "is codex usable right now?": the tool an agent reaches for because
something is already wrong was the one least able to report it. All three are now guarded and
advertiseinternal_errorin their per-toolerror_codes; their output schemas already carried
a success|error union, so no schema shape changed. The resultfingerprintmovesschema-86->
schema-87. Adding an error code is backward-compatible, so this is not breaking. -
A
codexonPATHthat cannot be executed is a readiness fact, not a crash (#541):
binpath.codex_bin()deliberately falls back to the bare literal"codex"when resolution finds
nothing, andsubprocessthen does its ownPATHlookup without theis_file()+X_OK
predicatebinresolveapplies to its own candidates. An executable directory namedcodex(or a
file with the execute bit set that the kernel cannot exec) therefore raisedPermissionError/
OSError(ENOEXEC)straight out of the probes. Those spawn failures are now classified the way a
missing binary already was, socodex_statusreportscodex_found: falseinstead of raising. -
The three free tools now report their own pinned posture on an invalid-argument error
(#541):READMEdocuments that every shipped tool pins its own tier and sandbox and ignores
CODEX_IN_CLAUDE_TIER_DEFAULT/CODEX_IN_CLAUDE_SANDBOX_DEFAULT. Because the three unguarded
tools had no entry in the posture map, theirinvalid_argumentsenvelope fell back to
config.defaults()and reported whatever an operator had configured — an operator running
TIER_DEFAULT=proposesawcodex_capabilitiesclaimpropose/workspace-write. Guarding them
registers their real posture and restores the documented promise.metavalues are not part of
the discovered surface, so this moves no fingerprint of its own.
Changed
developer_instructionsnow documents that compliance is best-effort (#563): the only
"instructed, not compelled" statement on the surface was scoped to verdicts, which reads as a
promise that everything else applies. It does not — Codex may honor the caller text in full, in
part, or not at all, and no result field attests which happened —meta's fingerprint attests
the request the server accepted and staged, never what the model did with it. Non-compliance may be silent,
though the server's framing does instruct Codex to disclose a conflict with the rules above it,
anddetail="full"returns the raw model text. Stated once in the parameter contract (inline
summary andcodex://paramsfull text), and echoed inREADME.md,COMPATIBILITY.md, and the
bundled skill. The resultfingerprintmovesschema-85->schema-86; documenting an
existing limit weakens no guarantee, so this is not breaking.- The bundled skill no longer overstates what the fingerprint proves (#564): the
options-and-errors reference called it "how you tell a steered run from a default one". It
separates a steered request — it attests what the server SENT, not what the model did — and
its absence means only that the per-call parameter was omitted, not that the run was otherwise
unsteered. The same reference now also states what validation does and does not cost: a value
the server REFUSES costs nothing — unsafe text, the byte cap, framing markers, and the combined
input budget are all rejected pre-spend — while a value it ACCEPTS goes straight into the paid
run, so there is no free positive check. Because the checks stop at the first failure in
unsafe -> byte cap -> marker order, a value that is both over-cap and marker-bearing reports
only the cap.
codex-in-claude v0.21.0
A caller-steering release. Consult and review gain an optional developer_instructions
parameter — caller stance for Codex's developer turn, always behind this server's framing, with
a {sha256, bytes} fingerprint reported in meta — and the operator passthrough loses the
ability to set the instruction-bearing config keys that could outrank that framing (the one
breaking change, operator surface only). The bundled skill teaches agents how to route content
into the new parameter and how to recover its refusals. The result fingerprint moves
schema-84 → schema-85 and the persisted RESULT_FORMAT 10 → 11 (both additive); the tracked
Codex version moves to 0.151 with no contract break.
Added
developer_instructionsoncodex_consult,codex_consult_async,codex_review_changes,
andcodex_review_changes_async(#556): optional caller stance/focus text for Codex's
developer turn, composed into ONE-c developer_instructionsvalue — this server's framing
always leads, the caller text is delimited on both sides, and the closing marker outranks
anything between the markers. codex places the value as the first developer-role message,
ahead of its own developer messages (verified viacodex debug prompt-inputon0.151.0), so
that placement is disclosed rather than reordered. Normalized once (stripped; blank =
omitted); refused pre-spend over 4096 bytes, on control characters (C0 except tab/LF/CR, DEL,
NUL) or lone surrogates, or when the text carries a framing-marker line (invalid_arguments
with a reason namingforged_framing_marker); counted against
CODEX_IN_CLAUDE_MAX_INPUT_BYTESwith the call's other caller-authored inputs. Emitted only
when text is present, so the common run's argv is byte-identical to0.20.0's and never arms
--strict-config; an instruction-carrying run does arm it, and a strict-config rejection of
the plugin's own key is attributed to the plugin (cli_contract_changed), never to the user's
config.metagains adeveloper_instructions{sha256, bytes}fingerprint on sync results,
the async launch handle, and fetched job results; the text itself is never echoed, though it
does ride the codex command line and the on-disk background-job record — the tool description
and skill disclose both carriers. Deliberately NOT oncodex_delegate/codex_delegate_async.
Newdeveloper_instructionsparameter contract incodex://params.fingerprint
schema-84→schema-85; persistedRESULT_FORMAT10 → 11 (newMetafield). Non-breaking
(additive).- The bundled skill teaches when and how to use
developer_instructions(#560): a
content-routing rule (stance/persona/emphasis indeveloper_instructions; the target via the
review scope or a consult'squestion; facts and quoted artifacts inextra_context; data
never in the developer turn), refusal recovery forforged_framing_markerand the two byte
limits, a dry-run caveat (codex_dry_runtakes nodeveloper_instructions), an independence
hazard (a stance hinting at Claude's approach forfeits the two-member pattern), and framing
stability across review–revise passes with themeta.developer_instructionsfingerprint as
the equality check. SKILL.md gains a Data-exposure bullet for the argv/job-record carriers and
three binding rules (routing, exposure, independence) so the rules agents apply are as wide as
the disclosures. Skill prose only — no wire text.
Changed
- BREAKING (operator surface): the extra-args passthrough can no longer set the
instruction-bearing config keys (#555).CODEX_IN_CLAUDE_EXTRA_ARGSnow refuses
-c developer_instructions=…,model_instructions_file, its deprecated alias
experimental_instructions_file, the documented-as-reservedinstructions, and
model_catalog_json(a catalog entry can redefine a model'sbase_instructions) — plus the
usual case/quote lookalikes — at parse time withextra_args_rejected, before any spend.
Every framing string this server sends rides the user turn whiledeveloper_instructions
lands as the first developer-role message, so a passthrough value outranked the server's own
guardrails with nometarecord; the per-call, meta-reported parameter (above) is the
first-class replacement.--profileand, atinheritisolation, the user'sconfig.toml
remain the documented operator-trust boundary. Not itself afingerprintmove: the denylist
is not part of the discovered surface. - pontonier 0.6.0 → 0.7.0: the caller text crosses the backend adapter on the new
first-classRunRequest.instructions_appendfield (pontonier#12) instead of riding
extra_args, which the protocol reserves for operator descriptors;
CodexBackend.validate_requestmirrors the server boundary andprepare()fails closed for
direct adapter callers (#558). - Tracked Codex version is now
0.151.SUPPORTED_VERSIONStracks(0, 151); a0.149
or0.150CLI still runs and reports the advisory untracked-version warning in
codex_status. Verified end to end againstcodex-cli 0.151.0and A/B'd against a
side-by-side0.149.1. No contract break and no agent-visible change.- All 12
ALWAYS_SENDflags, the--modelhelp-gated flag, and all three--sandboxvalues
are present; the five relevant help screens are byte-identical to0.149.1. - Both security pins re-verified live with positive controls, each against the config file
and an operator--profile:sandbox_workspace_write.network_access=falsestill blocks
egress, andsandbox_workspace_write.writable_roots=[]still blocks writes outside the
workspace; upstream'sSandboxWorkspaceWritestruct still carries exactly the four fields
COMPATIBILITY.mdaccounts for. - The
--strict-configgrammars still parse,--ignore-user-configstill exempts the config
file, and the app-server schema diff left all seven consumed schemas byte-identical. KNOWN_MODEL_SLUGSis unchanged, and the implicit-context presence matrix (AGENTS.md
sources and both skills roots) is identical to0.149.1under the read-forbidding probe.
0.151adds a default-offskip_host_skill_discoveryfeature flag — inert here, but the
flag to re-probe first when it stages.- New
0.151surface is deliberately not adopted;remote_plugin,view_image, and
recommended_pluginsall hold their recorded stage and default.
- All 12
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).
codex-in-claude v0.19.0
A sandbox-hardening and egress-disclosure release.
Three workspace-write guarantees that were advertised but not enforced now hold at every
isolation level: network egress and the writable-root set are pinned on the command line, where
the user's own $CODEX_HOME/config.toml can no longer widen them, and codex exec --strict-config turns a pin codex silently ignores into a zero-spend startup failure instead of
a guarantee that quietly stopped holding.
The egress disclosures were corrected in the unsafe direction they were wrong in. Codex's reads
are not bounded by the workspace; AGENTS.md is auto-loaded from the repository root and from
$CODEX_HOME as well as from the resolved workspace; a discovered skill's body reaches OpenAI
through a read the model itself issues after selecting it on its description alone; a dry-run
preview does not bound what the paid call sends; and the propose tier's throwaway worktree does
not bound where Codex writes, because workspace-write grants the OS temp roots by default.
Every one of these was probed live on codex-cli 0.148.0 with negative controls; none is a
behavior change, and each corrects a claim that never held.
Foreign text is no longer echoed raw. A rejected config key, codex's stderr, an unknown argument
name, a job id, and the codex --version string are now sanitized, bounded, or rejected before
they reach a result envelope, so a control character can neither spoof a terminal nor wedge
itself into a secret to defeat the redactor.
The tracked Codex version moves to 0.148.
Three breaking changes: an unknown key in your own Codex config now fails pin-carrying runs; the
propose-tier surface retracts its "writes only inside a throwaway worktree" promise; and five
parameters reject control characters in their values. The agent-visible surface changed nine
times (result fingerprint codex-in-claude/0.1/schema-75 → schema-84, and the persisted
RESULT_FORMAT 8 → 10), so pre-1.0 this is a minor release; clients that cache by
fingerprint re-fetch the contract, and a reader older than RESULT_FORMAT 10 rejects newly
stored job records.
Changed
-
BREAKING:
codex exec --strict-configguards the guarantee-bearing-ckey pins, and a new
user_config_rejectederror code reports an unknown key in your own Codex config. codex
silently tolerates an unknown config key, so an upstream rename of
sandbox_workspace_write.network_access(#518),sandbox_workspace_write.writable_roots
(#520), ormodel_reasoning_effort(#309) would leave the plugin sending a key codex no longer
reads — reopening the guarantee with no signal. The flag turns that into a zero-spend startup
failure, since config parsing precedes auth and any model call. It is emitted only on runs
that carry a-coverride — everyworkspace-writerun, every effort-carrying run, and any
run with an operator-cinCODEX_IN_CLAUDE_EXTRA_ARGS— because at the defaultinherit
isolation the flag also hard-fails on an unknown key anywhere in the user's own config,
including tables for profiles the run never selects; a plain read-only consult carries no pin,
so sending it there would risk availability while guarding nothing.Breaking because a previously accepted operator environment — a junk or version-skewed key in
$CODEX_HOME/config.toml— now hard-fails pin-carrying runs. Ownership keys on the rejected
KEY, never on the shared-cdescriptor codex's own message contains: a rejected plugin pin is
cli_contract_changed(the fail-loud drift signal this exists to produce), an operator key or
an operator--profile-selected file isextra_args_rejected, and any other config file is
the newuser_config_rejected— permanent, repairing withcorrect_config, naming the file
and line to fix and offeringisolation="ignore-config"only as an explicitly lossy fallback,
since it drops your entire config. Neithercodex_statusnor a dry run parses Codex config, so
neither can predict this failure. Strict validates key names only, so a bad
reasoning_effortVALUE still takes the backendinvalid_reasoning_effortpath unchanged.
(#524) -
BREAKING: the propose-tier surface no longer promises "writes only inside a throwaway
worktree", and the delegate tools now advertisedestructiveHint: true. codex's
workspace-writesandbox grants the OS temp roots (/tmpand$TMPDIR) by default
(exclude_slash_tmp/exclude_tmpdir_env_vardefault false), verified live at this plugin's
exact argv, so the exclusivity claim never held. Theexclude_*keys are deliberately not
pinned closed — build tools,uv,git, and test runners need$TMPDIR— so every carrier
discloses the grant instead: a canonicalcli_contract.WORKSPACE_WRITE_SCOPE_FACTis carried
verbatim by both delegate descriptions, theircodex_capabilitiesreturnsentries, and a new
negative_scopeitem, each alongside the persistence clause (temp-root writes are neither
captured in the returned diff nor cleaned up);Meta.tierandMeta.sandboxcarry a short
form. The annotation preset splits: MCP definesdestructiveHint: falseas "performs only
additive updates", and a delegated task can overwrite or delete pre-existing files under the
temp roots, socodex_delegate/codex_delegate_asyncflip totruewhile consult and review
keepfalse— the split is stated onannotations_reading.Breaking because the retired carriers made an exclusive promise ("writes only inside", "the
worktree bounds what Codex may WRITE"), and correcting them weakens a documented guarantee
a client could have relied on — even though runtime behavior is unchanged and the guarantee
never held. What remains true and promised: the plugin never applies anything to your working
tree, and the returned diff is gathered from the worktree only. (#523) -
The egress disclosures no longer scope Codex's reads to the workspace or the repo. Several
descriptions said Codex reads "files ... from its resolved working dir", "other repo files",
or "tracked files in the throwaway worktree". A reader takes that as a bound; it is not one.
--sandbox read-onlybounds writes: probed oncodex-cli 0.148.0under this plugin's own
flags with the workspace a bare non-repo directory, the model shelled out and read a file in
$HOMEon both tiers, while a write attempt in the same read-only run was refused — the
negative control that makes the read informative. A canonicalcli_contract.READ_SCOPE_FACT
now states the truth at every carrier: the server instructions block, thecodex_status
caveat,codex_capabilities'negative_scope, all six active tools'returnsand docstrings,
readonly_honesty_statement, theextra_contextparameter contract, andworkspace_root's
own description, which now says it selects where Codex works, not what it can read. It is a
ceiling, not a warrant — the probe shows reads far outside the workspace, not that every
file the OS user can read is reachable, since a platform sandbox can still deny individual
paths. Not breaking: no carrier ever promised reads were confined (every one said "may read"),
so this widens a disclosure. The practical consequence for callers: narrowingworkspace_root
is no longer a mitigation for egress. -
The egress disclosure names every
AGENTS.mdCodex auto-loads, not just the workspace's own.
Two further sources reach the model, both previously understated: inside a repository the load
walks up from the resolved workspace to the repository root, so a caller who narrows
workspace_rootto a subdirectory precisely in order to bound egress still ships the repo-root
AGENTS.md— a file it never named (#472); and a user-global$CODEX_HOME/AGENTS.override.md,
else$CODEX_HOME/AGENTS.md, loads on every call from any workspace — theAGENTS.mdtwin of
the$CODEX_HOME/skills/hole (#358). Neither--ignore-user-confignor
-c project_doc_max_bytes=0suppresses the user-global file, though the latter does suppress
the workspace and ancestor files. Outside a repository there is no walk at all.
cli_contract.SKILLS_DISCOVERY_FACTand every carrier state all three sources. Not breaking. -
Every disclosure now states HOW a skill's body reaches OpenAI, not just that skills are
discovered. The two halves of Codex's implicit context arrive differently:AGENTS.md
content is auto-loaded, already in context before the turn, while a skill is
auto-discovered as name and description only, and its body follows a read the model
itself issues once it selects that skill (#480). The wire and the prose now agree on this, in
one canonical sentence (cli_contract.SKILL_BODY_FACT) across the server instructions block,
thecodex_statuscaveat,negative_scope, all six active tools'returnsand descriptions,
SECURITY.md,README.md,COMPATIBILITY.md, thecollaborating-with-codexskill and its
fallback reference, and the upgrade docs. Four wire carriers gained disclosure rather than
wording:codex_consult_asyncandcodex_review_changes_asyncnamed the skills roots with no
mechanism at all, as did both delegatereturns. The warnings are stronger, not weaker —
each site now says the metadata alone is enough for the model to select a skill, and that
selecting it pulls the body to OpenAI even when the prompt names neither the skill nor the
file. Not breaking. (#498, #501) -
The dry-run previews no longer imply they bound what a paid call can send.
codex_dry_run
led with "Preview what acodex_review_changescall would send", so a clean preview read as
"nothing sensitive will be sent". It cannot establish that: a dry run never invokes Codex, so
the model's own reads have not happened yet and none can be listed, and those reads are not
bounded by the work...
codex-in-claude v0.18.0
Added
cli_contract.PONTONIER_CONTRACT— the declarative CLI contract in the shared
BackendContractshape, derivation-pinned against the legacy constants.- Surface-honesty gates via
pontonier.testing:FORBIDDEN_SURFACE_PHRASES
(cross-bridge contamination canaries and refused-mechanism claims) enforced
against the built manifest. backend.CodexBackend— this bridge's adapter on the frozen pontonier
AgentBackendprotocol (contract_api_version = 1), validated by an argv
differential against the production command builder.
Changed
-
Every model-bearing run now goes through the pontonier
AgentBackendadapter:
codex.run_codex_execstages viaCodexBackend.prepare()(temp artifacts,
argv from the shared builder, prompt over stdin, help-gate drops surfaced on
PreparedRun.dropped_flags) and keeps only the execution step — timeouts,
output byte caps, and event streaming stay this bridge's. The consult-only
--skip-git-repo-checkmoved from an inline call-site flag to backend policy
derived from the canonicalkind, pinned by the argv differential test (which
now compares against true production argv — it previously validated a variant
without the flag). Wire snapshots are byte-identical; argv is unchanged for
every tier. -
Generic core machinery (
jobs,worktree,gitdiff,redaction,runtime,
gitproc,streamcap,idempotency,workspace,jsoncache) now comes from
the shared pontonier library
instead of the vendored_core/package. This bridge's worktree knobs
(cic-worktree-prefix,codex-in-claude@localbaseline identity) are pinned
inconfig.WORKTREE_CONFIG, so git-visible behavior is unchanged; all wire
snapshots are byte-identical. -
Docs and code comments retire the deleted
_core/package.CONTRIBUTING.md's one-way
import rule named a directory that no longer exists, andSECURITY.md/COMPATIBILITY.md
cited_core/redaction.py/_core/workspace.pyas the authority for security-relevant
behavior; all now point atpontonier.core. Eight source and test comments were updated the
same way, andtests/test_backend.pyno longer calls the protocol "provisional" while every
other doc calls it frozen. No agent-visible surface changed — the built manifest is
byte-identical, so noFINGERPRINTbump. -
Runtime dependency bumps.
fastmcp3.4.5 → 3.4.7. The built manifest is byte-identical
under the new version, so the agent-visible surface is unmoved and noFINGERPRINTbump follows. -
Tracked Codex version is now
0.147.SUPPORTED_VERSIONStracks(0, 147); a0.146CLI
still runs and only draws the advisorycodex_statuswarning. Thedocs/UPGRADING-CODEX.md
procedure was run end to end againstcodex-cli 0.147.0, A/B'd against a side-by-side
0.146.0from npm (the retrieved binary authenticated clean against the committed0.146.0
help snapshots, and an npm-vs-Homebrew diff of the new version retired the cross-channel
concern). Nothing this plugin sends or reads changed: all 11ALWAYS_SEND_FLAGS,--model, and
the three sandbox values are present, and the guarantee semantics were re-probed live —
read-onlyblocked a write,workspace-writeallowed the workspace write and blocked network
egress,--output-last-messagereceived the final message, and an--output-schemarun
validated againstFINDINGS_OUTPUT_SCHEMA. Themodel_reasoning_effortconfig key is still
applied (the backend rejection carried both[ReasoningEffortParam]and[reasoning.effort]),
andcodex execstill has no dedicated effort flag. Contract-drift stderr signatures still
match.docs/codex-help/0.147.0/carries fresh snapshots and the liveintegrationsuite
passes against the new binary. -
KNOWN_MODEL_SLUGSgainsgpt-5.6-sol-wm. The bundled advisory fallback is refreshed from
the0.147.0-writtenmodels_cache.json. The catalog is backend-served rather than shipped in
the binary, so this slug had already appeared under0.146.0— it is catalog drift the upgrade's
slug diff caught, not a0.147change. The reasoning-effort discovery fields still hold their
pinned shape. -
--approve-for-me(new in0.147.0) is deliberately not adopted, andcli_contract.pynow
records why alongside the other never-sent flags: it routes approval requests through an
automatic review under theworkspace-writesandbox, which would let a read-only-tier run
acquire write capability without the caller electing a write tier. -
App-server: the
0.146.0→0.147.0generated-schema diff is additive only on the consumed
surface — an optionalextensionsmap onInitializeParams(not sent), an optionaltitleon
the import progress/completed per-item results (read tolerantly, ignored), and two new
PlanTypevalues,self_serve_business_proliteandenterprise_cbp_automation, already read as
a free-form capped string. Ten unconsumed v2ThreadSection*messages were added; none removed.
Fixed
- Multi-line private-key blocks (PEM/PKCS8/OpenSSH/PGP) in gathered diffs and
returned prose are now redacted statefully (via pontonier): the BEGIN/END
markers stay visible, every body line between them is replaced with a
[redacted: secret value]marker, and an unterminated block fails closed. Previously only the
BEGIN marker was masked while the entire base64 body was sent. - Bare provider tokens are now redacted. The shared redactor adds patterns for
github_pat_,
glpat-,sk-ant-,npm_, andpypi-tokens. The vendored redactor already caught these in a
labelled assignment (token = "…"), because its value-pattern matcher keys on the label; what
changed is the unlabelled case — a bare token in prose, or a diff line with nokey =in front
of it, which that redactor passed through verbatim. Measured old-vs-new over both spellings: five
improvements, and no payload where the new redactor leaks something the old one caught. - Redaction preserves the diff's trailing newline, so delegate diffs are
git apply-able again (ports moonbridge's fix). COMPATIBILITY.mdcorrects the 2026-08-02 "parentAGENTS.mdabove the git root is loaded"
observation. That mechanism does not reproduce on0.146.0or0.147.0: the parent codeword
was absent from both binaries with a projectAGENTS.mdpresent, with it removed, and with
--cdset to a repository subdirectory. The corrected behavior is that codex walks upward from
the resolved workspace directory and stops at the git root. What is retracted is the
mechanism, not the concern — becauseresolve_workspacereturns an explicitworkspace_root
unchanged, the resolved workspace can be a subdirectory, and the walk then crosses above it
(fromrepo/sub, codex loadedrepo/AGENTS.md). The published "the resolved workspace's
AGENTS.md" caveat therefore still understates egress and issue #472's conclusion stands.
Correcting that published wording is aFINGERPRINT-bumping change tracked in #472, so this
entry changes no agent-visible text and carries no bump.config.py's extra-args comment no longer states a plugin-side narrowing as a codex fact.
It claimed an attached-cKEY=VALis "rejected"; codex accepts it (clap attached short-option
value) on both0.146.0and0.147.0. It is this parser that refuses it, because the
attached-form split fires only on long--flag=value. Behavior is unchanged and
safe-direction — the plugin passes through strictly less than codex would take.- Shared-core diagnostics stay inside the server's log configuration. Moving the core out of
codex_in_claude._coremade its loggers siblings of the server namespace rather than children,
sopontonier.core.*records inherited none of the configured handlers and propagated to the
stdlib root logger instead — the exact escapepropagate = Falseexists to prevent, since an
embedding host may have wired root to stdout (the stdio JSON-RPC channel).obs.configure()now
configures thepontoniernamespace alongsidecodex_in_claude, so library diagnostics honor
CODEX_IN_CLAUDE_LOG_LEVEL/CODEX_IN_CLAUDE_LOG_FILEand reach the same stderr and file
handlers.
codex-in-claude v0.17.0
A secret-redaction release. The inline redactor was rebuilt around merged candidate spans so no
matcher can strand part of a secret another one covers whole; its labelled-secret pattern now
reaches quoted-JSON and bracket-subscripted keys without scrubbing ordinary source out of reviewed
diffs; two quadratic patterns were bounded; and a replacement that may be incomplete now says so
instead of advertising a completeness it doesn't have. Alongside that, codex_review_changes and
codex_dry_run disclose how a diff was redacted — files withheld whole versus files sent with
values masked inline — codex_capabilities declares the targeted MCP protocol revision and its
readOnlyHint reading, and the dry-run tools warn when the call they preview may exceed the
synchronous deadline. The tracked Codex version moves to 0.146. One breaking change: a blank
question or task is now rejected before any spend. The agent-visible surface changed nine times
(result fingerprint
codex-in-claude/0.1/schema-66 → schema-75, and the persisted RESULT_FORMAT 7 → 8), so
pre-1.0 this is a minor release; clients that cache by fingerprint re-fetch the contract.
Added
coverage.redactiondistinguishes withheld files from inline masks on
codex_review_changesandcodex_dry_run(#433). Theredactedomission reason previously
conflated two different things a secret-looking diff can trigger: a whole file dropped (its path
itself looked secret-bearing, e.g..env) versus a file sent with one or more values masked
inline. The new optionalRedactionSummary—withheld_paths,masked_paths, and
inline_masks(markers actually emitted, after span merging, not raw pattern candidates) —
splits them, counting only content that lands in the retained, byte-capped diff text.
meta.redacted_pathsandDryRunResult.redacted_paths/redacted_paths_countare unchanged:
still the single backward-compatible union of both path lists, and stillcodex_delegate's only
redaction disclosure. The field is optional (so aCoveragepersisted before it existed still
replays) and theredactedreason can fire without it — a legacy-shaped producer, or a
disclosure dropped entirely by byte-cap truncation — but the field is never populated without
the reason.FINGERPRINTschema-74→schema-75;RESULT_FORMAT7→8for the persisted
Coverage. Additive, not breaking.codex_capabilitiesdeclares the targeted MCP protocol revision via a new
protocol_revisionfield ("2025-11-25"), previously derivable only by inspecting the
initializewire response (#423). Its description points at the new
docs/adr/0004-mcp-2026-07-28-migration.md, which records why this server stays on 2025-11-25
(verified FastMCP/mcpSDK support, not any spec-granted support window) and the per-feature
migration plan for the deprecated pieces already in use.FINGERPRINTschema-70→
schema-71; additive, not breaking.codex_capabilitiesdocuments the server'sreadOnlyHintreading via a new
annotations_readingfield, previously stated only in source comments (#426). The hint tracks
whether a call changes observable state that outlives the response (a job record, committed
spend) rather than file I/O — which is whycodex_consult,codex_review_changes, and
codex_delegatearereadOnlyHint: falseeven though consult and review never write files,
while the two dry-run tools, which create no job record, staytrue. Consistency guards assert
the claim against the live tool annotations.FINGERPRINTschema-71→schema-72; additive,
not breaking.codex_dry_runandcodex_delegate_dry_runadvise when the previewed call may exceed the
synchronous deadline via a newdeadline_advisoryfield, described identically on both
(#342). Non-null exactly when the previewed call would run the model and either its prompt
exceeds 100,000 bytes or its resolvedreasoning_effortishigh/xhigh(an exact match — a
near-miss like"Xhigh"is deliberately not treated as high); null whenever the call would run
no model at all. It is a hint, not a refusal, and it names the previewed paid tool's own
_asynccounterpart, never the dry-run tool's own name, which has none.FINGERPRINT
schema-73→schema-74; additive, not breaking.
Changed
- Tracked Codex version is now
0.146.SUPPORTED_VERSIONStracks(0, 146); a0.145CLI
still runs and now warns as untracked (the gate is advisory and never blocks). The upgrade
procedure was run end to end againstcodex-cli 0.146.0, A/B'd against a side-by-side
0.145.0, and required no code change: help text forcodex/exec/review/exec reviewis
byte-identical, every guarantee-bearing flag and sandbox value is present, the drift signatures
still match observed output, and the model catalog is unchanged. Semantics were re-probed rather
than assumed —read-onlystill blocks writes,workspace-writestill writes in-workspace while
blocking network egress,--output-last-messagestill delivers,-c model_reasoning_effortis
still applied, and structured output still conforms. Upstream additions were reviewed and none is
consumed: a newapp-server --helpflag, two new app-server messages, an optionalproviderId
and anent26PlanTypevalue on the consumed schemas (the plan type is read as a free-form
capped string, not an enum), and four new feature flags — includingmcp_2026_07_28, still
under development, which corroborates this release's decision to target MCP 2025-11-25.
remote_pluginremains default-on, so the--disable remote_pluginisolation guarantee still
applies and still fails closed. - The skills-discovery egress caveat now reads identically everywhere it appears (#427).
Fifteen prose sites — the server instructions, thecodex_statuscaveat, six
ToolCapability.returnsclauses,codex_capabilities'negative_scope, and six tool
docstrings — stated the same fact (Codex auto-loads the resolved workspace'sAGENTS.mdand
discovers skills in that workspace's.agents/skills/and in the user-global
$CODEX_HOME/skills/, and the plugin's isolation flags do not suppress any of it) in
independently drifted wording. All now share one canonical sentence pair defined in
cli_contract.py, already the single source of truth for every Codex CLI assumption. This is a
wording convergence, not a byte-identical refactor — no carrier shared one sentence before — and
no guarantee is removed or weakened; the three_asyncdocstrings keep their deliberately
lighter subset.tools/listgrew 157 bytes, inside its existing budget.FINGERPRINT
schema-72→schema-73; not breaking. Meta.timeout_secondsis now described — it was the only semantically-loadedMetamember
published without a description (#413). It states which deadline the value is, by envelope: a
synchronous paid call reports that call's own resolved deadline, post-clamp (10–600s); a
background job's start handle, or a latercodex_job_resultfetch of that job's originating
run, reports the job-lifecycle ceiling instead (job_max_seconds, default 1800, clamped
60–7200); andinvalid_argumentsorinternal_error, tied to no run, report the server's
configured sync deadline. Wording only:FINGERPRINTschema-67→schema-68,
RESULT_FORMATunmoved, not breaking.- The three sync tools' Progress & recovery paragraphs now describe job-record recovery
correctly (#414). They named onlycodex_job_list→codex_job_result, omitting
codex_job_statusas the polling step in between, and framed recovery as a fallback for a
dropped connection — when some MCP clients background a long tool call well before the server's
own deadline. Wording only:FINGERPRINTschema-68→schema-69, not breaking.
Fixed
-
BREAKING (agent surface): a blank
questionortaskis now rejected before any spend
(#411). An empty or whitespace-only value passed validation and bought a real Codex run that
could produce nothing — the prompt builders strip it, so the model received framing scaffolding
and no ask (a reportedcodex_consult(question=" ")burned 23,120 tokens and returned
ok: true).codex_consult,codex_consult_async,codex_delegate,codex_delegate_async,
andcodex_delegate_dry_runnow returninvalid_argumentsnaming the offending argument, with
acorrect_argumentsrepair. Flagged breaking because it narrows an accepted value set: input
that previously returnedok: truenow returns an error. Blankness is Python'sstr.strip(),
so U+00A0 is blank while U+200B counts as content, and the check sits after the byte-limit
guard, so an oversize whitespace-only value still reportsinput_too_large.FINGERPRINT
schema-66→schema-67;RESULT_FORMATunchanged. -
Redaction no longer strands part of a secret behind a marker that looks complete (#443,
#445). The inline patterns ran as sequentialre.subpasses, each over the previous pass's
output, andsubnever revisits consumed text — so a matcher with a narrower value class could
eat a prefix of a value a later matcher would have covered entirely:token=ghp_<20 chars>-tail
came out astoken=[redacted: secret value]-tail. The worse harm is not the disclosure but that
the output advertised itself as fully redacted, suppressing the reaction a bare secret would
provoke. Every pattern is now run withfinditeragainst the original line, each candidate
contributes the span it would replace, the spans are merged on strict overlap, and the line is
rebuilt with one marker per merged interval — so the order of the pattern list no longer affects
output at all. Touching (non-overlapping) candidates deliberately stay separate, and a preserved
prefix (a labelled key, anAuthorization:header...
codex-in-claude v0.16.0
A discovery-metadata and envelope-slimming release. Every tool now states its cost, its title, and
its stability tier in tools/list itself; codex_capabilities gains a summary default and a
contracts mode, so a client can fetch a schema or recheck a fingerprint without re-reading the
whole tool inventory; codex_job_list gains limit/status filters; and each codex:// resource
carries triage metadata a client can read before spending context on the body. Delivered success
envelopes stop sending explicit null meta members, and meta.roots_source now reports what the
MCP-roots probe actually saw on every envelope that reports it. Several capability and
documentation contracts that described something other than what was being sent are corrected. The
agent-visible surface changed ten times (result fingerprint codex-in-claude/0.1/schema-56 →
schema-66, and the persisted RESULT_FORMAT 6 → 7), so pre-1.0 this is a minor release;
clients that cache by fingerprint re-fetch the contract. Every change is backward-compatible — no
tool, field, or error code was removed or retyped.
Added
-
codex_capabilitiesacceptsdetail="contracts", which omitstool_detailsand returns
everything else unchanged. Theinclude_schemasfallback for resource-blind clients previously
re-sent the whole tool inventory on every schema fetch; pairing it withcontractsdrops a
schema fetch from 30,404 to 23,082 bytes, anddetail="contracts"alone is a 3,787-byte
fingerprintrecheck for cache revalidation (was 11,109).tool_detailsis the only field
removed, and it is already optional in the published schemas, so acontractsresponse still
validates against both the tool'soutputSchemaandcodex://capabilities-result— no schema
change was needed. The new token lives on a separateCapabilitiesDetailLiteral used only by
this tool; the sharedDetailstays two-valued, so the five other tools takingdetailare
unaffected. Backward-compatible:summaryremains the default and both existing modes are
unchanged. -
Every tool now states its cost in its own description — five tools that previously relied
oncodex_capabilitiesalone (codex_consult,codex_review_changes,codex_delegate,
codex_job_result,codex_job_consume_result) now sayPAIDorFreeexplicitly, and two
more (codex_dry_run,codex_delegate_dry_run), which already statedNO model call and no spendin prose, were normalized onto the sameFreetoken — so a client reading only
tools/listgets one consistent cost marker across every tool. -
Every cost marker now uses one canonical token —
PAID —for active tools,Free — no model callfor free ones — instead of near-miss variants (FREE —, a line-wrapped
Free —, or bare mentions of "spend").codex_transfer,codex_capabilities,
codex_consult_async,codex_review_changes_async, andcodex_delegate_asyncgain the
literal marker; the three asyncPAID —blocks each point at the correct preview tool
(codex_statusonly forcodex_consult_async,codex_dry_runfor
codex_review_changes_async,codex_delegate_dry_runforcodex_delegate_async), and all
six active tools now say "every new call" rather than "every call" so the marker doesn't
contradictidempotency_key's no-new-spend replay semantics. -
Every tool now carries a
titlefor human-facing pickers and a namespaced
_metastability tier, so a client reading onlytools/listcan see which tools are
experimental. -
codex_job_listgained optionallimit(1-1000) andstatusfilters to narrow the
returned jobs. Both are purely opt-in: omittinglimit(the default) still returns every
job the store retains, so the tool every error's repair hint names as the way to recover
a lostjob_idnever hides a row the server deliberately kept — the job store's own
retention policy is the only server-side limiter, and a soft one, since running jobs are
exempt from eviction. Only an explicit
limitcan truncate; when more jobs match, the response setstruncated: truewith a
truncation_hintpointing back at omittinglimit(or narrowing withstatus) — the
extra rows are dropped, not paged, so there is no cursor. Running jobs are never evicted,
so a busy workspace can retain more rows thanlimit's 1000 ceiling can ask for, which is
why omitting it is the only complete listing. The tool'scodex_capabilitiesrecord
advertises both params and the cap-not-a-page semantics, so a client treating the
detail="full"inventory as authoritative sees them too (audit F5, #396, #395). -
meta.roots_source(and the matching field oncodex_dry_run/codex_delegate_dry_run)
reports which of three states the MCP-roots probe saw:client(the client advertised the
roots capability and the probe returned, possibly an empty list),not_negotiated(this client
never advertised the roots capability — passworkspace_rootinstead), orprobe_failed
(roots were advertised but the call errored this turn — retrying may help). It reports the
probe, not where the workspace came from —workspace_sourceanswers that. Previously all
three collapsed into a silent empty list and a fallback to the server's own cwd;rootsstays
advisory either way, andworkspace_rootremains the durable path (audit F8). Which run the
value describes now depends on the envelope, andcodex://result-metastates the rule: when
present, a DELIVERED consult/review/delegate result reports the ORIGINATING run (like
meta.tier) whether returned synchronously or fetched later, while a*_asynchandle, a
dry-run preview, and an error acodex_job_*call generates instead of delivering a stored
result each report the CURRENT call — so a replay handle and the result later fetched for the
same job may legitimately differ. Absence implies none of that: it means only that the value
was not reported (an unexpected server-sideinternal_error, for instance, is built without
one), so never infer a run's age or identity from a missing key. Bumps the persisted result-format (RESULT_FORMAT6→7)
for the newMetafield; not breaking. -
Each
codex://resource now carries a namespaced_meta["dev.bconnelly.codex-in-claude/triage"]
so an agent can decide whether a body is worth the context before reading it: the five static
schema resources declaresize_bytes(computed from the payload at registration, so it cannot
drift from the body);codex://models, whose body is a refreshed cache, declaresvolatile: truewith afreshness_viapointer to the payload'sfetched_atfield instead of a size that
would go stale (audit F4).size_bytesnow counts the UTF-8-encoded bytes rather than
len()of the JSON string (Copilot review of #385): today's payloads are pure ASCII under
json.dumps's defaultensure_ascii=True, so the count is unchanged for all five resources,
but the name is now true by construction instead of by coincidence of that default.
Changed
- Delivered
codex_consult/codex_review_changes/codex_delegatesuccess envelopes now omit
metamembers whose value is null, instead of sending a run of explicit null keys on every
call — between 5 and 17 of them across the representative envelopes in
tests/fixtures/wire_shape_snapshot.json, though a real call's count varies with which
members apply to it.
A key's absence means exactly what the null meant — not applicable, or not reported for this
run — so readmetawith a null-safe accessor rather than by testing key presence. Measured
against those same representative envelopes: ~6–37% smaller atdetail="summary", and the
same ~6–37% atfull; the saving scales with how many members the run leaves
inapplicable, so the sparsest envelope gains most. The six requiredmetamembers are
always present and empty arrays stay empty arrays; everything outsidemetais delivered
verbatim, so top-level fields (includingcodex_delegate'sdiff, which is null when a run
proposes no changes) and all ofraw_responsekeep their keys. The*_asyncjob handle and
codex_job_statusare unaffected. Trimming happens on delivery, so the storedresult.json
is byte-for-byte unchanged (RESULT_FORMATstays7and already-stored job results stay
readable) and a replayed result still matches a fresh synchronous one. Not breaking: the
published output schemas andcodex://result-metaalready accept absence for every affected
field. BumpsFINGERPRINT(schema-60→schema-61); the rule is published on
codex://result-meta(#334). codex_capabilitiesnow defaults todetail="summary", returning only the per-tool facts
tools/listdoes not already carry (name,cost,stability,error_codes, and, for the
*_asynctools only,async_lifecycle). Passdetail="full"for the previous payload. The
extra_contextparameter contract moved its full text tocodex://paramsand
idempotency_key's inline summary was compressed. The durable size win iscodex_capabilities'
own response — 21,763 → 11,109 bytes (−49%) — but that is paid only by clients that call the
tool, not by every client the waytools/listis.- Net effect on the preloaded discovery surface: this release's other additions (per-tool cost
markers, titles, stability tiers,codex_job_listfilters,roots_sourceprovenance,
resource triage metadata) outgrew the compression above for every client, not just
codex_capabilitiescallers.tools/listwent from 79,242 to 83,895 bytes (+5.9%). That is a
deliberate trade: a larger preloaded surface in exchange for cost/stability/next-step metadata
that was previously missing or unreachable.
Fixed
codex_capabilitiesdelivered the per-toolstabilitykey in one detail mode but not the
other (#399).detail="summary"force-addedstability: null...
codex-in-claude v0.15.0
A status-signal and diff-gather-hardening release. codex_status learns to report an
administrative spend block, codex_job_status/codex_job_list expose a finished job's
success/failure without a fetch, the diff-gather paths gain consistency and bounded-memory
guarantees across every review scope, and several capability-discovery contracts are corrected to
match what they advertise. Tracked Codex is bumped to 0.145. The agent-visible surface changed
(result fingerprint codex-in-claude/0.1/schema-49 → schema-56, and the persisted
RESULT_FORMAT 4 → 6), so pre-1.0 this is a minor release; clients that cache by fingerprint
re-fetch the contract. Every change is backward-compatible — no tool, field, or error code was
removed or retyped.
Added
-
codex_statusreports an administrative spend block (#359). codex 0.145 added
spendControlReachedto the app-server's rate-limit snapshot — a spend control the backend
enforces, distinct from a quota window: no reset clears it.rate_limitnow carries
spend_control_reached(true|false|null) andrate_limit.statusgains the value
blocked, which outranks every window-derived verdict because its remedy is not waiting; it
reportslimiting_window: nulland anotesaying a reset will not clear it. The tri-state is
load-bearing —nullis notfalse, and a null never downgrades a healthy window reading to
unknown(which would reintroduce the permanent-unknownfailure #321 removed). The bundled
collaborating-with-codexskill gains the matching rule:blockedis the one non-advisory
rate-limit state — refuse the paid call rather than deferring it. Bumps the persisted
result-format (RESULT_FORMAT5→6) for the new status value; not breaking. -
result_okon job status and list entries (#335).codex_job_statusand each
codex_job_listentry now carryresult_ok— a finished job's producer-declared outcome
(true= success,false= a stored error envelope,null= running, unclassifiable, or a
record finalized before this field) — so a stored failure can be triaged without a per-job fetch.
It reports the outcome recorded when the result was written and does not guarantee the payload
is still readable across releases.codex_capabilitiesgains an
async_lifecycle.result_ok_fieldentry for structural discovery. Backward-compatible output
addition; not breaking.
Changed
-
Diff-gather git reads are now counted in bounded memory (#331, #350, #351, #353). The
untracked-file listing, the tracked-diff--numstatsummary, and the untracked-count stderr read
— all reached on the defaultcodex_review_changesand freecodex_dry_runpaths — previously
materialized whole git outputs unbounded in the workspace's changed-file count. They now stream
through a single shared bounded runner, so a pathological workspace can neither exhaust server
memory nor stall a review past its deadline, and a latent stderr deadlock is removed. The same
work fixed a real corruption bug: a carriage return in a git-produced filename was rewritten by
Python's universal-newline translation before the NUL-splitter saw it, silently omitting one file
while still counting it as included — a quiet coverage-contract violation (#353). Reported counts
and error vocabulary are unchanged, so nofingerprintchange. -
Every egress caveat now discloses that user-global Codex skills auto-load (#358). Skills under
$CODEX_HOME/skills/(default~/.codex/skills/) are discovered from outside the workspace
and their bodies can reach OpenAI on any active call, despite--ignore-user-config— pre-existing
behavior (verified againstcodex-cli 0.144.1and0.145.0), not new. The disclosure is
corrected across the server instructions, all six tool descriptions and their capability entries,
codex_capabilities' negative scope,README.md,SECURITY.md,COMPATIBILITY.md,
cli_contract.py, and thecollaborating-with-codexskill. A reword of covered descriptions, so
it bumps thefingerprint; not breaking — the contract only ever promisedignore-config
drops$CODEX_HOME/config.toml, never that all$CODEX_HOMEcontent stays local. -
Tracked Codex version bumped to
0.145(#361).SUPPORTED_VERSIONSnow tracks(0, 145);
the CLI contract, help snapshots, andKNOWN_MODEL_SLUGSfallback are verified against
codex-cli 0.145.0, which required no code change — help text, sandbox values, drift/auth
signatures, and reasoning-effort handling are all intact, and the app-server schema diff is
additive-only for the surface this plugin consumes. Advisory only: an untracked version warns in
codex_statusbut never blocks, and theCODEX_IN_CLAUDE_SUPPORTED_VERSIONSoverride still
applies. No agent-visible surface change, so nofingerprintbump.
Fixed
-
Diff gathers are now consistency-checked and atomic across every review scope (#336, #355). A
gather runs its context summary and the transmitted diff as separate git invocations, so a
concurrent edit or ref move between them could make the summary and the reviewed patch describe
different content whilecoverage.statusstill reported"complete".working_treereviews now
bracket that window with a best-effort porcelain state token; on a mismatch they set a new
tree_changed_during_gathervalue oncoverage.omission_reasons, degrading coverage topartial
and (via the #319 rules) apassverdict tounknown. It is a consistency caveat, not a claim
that specific content was omitted, and it does not trip on a content-only re-edit — socomplete
is documented as no longer proof the tree held still.branchandcommitreviews now resolve
their refs to immutable commit object IDs once, up front, so the summary and diff cannot split
under a mid-gather commit/reset/checkout; an unborn HEAD fails closed, a concurrent HEAD move is
disclosed via the same token, and acommit=<annotated-tag>review peels the tag to its commit.
WidensCoverageOmissionReasonand bumps the persisted result-format (RESULT_FORMAT4→5,
since an older closed-schema reader could reject the new enum value); not breaking. -
Capability-discovery contracts corrected to match what they advertise (#337, #370, #372, #373).
Severalcodex_capabilitiesand error-envelope contracts were narrower or staler than their
promise.fingerprint_coverssaid any change in a covered category bumps thefingerprintbut
silently excluded release-identity fields (serverInfo.version, the capabilitiesversion); it
now carries a description disclosing the carve-out in both directions, so a caching client can
trust that an unchangedfingerprintmeans an unchanged contract. Thecodex_capabilitiestool
description advertised only two of the fiveinclude_schemastokens (the parameter schema
already listed all five), hiding the resource-blind route toparameter-contracts. And
invalid_argumentsomittedallowed_valuesfor a rejectedlist[Literal]element — the one
parameter shape the domain resolver did not read — on the very field whose repair hint says to use
those values. Theinclude_schemastoken set is additionally drift-proofed against its runtime
payload by an exact-equality test. Wording and coverage corrections that widen what is documented
and weaken no guarantee — thefingerprintbump is for thefingerprint_coversand tool-
description changes (theallowed_valuesfix is per-call envelope data, no discovered surface);
not breaking. -
invalid_reasoning_effortgives the local pre-spend guard a provenance-specific repair (#332).
The code is emitted from two paths: the Codex backend rejecting a sent effort (table repair
correct_arguments+codex_models, correct there) and the local pre-spend guard refusing a
hostile resolved value before any subprocess (zero spend). The guard previously inherited the
backend repair, misdirecting an agent that branches onerror.repairto a uselesscodex_models
call. It now emits a provenance-specific repair with no tool:correct_configwhen the
invalid value is the resolvedCODEX_IN_CLAUDE_REASONING_EFFORTdefault,correct_argumentswhen
it is an explicit per-call argument. Bumps thefingerprintfor the corrected parameter contract;
not breaking (correct_configis already a publishedRepairStep).
codex-in-claude v0.14.0
A discovery-slimming and sync-timeout release. The tools/list catalog gets lighter and a new
codex://params resource becomes the single home for the full parameter contracts, the sync tools
steer long-running work to their _async variants at selection time, and the built-in sync
timeout_seconds default rises from 180 to 300. The agent-visible surface changed (result
fingerprint codex-in-claude/0.1/schema-46 → schema-49), so pre-1.0 this is
a minor release; clients that cache by fingerprint re-fetch the contract. Every change is
backward-compatible — no tool, field, or error code was removed or retyped.
Added
codex://paramsresource andparameter-contractscapabilities fold-in (#333). A new
read-only resource serves the full lifecycle/validation semantics for parameters whose
tools/listdescription is a compressed summary, backed by a single-source
PARAMETER_CONTRACTSregistry so the inline summary and the resource body cannot drift.
codex_capabilities(include_schemas=["parameter-contracts"])embeds the same document for
resource-blind clients. Its content is guarded by the newparameter_contracts
FINGERPRINT_COVERScategory and the manifest snapshot.
Changed
-
Slimmed the
tools/listcatalog (#333). MCP inlines each parameter description into every
tool's schema, so a long shared description repeats on the wire. Theidempotency_keyand
reasoning_effortinline descriptions are compressed to their selection-, safety-, and
spend-critical facts (the full lifecycle/validation detail moves tocodex://params),
workspace_root/isolationare tightened, and the sync/async tool docstrings are slimmed —
reducing the serializedtoolscatalog by ~6% (~85.2 KB → ~80.2 KB, snapshot measure) with
no weakened guarantee: a table-driven per-tool freeze test asserts every egress/security
guarantee (raw-input, files-read, auto-loadedAGENTS.md/.agents/skills, isolation, best-
effort redaction, delegate no-network, review diff-redaction) still ships inline. This bumps the
resultfingerprintbut is not breaking; the deeper≤60 KBtarget requires opaquing the output
schemas (tracked separately). -
Sync tools steer long-running work to their
_asyncvariants, and the default sync
timeout_secondsrises from 180 to 300 (#338, #341). Two changes to how the synchronous tools
handle work that can outlast a foreground call:- The
codex_consult/codex_review_changes/codex_delegatedescriptions, their_async
counterparts, all sixcodex_capabilitiesuse_whenentries, and the serverinstructions
block now name the shapes that can exceed the synchronous deadline — a high-reasoning-effort or
broad repo-grounded consult, a multi-file or whole-branch review, or a substantial
implementation task — and recommend the matching_asynctool, so the steer reaches the agent
at tool-selection time instead of only in the post-timeout repair, after the paid run was
already lost. - The built-in default sync
timeout_secondsrises from 180 to 300: a sync call that omits
timeout_secondsnow waits up to 300s before terminating. The 10–600s clamp and the
CODEX_IN_CLAUDE_TIMEOUT_SECONDSoperator override are unchanged, and a caller wanting the
prior deadline can passtimeout_seconds=180. 300 is the smallest round value that recovers the
mid-tier consult/review runs observed exceeding the old 180s cap; the destructive >~420s cliff
stays the domain of the_asyncvariants (separate 1800s job deadline), so the raise reduces
the frequency of mid-tier sync timeouts rather than removing the cliff. A longer sync deadline
only helps a client whose own foreground window is at least the server deadline; a client with a
short window already backgrounds long sync calls, and thetimeout_seconds/env override remains
the escape hatch either way.
The
collaborating-with-codexskill routing and the/codex:consult|review|delegatecommand
prompts carry the same steer. Wording and default-value changes that narrow no input and weaken no
guarantee — the deadline was already documented as overridable — so they move the result
fingerprintbut are not breaking. Acodex_dry_runsize advisory remains tracked separately
(#342). - The
-
Internal: the stripped git-subprocess environment is now built by a single
gitdiff._base_git_env()helper shared across_core(previously duplicated at five
call sites), so the hardening posture cannot drift between them.
Fixed
- Untracked-file handling now honors the user's global gitignore (#330). The git
subprocesses that enumerate untracked files run with a HOME-stripped environment
(deliberate hardening — no user hooks/fsmonitor/attributes), which also prevented git
from resolving the user's global excludes (core.excludesFilefrom global config,
or the default~/.config/git/ignore/$XDG_CONFIG_HOME/git/ignore). As a result a
globally-ignored file (e.g. a~/.config/git/ignore-listed.claude/settings.local.json)
was misclassified as untracked: it inflated theuntracked_files_detected/
delegate-planuntrackedcounts, and underuntracked="include"its contents were
gathered and sent to OpenAI, contrary to the documented "non-ignored untracked files"
contract. The effectivecore.excludesFileis now resolved from the server's own
environment (mirroring git's own precedence, including a repo-local override) and passed
explicitly as-c core.excludesFile=<path>to only the untracked-enumeration calls, so
the global ignore layer is honored without restoringHOME(no other global config
becomes readable). The resolver drops inheritedGIT_DIR-family variables so a stray
GIT_DIRcannot anchor resolution to another repo, andGIT_CONFIG(which only
git confighonors) so it mirrors whatls-filesactually reads. Repo-local
(.gitignore,.git/info/exclude) and local/systemcore.excludesFilelayers with
ordinary absolute or relative paths were already honored; a~-containing local/system
core.excludesFilepreviously failed to expand under the HOME-stripped child (a fatal
error) and now resolves too, since the value is~-expanded in the server. Behavior-only
fix restoring the already-documented meaning — no change to the agent-visible schema or
descriptions, so the resultfingerprintis unchanged.
codex-in-claude v0.13.0
A review-honesty and rate-limit-recovery release. codex_review_changes no longer reports an
unreviewed working tree — the all-untracked shape most agent work takes — as a high-confidence
pass, and codex_status reads live rate-limit quota from the codex app-server again after
codex 0.144 moved it off the codex exec stream. Both are breaking on the agent-visible
surface: the result fingerprint moves twice (codex-in-claude/0.1/schema-44 → schema-46) and
RESULT_FORMAT twice (2 → 4), so pre-1.0 this is a minor release and clients that cache by
fingerprint re-fetch the contract.
Changed
-
codex_review_changesno longer reports an unreviewed tree as a high-confidence pass
(#319, breaking). A working tree whose only changes were untracked (new) files — the most
common shape of agent work — used to short-circuit toverdict: "pass",confidence: "high"
with no model call, indistinguishable from a genuinely clean review. Now:- The result carries top-level
review_status(completed|not_run) and acoverageobject
(statuscomplete|partial; pathspec-scopeduntracked_files_detected/included/omitted
counts, null outsideworking_treescope; a closedomission_reasonsset ofuntracked_omitted
/truncated/redacted). Untracked files are inventoried withgit ls-files --others— a
count only, no contents read, so the blind spot is disclosed at zero egress. - A review that never ran the model returns
verdict: "unknown",confidence: "low",
review_status: "not_run"— neverpass. A modelpassoverpartialcoverage (omitted
untracked files, a truncated diff, or a redacted file) is surfaced asunknown/lowwith the
caveat prefixed tosummary; concretefail/concernsfindings are always retained. - A new
untrackedinput (explicit_onlydefault |include|exclude) on
codex_review_changes,codex_review_changes_async, andcodex_dry_run.explicit_only
preserves #74 (only untracked files named inpathsare reviewed);includereviews every
non-ignored untracked file (opt-in egress — it sends their contents);excludeincludes none. codex_dry_runnow reportswould_call_modeland the samecoverageobject, and its
prompt_bytesis0when the previewed call would send nothing — matching the paid path
instead of reporting the size of a prompt never sent (#320).- Git invocations in the diff-gathering path now run with
-c core.fsmonitor=false, so a
working-tree review of an untrusted repo cannot execute a repo-configured fsmonitor program in
the server process. - Hardening (from an implementation review): the untracked inventory is stream-counted in
bounded chunks (an untrusted workspace with arbitrarily many untracked files cannot exhaust
memory); an invaliduntrackedpolicy reaching the core is rejected asinvalid_arguments
rather than silently behaving likeexclude; the coverage counts come from a single
enumeration sodetected == included + omittedcan't be violated under concurrent mutation,
andCoveragenow validates that invariant;review_status/would_call_modelare required
(no unsafe positive default); and the empty-review repair hint is tailored to the active
untrackedpolicy.
Bumps
FINGERPRINT(schema-44→schema-45) andRESULT_FORMAT(2→3); clients that
cache byfingerprintre-fetch the contract, and cross-release job replay of a review result
written by an older version is refused rather than misread. - The result carries top-level
-
codex_delegate_dry_run's worktree preview counts in bounded memory (#323, #326). All three
counts inworktree.plan()— untracked files, tracked files/bytes (git ls-tree -r --long), and
uncommitted tracked files (git diff --numstat) — previously materialized their whole git listing
in memory. The untracked count now delegates to the sharedgitdiff.count_untrackedinventory
(the same NUL-delimited, fsmonitor-hardened enumerationcodex_review_changes/codex_dry_run
use), and the other two stream through a new shared_core/gitproc.run_linesrunner (bounded
per-line reader, concurrent capped stderr drain, process-group kill/reap on timeout or consumer
failure — lifecycle guarantees ported from the diff streamer), so a repo with a pathological
number of tracked, changed, or untracked files is counted without exhausting memory. Reported
counts and failure semantics are unchanged — a git failure surfaces as a structured
worktree_error(or, fornumstat, still degrades to0) instead of a silently-authoritative
0— so nofingerprintchange. (The newline over-count originally filed as #323 did not
reproduce: git C-quotes control characters, newline included, by default, soplan()'s non--z
line-count was already correct.)
Fixed
-
codex_statusreports live rate-limit quota again on codex 0.144+ (#321, breaking).
codex 0.144 removed thetoken_countevent that carried the quota block on thecodex exec
stream, sorate_limithad gone permanentlyunknownwhile the note told you to "run any Codex
call to populate it" — advice that could never work. The data had moved to the app-server
protocol, not disappeared. Now:codex_statusfetches quota live fromcodex app-server(account/rateLimits/read) — a
read-only call with no model-token spend — reusing the hardened one-shot client that backs
codex_transfer. The read is ephemeral: nothing is persisted, socodex_statusstays a
genuinely read-only call and no stale cache can mislead a spend decision.rate_limit.source
isapp_server_live.- Windows are re-slotted by duration, not by the app-server's slot order:
primaryis the
shorter/rolling window (historically 5-hour),secondarythe longer (weekly). The 0.144
app-server reports only the windows that currently bind an account and may place the weekly
window in theprimaryslot with no secondary — so a naive field rename would have kept the
bug. An absent window is no longer treated as "unobserved," so a single healthy window now
correctly reportsavailableinstead of a permanentunknown. - New
rate_limit.statusvalueunavailable(this codex/account exposes no quota data) and
rate_limit.sourcevalueapp_server_live;codex_status's meaning changes from a cached
paid-run snapshot to a live read (breaking under the versioning rules — a closed-schema
output meaning changed, andmeta.rate_limitis nownullon current CLIs). A read that finds
the method missing, the protocol drifted, or a malformed result is surfaced asunavailable
(never as a plausible "no quota") with a note that the plugin may need an update — a loud
signal, not another silentunknown. A committed real-shape fixture plus an integration test
against the live app-server guard against the next such drift. - Untrusted app-server output is hardened:
planTypeis length-bounded and
rateLimitReachedTypeis accepted only from the known enum (an unknown value is dropped, never
trusted as a falseexhausted); a cached reason code degrades tounknownonce every window
has reset; a pathological numeric field (e.g. a 400-digitusedPercent) degrades to absent
instead of raising; two windows are duration-sorted soprimaryis always the shorter horizon;
and the read response is correlated on an unpredictable request id so a prequeued/unsolicited
message can't be trusted as quota. - The dead exec-stream quota parser (
normalize.parse_rate_limit), the per-run capture, and the
snapshot cache (CODEX_IN_CLAUDE_RATE_LIMIT_FILE) are removed.
Bumps
FINGERPRINT(schema-45→schema-46) andRESULT_FORMAT(3→4) for the added
enum values and the changed meaning of therate_limitblock.