Skip to content

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.

LayerUse it for
Agent skillTeaching a coding agent how to use Herdr from inside a pane.
CLI wrappersShell scripts, simple orchestration, and human debugging.
Raw socket APICustom tools, protocol clients, and event subscribers.

The layers share the same control surface.

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

Create a workspace:

Terminal window
herdr workspace create --cwd ~/project --label api

Create a tab:

Terminal window
herdr tab create --label logs

Split a pane and run a command:

Terminal window
herdr pane split w1:p1 --direction right
herdr pane run w1:p2 "npm test"

Inspect and rearrange panes:

Terminal window
herdr pane layout --current
herdr pane neighbor --direction right --current
herdr pane resize --direction right --amount 0.1 --current
herdr pane swap --direction right --current
herdr pane zoom --on --current
herdr pane split w1:p1 --direction right --ratio 0.333

Wait for an agent:

Terminal window
herdr wait agent-status w1:p1 --status done

Read pane output:

Terminal window
herdr pane read w1:p2 --source recent --lines 50

Raw socket method names use dot notation:

AreaMethods
Serverping, server.stop, server.reload_config, server.agent_manifests, server.reload_agent_manifests
Notificationnotification.show
Clientclient.window_title.set, client.window_title.clear
Workspaceworkspace.create, workspace.list, workspace.get, workspace.focus, workspace.rename, workspace.close
Worktreeworktree.list, worktree.create, worktree.open, worktree.remove
Tabtab.create, tab.list, tab.get, tab.focus, tab.rename, tab.close
Panepane.split, pane.swap, pane.move, pane.zoom, pane.layout, pane.process_info, pane.neighbor, pane.edges, pane.focus_direction, pane.resize, pane.list, pane.current, 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
Layoutlayout.export, layout.apply
Agentagent.list, agent.get, agent.read, agent.explain, agent.send, agent.rename, agent.focus, agent.start
Eventsevents.subscribe, events.wait
Integrationsintegration.install, integration.uninstall
Pluginsplugin.link, plugin.list, plugin.unlink, plugin.enable, plugin.disable, plugin.action.list, plugin.action.invoke, plugin.log.list, plugin.pane.open, plugin.pane.focus, plugin.pane.close

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 w1:p1. Methods whose schema makes pane_id optional use the server’s active focused pane when it is omitted. pane.move always requires the source pane_id.

pane.send_keys and pane.send_input.keys accept Herdr key-combo strings: plain printable keys, special keys like enter and esc, modifier chords like ctrl+h, control+j, alt+x, and shift+tab, function keys like f1, and named punctuation like minus and plus. They do not accept prefix+ binding strings.

{"id":"req_current","method":"pane.current","params":{"caller_pane_id":"w1:p1"}}
{"id":"req_layout","method":"pane.layout","params":{"pane_id":"w1:p1"}}
{"id":"req_neighbor","method":"pane.neighbor","params":{"pane_id":"w1:p1","direction":"right"}}
{"id":"req_edges","method":"pane.edges","params":{"pane_id":"w1:p1"}}
{"id":"req_focus","method":"pane.focus_direction","params":{"direction":"right"}}
{"id":"req_resize","method":"pane.resize","params":{"pane_id":"w1:p1","direction":"right","amount":0.1}}
{"id":"req_zoom","method":"pane.zoom","params":{"pane_id":"w1:p1","mode":"toggle"}}
{"id":"req_split","method":"pane.split","params":{"direction":"right","ratio":0.333,"env":{"HERDR_ROLE":"tests"}}}
{"id":"req_process","method":"pane.process_info","params":{"pane_id":"w1:p1"}}

pane.current returns a single PaneInfo. When caller_pane_id is present, Herdr returns that pane. When it is omitted, Herdr returns the active focused pane.

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.process_info returns the pane’s shell pid, foreground process group id when available, and foreground processes with pid, name, argv/cmdline, and cwd when the platform exposes them.

layout.export returns a portable tab layout tree. Omit tab_id and pane_id to export the active tab, pass tab_id to export that tab, or pass pane_id to export the tab containing that pane.

{"id":"req_export","method":"layout.export","params":{"tab_id":"w1:t1"}}

The response includes workspace_id, tab_id, zoomed, focused_pane_id, and root. root is a BSP tree of pane and split nodes. Pane nodes can include pane_id, label, cwd, and argv command. Split nodes use direction (right or down), ratio, first, and second.

layout.apply creates a fresh tab from a declarative tree. If tab_id is provided, Herdr creates the replacement tab first and then closes the old tab. This restores structure, labels, cwd, env, and optional argv commands; it does not preserve live PTYs, scrollback, or running processes.

{
"id": "req_apply",
"method": "layout.apply",
"params": {
"workspace_id": "wabc",
"tab_label": "dev",
"focus": true,
"root": {
"type": "split",
"direction": "right",
"ratio": 0.65,
"first": {
"type": "pane",
"label": "editor",
"cwd": "/repo"
},
"second": {
"type": "pane",
"label": "tests",
"cwd": "/repo",
"command": ["sh", "-c", "just test"],
"env": { "HERDR_ROLE": "tests" }
}
}
}
}

Process-launching methods accept an env object. Herdr applies those key/value pairs to the newly launched process only. Herdr also injects HERDR_SOCKET_PATH, HERDR_ENV=1, HERDR_WORKSPACE_ID, HERDR_TAB_ID, and HERDR_PANE_ID into managed pane processes. Herdr-managed variables are authoritative when they conflict with caller-provided env.

pane.swap supports directional and explicit forms:

{"id":"req_swap_dir","method":"pane.swap","params":{"pane_id":"w1:p1","direction":"right"}}
{"id":"req_swap_explicit","method":"pane.swap","params":{"source_pane_id":"w1:p1","target_pane_id":"w1:p2"}}

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.move moves a running pane to a different tab, a new tab, or a new workspace:

{"id":"req_move_tab","method":"pane.move","params":{"pane_id":"w1:p2","destination":{"type":"tab","tab_id":"w1:t2","target_pane_id":"w1:p3","split":"right","ratio":0.5},"focus":true}}
{"id":"req_move_new_tab","method":"pane.move","params":{"pane_id":"w1:p2","destination":{"type":"new_tab","workspace_id":"w1","label":"logs"},"focus":true}}
{"id":"req_move_new_workspace","method":"pane.move","params":{"pane_id":"w1:p2","destination":{"type":"new_workspace","label":"logs","tab_label":"main"},"focus":true}}

Existing-tab moves require split: "right" | "down". target_pane_id is optional and defaults to the target tab’s focused pane. Same-tab layout changes remain pane.swap; moving to the source tab returns changed: false with reason: "same_tab". Moves involving a zoomed source or target tab return changed: false with reason: "zoomed_tab".

The response is type: "pane_move" with changed, optional reason, previous_pane_id, previous_workspace_id, previous_tab_id, the moved pane, optional source_layout, target_layout, optional created workspace or tab records, optional closed workspace or tab ids, and focused_pane_id. Cross-workspace moves keep the internal pane and terminal alive but assign a new public pane id in the destination workspace. Subscribers can listen for pane.moved; Herdr does not emit fake pane close/create events for the moved terminal process.

pane.zoom toggles, enables, or disables zoom for the target pane’s tab:

{"id":"req_zoom_toggle","method":"pane.zoom","params":{"pane_id":"w1:p1"}}
{"id":"req_zoom_on","method":"pane.zoom","params":{"pane_id":"w1:p1","mode":"on"}}
{"id":"req_zoom_off","method":"pane.zoom","params":{"pane_id":"w1:p1","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:

Terminal window
herdr notification show "build failed" --body "api workspace" --position top-left --sound request

Show 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.

Set or clear the foreground client’s outer terminal window title:

{"id":"req_title","method":"client.window_title.set","params":{"title":"herdr api"}}
{"id":"req_title_clear","method":"client.window_title.clear","params":{}}

client.window_title.clear restores Herdr’s default title. The response is type: "client_window_title" with changed and reason set, cleared, or no_foreground_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":"w1","branch":"worktree/api","focus":false}}

Open an existing checkout:

{"id":"req_2","method":"worktree.open","params":{"workspace_id":"w1","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.

Worktree commands also emit lifecycle events. worktree.create emits workspace.created, tab.created, pane.created, and worktree.created. worktree.open emits worktree.opened, and it also emits workspace/tab/pane creation events when it opens a new Herdr workspace. worktree.remove emits worktree.removed; if the linked workspace is still open, it also emits workspace.closed.

The plugin API is an early host surface for executable workflow tools. A plugin is a package with a herdr-plugin.toml manifest. The manifest declares shareable actions, event hooks, terminal pane entrypoints, and link handlers. Actions and panes are manifest-only; runtime action registration and runtime argv pane creation are not part of v1.

Installed and linked plugins persist across restarts. Herdr writes a plugins.json registry file alongside session.json on plugin.link, plugin.unlink, plugin.enable, and plugin.disable. The herdr plugin install CLI also writes the same registry when Herdr is not running, then startup loads it automatically. On startup, Herdr re-reads each manifest from its original path; if the file is missing or unparseable the entry is kept with a warnings field so plugin.list surfaces it.

Event hook on values are validated against the known Herdr event names at link time. An unrecognised name is not an error — the link still succeeds — but the returned plugin info includes a warning (e.g. "unknown event 'worktree.craeted'"). Check the warnings field in the plugin.link and plugin.list responses.

Link a local plugin manifest:

{"id":"req_plugin_link","method":"plugin.link","params":{"path":"/path/to/plugin","enabled":true}}

plugin.link also accepts optional source metadata. The CLI uses this when it installs from GitHub so plugin.list can show origin, requested ref, resolved commit, and managed checkout path:

{"id":"req_plugin_link","method":"plugin.link","params":{"path":"/managed/plugin/herdr-plugin.toml","enabled":true,"source":{"kind":"github","owner":"ogulcancelik","repo":"herdr-plugin-examples","subdir":"worktree-bootstrap","requested_ref":"main","resolved_commit":"abc123","managed_path":"/data/plugins/github/<managed-checkout>","installed_unix_ms":1780000000000}}}

The path can be a plugin directory containing herdr-plugin.toml or a direct manifest path. The manifest shape is:

id = "example.worktree-bootstrap"
name = "Worktree Bootstrap"
version = "0.1.0"
min_herdr_version = "0.7.0"
description = "Prepare new worktrees"
platforms = ["linux", "macos", "windows"]
[[build]]
command = ["bun", "install"]
[[actions]]
id = "bootstrap"
title = "Bootstrap worktree"
contexts = ["workspace"]
command = ["bun", "run", "bootstrap.ts"]
[[events]]
on = "worktree.created"
command = ["bun", "run", "bootstrap.ts"]
[[panes]]
id = "board"
title = "Worktree board"
placement = "overlay"
command = ["bun", "run", "board.ts"]
[[link_handlers]]
id = "github-issue"
title = "Open GitHub issue"
pattern = "^https://github\\.com/[^/]+/[^/]+/(issues|pull)/[0-9]+$"
action = "bootstrap"

min_herdr_version is required. The server refuses to link a plugin when the field is missing, invalid, or newer than the running Herdr binary.

Declare platforms at the top level with the OS identifiers (linux, macos, windows) your plugin supports. Omitting platforms is allowed for local development — plugin.link succeeds but the response includes a warning. Individual build commands, actions, event hooks, panes, and link handlers can declare their own platforms to override the plugin-level list; if omitted they inherit from the plugin. Invoking an action or opening a pane whose effective platforms do not include the current OS returns a platform_unsupported error.

List, enable, disable, or unlink linked plugins:

{"id":"req_plugin_list","method":"plugin.list","params":{}}
{"id":"req_plugin_disable","method":"plugin.disable","params":{"plugin_id":"example.worktree-bootstrap"}}
{"id":"req_plugin_enable","method":"plugin.enable","params":{"plugin_id":"example.worktree-bootstrap"}}
{"id":"req_plugin_unlink","method":"plugin.unlink","params":{"plugin_id":"example.worktree-bootstrap"}}

Actions are resolved from the linked manifest. plugin.action.list returns all actions across installed plugins; pass plugin_id to filter.

{"id":"req_plugin_actions","method":"plugin.action.list","params":{}}
{"id":"req_plugin_actions_filtered","method":"plugin.action.list","params":{"plugin_id":"example.worktree-bootstrap"}}

plugin.action.list returns each action’s effective platforms after applying plugin-level inheritance.

Invoke an action by its qualified id or bare action id:

{"id":"req_plugin_invoke","method":"plugin.action.invoke","params":{"action_id":"example.worktree-bootstrap.bootstrap","context":{"invocation_source":"keybinding"}}}

plugin.action.invoke resolves the manifest action, starts the manifest command, and returns the Herdr-built invocation context plus the started command log record. Missing context fields are filled from the active workspace, tab, focused pane, worktree provenance, and request id. Invoking an action from a disabled plugin returns a plugin_disabled error.

Herdr injects HERDR_SOCKET_PATH, HERDR_BIN_PATH, HERDR_ENV=1, HERDR_PLUGIN_ID, HERDR_PLUGIN_ROOT, HERDR_PLUGIN_CONFIG_DIR, HERDR_PLUGIN_STATE_DIR, HERDR_PLUGIN_CONTEXT_JSON, and available HERDR_WORKSPACE_ID, HERDR_TAB_ID, and HERDR_PANE_ID values. Action commands also receive HERDR_PLUGIN_ACTION_ID; event hooks receive HERDR_PLUGIN_EVENT and HERDR_PLUGIN_EVENT_JSON; pane commands receive HERDR_PLUGIN_ENTRYPOINT_ID.

List recent action and event command logs:

{"id":"req_plugin_logs","method":"plugin.log.list","params":{"plugin_id":"example.worktree-bootstrap","limit":20}}

Event hooks run for enabled installed plugins when Herdr emits a matching event name such as worktree.created.

There is no Herdr-managed plugin storage API in v1. HERDR_PLUGIN_CONFIG_DIR and HERDR_PLUGIN_STATE_DIR are path discovery only; plugins own their files, schemas, migrations, and cleanup.

Open a managed terminal UI:

{"id":"req_plugin_pane","method":"plugin.pane.open","params":{"plugin_id":"example.board","entrypoint":"board","placement":"zoomed","target_pane_id":"w1:p1","env":{"HERDR_ROLE":"board"},"focus":true}}

plugin.pane.open requires an installed, enabled, platform-compatible plugin, then launches the requested manifest [[panes]] entrypoint as an argv-backed terminal pane. Manifest pane placement defaults to overlay; request placement overrides the manifest with overlay, split, tab, or zoomed. Overlay panes target the active pane. Split and zoomed panes target an existing pane; tab panes can target a workspace. The pane behaves like a normal Herdr pane, but plugin.pane.focus and plugin.pane.close only operate on panes opened through the plugin API. Focus returns plugin_pane_focused; close returns plugin_pane_closed.

Herdr uses newline-delimited JSON over a local socket. On Unix, that socket is a Unix domain socket. On Windows, it is a named pipe.

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.

The default socket lives under your Herdr config directory.

Named sessions have separate sockets:

~/.config/herdr/herdr.sock
~/.config/herdr/sessions/<name>/herdr.sock

Resolution order:

  1. explicit CLI --session <name>
  2. HERDR_SOCKET_PATH
  3. HERDR_SESSION=<name>
  4. default session socket

Use HERDR_SOCKET_PATH only for low-level overrides.

For plugins, prefer invoking HERDR_BIN_PATH and the CLI wrappers when you need portable Windows behavior. Raw socket clients are responsible for using the platform-native local socket form.

Integrations report agent state with pane.report_agent.

{
"id": "req_1",
"method": "pane.report_agent",
"params": {
"pane_id": "w1:p1",
"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 activity 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": "w1:p1",
"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": "w1:p1",
"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 activity label, 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.

Presentation text is normalized before storage. Herdr trims surrounding whitespace, removes control characters, caps custom_status at 32 characters, and caps title, display_agent, and each state label at 80 characters. Empty normalized values are ignored.

source and applies_to_source are source identifiers. They must be 80 characters or fewer and may contain only ASCII letters, digits, colon, dot, underscore, and hyphen.

Use ttl_ms for short-lived metadata. It must be between 1 and 86400000 milliseconds. Omit ttl_ms for metadata that should stay until replaced, cleared, or the pane closes. When the TTL expires, Herdr removes that source’s metadata and emits a presentation change if the visible pane presentation changed.

Use seq when a hook may send updates out of order. For the same source, reports with a sequence number less than or equal to the last accepted sequence are accepted by the API but ignored by the pane state.

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": "w1:p1", "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. Workspace events describe Herdr UI/runtime lifecycle. workspace.created includes optional workspace.worktree provenance when the workspace belongs to a worktree group. workspace.closed includes a final workspace snapshot when Herdr can still identify it before removal. Pane event subscriptions include pane.created, pane.closed, pane.focused, pane.moved, pane.exited, pane.agent_detected, pane.output_matched, and pane.agent_status_changed.

Worktree event subscriptions include worktree.created, worktree.opened, and worktree.removed. Worktree events describe Git checkout lifecycle. worktree.created includes the opened workspace and created worktree. worktree.opened includes the target workspace, opened worktree, and already_open. worktree.removed includes the workspace_id, removed worktree, and forced.

Use events.subscribe for lifecycle events. Dedicated wait helpers are documented separately when a one-shot wait is supported.

Use pane.read through the CLI unless you are writing a protocol client.

Terminal window
herdr pane read w1:p1 --source visible --lines 80
herdr pane read w1:p1 --source recent --lines 120
herdr pane read w1:p1 --source recent-unwrapped --lines 120
herdr pane read w1:p1 --source detection

recent-unwrapped is useful for logs because it ignores soft wrapping. detection returns the bottom-buffer snapshot used by agent screen detection.

Use waits to coordinate agents and scripts.

Terminal window
herdr wait agent-status w1:p1 --status done
herdr wait agent-status w1:p1 --status blocked

Agent waits observe semantic state, not arbitrary command completion.

Successful responses look like this:

{
"id": "req_1",
"result": {
"type": "pane_info",
"pane": {
"pane_id": "w1:p1",
"terminal_id": "term_abc123",
"workspace_id": "w1",
"tab_id": "w1:t1",
"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": "w1:p1" }
}

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"
}
}

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.