1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
|
---
title: Socket API
description: Control a running Herdr server from scripts, tools, and coding agents.
---
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
| 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
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
Create a workspace:
```bash
herdr workspace create --cwd ~/project --label api
```
Create a tab:
```bash
herdr tab create --label logs
```
Split a pane and run a command:
```bash
herdr pane split 1-1 --direction right
herdr pane run 1-2 "npm test"
```
Inspect and rearrange panes:
```bash
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 1-1 --direction right --ratio 0.333
```
Wait for an agent:
```bash
herdr wait agent-status 1-1 --status done
```
Read pane output:
```bash
herdr pane read 1-2 --source recent --lines 50
```
## 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.
```json
{"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:
```json
{"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:
```json
{"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:
```bash
herdr notification show "build failed" --body "api workspace" --position top-left --sound request
```
Show a user notification through the configured toast delivery:
```json
{"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:
```json
{"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:
```json
{"id":"req_1","method":"worktree.create","params":{"workspace_id":"1","branch":"worktree/api","focus":false}}
```
Open an existing checkout:
```json
{"id":"req_2","method":"worktree.open","params":{"workspace_id":"1","branch":"worktree/api","focus":true}}
```
Remove a linked checkout:
```json
{"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
Herdr uses newline-delimited JSON over a Unix domain socket.
Send one request per line:
```json
{"id":"req_1","method":"ping","params":{}}
```
A successful response includes the same `id`:
```json
{"id":"req_1","result":{"type":"pong"}}
```
Event subscriptions keep the connection open after the initial response.
## Socket paths
The default socket lives under your Herdr config directory.
Named sessions have separate sockets:
```text
~/.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.
## Agent state reporting
Integrations report agent state with `pane.report_agent`.
```json
{
"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.
```json
{
"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:
```json
{
"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.
```json
{
"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
Subscribe to events when you need a long-lived stream:
```json
{
"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
Use `pane.read` through the CLI unless you are writing a protocol client.
```bash
herdr pane read 1-1 --source visible --lines 80
herdr pane read 1-1 --source recent --lines 120
herdr pane read 1-1 --source recent-unwrapped --lines 120
herdr pane read 1-1 --source detection
```
`recent-unwrapped` is useful for logs because it ignores soft wrapping.
`detection` returns the bottom-buffer snapshot used by agent screen detection.
## Waiting for state
Use waits to coordinate agents and scripts.
```bash
herdr wait agent-status 1-1 --status done
herdr wait agent-status 1-1 --status blocked
```
Agent waits observe semantic state, not arbitrary command completion.
## Response shapes
Successful responses look like this:
```json
{
"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:
```json
{
"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:
```json
{
"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:
```json
{
"id": "req_1",
"error": {
"code": "not_found",
"message": "pane not found"
}
}
```
## 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.
|