feat: Codex first-class harness adapter - #124
Conversation
Codex panes get the Claude-level treatment: the boxless chrome stripped and re-surfaced natively, plus native buttons for the three probed dialogs. Registered by exact agent string only (the AltanS#99 bar): adapterFor stays Object.hasOwn over adapter.agent; variant spellings stay raw. Every keystroke the adapter emits comes from a dated live probe recorded in the notes files, against a byte-faithful fixture corpus (identifying strings length-preservingly genericized — public repo), with describeAdapterConformance running codex against the claude and omp cohorts and vice versa. Chrome (Tier 1): Codex paints a bare `› ` prompt row that word-wraps onto indented continuation rows, a dot-separated status row beneath, and exactly one blank row between every section. The composer is located by the status row at the tail (the ` · Context N% left` token; everything before it is opaque — models and paths change) with the prompt row above it — a transcript echo of a submitted message paints the same `› ` prefix and never has the status row beneath it. The status row feeds the strip, the prompt row the draft (wrapped drafts rejoin with single spaces, verified against the typed original) and composerPrompt. Trust prompt: the exact captured two-option layout lifts as family trust. Digits confirm directly — 2 quit on the spot, 1 accepted through the guarded send path (both probed). Exec approval: classified, not layout-pinned — the first row must be the one-shot Yes, the last the reject, and every row between must prove itself persistent (`don't ask again …`); those never become buttons. An unclassified row refuses the whole card. Digits confirm directly: 1 ran the approved command, 3 rejected it with the command verified never run. The header, Reason and `$ command` rows stay in the raw mirror above the buttons. Only the exec approval is captured; other approval kinds fail closed until probed. Note the config dependency: approvals_reviewer = "auto_review" (the host default here) approves silently and the dialog never paints — probing used `-c approvals_reviewer=user`. request_user_input: the Question X/Y card lifts as family select, one digit per row — a digit answers the current question, advances a multi-question set, and submits on the last (all probed). The notes-box state refuses to raw (footer `tab or esc to clear notes`, or the `› Add notes` row): a digit there would type into the box. Esc interrupts the whole conversation and is never emitted. Live-verified end to end through the bridge on a real pane: trust accepted, a reply typed-verified-submitted through the new draft extraction, an approval Yes landing its command, and an ask answer registering in the tool result — every press through the guarded /keys endpoint with its region signature. Versions untouched per the fork-PR rule; CHANGELOG line in the PR body. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFH7jQVDVhPGC6FqLetNfd
|
Reviewed — this is a strong contribution: fail-closed, tail-anchored detectors, exact-label matching, conformance cohorts extended in all three directions, and the version files correctly left alone. All gates pass here (root 669, web 2709, both typechecks clean). Two changes requested before merge:
Two smaller notes, take or leave: On our side: the Tier-2 bar means we live-verify the dialog recipes against a real Codex pane before merge — that's on us, not you. Plan is to merge this PR first, then ask for a quick rebase of #123 (they overlap in six files, all trivially). |
An 8-row cap made locateComposer return null on a wrapped phone draft, so the composer went dark. Same 100 as the other adapters. Approval startLine swallowed the whole option run, hiding the persistent "don't ask again" row from both the buttons and the raw mirror. The block now starts at the reject so that row stays visible.
|
Review follow-up:
|
Summary
Codex CLI panes become first-class: the boxless chrome stripped and re-surfaced natively, and the three probed dialogs lifted into tappable buttons. Independent of the Grok adapter PR — cut straight from
main, no shared new model fields.web/src/lib/harness/codex/: Codex paints a bare›prompt row that word-wraps onto indented continuation rows, a dot-separated status row beneath, and exactly one blank row between sections. The composer is located by the status row at the tail (the· Context N% left|usedtoken behind two opaque·-separated fields — model names and paths are never matched) with the prompt row above; a transcript echo of a submitted message paints the same›prefix and never has the status row beneath it. Wrapped drafts rejoin with single spaces (verified against the typed original). Known safe limit, documented: a custom or disabledtui.status_linenever matches, so those configs fall back to the raw mirror with replies refused.Yes, proceed, the last exactlyNo, and tell Codex what to do differently(post-shortcut-strip), and every row between must prove itself persistent (don't ask again …) — those never become buttons; any unclassified or suffix-altered row refuses the whole card. Digits probed with negative controls: 1 ran the approved command, 3 rejected it with the command verified never run on disk (Codex still paints a• Ran <cmd>transcript row for the cancelled call — documented rendering quirk). Only the exec approval is captured; patch/MCP approvals have different headers and fail closed until probed. Config note: withapprovals_reviewer = "auto_review"eligible requests route through a reviewer subagent and no dialog paints — captures used-c approvals_reviewer=user.Question X/Ycards lift one digit per row; a digit answers the current question, advances a multi-question set, and submits on the last (all probed, including the auto-addedNone of the aboverow). The notes-box state refuses to raw (tab or esc to clear notesfooter or the› Add notesrow): a digit there would type into the box. Esc interrupts the whole conversation and is never emitted.The #99 bar
agent: "codex"); variant spellings pinned raw in the registry test.codex--*.txt, Codex v0.149.0), three notes files with the probe tables,describeAdapterConformancerunning codex against the claude+omp cohorts and vice versa./keyspath with its region signature.Privacy
The corpus is sanitized with length-preserving substitutions (row padding stays byte-identical): username, hostname, the Darwin per-user temp-dir token, and the Codex session UUIDs from
codex resumelines.Suggested CHANGELOG entry
Codex CLI gets a first-class harness adapter — strips the boxless composer chrome, re-surfaces the status row, and lifts the folder-trust prompt, exec approvals and request_user_input question cards into native buttons.
Verification
cd web && bun run typecheckclean; full web suite + conformance green;bun test bridge/green; live pass as above. Versions untouched at 0.32.0 per the fork-PR rule.🤖 Generated with Claude Code
https://claude.ai/code/session_01PFH7jQVDVhPGC6FqLetNfd