Repository navigation
feat: structured claude agents in labs, driven through their stream instead of their screen - #76
Merged
Merged
Conversation
…nstead of their screen With Settings › Labs › Structured Claude agents on, a new Claude Code agent runs as `claude -p` speaking stream-json under `berthd agent-host`, and the new Claude driver (internal/box/claudedriver.go) does everything the terminal path did by screen and keys from the stream: - the conversation is read from the host's log with the same Claude parser (internal/transcript/host.go), so tool details, older pages and questions read as before, and the reply streams into the chat as a draft - prompts, interrupt, permission mode and question answers go in as JSON lines and control requests - each permission comes to the chat with Allow / Always allow / Deny (ask.choices). "Bypass" is berthd allowing them, so it also works as root; questions and plans always wait for a person - turn state comes from the stream, and berthd's own hooks stay quiet in these sessions (BERTH_DRIVER=structured), so nothing is counted twice - berthd restarting reads the log back and picks up where it was Sessions and tasks take driver: "claude" and mode on boxes with the driver.claude capability. Structured sessions open as conversations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
sean-brydon
added a commit
that referenced
this pull request
Oct 13, 2026
…they do in order - a structured session started again (resumed, or back from Claude Code's terminal) puts its own agent's CLI on PATH, not Claude Code's: on Linux, where a login shell resets PATH, a resumed Codex or ACP agent wasn't found - the terminal hand-off's mark is "size path": tmux on Linux shows a control character in an option escaped, so the turns typed in the terminal weren't read back into the chat - an answered permission says the agent works again when the answer is read back from its log, so it comes before anything the agent says after it; said at once, it could land after the turn's end and leave it running - a driver keeps following a session while a host answers on its socket: a host started again listens a moment before its log says so These are the failures in the Go job since #76, reproduced on Linux. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 13, 2026
cursor Bot
pushed a commit
to edimitchel/berthd
that referenced
this pull request
Oct 13, 2026
…they do in order - a structured session started again (resumed, or back from Claude Code's terminal) puts its own agent's CLI on PATH, not Claude Code's: on Linux, where a login shell resets PATH, a resumed Codex or ACP agent wasn't found - the terminal hand-off's mark is "size path": tmux on Linux shows a control character in an option escaped, so the turns typed in the terminal weren't read back into the chat - an answered permission says the agent works again when the answer is read back from its log, so it comes before anything the agent says after it; said at once, it could land after the turn's end and leave it running - a driver keeps following a session while a host answers on its socket: a host started again listens a moment before its log says so These are the failures in the Go job since cosscom#76, reproduced on Linux. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 3 of
design/structured-agents/plan.md, behind Labs and off by default: Settings › Labs › Structured Claude agents.What a structured agent is
claude -p --input-format stream-json --output-format stream-json --include-partial-messages --replay-user-messages --permission-mode <mode> --permission-prompt-tool stdio, run underberthd agent-host(#75) in the session's tmux pane. The session carries@berth_driver=claude, its log folder and its socket. The new Claude driver (internal/box/claudedriver.go) follows the host's log and does everything the terminal path did by screen and keys from the stream instead:~/.claude/projects/*.jsonl, guessing which file belongs to the sessioninternal/transcript/host.go); tool details and older pages read it toostream_eventtext deltas{"type":"user",…}lineinterruptcontrol request, then wait forresultset_permission_modecan_use_toolrequest; the session'sask.choicesdrive the same Allow / Always / Deny buttonsupdatedInput.answersBERTH_DRIVER=structured)Bypass is berthd allowing every
can_use_tool(Claude itself runs indefault), so it works where Claude refuses its own bypass, such as when run as root. AskUserQuestion and ExitPlanMode always wait for a person. "Always allow" keeps Claude's suggested rule to the session, not the project's settings file.Restarts: a driver that starts again reads the log back silently, reconciles the turn ledger (and allows, in bypass, anything asked while it was away), then attaches live. It follows a session from its start and, after a restart, from the first session list.
API / app
POST sessionsandPOST taskstakedriver: "claude"andmode, on boxes listingdriver.claude.Askgainschoices.DrivergainsRecord(older pages) andDraft.startWorkpasses the driver for Claude picks. Structured sessions open as conversations.useAskusesask.choiceswhen present, so nothing is read off a screen.Not in this PR (planned)
The terminal hand-off (
claude --resumein the pane and back), the SQLite conversation index and resume (phase 4), the Codex driver (phase 5) and ACP (phase 6).Tests
TestAStructuredClaudeSessionIsDrivenThroughItsStream: a stand-inclaudespeaking the spike's recorded stream-json (python3), under this test binary as agent host. It covers the first prompt, turn state with no hooks, the agent's session id, a permission with choices answered by key, bypass, a question answered from the chat, draft plus interrupt, and a fresh driver reading the log back and driving on. It passed 5/5 under-race.TestALiveClaudeSessionWorks(opt-inBERTH_LIVE_CLAUDE=1): real Claude Code (haiku), a reply, then a Bash command allowed by bypass. It passed locally in 4s.TestAStructuredSessionsLogReadsAsItsConversationuses a recorded, anonymised log (internal/transcript/testdata/claude_host_ask.jsonl).BERTH_DRIVER=structured.go test -race ./...,go vet ./..., apptsc,typecheck:plugins,check:titles/csp/themes,pnpm test,pnpm build,tsc -p e2eand the docs-site build all pass.🤖 Generated with Claude Code