Summary
codex exec --json omits reasoning-token accounting from the turn.completed event, even for reasoning models (e.g. gpt-5.4-mini). The field is already tracked internally and persisted to the session rollout file, but it never reaches the JSON stdout stream — so any tool consuming Codex programmatically (IDE extensions, gateways, SDKs) can't distinguish visible output from reasoning.
Current behavior
$ codex exec --json --skip-git-repo-check --ephemeral -m gpt-5.4-mini \
"What is 147 times 839? Reply only with the number."
…
{"type":"item.completed","item":{"id":"…","type":"agent_message","text":"123333"}}
{"type":"turn.completed","usage":{"input_tokens":32161,"cached_input_tokens":1920,"output_tokens":47}}
usage carries three keys only. 47 output tokens for a six-character reply — the remainder is reasoning, but the stream doesn't say so.
Verified on a longer prompt ("prove there are infinitely many primes"): 139 output tokens, still no reasoning field in the --json output, still no agent_reasoning item events in the stream.
Where the data already lives
Codex does record reasoning_output_tokens — but only to the on-disk session rollout (~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl), inside an event_msg with payload.type == "token_count":
{
"timestamp": "2026-04-19T11:23:41.622Z",
"type": "event_msg",
"payload": {
"type": "token_count",
"info": {
"total_token_usage": {
"input_tokens": 31751,
"cached_input_tokens": 14720,
"output_tokens": 2367,
"reasoning_output_tokens": 413,
"total_tokens": 34118
},
"last_token_usage": { "…": "same shape" },
"model_context_window": 258400
},
"rate_limits": { "…": "…" }
}
}
Two reasons this isn't a workable consumer path:
- The rollout file is a user-facing session log, not a programmatic API. Tools running Codex non-interactively (e.g. CI, gateways, LSP features) wouldn't reliably parse it.
- It doesn't exist when Codex runs with
--ephemeral. Tools that intentionally avoid writing per-request session state to the user's home directory cannot access it at all.
Requested change
Add reasoning_output_tokens (and ideally the other total_token_usage fields — total_tokens, model_context_window, rate-limit info) to the turn.completed.usage payload on --json, so the stream is the complete source of truth for the turn.
A minimal, backward-compatible version:
{
"type": "turn.completed",
"usage": {
"input_tokens": 32161,
"cached_input_tokens": 1920,
"output_tokens": 47,
"reasoning_output_tokens": 41
}
}
Since the field is additive and optional, existing consumers are unaffected.
Why it matters
Consumers surfacing per-turn cost or context-fill to end users need the reasoning share to avoid misrepresenting output spend. A one-word reply that costs 22 output tokens with 21 reasoning is not meaningfully "22 tokens of output" to the user — it's "1 visible, 21 reasoning." The former reads as a buggy totals display.
Concrete consumer: I'm building an AI gateway that exposes ChatGPT Pro models through an OpenAI-compatible API. I want to pass the reasoning split through to clients (already present in OpenAI's native chat-completions response as usage.completion_tokens_details.reasoning_tokens). Today I have to either drop --ephemeral and parse rollout files back (stateful, race-prone, filesystem pollution) or ship a seam that never populates.
Environment
codex-cli 0.121.0 (installed via Homebrew on macOS)
- Darwin 25.3
- Observed: 2026-04-22
Summary
codex exec --jsonomits reasoning-token accounting from theturn.completedevent, even for reasoning models (e.g.gpt-5.4-mini). The field is already tracked internally and persisted to the session rollout file, but it never reaches the JSON stdout stream — so any tool consuming Codex programmatically (IDE extensions, gateways, SDKs) can't distinguish visible output from reasoning.Current behavior
usagecarries three keys only. 47 output tokens for a six-character reply — the remainder is reasoning, but the stream doesn't say so.Verified on a longer prompt ("prove there are infinitely many primes"): 139 output tokens, still no reasoning field in the
--jsonoutput, still noagent_reasoningitem events in the stream.Where the data already lives
Codex does record
reasoning_output_tokens— but only to the on-disk session rollout (~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl), inside anevent_msgwithpayload.type == "token_count":{ "timestamp": "2026-04-19T11:23:41.622Z", "type": "event_msg", "payload": { "type": "token_count", "info": { "total_token_usage": { "input_tokens": 31751, "cached_input_tokens": 14720, "output_tokens": 2367, "reasoning_output_tokens": 413, "total_tokens": 34118 }, "last_token_usage": { "…": "same shape" }, "model_context_window": 258400 }, "rate_limits": { "…": "…" } } }Two reasons this isn't a workable consumer path:
--ephemeral. Tools that intentionally avoid writing per-request session state to the user's home directory cannot access it at all.Requested change
Add
reasoning_output_tokens(and ideally the othertotal_token_usagefields —total_tokens,model_context_window, rate-limit info) to theturn.completed.usagepayload on--json, so the stream is the complete source of truth for the turn.A minimal, backward-compatible version:
{ "type": "turn.completed", "usage": { "input_tokens": 32161, "cached_input_tokens": 1920, "output_tokens": 47, "reasoning_output_tokens": 41 } }Since the field is additive and optional, existing consumers are unaffected.
Why it matters
Consumers surfacing per-turn cost or context-fill to end users need the reasoning share to avoid misrepresenting output spend. A one-word reply that costs 22 output tokens with 21 reasoning is not meaningfully "22 tokens of output" to the user — it's "1 visible, 21 reasoning." The former reads as a buggy totals display.
Concrete consumer: I'm building an AI gateway that exposes ChatGPT Pro models through an OpenAI-compatible API. I want to pass the reasoning split through to clients (already present in OpenAI's native chat-completions response as
usage.completion_tokens_details.reasoning_tokens). Today I have to either drop--ephemeraland parse rollout files back (stateful, race-prone, filesystem pollution) or ship a seam that never populates.Environment
codex-cli 0.121.0(installed via Homebrew on macOS)