codex exec --ephemeral --json: expose provider-reported model ID on turn.completed
What version of Codex CLI is running?
codex-cli 0.147.0
What platform is your computer?
macOS 26.5.2 (arm64)
Authentication mode
ChatGPT authentication
Feature request
Please expose the provider-reported response model in the JSONL emitted by codex exec --json, including when the invocation is --ephemeral.
The proposed additive field is:
{
"type": "turn.completed",
"usage": {
"input_tokens": 123,
"cached_input_tokens": 45,
"output_tokens": 67
},
"server_model": "provider-reported-model-id"
}
server_model should be copied from provider response metadata for that completed turn. It must not be inferred or backfilled from --model, local configuration, alias resolution, the startup banner, rollout context, a generic model field, or model-authored output.
Why this is needed
I am building a governed evaluation harness around codex exec --ephemeral --json. The requested model and configured/resolved model are useful diagnostics, but they do not independently establish which model identifier the provider returned for a turn. That prevents a pinned-model evaluation from detecting silent rerouting or failing closed when observed identity is unavailable.
The current ephemeral JSONL includes lifecycle, item, completion, and detailed usage events, but the runs I inspected did not include provider-response model identity. Dropping --ephemeral to inspect persisted rollout context would weaken the sealed-task non-persistence boundary, and rollout/banner values still describe configured or resolved state rather than the provider response.
This is the codex exec JSONL counterpart to #33880, which requests provider-reported response model data for app-server v2.
Minimal reproduction shape
No production prompt is needed:
codex exec \
--ephemeral \
--json \
--model PINNED_MODEL_ID \
--sandbox read-only \
"Reply with exactly OK."
The terminal turn.completed event reports usage but not the provider-returned model identifier.
Requested semantics
- Type: a non-empty string when present.
- Provenance: provider response metadata for the completed turn.
- Representation: the provider-returned identifier, not a client-side alias substitution.
- Missing metadata: omit or emit
null; never fall back to the requested/configured model. - Streaming: retain the value through aggregation and emit it on the terminal turn event.
- Conflict: if one logical turn observes multiple distinct non-empty provider model IDs, emit an explicit conflict/error rather than choosing one silently.
- Ephemeral parity: emit the field with
--ephemeralwithout persisting a rollout. - Privacy: do not add prompts, responses, auth material, headers, account identifiers, or a telemetry requirement.
An adjacent server_provider field could be useful, but it is not a substitute for server_model.
Acceptance cases
| Case | Requested | Provider metadata | Required result |
|---|---|---|---|
| Match | model-a | model-a | server_model: "model-a" |
| Reroute | model-a | model-b | server_model: "model-b" |
| Missing | model-a | absent | omitted/null, never model-a |
| Conflict | model-a | model-a, then model-b | explicit conflict/error |
| Alias | alias-a | dated-model-a | server_model: "dated-model-a" |
| Ephemeral | any | present | field emitted; no rollout persisted |
Existing turn.completed.usage values should remain unchanged, and consumers that ignore unknown fields should continue to work.
Assurance limit
A provider-reported identifier is still a provider assertion, not cryptographic weight attestation. The goal is narrower: make the served identifier observable independently of the client-side request so evaluation tooling can detect a requested-versus-served mismatch and fail closed when the value is absent.
1 Comment
Follow-up reproduction: a single public
codex exec --ephemeral --jsoninvocation on Codex CLI 0.147.0 completed and emittedturn.completed, but that event still contained noserver_model. The consumer therefore left observed identity unavailable and failed closed rather than substituting the requested/configured model. There was no retry, and no raw log, session identifier, or attachment is included.