Skip to content

codex-in-claude v0.16.0

Choose a tag to compare

@github-actions github-actions released this 29 Jul 00:24
· 91 commits to main since this release
3e6393c

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 67), 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_capabilities accepts detail="contracts", which omits tool_details and returns
    everything else unchanged. The include_schemas fallback for resource-blind clients previously
    re-sent the whole tool inventory on every schema fetch; pairing it with contracts drops a
    schema fetch from 30,404 to 23,082 bytes, and detail="contracts" alone is a 3,787-byte
    fingerprint recheck for cache revalidation (was 11,109). tool_details is the only field
    removed, and it is already optional in the published schemas, so a contracts response still
    validates against both the tool's outputSchema and codex://capabilities-result — no schema
    change was needed. The new token lives on a separate CapabilitiesDetail Literal used only by
    this tool; the shared Detail stays two-valued, so the five other tools taking detail are
    unaffected. Backward-compatible: summary remains the default and both existing modes are
    unchanged.

  • Every tool now states its cost in its own description — five tools that previously relied
    on codex_capabilities alone (codex_consult, codex_review_changes, codex_delegate,
    codex_job_result, codex_job_consume_result) now say PAID or Free explicitly, and two
    more (codex_dry_run, codex_delegate_dry_run), which already stated NO model call and no spend in prose, were normalized onto the same Free token — so a client reading only
    tools/list gets one consistent cost marker across every tool.

  • Every cost marker now uses one canonical token — PAID — for active tools, Free — no model call for 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, and codex_delegate_async gain the
    literal marker; the three async PAID — blocks each point at the correct preview tool
    (codex_status only for codex_consult_async, codex_dry_run for
    codex_review_changes_async, codex_delegate_dry_run for codex_delegate_async), and all
    six active tools now say "every new call" rather than "every call" so the marker doesn't
    contradict idempotency_key's no-new-spend replay semantics.

  • Every tool now carries a title for human-facing pickers and a namespaced
    _meta stability tier, so a client reading only tools/list can see which tools are
    experimental.

  • codex_job_list gained optional limit (1-1000) and status filters to narrow the
    returned jobs. Both are purely opt-in: omitting limit (the default) still returns every
    job the store retains, so the tool every error's repair hint names as the way to recover
    a lost job_id never 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
    limit can truncate; when more jobs match, the response sets truncated: true with a
    truncation_hint pointing back at omitting limit (or narrowing with status) — 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 than limit's 1000 ceiling can ask for, which is
    why omitting it is the only complete listing. The tool's codex_capabilities record
    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 on codex_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 — pass workspace_root instead), or probe_failed
    (roots were advertised but the call errored this turn — retrying may help). It reports the
    probe, not where the workspace came from — workspace_source answers that. Previously all
    three collapsed into a silent empty list and a fallback to the server's own cwd; roots stays
    advisory either way, and workspace_root remains the durable path (audit F8). Which run the
    value describes now depends on the envelope, and codex://result-meta states 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 *_async handle, a
    dry-run preview, and an error a codex_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-side internal_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_FORMAT 67)
    for the new Meta field; 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 declare size_bytes (computed from the payload at registration, so it cannot
    drift from the body); codex://models, whose body is a refreshed cache, declares volatile: true with a freshness_via pointer to the payload's fetched_at field instead of a size that
    would go stale (audit F4). size_bytes now 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 default ensure_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_delegate success envelopes now omit
    meta members 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 read meta with a null-safe accessor rather than by testing key presence. Measured
    against those same representative envelopes: ~6–37% smaller at detail="summary", and the
    same ~6–37% at full; the saving scales with how many members the run leaves
    inapplicable, so the sparsest envelope gains most. The six required meta members are
    always present and empty arrays stay empty arrays; everything outside meta is delivered
    verbatim, so top-level fields (including codex_delegate's diff, which is null when a run
    proposes no changes) and all of raw_response keep their keys. The *_async job handle and
    codex_job_status are unaffected. Trimming happens on delivery, so the stored result.json
    is byte-for-byte unchanged (RESULT_FORMAT stays 7 and already-stored job results stay
    readable) and a replayed result still matches a fresh synchronous one. Not breaking: the
    published output schemas and codex://result-meta already accept absence for every affected
    field. Bumps FINGERPRINT (schema-60schema-61); the rule is published on
    codex://result-meta (#334).
  • codex_capabilities now defaults to detail="summary", returning only the per-tool facts
    tools/list does not already carry (name, cost, stability, error_codes, and, for the
    *_async tools only, async_lifecycle). Pass detail="full" for the previous payload. The
    extra_context parameter contract moved its full text to codex://params and
    idempotency_key's inline summary was compressed. The durable size win is codex_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 way tools/list is.
  • Net effect on the preloaded discovery surface: this release's other additions (per-tool cost
    markers, titles, stability tiers, codex_job_list filters, roots_source provenance,
    resource triage metadata) outgrew the compression above for every client, not just
    codex_capabilities callers. tools/list went 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_capabilities delivered the per-tool stability key in one detail mode but not the
    other
    (#399). detail="summary" force-added stability: null for the eight default-tier tools,
    while detail="full" had it stripped by exclude_none — so a client moving from the default mode
    to the richer one lost a key, contradicting the docstring's framing of full as additive. The
    key is now forced once, before either mode branches, so full can no longer drop a key
    summary carries and a single code path is what puts stability on both;
    detail="contracts" carries no inventory and is unaffected. Null keeps its published meaning — the tool inherits the server-wide stability — and
    the field stays optional in both published schemas, so this only adds key presence:
    FINGERPRINT moves (schema-65schema-66), RESULT_FORMAT does not, and it is not breaking.
    The subset regression no longer carves stability out and now also pins that both modes describe
    the same tools and agree on every shared value.

  • The documented contract misdescribed detail rejection and roots_source (#397). Three
    corrections, none of which changes behavior. (1) docs/REFERENCE.md told direct MCP callers that
    an unrecognized detail value is rejected with unsupported_detail; it is not reachable that way
    detail is a closed enum in each tool's input schema, so the call boundary rejects the value as
    invalid_arguments before the handler runs, and unsupported_detail stays deliberately
    unadvertised as an in-handler guard for direct Python callers. A parametrized regression now pins
    the boundary behavior across the five tools sharing the result Detail enum, so the corrected
    sentence cannot go stale unnoticed (codex_capabilities has its own three-valued
    CapabilitiesDetail; pinning that one is #398's item 3). The same published description no longer
    offers an unsupported detail as an example of a lifecycle-generated error, since over MCP that
    rejection never reaches the handler. (2) roots_source was absent from REFERENCE.md entirely,
    including the workspace-selection section that owns workspace resolution; it is now documented
    there with the probe-versus-selection distinction and the correct placement note that the two
    dry-run tools expose it top-level on a successful preview, while their error envelopes carry it
    under meta like every other tool. (3) The published codex://result-meta description
    said client means roots "were used", which conflates the roots probe with workspace selection —
    client only reports that the probe returned, and it coexists normally with
    workspace_source: "param" (an explicit workspace_root wins) or "cwd" (the probe returned no
    usable root). Wording only, so it bumps FINGERPRINT (schema-64schema-65) without moving
    RESULT_FORMAT; not breaking.

  • meta.roots_source never reached a delivered paid success envelope — the one surface it
    was added for. A synchronous codex_consult/codex_review_changes/codex_delegate success is
    delivered from the worker-written result.json, not from the meta the handler prepared, and
    the job spec never carried roots_source, so the worker stored null and the null-omission
    rule then dropped the key entirely. The three job specs now carry it and the worker reads it
    back, so a paid success — and a paid failure, whose stored envelope is built the same way —
    reports the roots state its run actually saw. The same value now also reaches the
    lifecycle-generated error envelopes (job_not_found, a running/corrupt/incompatible job, and
    codex_job_cancel — plus the in-handler unsupported_detail guard, which only a direct Python
    caller can reach, since over MCP a bad detail is rejected as invalid_arguments before the
    handler runs), which probed roots and then discarded the answer;
    those report the CURRENT lookup, not the inspected job's originating run. roots_source is
    provenance rather than call identity, so it is excluded from the idempotency argument hash: a
    keyed call that reconnects with a different roots state still replays instead of failing
    idempotency_conflict, and pre-existing dedup entries keep matching unchanged. Results stored
    before this change carry no value and keep omitting the key. RESULT_FORMAT stays 7 (the
    field is already known at that format, so no stored payload became unreadable); bumps
    FINGERPRINT (schema-63schema-64) for the published codex://result-meta description
    that now states which run the value describes. Not breaking (#393).

  • capabilities-result schema's required list contradicted the default response.
    ToolCapability.use_when/.returns were marked required in the published schema (reachable via
    codex_capabilities(include_schemas=["capabilities-result"])), but detail="summary" — the
    default — strips both from every tool_details entry, so a strict client validating the
    default response against its own published schema failed. Both fields are now optional in the
    schema, modeling both detail modes; the response bytes are unchanged in either mode. Non-breaking:
    the schema is corrected to describe what was already being sent.

  • CapabilitiesDetailParam's description and the codex_capabilities docstring described
    async_lifecycle as part of every summary entry; it is only present for the *_async tools
    (3 of 17), which both now say explicitly.

  • A resources/read failure's error.data now carries resource_uri (the URI that was
    requested) and request_id, matching the correlation fields the tool-error carrier already
    has via meta.request_id. Both are optional and populated only on the JSON-RPC (resource)
    carrier — the tool carrier is unaffected, since duplicating request_id there would be two
    homes for one fact (audit F6, #185). resource_error_carrier in codex_capabilities is
    updated to name both fields and to disclose, as a deliberate divergence, that this server
    keeps code/message rather than the machine_code/human_message spelling some §6
    profiles use.

  • The *_async tools' JobStarted result now carries a follow_up object
    ({next_step, tool, arguments, alternative}, the same shape as error.repair) naming
    codex_job_status with literally callable arguments (audit F7).