@hydra-acp/cli 0.1.145 → 0.1.147
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +507 -334
- package/dist/cli.js +331 -324
- package/dist/daemon.js +68 -68
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,322 +23,215 @@
|
|
|
23
23
|
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠉⠛⠙⠋⠉⠁⠀⠀⠀⠀⠀⠁⠀⠀⠋⠙⠉⠟⠉⠀⠈⠈⠁⠉
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
> **Status: experimental.**
|
|
26
|
+
> **Status: experimental.** One daemon owns your coding agents, so a session isn't tied to the window that started it. Built on the [Agent Client Protocol (ACP)](https://agentclientprotocol.com/).
|
|
27
27
|
|
|
28
28
|
## What it is
|
|
29
29
|
|
|
30
|
-
`hydra-acp` is a **session manager, multiplexer, and TUI** for AI coding agents.
|
|
30
|
+
`hydra-acp` is a **session manager, multiplexer, and TUI** for AI coding agents. Many clients — a terminal TUI, your editor, a browser, Slack — attach to the same live session at once and see it update in real time. Start a session at your desk, follow it from your phone, hand it off to a teammate.
|
|
31
|
+
|
|
32
|
+
The agent doing the actual work is one you already know: Claude, Codex,
|
|
33
|
+
opencode, pi, or anything else in the ACP registry. Hydra defaults to
|
|
34
|
+
`opencode` so there's something to run before you've chosen, and [pointing it
|
|
35
|
+
at whatever you have a license for](#choosing-an-agent) is a one-liner.
|
|
31
36
|
|
|
32
37
|
```bash
|
|
33
|
-
npm install -g @hydra-acp/cli
|
|
34
|
-
hydra-acp
|
|
38
|
+
npm install -g @hydra-acp/cli # Node 20+
|
|
39
|
+
hydra-acp # launch the TUI and start a session
|
|
35
40
|
```
|
|
36
41
|
|
|
37
|
-
That
|
|
42
|
+
That puts `hydra-acp` (and `hydra`) on your PATH, and there is no setup step
|
|
43
|
+
after it. `~/.hydra-acp/config.json` and the bearer token are written on first
|
|
44
|
+
use, and the daemon starts itself the first time anything needs it. If you'd
|
|
45
|
+
rather be explicit, `hydra-acp daemon start` brings it up ahead of time and
|
|
46
|
+
`hydra-acp init` writes the config early — the latter mostly earns its keep as
|
|
47
|
+
`init --rotate-token`.
|
|
48
|
+
|
|
49
|
+
The TUI is the front door: it picks an agent, drives the conversation, lists your sessions, and lets you attach to one that's already running. From there you can wire up your editor, add a browser or Slack bridge, or pipe data through it on the command line.
|
|
38
50
|
|
|
39
51
|
### What it gives you
|
|
40
52
|
|
|
41
53
|
- **A session manager.** Every session is tracked, named, listable (`hydra-acp session`), exportable, and resurrectable. Close your terminal and the session keeps running in the daemon; reattach later from anywhere.
|
|
42
54
|
- **A multiplexer.** Many clients share one live session. Events broadcast to all attached clients; prompts serialize through a per-session queue; permission requests race (first response wins). Editor, TUI, browser, and Slack all watch the same agent at once.
|
|
43
55
|
- **A TUI.** A full terminal UI for driving sessions interactively — picking agents, prompting, approving tool calls, scrolling transcripts, and switching between live sessions.
|
|
56
|
+
- **Long sessions that stay coherent.** History [compacts](#compaction-and-recall) as it approaches the context window, and the agent keeps a recall tool to search back into what was compacted away. `/hydra uncompact` reverses the last one, and `/hydra fork` branches a conversation so you can chase a tangent without losing the thread.
|
|
44
57
|
- **Extensions.** Optional companion processes — Slack bridge, web UI, desktop notifier, auto-approver, cross-machine sync — that the daemon spawns and manages for you. See [Extensions](#extensions) below.
|
|
45
58
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Under the hood, `hydra-acp` is a daemon + CLI shim that implements two open ACP RFDs as a single coherent surface, on top of the standard ACP protocol (including `session/list` for session discovery), plus the official ACP Registry as its agent-distribution mechanism. The rest of this section is the protocol detail; skip to [Quick start](#quick-start) if you just want to use it.
|
|
49
|
-
|
|
50
|
-
### The standards it stitches together
|
|
51
|
-
|
|
52
|
-
ACP itself is the [Agent Client Protocol](https://agentclientprotocol.com/) — a JSON-RPC 2.0 protocol between editors (clients) and AI coding agents. Today the protocol is canonically a 1:1 stdio relationship: one editor spawns one agent and owns its stdin/stdout. Two RFDs in the [`agentclientprotocol/agent-client-protocol`](https://github.com/agentclientprotocol/agent-client-protocol) repo extend that model. `hydra-acp` is one daemon that implements both together so they can be used as a coherent system rather than two independent extensions.
|
|
53
|
-
|
|
54
|
-
#### 1. Multi-Client Session Attach — [RFD #533](https://github.com/agentclientprotocol/agent-client-protocol/pull/533)
|
|
55
|
-
|
|
56
|
-
Adds two new methods that turn ACP from 1:1 into 1:N:
|
|
57
|
-
|
|
58
|
-
- **`session/attach { sessionId, historyPolicy, clientInfo? }`** — a second (or third, or N-th) client connects to a session that's already live. `historyPolicy` controls replay on attach: `"full"`, `"pending_only"`, or `"none"`.
|
|
59
|
-
- **`session/detach { sessionId }`** — graceful disconnect; the session continues as long as one client remains attached.
|
|
60
|
-
|
|
61
|
-
Every event the agent emits is broadcast to every attached client; clients self-filter what they act on. Permission requests broadcast the same way: the first response wins, and the rest receive a `session/update` notification with `sessionUpdate: "permission_resolved"`. Capability is advertised in `initialize` under `agentCapabilities.sessionCapabilities.attach`.
|
|
62
|
-
|
|
63
|
-
#### 2. Streamable HTTP & WebSocket Transport — [RFD: streamable-http-websocket-transport](https://agentclientprotocol.com/rfds/streamable-http-websocket-transport) (WebSocket profile only)
|
|
64
|
-
|
|
65
|
-
Defines the network transport that lets ACP run between processes that aren't parent and child. The RFD specifies two profiles on one `/acp` endpoint: a Streamable HTTP profile (POST/GET-SSE/DELETE with `Acp-Connection-Id` and `Acp-Session-Id` headers, HTTP/2 required) and a WebSocket profile (GET with `Upgrade: websocket`). The RFD explicitly permits servers to support **only** the WebSocket profile, and that's the route `hydra-acp` takes — the Streamable HTTP half isn't implemented. The RFD itself is still Draft as of April 2026, with the routing model rewritten twice in the six weeks before this writing, so deferring HTTP-transport work until the spec stabilizes is deliberate.
|
|
66
|
-
|
|
67
|
-
On the WebSocket side, `hydra-acp` exposes its WSS endpoint at `/acp`: a client sends `GET /acp` with `Upgrade: websocket`, receives a `101 Switching Protocols` response, and the connection becomes a bidirectional stream of JSON-RPC text frames (binary frames are ignored). The server negotiates the `acp.v1` subprotocol via the standard `Sec-WebSocket-Protocol` mechanism (echoed back in the 101 when advertised; absent otherwise). Authentication is layered on top — HTTP headers, query parameters, or WebSocket subprotocols — and is treated as orthogonal by the spec. `hydra-acp` authenticates via a bearer token carried in a `hydra-acp-token.<token>` subprotocol entry or a `?token=<token>` query parameter.
|
|
68
|
-
|
|
69
|
-
### Standard ACP it relies on
|
|
70
|
-
|
|
71
|
-
Beyond the bedrock of `initialize` / `session/new` / `session/prompt`, the daemon implements **`session/list`** ([Protocol: Session List](https://agentclientprotocol.com/protocol/session-list), stabilized 2026-03-09) so any compliant client can enumerate sessions known to the daemon and attach to one — `{ sessionId, cwd, title?, updatedAt?, _meta? }` per entry, with `cwd` filtering and `cursor`-based pagination. Hydra-specific fields ride under `_meta["hydra-acp"]` per the [Extensibility](https://agentclientprotocol.com/protocol/extensibility) convention.
|
|
72
|
-
|
|
73
|
-
### The registry it depends on
|
|
74
|
-
|
|
75
|
-
Agents are sourced from the [ACP Registry](https://github.com/agentclientprotocol/registry) — a CDN-hosted JSON document at `https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json`. Each entry declares `id`, `name`, `version`, `description`, and a `distribution` block that selects between `npx`, `binary`, or `uvx` installation. `hydra-acp` caches the registry locally with a 24-hour TTL, falls back to the cached copy on network failure, and resolves an agent's `distribution` to a spawn plan when a session needs that agent.
|
|
76
|
-
|
|
77
|
-
## Architecture
|
|
78
|
-
|
|
79
|
-
```
|
|
80
|
-
editor browser Slack ← clients
|
|
81
|
-
│ │ │
|
|
82
|
-
hydra-acp hydra-acp-browser hydra-acp-slack ← hydra extensions
|
|
83
|
-
│ │ │
|
|
84
|
-
└───────────────┼───────────────┘
|
|
85
|
-
│
|
|
86
|
-
WSS / HTTP
|
|
87
|
-
│
|
|
88
|
-
hydra-acp ← hydra daemon
|
|
89
|
-
│
|
|
90
|
-
T1 → T2 → … → Tn ← hydra transformers
|
|
91
|
-
│
|
|
92
|
-
┌───────────────┼───────────────┐
|
|
93
|
-
│ │ │
|
|
94
|
-
claude opencode gemini ← agents
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### How it works
|
|
98
|
-
|
|
99
|
-
1. **Editor spawns `hydra-acp`** as it would any ACP agent. The shim looks like a normal stdio agent.
|
|
100
|
-
2. **Shim opens a WSS connection** to the daemon at `/acp`, authenticating via the bearer token.
|
|
101
|
-
3. **`session/new` from the editor** → daemon resolves the requested agent against the cached ACP Registry, downloads it on first use under `~/.hydra-acp/agents/`, spawns it as a child process, and creates an ACP session inside it.
|
|
102
|
-
4. **`session/attach` from a second client** → daemon adds the new client to the session's broadcast list and replays history per `historyPolicy` (per RFD #533).
|
|
103
|
-
5. **Notifications** fan out to every attached client. **Prompts** are serialized through the daemon's per-session queue. **Permission requests** broadcast to every attached client; first response wins and the rest receive a `session/update` with `sessionUpdate: "permission_resolved"` carrying the resolving client's outcome.
|
|
104
|
-
6. **`session/list`** returns the daemon's sessions (live and cold), filterable by `cwd`.
|
|
105
|
-
7. **`session/detach`** lets a client leave voluntarily; the session continues until the last client detaches (per RFD #533).
|
|
106
|
-
|
|
107
|
-
### Why a shim?
|
|
108
|
-
|
|
109
|
-
Existing ACP clients are stdio-based: they `spawn(command)` a process and exchange JSON-RPC over its stdin/stdout. A shim that *looks* like an ACP agent on stdio is zero-integration on the client side — the client doesn't need to know anything about hydra, the daemon, or WSS. It just spawns `hydra-acp` and starts talking ACP.
|
|
110
|
-
|
|
111
|
-
Clients that adopt the streamable-http-websocket-transport RFD natively can connect to the daemon's `/acp` endpoint directly without the shim.
|
|
112
|
-
|
|
113
|
-
### Cat mode
|
|
59
|
+
## Choosing an agent
|
|
114
60
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
usable as a unix filter, with the agent as the program in the middle of the
|
|
120
|
-
pipeline.
|
|
61
|
+
Hydra has no model of its own. Every session is a real ACP agent running as a
|
|
62
|
+
child process, and hydra is the thing that owns it, keeps it alive, and lets
|
|
63
|
+
more than one client talk to it. So the first real decision is which agent sits
|
|
64
|
+
underneath.
|
|
121
65
|
|
|
122
|
-
|
|
66
|
+
Out of the box that's `opencode`, because it's the least setup to get something
|
|
67
|
+
working. It's a reasonable place to start, not a recommendation. Most people
|
|
68
|
+
point hydra at whatever they already pay for and never think about it again.
|
|
123
69
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
bytes flow into a ring buffer and the agent pulls them on demand via
|
|
133
|
-
`head`, `tail`, `grep`, `read`, and `info`. A multi-gigabyte log isn't a
|
|
134
|
-
context-window problem; it's a fixed-size buffer the agent samples.
|
|
135
|
-
- **`--follow` for live streams.** Pipe `tail -f` into `--follow` and each quiet
|
|
136
|
-
burst on stdin is sent as a new turn. The standing prompt (`-p`) is sent
|
|
137
|
-
only on the first turn; later turns carry just the new bytes.
|
|
138
|
-
- **`--detach` to share the session.** By default the session lives as long as
|
|
139
|
-
the cat process; on stdin EOF it dies. With `--detach` it stays in the
|
|
140
|
-
daemon, `hydra-acp session` lists it, and the slack / browser / notifier
|
|
141
|
-
extensions can ride on it. Useful for kicking off a long-running watch
|
|
142
|
-
from a shell script and following it on your phone.
|
|
143
|
-
|
|
144
|
-
A few examples:
|
|
70
|
+
Hydra spawns the same agent you'd run yourself, so it inherits whatever you
|
|
71
|
+
already set up. If opencode, pi, or Claude work from your shell today, their
|
|
72
|
+
existing login and config carry over as-is and there's nothing extra to do.
|
|
73
|
+
For an agent you've never run before, run its own CLI once and complete its
|
|
74
|
+
login there. `hydra-acp agent auth <id>` tries to drive that flow from inside
|
|
75
|
+
hydra, but registry coverage of auth methods is patchy and it's less exercised
|
|
76
|
+
than the rest of the surface, so treat it as a shortcut rather than the
|
|
77
|
+
supported path.
|
|
145
78
|
|
|
146
79
|
```sh
|
|
147
|
-
#
|
|
148
|
-
hydra-acp -p "tools to convert a HEIC photo to JPEG on linux?"
|
|
149
|
-
|
|
150
|
-
# Analyze a big log without copy-pasting it into a chat window.
|
|
151
|
-
journalctl -u nginx --since "1 hour ago" | hydra-acp cat -p "anything alarming?"
|
|
152
|
-
|
|
153
|
-
# Treat hydra as the filter in a unix pipeline — output is plain text,
|
|
154
|
-
# so tee / grep / jq downstream just work.
|
|
155
|
-
git log --since="last monday" --pretty=full | hydra-acp cat -p "draft release notes, one bullet per user-visible change, grouped by version" | tee RELEASE_NOTES.md
|
|
156
|
-
|
|
157
|
-
# Watch a live log and only speak up when something's wrong. --detach
|
|
158
|
-
# keeps the session in the daemon, so you can follow it on your phone
|
|
159
|
-
# via the slack extension after closing the shell.
|
|
160
|
-
tail -F /var/log/app.log | hydra-acp cat --follow --detach -p "if a line looks like an error or stack trace, summarize it. otherwise stay silent."
|
|
80
|
+
hydra-acp agent list # what exists, and what you already have installed
|
|
161
81
|
```
|
|
162
82
|
|
|
163
|
-
|
|
164
|
-
|
|
83
|
+
Nothing needs installing ahead of time. Agents are downloaded on first use into
|
|
84
|
+
`~/.hydra-acp/agents/`. Short names work: `claude`, `pi`, and `codex` all
|
|
85
|
+
resolve to their full registry ids (see [Registry id resolution](#registry-id-resolution)).
|
|
165
86
|
|
|
166
|
-
|
|
87
|
+
How you switch depends on how permanent you want it:
|
|
167
88
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
89
|
+
| Scope | How |
|
|
90
|
+
|---|---|
|
|
91
|
+
| Your default, everywhere | `hydra-acp agent set claude`, then `hydra-acp daemon restart` |
|
|
92
|
+
| Default model for an agent | `hydra-acp agent set opencode openai/gpt-5-codex` |
|
|
93
|
+
| A single session | `hydra-acp --agent codex` (or `HYDRA_ACP_AGENT=codex`) |
|
|
94
|
+
| Mid-conversation, keeping the transcript | type `/hydra agent codex` in any composer |
|
|
95
|
+
| One editor, pinned | point the editor at `hydra-acp launch claude` |
|
|
171
96
|
|
|
172
|
-
|
|
97
|
+
Only the first needs the restart: `agent set` writes `config.defaultAgent` and
|
|
98
|
+
the daemon reads that at startup. The others take effect immediately.
|
|
173
99
|
|
|
174
|
-
|
|
100
|
+
The mid-conversation swap is worth knowing about if you hold licenses for
|
|
101
|
+
several. `/hydra agent <id>` spawns the new agent, feeds it the conversation so
|
|
102
|
+
far, and carries on in the same session, so you can hand an in-progress problem
|
|
103
|
+
to a different model without starting over.
|
|
175
104
|
|
|
176
|
-
|
|
177
|
-
hydra-acp agent list # browse known agent ids
|
|
178
|
-
hydra-acp agent set opencode # update the default agent
|
|
179
|
-
hydra-acp agent set opencode openai/gpt-5-codex # update the default model for agent
|
|
180
|
-
hydra-acp daemon restart # restart the daemon to pickup changes
|
|
181
|
-
```
|
|
105
|
+
## Running it
|
|
182
106
|
|
|
183
|
-
|
|
107
|
+
### From the terminal
|
|
184
108
|
|
|
185
109
|
```bash
|
|
186
|
-
#
|
|
187
|
-
# bearer token. If you skip this, the first invocation of `daemon start`,
|
|
188
|
-
# `shim`, or `tui` writes the config for you. Run init explicitly only
|
|
189
|
-
# when you want to rotate the token (`hydra-acp init --rotate-token`).
|
|
190
|
-
hydra-acp init
|
|
191
|
-
|
|
192
|
-
# 2. (Optional) Start the daemon. If you skip this step, the shim will
|
|
193
|
-
# auto-start the daemon the first time an editor invokes it.
|
|
194
|
-
hydra-acp daemon start
|
|
195
|
-
|
|
196
|
-
# 3. Configure your editor to spawn `hydra-acp shim` instead of an agent
|
|
197
|
-
# directly. The `shim` verb forces shim mode — the right form for
|
|
198
|
-
# spawned-by-editor cases where stdio is already piped. The first
|
|
199
|
-
# session/new asks the daemon which agent to spawn (defaults to
|
|
200
|
-
# config.defaultAgent). If you'd rather the editor pin a specific agent,
|
|
201
|
-
# spawn `hydra-acp launch <agent>` (see "Launcher mode" below).
|
|
202
|
-
|
|
203
|
-
# 4. From a terminal, drive a session interactively (TUI).
|
|
204
|
-
hydra-acp # bare invocation in a TTY launches the TUI
|
|
110
|
+
hydra-acp # TTY: picker if you have sessions, else a new one
|
|
205
111
|
hydra-acp tui # explicit form
|
|
206
|
-
|
|
207
|
-
#
|
|
208
|
-
hydra-acp session
|
|
209
|
-
|
|
210
|
-
# 6. Attach a second client to an existing session.
|
|
211
|
-
# Bare invocation auto-detects: TUI in a terminal, ACP shim when piped.
|
|
212
|
-
hydra-acp --session hydra_session_abc123
|
|
112
|
+
hydra-acp session # list sessions
|
|
113
|
+
hydra-acp --reattach # reattach to this terminal's last session
|
|
114
|
+
hydra-acp --session hydra_session_abc123 # attach to a specific session
|
|
213
115
|
```
|
|
214
116
|
|
|
215
|
-
|
|
117
|
+
In the TUI, `^G` opens the key-binding help — that's the one binding worth
|
|
118
|
+
memorizing. The others you'll reach for early: `^P` switches sessions, `^R`
|
|
119
|
+
searches your prompt history and then scrollback, `^T` toggles the agent's
|
|
120
|
+
reasoning, `^D` on an empty prompt detaches. Detaching leaves the session
|
|
121
|
+
running in the daemon; `hydra-acp session` will still list it and you can
|
|
122
|
+
attach again from anywhere.
|
|
216
123
|
|
|
217
|
-
|
|
124
|
+
`^S` on an empty prompt opens the sidebar, which runs alongside the
|
|
125
|
+
conversation and tracks what the agent is doing: running tools, the current
|
|
126
|
+
todo list, files edited so far, git status, context usage, and your other
|
|
127
|
+
sessions. Click a gadget's title to fold it. (With a draft in the composer,
|
|
128
|
+
`^S` amends the in-flight turn instead.)
|
|
218
129
|
|
|
219
|
-
|
|
130
|
+
### From your editor
|
|
220
131
|
|
|
221
|
-
|
|
132
|
+
Point your editor's ACP agent command at `hydra-acp acp` instead of the agent
|
|
133
|
+
binary. The `acp` verb forces shim mode, which is the right form when stdio is
|
|
134
|
+
already piped (`hydra-acp shim` is a long-standing alias for it, and a bare
|
|
135
|
+
`hydra-acp` also lands in shim mode when stdout isn't a TTY). The first
|
|
136
|
+
`session/new` asks the daemon which agent to spawn, defaulting to
|
|
137
|
+
`config.defaultAgent`:
|
|
222
138
|
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
hydra-acp extension add hydra-acp-slack
|
|
226
|
-
hydra-acp extension restart hydra-acp-slack
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
You'll also need a Slack app and a config at `~/.hydra-acp-slack.conf` — see the [package's setup section](https://github.com/smagnuso/hydra-acp-slack#setup) for scopes, tokens, and authorized users.
|
|
230
|
-
|
|
231
|
-
**[`@hydra-acp/browser`](https://github.com/smagnuso/hydra-acp-browser) — local web UI.** Single-page app that lists live sessions, attaches to each one, and renders the transcript (agent messages, tool calls, plans, mode/model changes) with a composer for prompting and permission widgets for approving tool use. Cheap to bring up when you want to spot-check an agent without firing up the editor.
|
|
232
|
-
|
|
233
|
-
```sh
|
|
234
|
-
npm install -g @hydra-acp/browser
|
|
235
|
-
hydra-acp extension add hydra-acp-browser
|
|
236
|
-
hydra-acp extension restart hydra-acp-browser
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
The first launch generates `~/.hydra-acp-browser/authkey` and writes the open URL (with `?authkey=…`) to `~/.hydra-acp-browser/link`. Defaults to localhost-only; see the [package's HTTPS section](https://github.com/smagnuso/hydra-acp-browser#https) for binding to a LAN address with TLS.
|
|
240
|
-
|
|
241
|
-
**[`@hydra-acp/notifier`](https://github.com/smagnuso/hydra-acp-notifier) — desktop notifications.** Always-on companion that fires `notify-send` (Linux) or `osascript` (macOS) when sessions emit notable events — by default, `turn_complete`. The default title is `🐉 <agentId> · <short-session-id> · <session-title-or-cwd>` and the body renders the agent's stop reason as friendly text (`Finished`, `Max token limit reached`, etc.). Drop a JS rule at `~/.hydra-acp/notifier.config.js` to customize per-event, or set `HYDRA_ACP_NOTIFY_CMD` to route everything to ntfy/Pushover/your phone.
|
|
242
|
-
|
|
243
|
-
```sh
|
|
244
|
-
npm install -g @hydra-acp/notifier
|
|
245
|
-
hydra-acp extension add hydra-acp-notifier
|
|
246
|
-
hydra-acp extension start hydra-acp-notifier
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
**[`@hydra-acp/approver`](https://github.com/smagnuso/hydra-acp-approver) — headless permission auto-responder.** Attaches to every live session and answers `session/request_permission` based on a JS rule at `~/.hydra-acp/approver.config.js`. When the rule returns an `optionId` it wins the race and dismisses the prompt before any human client sees it; when it abstains (returns `null`), the prompt stays open for your interactive clients. Useful for centralizing approval policy in one place so per-client approval can go away.
|
|
250
|
-
|
|
251
|
-
```sh
|
|
252
|
-
npm install -g @hydra-acp/approver
|
|
253
|
-
hydra-acp extension add hydra-acp-approver
|
|
254
|
-
hydra-acp extension start hydra-acp-approver
|
|
139
|
+
```text
|
|
140
|
+
hydra-acp acp
|
|
255
141
|
```
|
|
256
142
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
**[`@hydra-acp/archiver`](https://github.com/smagnuso/hydra-acp-archiver) — cross-machine session sync.** Uploads session bundles to a shared backend (Google Drive, plain filesystem) after every turn and imports peers' bundles in the background, so a session started on machine A shows up on machine B without manual export/import. Imported sessions carry an `importedFromMachine` breadcrumb that the picker, browser, slack, and `sessions list` honor for host filtering.
|
|
143
|
+
To pin a specific agent for that editor instead, spawn `hydra-acp launch
|
|
144
|
+
<agent>` — see [Launcher mode](#launcher-mode).
|
|
260
145
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
hydra-acp extension start hydra-acp-archiver
|
|
265
|
-
```
|
|
146
|
+
Either way the session is a normal hydra session: `hydra-acp session` lists it,
|
|
147
|
+
and the TUI, browser, or Slack can attach to the same live conversation while
|
|
148
|
+
your editor is still driving it.
|
|
266
149
|
|
|
267
|
-
|
|
150
|
+
### Handing one off
|
|
268
151
|
|
|
269
|
-
|
|
152
|
+
`hydra-acp session share` prints a `hydra://host/id` URL that someone else, or
|
|
153
|
+
you on another machine, pastes into `--session` to attach to the running
|
|
154
|
+
conversation:
|
|
270
155
|
|
|
271
156
|
```sh
|
|
272
|
-
|
|
273
|
-
hydra-acp
|
|
274
|
-
hydra-acp transformer start hydra-acp-planner
|
|
157
|
+
hydra-acp session share # most-recent session for this cwd
|
|
158
|
+
hydra-acp --session hydra://box:55514/hydra_session_abc123
|
|
275
159
|
```
|
|
276
160
|
|
|
277
|
-
|
|
161
|
+
This only reaches past your own machine if the daemon is listening past it, and
|
|
162
|
+
the daemon refuses to bind to a non-loopback address without TLS. So sharing
|
|
163
|
+
means setting `daemon.host`, pointing `daemon.tls.cert`/`key` at a certificate,
|
|
164
|
+
and setting `daemon.publicHost` to the name the URL should carry. See
|
|
165
|
+
[Security](#security). Without that, `share` still prints a URL and warns you
|
|
166
|
+
that it's loopback-only.
|
|
278
167
|
|
|
279
|
-
|
|
168
|
+
## Compaction and recall
|
|
280
169
|
|
|
281
|
-
|
|
170
|
+
Long sessions run into the model's context window. When a session's history
|
|
171
|
+
approaches it, hydra compacts asynchronously during an idle gap: the older
|
|
172
|
+
turns are summarized, the most recent ones are kept verbatim, and the
|
|
173
|
+
conversation carries on. `/hydra compact` returns immediately for the same
|
|
174
|
+
reason, with the summarization running behind it.
|
|
282
175
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
"hydra-acp-browser": {
|
|
288
|
-
"command": ["hydra-acp-browser"],
|
|
289
|
-
"args": ["--port", "9999"],
|
|
290
|
-
"env": { "UI_THEME": "dark" }
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
```
|
|
176
|
+
The part that matters is what happens to the history that got summarized away:
|
|
177
|
+
it isn't gone. The agent can pull it back on demand, so "what did we decide
|
|
178
|
+
three hours ago" stays answerable after the turns holding that decision have
|
|
179
|
+
left the live context.
|
|
295
180
|
|
|
296
|
-
|
|
181
|
+
Two escape hatches, typed in any composer:
|
|
297
182
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
| Env var | Example |
|
|
183
|
+
| Command | Effect |
|
|
301
184
|
|---|---|
|
|
302
|
-
| `
|
|
303
|
-
| `
|
|
304
|
-
| `HYDRA_ACP_DAEMON_PORT` | `55514` |
|
|
305
|
-
| `HYDRA_ACP_TOKEN` | `hydra_token_<hex>` |
|
|
306
|
-
| `HYDRA_ACP_WS_URL` | `ws://127.0.0.1:55514/acp` |
|
|
307
|
-
| `HYDRA_ACP_HOME` | `~/.hydra-acp` |
|
|
308
|
-
| `HYDRA_ACP_EXTENSION_NAME` | the `name` from config |
|
|
185
|
+
| `/hydra compact` | Compact now instead of waiting for the heuristic. `/hydra compact status` reports state without triggering. |
|
|
186
|
+
| `/hydra uncompact` | Roll back the most recent compaction. Only available immediately after one, before any new turns. |
|
|
309
187
|
|
|
310
|
-
|
|
188
|
+
The thresholds that decide when this fires, how much recent history survives
|
|
189
|
+
verbatim, and what context window to assume for an unfamiliar model all live
|
|
190
|
+
under `compaction` in `~/.hydra-acp/config.json`. `hydra-acp config get
|
|
191
|
+
compaction` prints them.
|
|
311
192
|
|
|
312
|
-
|
|
193
|
+
## CLI
|
|
313
194
|
|
|
314
|
-
|
|
315
|
-
hydra-acp extension list
|
|
316
|
-
hydra-acp extension restart hydra-acp-slack
|
|
317
|
-
hydra-acp extension log hydra-acp-slack --follow
|
|
318
|
-
```
|
|
195
|
+
Nine of these carry most of the daily traffic:
|
|
319
196
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
197
|
+
```sh
|
|
198
|
+
hydra-acp # open the TUI (picker if you have sessions, else a new one)
|
|
199
|
+
hydra-acp session # list sessions
|
|
200
|
+
hydra-acp --reattach # back into this terminal's last session
|
|
201
|
+
hydra-acp agent list # what agents are available
|
|
202
|
+
hydra-acp agent set claude # change the default agent
|
|
203
|
+
hydra-acp cat -p "..." # one-shot / piped, no TUI
|
|
204
|
+
hydra-acp session info <id> # what happened in a session
|
|
205
|
+
hydra-acp daemon log -f # follow the daemon log
|
|
206
|
+
hydra-acp config set <k> <v> # change a setting
|
|
207
|
+
```
|
|
323
208
|
|
|
324
|
-
|
|
209
|
+
The full surface:
|
|
325
210
|
|
|
326
211
|
```
|
|
327
212
|
hydra-acp # auto-dispatch: TUI in a TTY, shim when stdio is piped
|
|
328
|
-
|
|
213
|
+
# (with -p and no subcommand, dispatches to cat)
|
|
214
|
+
hydra-acp acp # explicit shim mode (forces shim regardless of TTY)
|
|
215
|
+
hydra-acp shim # alias for `acp`, kept for backward compatibility
|
|
329
216
|
hydra-acp tui # explicit terminal-UI mode
|
|
330
217
|
hydra-acp launch <agent> # launcher mode: shim that forces the
|
|
331
218
|
# daemon to spawn <agent> on session/new
|
|
332
|
-
hydra-acp cat [-p <prompt>] [--detach]
|
|
219
|
+
hydra-acp cat [-p <prompt>] [--detach] [--raw]
|
|
220
|
+
# pipe-friendly headless mode: feeds stdin
|
|
333
221
|
# to a session as prompts and streams the
|
|
334
|
-
# agent's reply to stdout
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
222
|
+
# agent's reply to stdout. --raw bypasses the
|
|
223
|
+
# markdown renderer and emits chunks as they land
|
|
224
|
+
|
|
225
|
+
# Session selection — valid on any entry point (tui / acp / launch / cat)
|
|
226
|
+
hydra-acp --session <id-or-url> # attach to existing session, by id or hydra:// URL
|
|
227
|
+
hydra-acp --reattach # reattach to this terminal's last session
|
|
228
|
+
# (falls back to the most-recent one for cwd)
|
|
338
229
|
hydra-acp --new # force a fresh session
|
|
339
230
|
hydra-acp --readonly # open a session as a transcript viewer (with --session)
|
|
231
|
+
hydra-acp --dangerously-skip-permissions # auto-approve every tool permission request
|
|
340
232
|
|
|
341
|
-
hydra-acp init
|
|
233
|
+
hydra-acp init [--rotate-token] # generate the service token
|
|
234
|
+
hydra-acp version [--json] # CLI, daemon, and extension/transformer versions
|
|
342
235
|
|
|
343
236
|
hydra-acp daemon [status] # output status of daemon
|
|
344
237
|
hydra-acp daemon start [--foreground] # detached by default; --foreground to attach
|
|
@@ -346,7 +239,17 @@ hydra-acp daemon stop # stop running daemon
|
|
|
346
239
|
hydra-acp daemon restart # stop then start the daemon
|
|
347
240
|
hydra-acp daemon log [-f] [-n N] # tail (default 50) or follow the daemon log
|
|
348
241
|
|
|
349
|
-
hydra-acp session [list]
|
|
242
|
+
hydra-acp session [list] [--all] [--json] [--host <h>] [--columns <list>]
|
|
243
|
+
# list sessions (live + 20 most-recent cold).
|
|
244
|
+
# --all lifts the cold cap and surfaces
|
|
245
|
+
# non-interactive sessions; --host filters by origin
|
|
246
|
+
# machine ('local' default, 'all', or a hostname);
|
|
247
|
+
# --columns picks/orders columns, e.g.
|
|
248
|
+
# --columns=session,state,title,cost
|
|
249
|
+
hydra-acp session share [<id>] [--host <name>]
|
|
250
|
+
# print a hydra:// URL the recipient can paste
|
|
251
|
+
# into --session (defaults to the most-recent
|
|
252
|
+
# session for cwd)
|
|
350
253
|
hydra-acp session info <id> [--verbose] [--json] [--diff] [--fold] [--no-color] [--no-pager]
|
|
351
254
|
# aggregate one session: turn count, tool histogram,
|
|
352
255
|
# files touched, cost/duration, synopsis.
|
|
@@ -361,6 +264,10 @@ hydra-acp session diff <id> [--json] [--no-color] [--no-pager] [--fold]
|
|
|
361
264
|
# same region (agent thrash) into one net-effect hunk.
|
|
362
265
|
hydra-acp session kill <id> # close a live session (keeps the on-disk record so it can be resurrected)
|
|
363
266
|
hydra-acp session remove <id> # remove a session entirely (live or cold)
|
|
267
|
+
hydra-acp session collect [--max-age-days <n>] [--limit <n>] [--json]
|
|
268
|
+
# delete cold sessions that never became a real
|
|
269
|
+
# conversation (cat one-shots, editor panels with
|
|
270
|
+
# no turn). The daemon also runs this on a timer.
|
|
364
271
|
hydra-acp session export <id> [--out <file>|.]
|
|
365
272
|
# write a session bundle (meta + history) to <file>,
|
|
366
273
|
# to a default-named file when --out=., or to stdout
|
|
@@ -387,7 +294,12 @@ hydra-acp transformer start|stop|restart <n> # lifecycle on a running transforme
|
|
|
387
294
|
hydra-acp transformer log <name> [-f] [-n] # tail (default 50) or follow a transformer's log
|
|
388
295
|
|
|
389
296
|
hydra-acp agent [list] # list agents in the registry
|
|
297
|
+
hydra-acp agent auth <id> # best-effort: drive <id>'s own login flow from
|
|
298
|
+
# hydra. Registry auth-method coverage is patchy;
|
|
299
|
+
# running the agent's own CLI is more reliable
|
|
390
300
|
hydra-acp agent install <id> # pre-install an agent (else lazy on first use)
|
|
301
|
+
hydra-acp agent uninstall <id> # delete <id>'s cached install so the next
|
|
302
|
+
# session re-downloads it
|
|
391
303
|
hydra-acp agent set [<id>] [model] # with no args, report the daemon's current default
|
|
392
304
|
# agent and its default model. With <id>, set <id> as
|
|
393
305
|
# the default agent (config.defaultAgent). With <id>
|
|
@@ -398,13 +310,34 @@ hydra-acp agent refresh # force a registry re-fetch
|
|
|
398
310
|
hydra-acp agent sync <id> # spawn <id> just long enough to ACP session/list it,
|
|
399
311
|
# then persist any sessions it remembers as cold rows
|
|
400
312
|
# (lets you bring in pre-existing agent sessions)
|
|
313
|
+
hydra-acp agent add <id> [--command CMD] [--args A,B,C] [--env K=V]
|
|
314
|
+
# define a local agent that bypasses the registry
|
|
315
|
+
# (e.g. your system `opencode`)
|
|
316
|
+
hydra-acp agent remove <id> # remove a local agent (config only)
|
|
317
|
+
hydra-acp agent pin <id> [packageSpec] # pin a registry agent to an npm version; omit
|
|
318
|
+
# packageSpec to clear. Sidesteps a bad upstream publish
|
|
319
|
+
hydra-acp agent log <id> [-f] [-n N] # tail or follow an agent's spawn/stderr log
|
|
320
|
+
|
|
321
|
+
hydra-acp registry pin | unpin # freeze the daemon on its cached registry, or
|
|
322
|
+
# resume normal TTL fetching
|
|
323
|
+
|
|
324
|
+
hydra-acp config [list] [<dotted.key>] # print effective config (or one subtree) as JSON
|
|
325
|
+
hydra-acp config get <dotted.key> # print one effective value (e.g. tui.mouse)
|
|
326
|
+
hydra-acp config set <dotted.key> <value> # persist a value, validated against the schema
|
|
327
|
+
hydra-acp config unset <dotted.key> # revert a key to its default
|
|
328
|
+
hydra-acp config path # print the config file path
|
|
401
329
|
|
|
402
330
|
hydra-acp auth # list active session tokens
|
|
403
331
|
hydra-acp auth password [--force] # set the daemon's master password
|
|
404
332
|
hydra-acp auth revoke <id> # revoke a session token
|
|
405
333
|
```
|
|
406
334
|
|
|
407
|
-
|
|
335
|
+
Any `hydra-acp <name>` that isn't a built-in verb is exec'd as `hydra-acp-<name>`
|
|
336
|
+
from PATH, git-style. Ecosystem packages like `@hydra-acp/planner` hang their
|
|
337
|
+
own subcommands off that mechanism. `hydra-acp --help` is authoritative and
|
|
338
|
+
covers flags this table leaves out; `hydra-acp <verb> --help` scopes it to one verb.
|
|
339
|
+
|
|
340
|
+
A bare invocation (`hydra-acp` with no subcommand) auto-dispatches based on whether stdout is a TTY: a real terminal launches the TUI, a piped stdio (the editor-spawned case) drops into shim mode. Pass `acp` or `tui` explicitly to force one or the other. Editors should configure `hydra-acp acp` so the choice is unambiguous regardless of how the editor wires stdio.
|
|
408
341
|
|
|
409
342
|
### Launcher mode
|
|
410
343
|
|
|
@@ -412,52 +345,74 @@ A bare invocation (`hydra-acp` with no subcommand) auto-dispatches based on whet
|
|
|
412
345
|
|
|
413
346
|
```text
|
|
414
347
|
# Configure your editor's ACP-launch command to:
|
|
415
|
-
hydra-acp launch claude
|
|
348
|
+
hydra-acp launch claude
|
|
416
349
|
```
|
|
417
350
|
|
|
418
|
-
When the editor sends `session/new`, the shim injects the agent id under `_meta["hydra-acp"].agentId` (
|
|
351
|
+
When the editor sends `session/new`, the shim injects the resolved agent id under `_meta["hydra-acp"].agentId` (here `"claude-acp"`) before forwarding to the daemon — the spec `session/new` params stay clean. The daemon resolves it against the cached ACP Registry, downloads/installs the agent on first use under `~/.hydra-acp/agents/`, and spawns the subprocess. The editor sees a normal ACP agent. From then on, `hydra-acp session` lists the live session and any other client can `session/attach` to it.
|
|
419
352
|
|
|
420
|
-
`<agent>` is
|
|
353
|
+
`<agent>` is a registry id or any shorthand that resolves to one — e.g. `claude`, `pi`, `codex`. Run `hydra-acp agent` to browse what's available.
|
|
421
354
|
|
|
422
|
-
If both `launch <agent>` and `--session
|
|
355
|
+
If both `launch <agent>` and `--session` are given, `--session` wins (attach mode); the agent is ignored because the agent process is already running.
|
|
423
356
|
|
|
424
|
-
###
|
|
357
|
+
### Registry id resolution
|
|
425
358
|
|
|
426
|
-
|
|
359
|
+
This is the machinery behind the short names in [Choosing an agent](#choosing-an-agent). When you ask hydra to spawn an agent (via `launch <agent>`, `--agent`, `HYDRA_ACP_AGENT`, or `config.defaultAgent`), the daemon walks a ladder of matches and takes the first hit:
|
|
427
360
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
361
|
+
1. A local agent from `config.agents` (see `hydra-acp agent add`), by exact id or with an implied `-acp` suffix. Locals shadow the registry entirely.
|
|
362
|
+
2. Exact match on the registry's `id` field.
|
|
363
|
+
3. The **npx package basename** — the segment after the last `/` and before the version `@` — so a package's own binary name works.
|
|
364
|
+
4. Your input plus an implied **`-acp` suffix**, case-insensitive. This is why the short forms below work.
|
|
365
|
+
5. A **unique case-insensitive prefix**. Ambiguous prefixes match nothing rather than guessing.
|
|
433
366
|
|
|
434
|
-
|
|
367
|
+
| You spawn… | Registry `id` | Resolves via |
|
|
368
|
+
|---|---|---|
|
|
369
|
+
| `claude-acp` | `claude-acp` | exact id |
|
|
370
|
+
| `claude` | `claude-acp` | implied `-acp` suffix |
|
|
371
|
+
| `pi` | `pi-acp` | implied `-acp` suffix |
|
|
372
|
+
| `codex` | `codex-acp` | implied `-acp` suffix |
|
|
435
373
|
|
|
436
|
-
|
|
374
|
+
So in practice you type the short name and hydra finds it. Prefix matching is the last resort and only fires when exactly one id matches — `cod` is ambiguous between `codex-acp` and `codebuddy-code`, so it resolves to nothing rather than picking one.
|
|
437
375
|
|
|
438
|
-
|
|
376
|
+
### Slash commands
|
|
439
377
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
```
|
|
378
|
+
Two different sets, which is worth knowing before you go looking for one in the
|
|
379
|
+
wrong place.
|
|
443
380
|
|
|
444
|
-
|
|
381
|
+
**`/hydra <verb>` is server-side**, intercepted by the daemon before the prompt
|
|
382
|
+
reaches the agent. They work from anywhere a prompt can be typed: the TUI, the
|
|
383
|
+
Slack thread, the browser composer, agent-shell. They never appear in the
|
|
384
|
+
conversation log.
|
|
445
385
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
386
|
+
| Command | Effect |
|
|
387
|
+
|---|---|
|
|
388
|
+
| `/hydra title [text]` | Regenerate the title and synopsis via the agent, or set the title directly with an argument. |
|
|
389
|
+
| `/hydra agent <id \| status>` | Schedule a swap to a different agent. Synthesizes a brief in the target's idiom and rotates on idle; `status` reports a pending swap. |
|
|
390
|
+
| `/hydra config [<id> [<value>]]` | List or set an agent-advertised config option (model, mode, effort). No args lists them; `<id>` shows its choices; `<id> <value>` applies one. |
|
|
391
|
+
| `/hydra compact [status]` | Compact history now. `status` inspects state without triggering. |
|
|
392
|
+
| `/hydra uncompact` | Roll back the most recent compaction, before any new turns. |
|
|
393
|
+
| `/hydra fork [verbatim]` | Fork into a new session. Default is a synopsis brief; `verbatim` slices at the last completed turn. |
|
|
394
|
+
| `/hydra restart` | Restart the agent with a fresh `session/new`, preserving history. Useful when the available models have changed underneath you. |
|
|
395
|
+
| `/hydra kill` | Close this session. The agent dies; the record is kept and can be resumed. |
|
|
449
396
|
|
|
450
|
-
|
|
397
|
+
Extensions and transformers register their own, reachable as `/hydra <name>
|
|
398
|
+
<verb>` — and the `hydra-acp-` prefix can be elided, so `/hydra planner status`
|
|
399
|
+
routes to `hydra-acp-planner`.
|
|
451
400
|
|
|
452
|
-
|
|
401
|
+
**The TUI has its own set**, which only work there:
|
|
453
402
|
|
|
454
403
|
| Command | Effect |
|
|
455
404
|
|---|---|
|
|
456
|
-
| `/
|
|
457
|
-
| `/
|
|
458
|
-
| `/
|
|
459
|
-
|
|
460
|
-
|
|
405
|
+
| `/help` | The built-in command list |
|
|
406
|
+
| `/btw <prompt>` | Ask a side question on a throwaway fork of this session, answered in an overlay, main conversation untouched. No args toggles the last overlay. |
|
|
407
|
+
| `/export [path]` | Write this session out as a markdown transcript |
|
|
408
|
+
| `/session <id \| next \| prev>` | Switch session; no argument opens the picker |
|
|
409
|
+
| `/model <id>`, `/agent <id>` | Switch model or agent via config options |
|
|
410
|
+
| `/rename [title]`, `/sessions`, `/resume`, `/clear`, `/quit` | Rename, list, re-pick, clear scrollback, exit |
|
|
411
|
+
|
|
412
|
+
`/btw` is the one worth trying early. It forks the conversation, asks your
|
|
413
|
+
question on the fork, and shows the answer in an overlay, so a "wait, what does
|
|
414
|
+
this error mean" detour doesn't end up in the transcript of the thing you were
|
|
415
|
+
actually doing. A follow-up `/btw` reuses the still-warm fork.
|
|
461
416
|
|
|
462
417
|
### Exporting and importing sessions
|
|
463
418
|
|
|
@@ -474,46 +429,19 @@ Each session carries a stable **`lineageId`** that survives every export/import
|
|
|
474
429
|
|
|
475
430
|
The first attach to an imported session is slow: hydra spawns a fresh agent, runs `session/new`, and feeds the imported history back in as a synthesized takeover transcript (same machinery as `/hydra agent`). Subsequent attaches use the normal `session/load` path. This is a text-level handover — the originating agent's internal state (tool-call chains, compacted earlier turns) isn't preserved, so the resumed conversation may be cognitively shallower than the original.
|
|
476
431
|
|
|
477
|
-
|
|
432
|
+
## Config
|
|
478
433
|
|
|
479
|
-
|
|
434
|
+
Config lives in `~/.hydra-acp/config.json`. You can edit it directly, or go
|
|
435
|
+
through the `config` verb, which validates against the schema before writing:
|
|
480
436
|
|
|
481
|
-
```
|
|
482
|
-
hydra-acp
|
|
437
|
+
```sh
|
|
438
|
+
hydra-acp config # dump the effective config
|
|
439
|
+
hydra-acp config get tui.mouse # read one value
|
|
440
|
+
hydra-acp config set tui.mouse true # write one value
|
|
441
|
+
hydra-acp config unset tui.mouse # back to the default
|
|
483
442
|
```
|
|
484
443
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
### Flag/env equivalence
|
|
488
|
-
|
|
489
|
-
Every config-knob flag has an `HYDRA_ACP_FOO_BAR` env-var equivalent. Flag wins over env; env wins over default.
|
|
490
|
-
|
|
491
|
-
| Flag | Env var |
|
|
492
|
-
|---|---|
|
|
493
|
-
| `--name` | `HYDRA_ACP_NAME` |
|
|
494
|
-
| `--agent` | `HYDRA_ACP_AGENT` |
|
|
495
|
-
| `--model` | `HYDRA_ACP_MODEL` |
|
|
496
|
-
| `--session` | `HYDRA_ACP_SESSION` |
|
|
497
|
-
|
|
498
|
-
`--model` is a one-shot override for the per-agent `defaultModels` entry in `~/.hydra-acp/config.json`. It only applies at fresh session creation — resurrect and `/hydra agent` switch ignore it (resurrected sessions stay on whatever model they were last using).
|
|
499
|
-
|
|
500
|
-
Action commands (`init`, `daemon`, `session`, `extension`, `transformer`, `agent`, `auth`, `cat`, `--help`, `--version`, `--rotate-token`) are not config knobs and are flag-only.
|
|
501
|
-
|
|
502
|
-
### Registry id resolution
|
|
503
|
-
|
|
504
|
-
When you ask hydra to spawn an agent (via `launch <agent>`, `--agent`, or `HYDRA_ACP_AGENT`), the daemon first tries an exact match against the ACP Registry's `id` field. If nothing matches, it falls back to matching against the **npx package basename** (the segment after the last `/` and before the version `@`). That means common binary names work transparently:
|
|
505
|
-
|
|
506
|
-
| You spawn… | Registry `id` | Resolves via |
|
|
507
|
-
|---|---|---|
|
|
508
|
-
| `claude-acp` | `claude-acp` | exact id |
|
|
509
|
-
| `claude-agent-acp` | `claude-acp` | npx package basename `claude-agent-acp` |
|
|
510
|
-
| `gemini` | `gemini` | exact id |
|
|
511
|
-
| `gemini-cli` | `gemini` | npx package basename `gemini-cli` |
|
|
512
|
-
| `codex-acp` | `codex-acp` | exact id |
|
|
513
|
-
|
|
514
|
-
## Config
|
|
515
|
-
|
|
516
|
-
`~/.hydra-acp/config.json`:
|
|
444
|
+
A minimal file looks like this:
|
|
517
445
|
|
|
518
446
|
```json
|
|
519
447
|
{
|
|
@@ -526,7 +454,7 @@ When you ask hydra to spawn an agent (via `launch <agent>`, `--agent`, or `HYDRA
|
|
|
526
454
|
"url": "https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json",
|
|
527
455
|
"ttlHours": 24
|
|
528
456
|
},
|
|
529
|
-
"defaultAgent": "claude-
|
|
457
|
+
"defaultAgent": "claude-acp"
|
|
530
458
|
}
|
|
531
459
|
```
|
|
532
460
|
|
|
@@ -534,13 +462,134 @@ The service token lives in its own file (`~/.hydra-acp/auth-token`, mode 0600) a
|
|
|
534
462
|
|
|
535
463
|
`daemon.sessionIdleTimeoutSeconds` (default 3600 — one hour) controls how long a session with no recorded agent or user activity stays alive before the daemon closes it. Snapshot-shaped state pings (model/mode/title/commands) and bare attach/detach don't count as activity — only recordable broadcasts (prompts, agent chunks, tool calls, permission prompts) do, so persistent observer clients like the slack/notifier/approver/browser extensions can't pin a quiet session open. In-flight turns and unresolved permission requests defer the close until they settle. The disk record stays so the session can be resurrected later via `session/load`, at which point extensions re-attach automatically through their poll loops. Set to `0` to disable.
|
|
536
464
|
|
|
537
|
-
`
|
|
465
|
+
`sessionListColdLimit` (top-level, default 20) caps how many cold (disk-only) sessions `hydra-acp session` lists. Live sessions are always shown; cold ones are sorted by recency and truncated to this count. `--all` lifts the cap and `--json` ignores it. Set to `0` to never list cold sessions.
|
|
466
|
+
|
|
467
|
+
`daemon.sessionGcMaxAgeDays` (default 2) is why a session can vanish from the list entirely: a background sweep deletes *non-interactive* cold records — one-shot `hydra-acp cat` runs and editor panels that never took a turn — once they're older than this. Sessions that held a real conversation are never swept. `daemon.sessionGcIntervalMinutes` (default 60) sets how often the sweep runs, and `hydra-acp session collect` triggers it by hand.
|
|
468
|
+
|
|
469
|
+
### Themes and the options panel
|
|
470
|
+
|
|
471
|
+
`^O` opens the options panel: tools, plan, thoughts, diffs, mouse, enter,
|
|
472
|
+
sidebar, and theme. `↑/↓` picks a row, `←/→` cycles its value, `s` saves the
|
|
473
|
+
current value as your default, `Esc` closes.
|
|
474
|
+
|
|
475
|
+
The theme row recolours the UI live as you cycle, so the quickest way to choose
|
|
476
|
+
one is to sit on that row and walk through them. Eighteen ship built in, and
|
|
477
|
+
`tui.theme` accepts a custom one if you want to write your own.
|
|
538
478
|
|
|
539
479
|
`tui.mouse` (default `false`) controls whether the TUI captures mouse events. With capture off (the default), plain click-drag selects text via your terminal emulator, but wheel-driven scrollback stops working — use `PgUp` / `PgDn` instead. Set to `true` to enable capture, which lets the scroll wheel drive scrollback at the cost of requiring `shift+drag` to select text.
|
|
540
480
|
|
|
541
481
|
`tui.defaultEnterAction` (default `"amend"`) controls what the unmodified Enter key does in the prompt composer. With `"amend"` (the default), Enter amends the in-flight turn and `Shift+Enter` enqueues a new prompt; with no turn in flight either key just enqueues, since there's nothing to amend. Set to `"enqueue"` to flip the two: Enter enqueues (sends immediately when idle, queues behind an in-flight turn) and `Shift+Enter` amends.
|
|
542
482
|
|
|
543
|
-
|
|
483
|
+
## Extensions
|
|
484
|
+
|
|
485
|
+
Hydra can spawn user-configured extension processes when the daemon starts. Extensions are arbitrary commands — written in any language — that talk to the daemon over its existing REST or WSS endpoints. Hydra handles their lifecycle (spawn on start, kill on stop, auto-restart on crash with exponential backoff up to ~60s) and injects daemon connection info via env vars.
|
|
486
|
+
|
|
487
|
+
Various ready-made extensions ship under the same `@hydra-acp` npm scope. All are optional and can be installed independently.
|
|
488
|
+
|
|
489
|
+
**[`@hydra-acp/slack`](https://github.com/smagnuso/hydra-acp-slack) — Slack thread bridge.** Each hydra session gets its own Slack thread; the agent's prose, tool cards, plans, and permission prompts stream in, and replies typed in the thread come back to the agent as user prompts. Useful for non-developer collaborators, or for driving an agent from your phone while you're away from the keyboard. Respects RFD #533's `prompt_received` and survives daemon restarts via session resurrection.
|
|
490
|
+
|
|
491
|
+
```sh
|
|
492
|
+
npm install -g @hydra-acp/slack
|
|
493
|
+
hydra-acp extension add hydra-acp-slack
|
|
494
|
+
hydra-acp extension restart hydra-acp-slack
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
You'll also need a Slack app and a config at `~/.hydra-acp-slack.conf` — see the [package's setup section](https://github.com/smagnuso/hydra-acp-slack#setup) for scopes, tokens, and authorized users.
|
|
498
|
+
|
|
499
|
+
**[`@hydra-acp/browser`](https://github.com/smagnuso/hydra-acp-browser) — local web UI.** Single-page app that lists live sessions, attaches to each one, and renders the transcript (agent messages, tool calls, plans, mode/model changes) with a composer for prompting and permission widgets for approving tool use. Cheap to bring up when you want to spot-check an agent without firing up the editor.
|
|
500
|
+
|
|
501
|
+
```sh
|
|
502
|
+
npm install -g @hydra-acp/browser
|
|
503
|
+
hydra-acp extension add hydra-acp-browser
|
|
504
|
+
hydra-acp extension restart hydra-acp-browser
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
The first launch generates `~/.hydra-acp-browser/authkey` and writes the open URL (with `?authkey=…`) to `~/.hydra-acp-browser/link`. Defaults to localhost-only; see the [package's HTTPS section](https://github.com/smagnuso/hydra-acp-browser#https) for binding to a LAN address with TLS.
|
|
508
|
+
|
|
509
|
+
**[`@hydra-acp/notifier`](https://github.com/smagnuso/hydra-acp-notifier) — desktop notifications.** Always-on companion that fires `notify-send` (Linux) or `osascript` (macOS) when sessions emit notable events — by default, `turn_complete`. The default title is `🐉 <agentId> · <short-session-id> · <session-title-or-cwd>` and the body renders the agent's stop reason as friendly text (`Finished`, `Max token limit reached`, etc.). Drop a JS rule at `~/.hydra-acp/notifier.config.js` to customize per-event, or set `HYDRA_ACP_NOTIFY_CMD` to route everything to ntfy/Pushover/your phone.
|
|
510
|
+
|
|
511
|
+
```sh
|
|
512
|
+
npm install -g @hydra-acp/notifier
|
|
513
|
+
hydra-acp extension add hydra-acp-notifier
|
|
514
|
+
hydra-acp extension start hydra-acp-notifier
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
**[`@hydra-acp/approver`](https://github.com/smagnuso/hydra-acp-approver) — headless permission auto-responder.** Attaches to every live session and answers `session/request_permission` based on a JS rule at `~/.hydra-acp/approver.config.js`. When the rule returns an `optionId` it wins the race and dismisses the prompt before any human client sees it; when it abstains (returns `null`), the prompt stays open for your interactive clients. Useful for centralizing approval policy in one place so per-client approval can go away.
|
|
518
|
+
|
|
519
|
+
```sh
|
|
520
|
+
npm install -g @hydra-acp/approver
|
|
521
|
+
hydra-acp extension add hydra-acp-approver
|
|
522
|
+
hydra-acp extension start hydra-acp-approver
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
Without a config file the approver abstains on everything — installing it has no behavioral effect until you write a rule.
|
|
526
|
+
|
|
527
|
+
**[`@hydra-acp/archiver`](https://github.com/smagnuso/hydra-acp-archiver) — cross-machine session sync.** Uploads session bundles to a shared backend (Google Drive, plain filesystem) after every turn and imports peers' bundles in the background, so a session started on machine A shows up on machine B without manual export/import. Imported sessions carry an `importedFromMachine` breadcrumb that the picker, browser, slack, and `sessions list` honor for host filtering.
|
|
528
|
+
|
|
529
|
+
```sh
|
|
530
|
+
npm install -g @hydra-acp/archiver
|
|
531
|
+
hydra-acp extension add hydra-acp-archiver
|
|
532
|
+
hydra-acp extension start hydra-acp-archiver
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
See the [package README](https://github.com/smagnuso/hydra-acp-archiver#readme) for backend setup (Drive OAuth, filesystem path).
|
|
536
|
+
|
|
537
|
+
**[`@hydra-acp/planner`](https://github.com/smagnuso/hydra-acp-planner) — multi-agent project orchestrator.** Invoked via `/hydra planner create <description>` from any session: asks the host agent to decompose the project into a task DAG, then spawns N worker sessions and drives them in parallel by prompt management, with progress streaming back into your original chat. Boards persist under `~/.hydra-acp/planner/projects/<id>/` so plans survive daemon restarts. Strictly speaking it's a [transformer](#transformers), not an extension — it sits in the daemon's message pipeline rather than attaching as a client — but it installs and configures the same way.
|
|
538
|
+
|
|
539
|
+
```sh
|
|
540
|
+
npm install -g @hydra-acp/planner
|
|
541
|
+
hydra-acp transformer add hydra-acp-planner
|
|
542
|
+
hydra-acp transformer start hydra-acp-planner
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
See the [package README](https://github.com/smagnuso/hydra-acp-planner#readme) for the full set of `/hydra planner` verbs (status, retry, cancel, …) and the task-DAG format.
|
|
546
|
+
|
|
547
|
+
### Configuring extensions
|
|
548
|
+
|
|
549
|
+
Configure in `~/.hydra-acp/config.json`:
|
|
550
|
+
|
|
551
|
+
```json
|
|
552
|
+
{
|
|
553
|
+
"extensions": {
|
|
554
|
+
"hydra-acp-slack": {},
|
|
555
|
+
"hydra-acp-browser": {
|
|
556
|
+
"command": ["hydra-acp-browser"],
|
|
557
|
+
"args": ["--port", "9999"],
|
|
558
|
+
"env": { "UI_THEME": "dark" }
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
If `command` is omitted, it defaults to `[<name>]` — useful when the package's `bin` matches its key (e.g. `npm install -g @hydra-acp/slack` exposes `hydra-acp-slack` on PATH, so `"hydra-acp-slack": {}` is enough).
|
|
565
|
+
|
|
566
|
+
Each extension is launched with these env vars set:
|
|
567
|
+
|
|
568
|
+
| Env var | Example |
|
|
569
|
+
|---|---|
|
|
570
|
+
| `HYDRA_ACP_DAEMON_URL` | `http://127.0.0.1:55514` |
|
|
571
|
+
| `HYDRA_ACP_DAEMON_HOST` | `127.0.0.1` |
|
|
572
|
+
| `HYDRA_ACP_DAEMON_PORT` | `55514` |
|
|
573
|
+
| `HYDRA_ACP_TOKEN` | `hydra_token_<hex>` |
|
|
574
|
+
| `HYDRA_ACP_WS_URL` | `ws://127.0.0.1:55514/acp` |
|
|
575
|
+
| `HYDRA_ACP_HOME` | `~/.hydra-acp` |
|
|
576
|
+
| `HYDRA_ACP_EXTENSION_NAME` | the `name` from config |
|
|
577
|
+
|
|
578
|
+
Extension stdout/stderr are appended to `~/.hydra-acp/extensions/<name>/current.log` (a symlink to the active rotated file). Logs rotate at 5 MB with the 5 most recent files retained as `<name>.<N>.log` alongside the symlink (~25 MB per extension).
|
|
579
|
+
|
|
580
|
+
While the daemon is running you can manage extensions without bouncing it:
|
|
581
|
+
|
|
582
|
+
```text
|
|
583
|
+
hydra-acp extension list
|
|
584
|
+
hydra-acp extension restart hydra-acp-slack
|
|
585
|
+
hydra-acp extension log hydra-acp-slack --follow
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
`stop` suppresses the auto-restart backoff; the extension stays down until the next `start`, `restart`, or daemon bounce. `add`/`remove` are config-only — restart the daemon to apply. Per-extension config (env vars, args, custom command paths) goes in the same `extensions` block. `hydra-acp extension log <name> -f` tails an extension's stdout/stderr if you need to debug.
|
|
589
|
+
|
|
590
|
+
**Trust model**: each extension receives its own per-process token scoped to that process's lifetime. The token grants the same read/write access to the daemon's REST and WSS surfaces as a logged-in client. Treat extensions as part of your trusted compute base — review extensions before installing and don't run untrusted code through this mechanism. See `cli/examples/client-observe.mjs` for an annotated reference implementation.
|
|
591
|
+
|
|
592
|
+
## Transformers
|
|
544
593
|
|
|
545
594
|
Transformers are a second kind of daemon-managed process. Where an extension is a *client* — it observes broadcast events and sends prompts — a transformer is *middleware*: it sits inside the daemon's message pipeline and sees every in-flight ACP message before the daemon acts on it, in both directions.
|
|
546
595
|
|
|
@@ -576,22 +625,127 @@ See `cli/examples/transformer-observe.mjs` for a working reference that logs all
|
|
|
576
625
|
|
|
577
626
|
**Trust model**: transformers receive the same per-process scoped token as extensions, but have structurally more access — they intercept traffic that no client ever sees. The transformer-specific methods are only callable with a transformer-kind token; an extension process that tries to call them receives `MethodNotFound`. Treat every entry in `transformers` as a higher-trust boundary than `extensions`.
|
|
578
627
|
|
|
579
|
-
|
|
628
|
+
## Cat mode
|
|
580
629
|
|
|
581
|
-
|
|
630
|
+
`hydra-acp cat` is a pipe-friendly headless verb: it feeds stdin to a fresh
|
|
631
|
+
session as the user prompt and streams the agent's text reply to stdout. No
|
|
632
|
+
TUI, no JSON-RPC for the caller, no terminal control sequences in the
|
|
633
|
+
output — just text in, text out, exit code 0 on a clean turn. Hydra ends up
|
|
634
|
+
usable as a unix filter, with the agent as the program in the middle of the
|
|
635
|
+
pipeline.
|
|
582
636
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
637
|
+
A few properties keep it well-behaved:
|
|
638
|
+
|
|
639
|
+
- **Sandboxed cwd by default.** Piped invocations get a fresh empty tempdir as
|
|
640
|
+
the agent's `cwd`, and the permission handler rejects every tool call that
|
|
641
|
+
isn't one of the `hydra-acp-stdin` MCP tools (head / tail / grep / read on
|
|
642
|
+
the piped bytes). The agent has nothing to look at except the data you
|
|
643
|
+
piped in. Override with `--cwd <path>` when you want it poking at the
|
|
644
|
+
project (e.g. "find docs in the codebase that mention this error").
|
|
645
|
+
- **Smart about size.** Small inputs are inlined into the prompt. Large inputs
|
|
646
|
+
(default >1 MiB) get the daemon's in-memory `hydra-acp-stdin` MCP server:
|
|
647
|
+
bytes flow into a ring buffer and the agent pulls them on demand via
|
|
648
|
+
`head`, `tail`, `grep`, `read`, and `info`. A multi-gigabyte log isn't a
|
|
649
|
+
context-window problem; it's a fixed-size buffer the agent samples.
|
|
650
|
+
- **`--follow` for live streams.** Pipe `tail -f` into `--follow` and each quiet
|
|
651
|
+
burst on stdin is sent as a new turn. The standing prompt (`-p`) is sent
|
|
652
|
+
only on the first turn; later turns carry just the new bytes.
|
|
653
|
+
- **`--detach` to share the session.** By default the session lives as long as
|
|
654
|
+
the cat process; on stdin EOF it dies. With `--detach` it stays in the
|
|
655
|
+
daemon, `hydra-acp session` lists it, and the slack / browser / notifier
|
|
656
|
+
extensions can ride on it. Useful for kicking off a long-running watch
|
|
657
|
+
from a shell script and following it on your phone.
|
|
658
|
+
|
|
659
|
+
A few examples:
|
|
660
|
+
|
|
661
|
+
```sh
|
|
662
|
+
# One-shot question, no stdin.
|
|
663
|
+
hydra-acp -p "tools to convert a HEIC photo to JPEG on linux?"
|
|
664
|
+
|
|
665
|
+
# Analyze a big log without copy-pasting it into a chat window.
|
|
666
|
+
journalctl -u nginx --since "1 hour ago" | hydra-acp cat -p "anything alarming?"
|
|
667
|
+
|
|
668
|
+
# Treat hydra as the filter in a unix pipeline — output is plain text,
|
|
669
|
+
# so tee / grep / jq downstream just work.
|
|
670
|
+
git log --since="last monday" --pretty=full | hydra-acp cat -p "draft release notes, one bullet per user-visible change, grouped by version" | tee RELEASE_NOTES.md
|
|
671
|
+
|
|
672
|
+
# Watch a live log and only speak up when something's wrong. --detach
|
|
673
|
+
# keeps the session in the daemon, so you can follow it on your phone
|
|
674
|
+
# via the slack extension after closing the shell.
|
|
675
|
+
tail -F /var/log/app.log | hydra-acp cat --follow --detach -p "if a line looks like an error or stack trace, summarize it. otherwise stay silent."
|
|
592
676
|
```
|
|
593
677
|
|
|
594
|
-
|
|
678
|
+
Sessions created by `cat` are normal hydra sessions, so `hydra-acp session`,
|
|
679
|
+
`session export`, `/hydra title`, and the rest of the surface all work on them.
|
|
680
|
+
|
|
681
|
+
## How it's built
|
|
682
|
+
|
|
683
|
+
The usage sections above are enough to run hydra; this section is the protocol detail underneath them.
|
|
684
|
+
|
|
685
|
+
Under the hood, `hydra-acp` is a daemon + CLI shim that implements two open ACP RFDs as a single coherent surface, on top of the standard ACP protocol (including `session/list` for session discovery), plus the official ACP Registry as its agent-distribution mechanism.
|
|
686
|
+
|
|
687
|
+
### The standards it stitches together
|
|
688
|
+
|
|
689
|
+
ACP itself is the [Agent Client Protocol](https://agentclientprotocol.com/) — a JSON-RPC 2.0 protocol between editors (clients) and AI coding agents. Today the protocol is canonically a 1:1 stdio relationship: one editor spawns one agent and owns its stdin/stdout. Two RFDs in the [`agentclientprotocol/agent-client-protocol`](https://github.com/agentclientprotocol/agent-client-protocol) repo extend that model. `hydra-acp` is one daemon that implements both together so they can be used as a coherent system rather than two independent extensions.
|
|
690
|
+
|
|
691
|
+
#### 1. Multi-Client Session Attach — [RFD #533](https://github.com/agentclientprotocol/agent-client-protocol/pull/533)
|
|
692
|
+
|
|
693
|
+
Adds two new methods that turn ACP from 1:1 into 1:N:
|
|
694
|
+
|
|
695
|
+
- **`session/attach { sessionId, historyPolicy, clientInfo? }`** — a second (or third, or N-th) client connects to a session that's already live. `historyPolicy` controls replay on attach: `"full"`, `"pending_only"`, or `"none"`.
|
|
696
|
+
- **`session/detach { sessionId }`** — graceful disconnect; the session continues as long as one client remains attached.
|
|
697
|
+
|
|
698
|
+
Every event the agent emits is broadcast to every attached client; clients self-filter what they act on. Permission requests broadcast the same way: the first response wins, and the rest receive a `session/update` notification with `sessionUpdate: "permission_resolved"`. Capability is advertised in `initialize` under `agentCapabilities.sessionCapabilities.attach`.
|
|
699
|
+
|
|
700
|
+
#### 2. Streamable HTTP & WebSocket Transport — [RFD: streamable-http-websocket-transport](https://agentclientprotocol.com/rfds/streamable-http-websocket-transport) (WebSocket profile only)
|
|
701
|
+
|
|
702
|
+
Defines the network transport that lets ACP run between processes that aren't parent and child. The RFD specifies two profiles on one `/acp` endpoint: a Streamable HTTP profile (POST/GET-SSE/DELETE with `Acp-Connection-Id` and `Acp-Session-Id` headers, HTTP/2 required) and a WebSocket profile (GET with `Upgrade: websocket`). The RFD explicitly permits servers to support **only** the WebSocket profile, and that's the route `hydra-acp` takes — the Streamable HTTP half isn't implemented. The RFD moved to Active in July 2026 and is targeted at ACP v1, but its reference implementation is still in flight, so deferring HTTP-transport work until the spec settles is deliberate.
|
|
703
|
+
|
|
704
|
+
On the WebSocket side, `hydra-acp` exposes its WSS endpoint at `/acp`: a client sends `GET /acp` with `Upgrade: websocket`, receives a `101 Switching Protocols` response, and the connection becomes a bidirectional stream of JSON-RPC text frames (binary frames are ignored). The server negotiates the `acp.v1` subprotocol via the standard `Sec-WebSocket-Protocol` mechanism (echoed back in the 101 when advertised; absent otherwise). Authentication is layered on top — HTTP headers, query parameters, or WebSocket subprotocols — and is treated as orthogonal by the spec. `hydra-acp` authenticates via a bearer token carried in a `hydra-acp-token.<token>` subprotocol entry or a `?token=<token>` query parameter.
|
|
705
|
+
|
|
706
|
+
### Standard ACP it relies on
|
|
707
|
+
|
|
708
|
+
Beyond the bedrock of `initialize` / `session/new` / `session/prompt`, the daemon implements **`session/list`** ([Protocol: Session List](https://agentclientprotocol.com/protocol/session-list), stabilized 2026-03-09) so any compliant client can enumerate sessions known to the daemon and attach to one — `{ sessionId, cwd, title?, updatedAt?, _meta? }` per entry, with `cwd` filtering and `cursor`-based pagination. Hydra-specific fields ride under `_meta["hydra-acp"]` per the [Extensibility](https://agentclientprotocol.com/protocol/extensibility) convention.
|
|
709
|
+
|
|
710
|
+
### The registry it depends on
|
|
711
|
+
|
|
712
|
+
Agents are sourced from the [ACP Registry](https://github.com/agentclientprotocol/registry) — a CDN-hosted JSON document at `https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json`. Each entry declares `id`, `name`, `version`, `description`, and a `distribution` block that selects between `npx`, `binary`, or `uvx` installation. `hydra-acp` caches the registry locally with a 24-hour TTL, falls back to the cached copy on network failure, and resolves an agent's `distribution` to a spawn plan when a session needs that agent.
|
|
713
|
+
|
|
714
|
+
## Architecture
|
|
715
|
+
|
|
716
|
+
```
|
|
717
|
+
editor browser Slack ← clients
|
|
718
|
+
│ │ │
|
|
719
|
+
hydra-acp hydra-acp-browser hydra-acp-slack ← hydra extensions
|
|
720
|
+
│ │ │
|
|
721
|
+
└───────────────┼───────────────┘
|
|
722
|
+
│
|
|
723
|
+
WSS / HTTP
|
|
724
|
+
│
|
|
725
|
+
hydra-acp ← hydra daemon
|
|
726
|
+
│
|
|
727
|
+
T1 → T2 → … → Tn ← hydra transformers
|
|
728
|
+
│
|
|
729
|
+
┌───────────────┼───────────────┐
|
|
730
|
+
│ │ │
|
|
731
|
+
claude opencode pi ← agents
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
### How it works
|
|
735
|
+
|
|
736
|
+
1. **Editor spawns `hydra-acp`** as it would any ACP agent. The shim looks like a normal stdio agent.
|
|
737
|
+
2. **Shim opens a WSS connection** to the daemon at `/acp`, authenticating via the bearer token.
|
|
738
|
+
3. **`session/new` from the editor** → daemon resolves the requested agent against the cached ACP Registry, downloads it on first use under `~/.hydra-acp/agents/`, spawns it as a child process, and creates an ACP session inside it.
|
|
739
|
+
4. **`session/attach` from a second client** → daemon adds the new client to the session's broadcast list and replays history per `historyPolicy` (per RFD #533).
|
|
740
|
+
5. **Notifications** fan out to every attached client. **Prompts** are serialized through the daemon's per-session queue. **Permission requests** broadcast to every attached client; first response wins and the rest receive a `session/update` with `sessionUpdate: "permission_resolved"` carrying the resolving client's outcome.
|
|
741
|
+
6. **`session/list`** returns the daemon's sessions (live and cold), filterable by `cwd`.
|
|
742
|
+
7. **`session/detach`** lets a client leave voluntarily; the session continues until the last client detaches (per RFD #533).
|
|
743
|
+
|
|
744
|
+
### Why a shim?
|
|
745
|
+
|
|
746
|
+
Existing ACP clients are stdio-based: they `spawn(command)` a process and exchange JSON-RPC over its stdin/stdout. A shim that *looks* like an ACP agent on stdio is zero-integration on the client side — the client doesn't need to know anything about hydra, the daemon, or WSS. It just spawns `hydra-acp` and starts talking ACP.
|
|
747
|
+
|
|
748
|
+
Clients that adopt the streamable-http-websocket-transport RFD natively can connect to the daemon's `/acp` endpoint directly without the shim.
|
|
595
749
|
|
|
596
750
|
## Disk layout
|
|
597
751
|
|
|
@@ -603,10 +757,12 @@ The daemon refuses to bind to non-loopback hosts without TLS configured.
|
|
|
603
757
|
├── daemon.<N>.log # rotated daemon logs (10 MB or daily, whichever first)
|
|
604
758
|
├── current.log # symlink to the active daemon.<N>.log
|
|
605
759
|
├── registry.json # cached ACP registry (24h TTL)
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
760
|
+
├── remotes.json # cached credentials for remote daemons (hydra:// URLs)
|
|
761
|
+
├── sessions/ # session records and history — your actual work
|
|
762
|
+
├── tty/ # per-terminal session pointers, used by --reattach
|
|
763
|
+
├── extensions/ # per-extension rotated logs
|
|
764
|
+
├── transformers/ # per-transformer rotated logs
|
|
765
|
+
└── agents/ # downloaded agent installs, keyed by platform and version
|
|
610
766
|
```
|
|
611
767
|
|
|
612
768
|
Logs are also fanned out to stderr while the daemon is running. To follow live: `tail -F ~/.hydra-acp/current.log`.
|
|
@@ -686,6 +842,23 @@ The daemon exposes a process-management surface. Treat the service token like an
|
|
|
686
842
|
- **Sandboxing is the user's responsibility.** Spawned agents inherit the daemon's filesystem and shell. Run the daemon under a restricted user or inside a container if you don't trust agents fully.
|
|
687
843
|
- **Subprocess scope:** agent processes inherit `cwd` and a sanitized environment. The daemon does not pass its service token through to spawned agents.
|
|
688
844
|
|
|
845
|
+
The service token (stored at `~/.hydra-acp/auth-token`, mode 0600) is generated on `hydra-acp init` and required as `Authorization: Bearer <token>` for every REST call and as a WebSocket subprotocol or query parameter for `wss://.../acp`. The token never leaves `~/.hydra-acp/`.
|
|
846
|
+
|
|
847
|
+
For remote access (binding to a non-loopback address), enable TLS via:
|
|
848
|
+
|
|
849
|
+
```json
|
|
850
|
+
{
|
|
851
|
+
"daemon": {
|
|
852
|
+
"tls": {
|
|
853
|
+
"cert": "/path/to/cert.pem",
|
|
854
|
+
"key": "/path/to/key.pem"
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
The daemon refuses to bind to non-loopback hosts without TLS configured.
|
|
861
|
+
|
|
689
862
|
## Registry entry mockup
|
|
690
863
|
|
|
691
864
|
If accepted, `hydra-acp` could land in the [ACP Registry](https://github.com/agentclientprotocol/registry):
|