Socket API
Herdr exposes a local socket API for scripts and agents that need to inspect or control a running session.
Most automation should start with the CLI wrappers. Use the raw socket API only when you need direct request/response control or long-lived event subscriptions.
Choose an integration layer
Section titled “Choose an integration layer”| Layer | Use it for |
|---|---|
| Agent skill | Teaching a coding agent how to use Herdr from inside a pane. |
| CLI wrappers | Shell scripts, simple orchestration, and human debugging. |
| Raw socket API | Custom tools, protocol clients, and event subscribers. |
The layers share the same control surface.
What you can control
Section titled “What you can control”The socket API can:
- create, list, focus, rename, and close workspaces
- create, list, focus, rename, and close tabs
- list, inspect, split, swap, focus, resize, rename, read, close, and send input to panes
- list, inspect, read, send to, rename, focus, start, and attach agents through CLI helpers
- report custom agent state from hooks and plugins
- subscribe to events and wait for output or state changes
- install and uninstall built-in integrations
- stop the server and reload config
CLI examples
Section titled “CLI examples”Create a workspace:
herdr workspace create --cwd ~/project --label apiCreate a tab:
herdr tab create --label logsSplit a pane and run a command:
herdr pane split 1-1 --direction rightherdr pane run 1-2 "npm test"Inspect and rearrange panes:
herdr pane layout --currentherdr pane neighbor --direction right --currentherdr pane resize --direction right --amount 0.1 --currentherdr pane swap --direction right --currentherdr pane zoom --on --currentherdr pane split 1-1 --direction right --ratio 0.333Wait for an agent:
herdr wait agent-status 1-1 --status doneRead pane output:
herdr pane read 1-2 --source recent --lines 50Raw methods
Section titled “Raw methods”Raw socket method names use dot notation:
| Area | Methods |
|---|---|
| Server | ping, server.stop, server.reload_config, server.agent_manifests, server.reload_agent_manifests |
| Notification | notification.show |
| Workspace | workspace.create, workspace.list, workspace.get, workspace.focus, workspace.rename, workspace.close |
| Worktree | worktree.list, worktree.create, worktree.open, worktree.remove |
| Tab | tab.create, tab.list, tab.get, tab.focus, tab.rename, tab.close |
| Pane | pane.split, pane.swap, pane.zoom, pane.layout, pane.neighbor, pane.edges, pane.focus_direction, pane.resize, pane.list, pane.get, pane.rename, pane.send_text, pane.send_keys, pane.send_input, pane.read, pane.report_agent, pane.report_agent_session, pane.report_metadata, pane.clear_agent_authority, pane.release_agent, pane.close, pane.wait_for_output |
| Agent | agent.list, agent.get, agent.read, agent.explain, agent.send, agent.rename, agent.focus, agent.start |
| Events | events.subscribe, events.wait |
| Integrations | integration.install, integration.uninstall |
Some CLI commands are conveniences around these methods. For example, herdr agent wait resolves an agent target and then subscribes to pane agent state events.
Pane control methods use public pane ids such as 1-1. Omit pane_id to use
the server’s active focused pane.
{"id":"req_layout","method":"pane.layout","params":{"pane_id":"1-1"}}{"id":"req_neighbor","method":"pane.neighbor","params":{"pane_id":"1-1","direction":"right"}}{"id":"req_edges","method":"pane.edges","params":{"pane_id":"1-1"}}{"id":"req_focus","method":"pane.focus_direction","params":{"direction":"right"}}{"id":"req_resize","method":"pane.resize","params":{"pane_id":"1-1","direction":"right","amount":0.1}}{"id":"req_zoom","method":"pane.zoom","params":{"pane_id":"1-1","mode":"toggle"}}{"id":"req_split","method":"pane.split","params":{"direction":"right","ratio":0.333}}pane.layout returns the tab layout snapshot with workspace_id, tab_id,
zoomed, outer area, focused_pane_id, pane rects, and split rects/ratios.
pane.neighbor and pane.edges include that same layout snapshot so clients
can make the next decision without private layout state.
pane.swap supports directional and explicit forms:
{"id":"req_swap_dir","method":"pane.swap","params":{"pane_id":"1-1","direction":"right"}}{"id":"req_swap_explicit","method":"pane.swap","params":{"source_pane_id":"1-1","target_pane_id":"1-2"}}Swap is same-tab only. It preserves split shape, split ratios, pane ids, and
running processes. The response is type: "pane_swap" with changed, optional
reason, source_pane_id, optional target_pane_id, focused_pane_id, and
layout. Reason values are no_neighbor, same_pane, not_found, and
cross_tab. When a tab is zoomed, swap keeps zoom active and mutates the hidden
full-tab layout.
pane.zoom toggles, enables, or disables zoom for the target pane’s tab:
{"id":"req_zoom_toggle","method":"pane.zoom","params":{"pane_id":"1-1"}}{"id":"req_zoom_on","method":"pane.zoom","params":{"pane_id":"1-1","mode":"on"}}{"id":"req_zoom_off","method":"pane.zoom","params":{"pane_id":"1-1","mode":"off"}}Omitting pane_id targets the server’s active focused pane. The response is
type: "pane_zoom" with changed, zoom_changed, focus_changed, optional
reason, pane_id, focused_pane_id, zoomed, and layout. changed is
true when either zoom state or focus changed. Reason values are single_pane,
already_zoomed, and already_unzoomed.
The CLI wrapper for notification.show is:
herdr notification show "build failed" --body "api workspace" --position top-left --sound requestShow a user notification through the configured toast delivery:
{"id":"req_notify","method":"notification.show","params":{"title":"build failed","body":"api workspace","position":"top-left","sound":"request"}}title is required and must contain visible text after control characters and repeated whitespace are removed. body is optional. Herdr collapses newlines, tabs, carriage returns, and repeated whitespace into spaces, then trims notification text to 80 characters for title and 240 characters for body. An empty sanitized title returns invalid_params. position is optional and applies only when ui.toast.delivery = "herdr"; desktop positions are relative to the full Herdr frame, and omitted positions use ui.toast.herdr.position. Terminal, system, and off delivery ignore position. sound is optional and can be none, done, or request; it defaults to none and plays only when the notification is shown.
The response reports whether anything was shown:
{"id":"req_notify","result":{"type":"notification_show","shown":true,"reason":"shown"}}Possible reasons are shown, disabled, rate_limited, no_foreground_client, and busy. disabled means ui.toast.delivery = "off". busy means an existing in-app toast was not replaced. Terminal and system delivery are best-effort through the current foreground attached Herdr client.
Worktree methods manage Git checkouts as Herdr workspaces. worktree.create creates a checkout and returns the new workspace, tab, root_pane, and worktree records. worktree.open opens an existing checkout or returns the already-open workspace. worktree.remove runs git worktree remove against a linked child workspace and never deletes the branch.
Create a worktree from a source workspace:
{"id":"req_1","method":"worktree.create","params":{"workspace_id":"1","branch":"worktree/api","focus":false}}Open an existing checkout:
{"id":"req_2","method":"worktree.open","params":{"workspace_id":"1","branch":"worktree/api","focus":true}}Remove a linked checkout:
{"id":"req_3","method":"worktree.remove","params":{"workspace_id":"2","force":false}}Use at most one of workspace_id or cwd for worktree.list, worktree.create, and worktree.open; omit both to use the active workspace. Use exactly one of path or branch for worktree.open. Raw socket cwd and path values must be absolute; the CLI expands relative --cwd and --path values before sending requests. Workspace responses include optional worktree provenance when a workspace belongs to a Herdr worktree group. Worktree commands can emit workspace.updated when an existing workspace gains or changes worktree provenance.
Socket transport
Section titled “Socket transport”Herdr uses newline-delimited JSON over a Unix domain socket.
Send one request per line:
{"id":"req_1","method":"ping","params":{}}A successful response includes the same id:
{"id":"req_1","result":{"type":"pong"}}Event subscriptions keep the connection open after the initial response.
Socket paths
Section titled “Socket paths”The default socket lives under your Herdr config directory.
Named sessions have separate sockets:
~/.config/herdr/herdr.sock~/.config/herdr/sessions/<name>/herdr.sockResolution order:
- explicit CLI
--session <name> HERDR_SOCKET_PATHHERDR_SESSION=<name>- default session socket
Use HERDR_SOCKET_PATH only for low-level overrides.
Agent state reporting
Section titled “Agent state reporting”Integrations report agent state with pane.report_agent.
{ "id": "req_1", "method": "pane.report_agent", "params": { "pane_id": "1-1", "source": "custom:docs", "agent": "docs-bot", "state": "working", "message": "building docs", "custom_status": "indexing" }}state is semantic. It affects waits, notifications, and rollups.
custom_status is visual. It can show a short label like indexing without changing semantic behavior.
Session-only official integrations report native session references with pane.report_agent_session. State-reporting integrations can still include native session references in pane.report_agent. State-independent session reports do not affect waits, notifications, or rollups.
{ "id": "req_2", "method": "pane.report_agent_session", "params": { "pane_id": "1-1", "source": "herdr:codex", "agent": "codex", "agent_session_id": "..." }}pane.get, pane.list, agent.get, and agent.list expose a read-only agent_session object when Herdr has a stored native session reference:
{ "agent_session": { "source": "herdr:codex", "agent": "codex", "kind": "id", "value": "..." }}If no native session reference is stored, the field is omitted.
pane.get, pane.list, agent.get, and agent.list also expose foreground_cwd when Herdr can resolve the cwd of the process currently controlling the pane PTY. The existing cwd field remains the pane/workspace cwd used for labels, follow-cwd behavior, and restored session state.
Use pane.report_metadata when a user hook wants to customize presentation without taking over lifecycle state from a Herdr integration.
{ "id": "req_2", "method": "pane.report_metadata", "params": { "pane_id": "1-1", "source": "user:claude-title", "agent": "claude", "title": "Refactor auth middleware", "display_agent": "Claude: auth", "custom_status": "refactor auth", "state_labels": { "working": "refactoring auth", "idle": "ready", "done": "review ready" }, "ttl_ms": 3600000 }}Metadata reports are display-only. Valid metadata can override the pane title, displayed agent name, compact custom status, and visible state labels. working, blocked, idle, waits, notifications, and rollups still come from semantic state. Native session restore comes from stored official session references. agent is an optional guard for the authoritative agent label; applies_to_source is an optional guard for the active lifecycle authority source. Use display_agent to change the visible name. state_labels keys must be idle, working, blocked, done, or unknown. Use clear fields such as clear_custom_status: true with the same source to remove one presentation override.
Event subscriptions
Section titled “Event subscriptions”Subscribe to events when you need a long-lived stream:
{ "id": "sub_1", "method": "events.subscribe", "params": { "subscriptions": [ { "type": "pane.agent_status_changed", "pane_id": "1-1", "agent_status": "blocked" } ] }}The first response acknowledges the subscription. Later lines are pushed events.
Workspace event subscriptions include workspace.created, workspace.updated, workspace.renamed, workspace.closed, and workspace.focused.
Use events.wait when you want one matching event and then a response.
Reading panes
Section titled “Reading panes”Use pane.read through the CLI unless you are writing a protocol client.
herdr pane read 1-1 --source visible --lines 80herdr pane read 1-1 --source recent --lines 120herdr pane read 1-1 --source recent-unwrapped --lines 120herdr pane read 1-1 --source detectionrecent-unwrapped is useful for logs because it ignores soft wrapping.
detection returns the bottom-buffer snapshot used by agent screen detection.
Waiting for state
Section titled “Waiting for state”Use waits to coordinate agents and scripts.
herdr wait agent-status 1-1 --status doneherdr wait agent-status 1-1 --status blockedAgent waits observe semantic state, not arbitrary command completion.
Response shapes
Section titled “Response shapes”Successful responses look like this:
{ "id": "req_1", "result": { "type": "pane_info", "pane": { "pane_id": "1-1", "terminal_id": "term_abc123", "workspace_id": "1", "tab_id": "1-1", "focused": true, "agent_status": "working", "revision": 42 } }}server.agent_manifests returns the active agent detection manifest sources and remote update diagnostics without reloading rules:
{ "id": "req_1", "result": { "type": "agent_manifest_status", "last_check_unix": 1781043522, "last_result": "checked", "manifests": [ { "agent": "cursor", "source": "/home/me/.config/herdr/agent-detection/cursor.toml", "source_kind": "local override", "active_version": "2026.06.10.1", "cached_remote_version": "2026.06.10.1", "local_override_shadowing_remote": true, "remote_update_result": "current" } ] }}Fields such as last_check_unix, last_result, active_version, cached_remote_version, remote_update_result, remote_update_error, remote_last_checked_unix, and warning are omitted when not available. server.reload_agent_manifests returns agent_manifest_reload with the same manifests item shape after reloading the in-memory rule cache.
agent.explain evaluates the target pane’s detection snapshot in the running server using the server’s active manifest cache:
{ "id": "req_2", "method": "agent.explain", "params": { "target": "1-1" }}The response contains the same explain object printed by herdr agent explain --json, including the final state, manifest source and version, matched rule, evaluated rule evidence, skip-state reason, idle fallback reason, and screen_detection_skip_reason when a full lifecycle hook authority makes screen rules non-authoritative.
Clients need a running server that supports agent.explain; after upgrading Herdr, restart or live-handoff the server before relying on this method.
Errors look like this:
{ "id": "req_1", "error": { "code": "not_found", "message": "pane not found" }}Protocol stability
Section titled “Protocol stability”Herdr has a protocol version for client/server compatibility. Protocol changes are reviewed with release compatibility in mind.
Check the server protocol with ping or herdr status before depending on new behavior. Handle unknown fields gracefully.