跳转到内容

Herdr for v0 cloud orchestrator

Yes — Herdr can satisfy the locked v0 plan. Confidence: high on session ownership, workspace/agent create, live status, and observe-only streaming; medium on clean DB↔Herdr create/rollback edge cases and durable “agent id” semantics (Herdr’s live targets are pane ids / names, not a separate UUID).

Herdr is the right runtime layer: do not build a custom PTY manager; do not adopt herdr-web (separate Go stack that owns its own PTYs). Own a thin observe bridge + coordinator.


From Cloud agent orchestrator (locked 2026-09-26):

LockIntent
Herdr owns sessionsStatus + I/O API; no DIY PTY
Our layerLocal coordinator + SQLite + Web UI
BridgeObserve-only: herdr observe → WebSocket → Ghostty Web; no web PTY control; no web prompt steer (CLI/TUI); not herdr-web
SQLiteprojects + runs only (no events / shared_context)
projectsid, name, path, herdr_workspace_id, created_at
runsid, project_id, herdr_agent_id, started_at — no status column; live state from Herdr
Create lifecycleInsert DB → create Herdr → write id back; on Herdr failure, delete DB row
Layout1 workspace ↔ 1 project; 1 agent ↔ 1 pane; multiple runs = multiple panes in that workspace

Sources: Socket API, CLI reference, Agent automation, Persistence and remote access, Quick start. Repo: herdrdev/herdr.

v0 needHerdr surfaceFit
Session ownership (no custom PTY)Background server; panes keep running across detach; CLI + local socket APIYes
Live agent status (not in SQLite)agent.list / agent.get; statuses idle / working / blocked / done / unknown; events.subscribe for pane.agent_status_changedYes
Workspace create (project → workspace)workspace.create / herdr workspace create [--cwd] [--label] [--no-focus]; returns .result.workspace.workspace_id, .result.tab, .result.root_pane.pane_idYes
Close / cleanup workspaceworkspace.close / herdr workspace closeYes
Pane for each runWorkspace create yields root pane; further runs via pane.split (direction right|down, optional --ratio)Yes
Agent start (run → agent)agent.start / herdr agent start NAME --kind KIND --pane ID; requires existing available shell pane; does not create layoutYes (multi-step)
Prompt / wait (CLI/TUI, not web)agent.prompt, agent.wait, agent.read, agent.send-keysYes (out of web UI for v0)
Observe-only stream for Ghostty Webherdr terminal session observe <pane|term|agent> [--cols N] [--rows N] → NDJSON terminal.frame (base64 ANSI) + terminal.closed; multiple observers; no input/resize/scroll/takeoverYes
Writable web control (explicitly out of v0)herdr terminal session control (stdin JSON terminal.input / resize / scroll / release)Available but do not use in v0 web
Snapshot / reconnect bootstrapsession.snapshot / herdr api snapshot; pair with events.subscribe to avoid gapsYes (useful for coordinator cache of ids/layout, not status store)
Schema introspectionherdr api schema --jsonYes
herdr-web as bridgeSeparate product; owns PTYs via termhost + libghostty-vtNo — avoid (matches lock)

Herdr does not document a durable UUID “agent id” independent of layout. Automation targets are:

  • Pane id (e.g. w1:p1) — location of the terminal; survives rename; changes on cross-workspace pane.move
  • Agent name (e.g. reviewer) — live alias required by agent start; cleared when the agent exits / is released / is replaced; must match [a-z][a-z0-9_-]{0,31}

Recommendation for the runs.herdr_agent_id column: store the pane id as the durable join key for observe + status (agent get <pane_id>), and treat the agent name as an optional live alias (or derive a stable name from the run id, e.g. run_<short>). Do not assume the name alone survives agent death.

Workspace side is clean: store .result.workspace.workspace_id in projects.herdr_workspace_id.


workspace.create is first-class (socket + CLI). Creating a workspace also creates first tab + root pane — good for “first run uses root pane; later runs split.” Capture ids from JSON; do not invent them (Agent automation).

Rollback when Herdr fails before create: delete DB project row — matches lock.
Orphan Herdr after success + crash before write-back: workspace.list / session.snapshot reconcile; GC unmatched workspaces. Document a startup reconcile pass.

2. Agent start APIs — medium risk (multi-step)

Section titled “2. Agent start APIs — medium risk (multi-step)”

agent start never creates panes. Run create is at least:

  1. Insert runs row
  2. Ensure pane (reuse workspace root for run #1, else pane.split)
  3. agent start --kind … --pane …
  4. Write pane id (and optional name) back to runs.herdr_agent_id

Failure modes vs the simple “insert → create → write / rollback delete” story:

FailureDB rowHerdr residueSuggested handling
Split failsDelete runNoneOK
Split OK, agent start fails / times outDelete runEmpty shell paneClose the new pane (pane.close) if it was created for this run; if it was the shared root pane, leave it
agent start returns agent_not_ready (blocked at startup)Name may still exist for read/send-keysAgent present but not prompt-readyTreat as partial success or retry after idle; do not blindly delete
Crash after Herdr success, before DB write-backMissing / incomplete runLive agent + paneReconcile via agent.list / pane list; attach or GC

Also: pane must be at interactive shell prompt before agent start. Coordinator should not leave foreground junk in the pane.

Default start timeout 30s (3s–300s configurable). Integrations improve detection accuracy (herdr integration install …).

3. Observe streaming → Ghostty Web — low–medium risk (build, not capability)

Section titled “3. Observe streaming → Ghostty Web — low–medium risk (build, not capability)”

Official observe path matches the lock exactly (Persistence and remote):

Terminal window
herdr terminal session observe w1:p1 --cols 120 --rows 40
# NDJSON: terminal.frame { base64 ANSI } … terminal.closed

Coordinator work:

  • Spawn observe per visible pane; decode frames; push bytes over WebSocket to a browser Ghostty/WASM terminal (e.g. coder/ghostty-web or libghostty-vt WASM). Write path only in the browser — ignore / disable onData for v0.
  • Pass --cols/--rows from the web viewport so the rendered surface matches; observe does not take resize ownership (control mode does — out of scope).
  • Stalled observer: on Linux/macOS, a socket write with no progress for 30s disconnects the observer; may end without terminal.closed; pane keeps running. Bridge must reconnect.
  • Do not use terminal session control or herdr-web for v0.

Community precedent for browser ANSI over a relay: herdr-outpost (polls herdr agent list, streams ANSI). It also exposes approve/prompt from the browser — richer than v0; steal observe/relay patterns only, not web steer.

CaseRiskMitigation
DB insert, Herdr create failsLowDelete DB row (locked)
Herdr create OK, DB update failsMediumReconcile + GC or retry attach by listing Herdr
Run multi-step: pane without agentMediumOn failed start, pane.close if pane was run-scoped
Agent exits; name cleared; DB still has runExpectedStatus from Herdr shows gone/unknown; keep historical run row; observe may hit terminal.closed
Herdr server wipe / new sessionHigh for id validityNamed sessions; detect missing workspace/pane; mark runs inactive or recreate
Cross-workspace pane.moveLow if avoidedPane id changes; v0 layout should keep panes in the project workspace

“No orphan Herdr without DB” is aspirational under crash: you need a reconcile job. “No lingering null-id rows” is enforceable in the happy path by transactional insert → create → update, delete-on-fail.

1 workspace ↔ 1 project maps cleanly to Herdr workspaces. Multiple agents = multiple panes via splits. Risk: many side-by-side splits clip columns. Mitigations within Herdr (no custom PTY):

  • Prefer balanced pane.split ratios; or new tabs inside the same workspace for overflow (still one workspace ↔ one project)
  • pane.zoom for focus-one-pane viewing in the native TUI; web UI can show one Ghostty surface per selected run
  • Avoid packing many agents into one tiny grid in the web view — one observe stream per selected pane is enough for v0

6. Explicitly out of scope (confirm non-blockers)

Section titled “6. Explicitly out of scope (confirm non-blockers)”
  • Personal-site pulse, Nix packaging, computer-use e2e — not required for Herdr fit
  • Herdr Cloud waitlist = connectivity relay, not hosted agents (landscape digest) — irrelevant to v0 local coordinator
  • Web prompt steer — defer; use CLI/TUI agent prompt / Herdr TUI

  1. Proceed with Herdr as the session runtime. Fits the locked architecture.
  2. Build: local coordinator (CLI wrappers or socket client) + SQLite (projects/runs) + Web UI that only observes via herdr terminal session observe → WebSocket → Ghostty Web.
  3. Steer only from CLI/TUI (agent prompt / Herdr attach). Do not wire web input to terminal session control in v0.
  4. Map ids: herdr_workspace_id ← workspace id; herdr_agent_id ← pane id (plus optional live agent name).
  5. Create flows:
    • Project: insert → workspace create --cwd <path> --label … --no-focus → write workspace id (optionally record root pane for run #1).
    • Run: insert → ensure pane → agent start → write pane id; on failure delete run and close run-scoped pane.
  6. Add a reconcile/GC on startup: list Herdr workspaces/agents vs SQLite; close or attach orphans; clear stale ids.
  7. Do not use herdr-web, do not cache status in SQLite, do not expand schema to events/shared_context in v0.


Method: WebSearch, WebFetch, GitHub MCP (search_repositories), vault Read. Did not use Codex. Did not fabricate APIs; checklist items above are cited from current Herdr docs.

Confidence notes: Docs describe observe/control NDJSON and workspace/agent create clearly. Exact JSON field names inside .result.agent beyond the documented return path were not re-verified against a live herdr api schema dump on this box (Herdr may not be installed here) — use herdr api schema --json at implement time for the agent object shape.