Skip to content

feat: a structured claude agent opens in claude code's own terminal and comes back - #77

Merged
sean-brydon merged 1 commit into
mainfrom
switch-view
Oct 13, 2026
Merged

sean-brydon merged 1 commit into
mainfrom
switch-view

Conversation

@sean-brydon

Copy link
Copy Markdown
Collaborator

Phase 4a of design/structured-agents/plan.md (Labs, structured Claude agents only).

What it does

POST /v1/sessions/{name}/switch with {"to": "terminal" | "chat"} (capability driver.switch) moves an idle structured session between its chat and Claude Code's own screen. It keeps the same conversation and runs one process at a time:

  • To the terminal:
    • note where Claude's transcript stands
    • agenthost.Stop (Claude ends at its stdin's end)
    • respawn-pane -k as claude --resume <id>, with BERTH_DRIVER cleared so berthd's hooks drive the turn ledger again
    • bypass carries over as --permission-mode bypassPermissions unless running as root
    • beginStartup watches for Claude's startup questions
  • Back to the chat:
    • /exit is typed as keys, then wait for the pane to end
    • copy what the TUI added to the transcript into berthd's log as out entries, so the chat reads on in order
    • respawn the host with --resume on the same log and socket, and wait for it to listen
    • also works from a TUI that was already left (/exit, or "No, exit" at the trust question)
  • The session keeps @berth_conv/@berth_sock throughout. Session.switch says where it can go: terminal while structured, chat while in its terminal.
  • App: for these sessions, the pane's existing Terminal | Conversation toggle performs the switch (tips: "Open in Claude Code's terminal" and "Back to the chat") and shows a toast on refusal.

Fixes in the Claude driver found while testing

  • Events went out from a goroutine each, so finished could overtake started. They now go through one ordered queue per session.
  • A prompt's started could reach the ledger before session.sent opened its turn, leaving the turn stuck running. The driver now waits briefly (Turns.AwaitSent).
  • A write to a host that is still starting (just launched, or just back from the terminal) now waits up to 10s instead of reporting the session as exited.
  • A host starting again clears the follower's "exited" mark.

Tests

  • TestAStructuredSessionGoesToItsTerminalAndBack, with a stand-in that also plays Claude's TUI when run without -p:
    • structured → terminal (driver terminal, switch chat, its screen shows the resumed session)
    • a second switch to the terminal is a 409
    • → chat: the turn typed in the terminal is read in its place, and a new prompt is answered
    • the conversation reads echo: hello | answered in the terminal | echo: back again
    • it passed 5/5 under -race, together with the structured-session test
  • TestALiveClaudeSessionWorks (opt-in BERTH_LIVE_CLAUDE=1) now also switches real Claude Code to its terminal (answering its trust question for the temporary folder) and back, and Claude still remembers the first reply. It passed locally in 16s.
  • Full go test -race ./..., go vet ./..., app checks and build, tsc -p e2e and the docs build all pass.

Next (4b)

Resuming a structured conversation after its session is gone, and the SQLite conversation index that lists them.

🤖 Generated with Claude Code

…nd comes back

POST /v1/sessions/{name}/switch {"to":"terminal"|"chat"} (driver.switch)
moves an idle structured session between its chat and Claude Code's own
screen, one process at a time, on the same conversation:

- to the terminal: the host stops Claude, and the pane is respawned as
  `claude --resume <id>`, with berthd's hooks back on and the session's
  bypass carried over as --permission-mode where Claude allows it
- back to the chat: Claude Code is told /exit, the turns typed there are
  copied from its transcript into berthd's log, and the host starts again
  with --resume on the same log and socket. It works from a terminal that
  was already left, too

The pane's Terminal | Conversation switch does it for these sessions.

Also: the Claude driver says what it reads in order, from one queue per
session, and a prompt's start waits for the send that opened its turn. A
write waits for a host that is still starting.

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 12:42pm UTC
berth-docs Ready Ready Preview Oct 13, 2026 12:42pm UTC

Request Review

@sean-brydon
sean-brydon merged commit 94886c9 into main Oct 13, 2026
7 of 8 checks passed
@linear-code

linear-code Bot commented Oct 13, 2026

Copy link
Copy Markdown

ENG-6004

This branch was successfully deployed

2 active deployments
Preview – berth-docs — 3fce9c00 Deployed Oct 13, 2026 by vercel[bot]
Preview – berth — 3fce9c00 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