Skip to content

feat: structured codex agents in labs, driven through codex's app server - #79

Merged
sean-brydon merged 1 commit into
resume-conversationsfrom
codex-driver
Oct 13, 2026
Merged

sean-brydon merged 1 commit into
resume-conversationsfrom
codex-driver

Conversation

@sean-brydon

Copy link
Copy Markdown
Collaborator

Stacked on #78 (base resume-conversations). Merge #78 first, and GitHub retargets this PR to main.

Phase 5 of design/structured-agents/plan.md: the Codex driver, behind the same Labs switch, now called Structured agents.

Driver (internal/box/codexdriver.go)

codex app-server runs under berthd agent-host, and berthd speaks its JSON-RPC (shapes from the spike's recordings and the generated schema):

  • Handshake: initialize / initialized, then thread/start (cwd, approval policy, sandbox, model), or thread/resume with the conversation's thread id (from meta.json or the log) when carried on. It runs again whenever its host starts.

  • Turns: each prompt is turn/start, carrying the session's mode, model and effort. Modes:

    Mode Approval policy Sandbox
    default on-request workspace-write
    untrusted untrusted workspace-write
    readOnly on-request read-only
    bypassPermissions never danger-full-access

    SetMode takes effect from the next turn. Interrupt is turn/interrupt.

  • Server requests:

    • item/commandExecution/requestApproval and item/fileChange/requestApproval produce ask.choices and the usual Allow (accept) / Always allow (acceptForSession) / Deny (decline) buttons.
    • item/tool/requestUserInput becomes a question card, answered via /answer.
    • Anything else gets a JSON-RPC error, so a turn never hangs.
  • Turn state: item/started userMessage (after AwaitSent), approvals, serverRequest/resolved and turn/completed (interrupted / failed). Codex's own berthd hooks stay quiet (BERTH_DRIVER).

  • Draft: from item/agentMessage/delta.

  • Conversation: read from the host's log by a new parser (internal/transcript/codexhost.go) that maps app-server items onto the existing conversation helpers: user messages, replies, commands as Run/Read/Search, file changes as edits, MCP calls, and questions with their answers. Detail gives a command and its output.

  • Resume and the conversation list (feat: a structured claude agent's conversation can be resumed, after its agent ends or its session is gone #78) work for Codex conversations: relaunch chooses the agent from meta.json. Codex has no terminal switch (switch stays empty).

App

The Labs switch is now Structured agents and covers Codex picks on boxes listing driver.codex. Structured Codex sessions open as conversations. Resumed panes show the right agent.

Tests

  • TestAStructuredCodexSessionIsDrivenThroughItsAppServer, with a stand-in app server (python3) following the recorded protocol:
    • the thread opens and its id reaches the ledger
    • an approval with choices is answered by key
    • bypass carries approvalPolicy: never so nothing is asked
    • a question is answered from the chat
    • draft, then interrupt
    • the host stops, the session resumes onto the same thread, and it comes back in bypass
  • All structured tests passed 4/4 under -race.
  • TestAStructuredCodexSessionsLogReadsAsItsConversation uses a recorded, anonymised app-server log, plus a command's details.
  • TestALiveCodexSessionWorks (opt-in BERTH_LIVE_CODEX=1, BERTH_LIVE_CODEX_MODEL): real Codex replies, then runs a shell command under bypass. It passed locally in 9.6s.
  • Full go test -race ./..., go vet ./..., app checks and build, tsc -p e2e and the docs build all pass.

Next: phase 6, the generic ACP driver (opencode, Gemini, Cursor).

🤖 Generated with Claude Code

With Labs › Structured agents on (it covered Claude Code alone before), a
new Codex agent runs as `codex app-server` under `berthd agent-host`, and
the new Codex driver (internal/box/codexdriver.go) speaks its JSON-RPC:

- it opens the thread (initialize, thread/start, or thread/resume with the
  conversation's thread id when it is carried on)
- prompts go in as turn/start, carrying the session's mode (approval
  policy and sandbox), model and effort; interrupt is turn/interrupt
- command and file-change approvals come to the chat as Allow / Always
  allow / Deny, Codex's questions as question cards, and anything else
  Codex asks gets an error so no turn waits for ever
- turn state comes from turn and item notifications, the draft from
  agentMessage deltas, and berthd's own Codex hooks stay quiet
- the conversation is read from the host's log
  (internal/transcript/codexhost.go), tool details included
- resume and the conversation list work for Codex conversations as for
  Claude's

Boxes list driver.codex; sessions and tasks take driver: "codex".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
berth Ready Ready Preview Oct 13, 2026 1:09pm UTC
berth-docs Ready Ready Preview Oct 13, 2026 1:09pm UTC

Request Review

@sean-brydon
sean-brydon merged commit 2ecdfd8 into resume-conversations Oct 13, 2026
7 of 8 checks passed
@linear-code

linear-code Bot commented Oct 13, 2026

Copy link
Copy Markdown

ENG-6007

sean-brydon added a commit that referenced this pull request Oct 13, 2026
feat: structured codex and acp agents (lands #79 and #80 on main)

This branch was successfully deployed

2 active deployments
Preview – berth-docs — b75e73a5 Deployed Oct 13, 2026 by vercel[bot]
Preview – berth — b75e73a5 Deployed Oct 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant