codex exec --ephemeral --json: expose provider-reported model ID on turn.completed
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the codex exec --json entry point and trace provider response metadata through streaming aggregation to the terminal turn.completed event, comparing ephemeral and persisted execution paths. Done means provider-reported server_model is emitted without fallback, missing metadata is handled safely, conflicts are explicit, and existing usage and ephemeral non-persistence behavior remain unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100