# hive-runtime The layer an agent's turns are driven through: one `Runtime` interface (`run`, `compact`, `archive`, `canceller`, `choices`) with a backend per runtime. - **claude** — `claude --print` through the `hive-claude` crate's `InfiniteSession`. A pass-through: same spawn, same session handling, same errors. - **acp** — any [Agent Client Protocol](https://agentclientprotocol.com) agent, spawned from a command, args and env handed to it (`RuntimeSpec`, read from `HIVE_RUNTIME` / `HIVE_ACP_COMMAND` / `HIVE_ACP_ARGS` / `HIVE_ACP_ENV`). It knows no agent by name; which agent runs, and how it is configured, is decided in nix (`services.hyperhive.agent.runtime`, `services.hyperhive.agent.acp.*`). Both backends report a turn through `hive_claude::Sink` in claude's `stream-json` shape. The ACP backend translates `session/update` notifications into it (text and thought chunks as whole blocks, tool calls as `tool_use` + `tool_result`, MCP tools named `mcp____`), so the harness's stream consumers read either backend unchanged. Context usage comes from ACP `usage_update`. The crate depends on no hyperhive binary crate, so `hive-agent` and `hive-subagent-mcp` can both drive turns through it. ## ACP backend: what it needs from the agent - `mcpCapabilities.http` in its `initialize` response. The hyperhive tools are only served over HTTP, so an agent without it is refused at startup. - `loadSession`, to pick its session back up after a harness restart. Without it every restart starts a new session. ## ACP backend: stopping a turn A turn is stopped with `session/cancel`: by the `Canceller` handle, or by the idle watchdog once no `session/update` has arrived for `Config::idle_timeout`. An agent that has not answered the prompt 10s later is sent SIGINT, and the next turn respawns it. A watchdog stop fails the turn with `AcpError::IdleTimeout` (`IdleKilled` if the agent was sent SIGINT). This is the only way a provider error the agent retries without reporting it, such as an HTTP 429, ends the turn. The agent leads its own process group. Stopping it, and dropping the runtime, sends that group SIGINT, so the commands the agent started get it too. Nothing escalates to SIGKILL: an agent that ignores SIGINT keeps running, and the next turn starts a new one beside it. ## ACP backend: compaction The same `CompactionPolicy` as the claude backend decides when: after a turn past the watermark, or on `compact` (the operator's `/compact`, the agent's `compact` tool). - If the agent advertises a `compact` command (`available_commands_update`), it is sent as the prompt `/compact` on the same session, as the ACP spec runs any advertised command. A proactive compaction runs the policy's checkpoint turn first, as on claude. - Otherwise the policy's checkpoint turn runs, then the session is archived, and the next turn starts a new one, carrying the system prompt again. - A `compact` command that fails, or sends nothing for 180s (the turn's idle window if that is shorter), is cancelled and handled as the case above; the checkpoint turn is not run a second time. So a compaction always leaves a smaller session behind, and a failed one is not retried on the next turn. ## ACP backend: model and effort Before each prompt, `Config::model` and then `Config::effort` are set on the session with `session/set_config_option`, as the value of its first `model` and `thought_level` config option. A value the session does not offer, or already holds, is not sent, so a claude alias such as `haiku` does nothing on an ACP agent. Model goes first because the effort levels on offer depend on it. The `Choices` handle (`Runtime::choices`) reads what the session offers now, including after a `config_option_update`. It is empty until a session is attached.