Depends on #728 (structured engine adapters).
Problem
Codex sessions run through runOneShot() in packages/browser-runner/src/coding/headless.ts with mode = "raw": we spawn codex exec --sandbox danger-full-access "<turn>" and capture stdout raw. Consequences, all structural:
Codex supports structured output
codex exec (the non-interactive form we already invoke — no TTY, no tmux) supports --json, which streams a JSONL event feed on stdout: thread/turn/item events including agent messages, reasoning, command executions, and token usage on turn completion. This is the direct analogue of Claude's --output-format stream-json. (--output-last-message <file> is also available for just the final message.)
Proposed fix
Implement a Codex adapter on the #728 abstraction:
Acceptance
Refs: #728, #556, #594, #545, #674
Depends on #728 (structured engine adapters).
Problem
Codex sessions run through
runOneShot()inpackages/browser-runner/src/coding/headless.tswithmode = "raw": we spawncodex exec --sandbox danger-full-access "<turn>"and capture stdout raw. Consequences, all structural:exec/curl/tool line in the transcript, then clipped by the transcript-line cap → 8000-char snapshot → 400-charcoding_timelinetail. Asking the agent to "be concise" cannot fix it — the loss is at the capture layer, and even a 3-line answer arrives buried in command chrome. (Observed live on the FIS coder session.)takeUsage()returns[]for raw engines, so Codex spend never reaches the ledger — this is A Codex coding session is completely invisible — unmetered like a tmux drive, but unlike a tmux drive the absence is never recorded, and classifyEngineMetering has zero production callers #556.takeActs()returns[], so a Codexgh pr merge/push is invisible to supervision (acts[].ok is documented in two supervision legends and absent from the payload; delegation budgets can enter 'exhausted' with no exit #594).Codex supports structured output
codex exec(the non-interactive form we already invoke — no TTY, no tmux) supports--json, which streams a JSONL event feed on stdout: thread/turn/item events including agent messages, reasoning, command executions, and token usage on turn completion. This is the direct analogue of Claude's--output-format stream-json. (--output-last-message <file>is also available for just the final message.)Proposed fix
Implement a Codex adapter on the #728 abstraction:
--jsonto the Codexexeccommand (update thecodexentry inhandlers.tscliCommandand the cloudDEFAULT_ENGINESpreset inworkers/api/src/lib/coding-engines.ts).turnEnd+ a usage record; error/turn-failed →error+ turn report.pendingUsage/pendingActs/turnReportpipeline Claude uses, so metering (A Codex coding session is completely invisible — unmetered like a tmux drive, but unlike a tmux drive the absence is never recorded, and classifyEngineMetering has zero production callers #556), acts (acts[].ok is documented in two supervision legends and absent from the payload; delegation budgets can enter 'exhausted' with no exit #594) and the turn report ([bug] Every Codex turn exited 1 and the session still reported alive/ready/idle — a non-zero engine exit is a line of prose in the pane and nothing else #545) light up for Codex with no further work.Acceptance
codex exec --jsonoutput; the transcript shows the assistant's message isolated from command execution lines.takeUsage()record (verify Engine spend appears on the Usage page — closes the Codex half of A Codex coding session is completely invisible — unmetered like a tmux drive, but unlike a tmux drive the absence is never recorded, and classifyEngineMetering has zero production callers #556).gh pr/push is attributed viatakeActs().--json/experimental — assert with a captured fixture, don't assume field names).--jsonsupport fall back to the generic raw adapter, unchanged.Refs: #728, #556, #594, #545, #674