@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.
Files changed (4) hide show
  1. package/README.md +507 -334
  2. package/dist/cli.js +331 -324
  3. package/dist/daemon.js +68 -68
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -23,322 +23,215 @@
23
23
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠉⠛⠙⠋⠉⠁⠀⠀⠀⠀⠀⠁⠀⠀⠋⠙⠉⠟⠉⠀⠈⠈⠁⠉
24
24
  ```
25
25
 
26
- > **Status: experimental.** A multi-client session daemon for the [Agent Client Protocol (ACP)](https://agentclientprotocol.com/). Many heads, one body, many feet: multiple clients (editors, dashboards, Slack bridges) attach to one daemon that manages the real ACP agent processes underneath. Every attached client sees the same live session in real time.
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. One daemon manages your agent processes and the sessions running inside them; 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.
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 # launch the TUI and start a session
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's the whole getting-started path: install, run, talk to an agent. 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.
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
- ### How it's built
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
- `hydra-acp cat` is a pipe-friendly headless verb: it feeds stdin to a fresh
116
- session as the user prompt and streams the agent's text reply to stdout. No
117
- TUI, no JSON-RPC for the caller, no terminal control sequences in the
118
- output — just text in, text out, exit code 0 on a clean turn. Hydra ends up
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
- A few properties keep it well-behaved:
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
- - **Sandboxed cwd by default.** Piped invocations get a fresh empty tempdir as
125
- the agent's `cwd`, and the permission handler rejects every tool call that
126
- isn't one of the `hydra-acp-stdin` MCP tools (head / tail / grep / read on
127
- the piped bytes). The agent has nothing to look at except the data you
128
- piped in. Override with `--cwd <path>` when you want it poking at the
129
- project (e.g. "find docs in the codebase that mention this error").
130
- - **Smart about size.** Small inputs are inlined into the prompt. Large inputs
131
- (default >1 MiB) get the daemon's in-memory `hydra-acp-stdin` MCP server:
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
- # One-shot question, no stdin.
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
- Sessions created by `cat` are normal hydra sessions, so `hydra-acp session`,
164
- `session export`, `/hydra title`, and the rest of the surface all work on them.
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
- ## Install
87
+ How you switch depends on how permanent you want it:
167
88
 
168
- ```bash
169
- npm install -g @hydra-acp/cli
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
- Drops `hydra-acp` (and `hydra`) on your PATH.
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
- Then pick the agent the daemon should spawn by default. `defaultAgent` is the registry id used when `session/new` doesn't specify one (the common case for editor-spawned shims), and `defaultModels[<agent>]` pins a per-agent default model. Both are read once at daemon startup, so a `daemon restart` is needed for changes to take effect:
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
- ```sh
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
- ## Quick start
107
+ ### From the terminal
184
108
 
185
109
  ```bash
186
- # 1. (Optional) Initialize: writes ~/.hydra-acp/config.json with a generated
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
- # 5. List live sessions.
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
- ## Extensions
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
- 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.
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
- Various ready-made extensions ship under the same `@hydra-acp` npm scope. All are optional and can be installed independently.
130
+ ### From your editor
220
131
 
221
- **[`@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.
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
- ```sh
224
- npm install -g @hydra-acp/slack
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
- Without a config file the approver abstains on everything installing it has no behavioral effect until you write a rule.
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
- ```sh
262
- npm install -g @hydra-acp/archiver
263
- hydra-acp extension add hydra-acp-archiver
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
- See the [package README](https://github.com/smagnuso/hydra-acp-archiver#readme) for backend setup (Drive OAuth, filesystem path).
150
+ ### Handing one off
268
151
 
269
- **[`@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.
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
- npm install -g @hydra-acp/planner
273
- hydra-acp transformer add hydra-acp-planner
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
- 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.
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
- ### Configuring extensions
168
+ ## Compaction and recall
280
169
 
281
- Configure in `~/.hydra-acp/config.json`:
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
- ```json
284
- {
285
- "extensions": {
286
- "hydra-acp-slack": {},
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
- 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).
181
+ Two escape hatches, typed in any composer:
297
182
 
298
- Each extension is launched with these env vars set:
299
-
300
- | Env var | Example |
183
+ | Command | Effect |
301
184
  |---|---|
302
- | `HYDRA_ACP_DAEMON_URL` | `http://127.0.0.1:55514` |
303
- | `HYDRA_ACP_DAEMON_HOST` | `127.0.0.1` |
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
- 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).
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
- While the daemon is running you can manage extensions without bouncing it:
193
+ ## CLI
313
194
 
314
- ```text
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
- `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.
321
-
322
- **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.
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
- ## CLI
209
+ The full surface:
325
210
 
326
211
  ```
327
212
  hydra-acp # auto-dispatch: TUI in a TTY, shim when stdio is piped
328
- hydra-acp shim # explicit shim mode (forces shim regardless of TTY)
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] # pipe-friendly headless mode: feeds stdin
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
- hydra-acp --session <id-or-url> # attach to existing session
336
- # (TUI in a TTY, shim otherwise)
337
- hydra-acp --reattach # pick the most-recent session for cwd
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 # generate the service token
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] # list sessions
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
- 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 `shim` or `tui` explicitly to force one or the other. Editors should configure `hydra-acp shim` so the choice is unambiguous regardless of how the editor wires stdio.
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-code
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` (e.g. `"claude-code"`) before forwarding to the daemon — the spec `session/new` params stay clean. The daemon resolves `claude-code` 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.
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 the registry ID — e.g. `claude-code`, `gemini-cli`, `codex`. Run `hydra-acp agent` to browse what's available, or fetch the registry CDN URL directly.
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-id` are given, `--session-id` wins (attach mode); the agent is ignored because the agent process is already running.
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
- ### Naming sessions from the editor
357
+ ### Registry id resolution
425
358
 
426
- Pass `--name <label>` or set `HYDRA_ACP_NAME` and the first `session/new` from that shim is labeled accordingly. The label flows through `_meta["hydra-acp"].title` on the wire, lands in `Session.title`, and shows up in `session/list` and `hydra-acp session`. Subsequent `session/new` calls from the same shim are not labeled first one wins. The label survives daemon restart (it's carried in the resume hints).
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
- ```text
429
- HYDRA_ACP_NAME="$BUFFER_NAME" hydra-acp launch claude-acp
430
- # or
431
- hydra-acp --name "$BUFFER_NAME" launch claude-acp
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
- After the first user prompt lands, hydra automatically replaces the label with the first line of that prompt (truncated, ≤80 chars) and emits a `session_info_update` so every attached client (TUI, slack, browser) refreshes its header. Agents that emit their own `session_info_update` override that — last write wins.
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
- ### Read-only viewer (`--readonly`)
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
- Sometimes you want to scroll through a session's transcript — usually one imported from another machine — without spawning the underlying agent. Pass `--readonly` to `tui` to attach in view-only mode:
376
+ ### Slash commands
439
377
 
440
- ```text
441
- hydra-acp tui --resume <id> --readonly
442
- ```
378
+ Two different sets, which is worth knowing before you go looking for one in the
379
+ wrong place.
443
380
 
444
- The daemon enforces the contract: a read-only attach to a *cold* session takes a viewer path that streams history straight from disk — no `manager.resurrect`, no agent process. Any mutating method sent from a read-only connection is refused. History replay and live updates are unchanged, so the existing scrollback search (`^R` when scrolled back) works over the full transcript. (Wire details — including how the read-only flag is carried — live in PROTOCOL.md.)
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
- The TUI suppresses the composer entirely — those rows go to scrollback so you see more of the conversation. The window title is suffixed `[VIEW ONLY]` so the mode is unambiguous. Prompt-shaped keys (Enter, Shift+Enter, Shift+Tab) are inert; `^P`, `^G`, `^L`, `^R`, `PgUp/PgDn`, `^C`, `^D` work as usual.
447
-
448
- From inside the TUI's session picker, **`v`** on a selected row enters view-only mode for that session. Enter still attaches normally. The mode is per-session: `^P` → pick another with Enter drops out of read-only; `v` re-enters it.
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
- ### Slash commands (typed in any composer)
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
- Slash commands of the form `/hydra <verb> [args]` are intercepted by hydra before the prompt reaches the agent. They never appear in the conversation log; the only client-visible signal is the notification(s) the verb implies.
401
+ **The TUI has its own set**, which only work there:
453
402
 
454
403
  | Command | Effect |
455
404
  |---|---|
456
- | `/hydra title` | Asks the agent for a one-line summary, applies it as the new title via `session_info_update`. The sub-prompt and reply are suppressed from clients. |
457
- | `/hydra title <text>` | Sets the title to `<text>` directly. No agent call. |
458
- | `/hydra agent <agent>` | Swaps the agent process backing this session. Spawns the new agent (must be in the registry — see `hydra-acp agent list`), kills the old one, and feeds the conversation transcript so far back in as the first prompt to the new agent. `session_info_update` carries the new `agentId`; a synthetic `agent_message_chunk` banner marks the switch in the transcript. The on-disk session record is updated so resurrection brings the session back on the new agent. |
459
-
460
- These work from anywhere a session prompt can be typed — the TUI's input box, agent-shell, the slack thread composer, the browser chat composer. Hydra detects them server-side; clients send them as ordinary `session/prompt` requests.
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
- ### Forwarding agent args (`hydra-acp launch <agent> ...`)
432
+ ## Config
478
433
 
479
- Anything you put after `<agent>` in launcher mode is forwarded to the underlying agent's command. Hydra appends the extra args to the registry-provided spawn plan. Example:
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
- ```text
482
- hydra-acp launch codex-acp -c sandbox_mode=danger-full-access
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
- The daemon spawns `npx -y @zed-industries/codex-acp@<version> -c sandbox_mode=danger-full-access`. Args survive daemon restart — they're stored alongside the resume hints, so a resurrected session re-spawns its agent with the same arguments.
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-code"
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
- `daemon.sessionRecentMinutes` (default 30) controls how far back `hydra-acp session` (and the `/v1/sessions` REST endpoint without `?all=true`) looks for cold (disk-only) sessions. Set to `0` to never list cold sessions.
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
- ### Transformers
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
- 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/`.
628
+ ## Cat mode
580
629
 
581
- For remote access (binding to a non-loopback address), enable TLS via:
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
- ```json
584
- {
585
- "daemon": {
586
- "tls": {
587
- "cert": "/path/to/cert.pem",
588
- "key": "/path/to/key.pem"
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
- The daemon refuses to bind to non-loopback hosts without TLS configured.
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
- └── agents/
607
- └── <agent-id>/
608
- ├── meta.json # registry entry snapshot
609
- └── ... # agent-specific install (npx cache, binary, etc.)
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):