Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/hive-runtime
Repository files (latest commit first)
Filename Latest commit message Latest commit date
atlas bcbeb8ac9c hive-runtime: cancel and idle watchdog for ACP turns
`Runtime` gets a fourth operation, `canceller()`: a handle that stops the
turn in flight from outside `run`. The ACP backend returns one; claude
returns `None`, because the harness stops a claude turn by signalling the
`claude` process, and that path is unchanged.

Both stops send the agent `session/cancel`:

- `Canceller::cancel()`, when asked from outside. The turn then ends
  normally, reported with stop reason `cancelled` whatever reason the agent
  gives. opencode 1.15.10, for one, answers a cancelled prompt with
  `end_turn` (`acp/agent.ts` `prompt()` always returns `end_turn`).
- The idle watchdog, once no `session/update` has arrived for
  `Config::idle_timeout`, the same field claude's watchdog reads. The turn
  fails with `AcpError::IdleTimeout`.

An agent that has not answered the prompt 10s after `session/cancel` is
killed (`IdleKilled` / `CancelIgnored`), and the next turn respawns it.

The watchdog is also what ends a turn stuck on a provider HTTP 429.
opencode 1.15.10 retries a retryable provider error with no attempt limit
(`session/retry.ts` `policy`, `session/processor.ts` `Effect.retry`) and
forwards neither `session.status` nor `session.error` over ACP (its
`handleEvent` only handles `permission.asked` and `message.part.*`). So the
ACP client sees nothing at all until the provider recovers. The
`IdleTimeout` message says a silently retried provider error looks like
this.

Refs #4391
2026-09-29 23:22:50 +02:00
..
src hive-runtime: cancel and idle watchdog for ACP turns 2026-09-29 23:22:50 +02:00
Cargo.toml hive-runtime: record a new ACP session only once its first prompt is answered 2026-09-29 22:29:36 +02:00
README.md hive-runtime: cancel and idle watchdog for ACP turns 2026-09-29 23:22:50 +02:00

hive-runtime

The layer an agent's turns are driven through: one Runtime interface (run, compact, archive, canceller) 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 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__<server>__<tool>), 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 killed, and the next turn respawns it. A watchdog stop fails the turn with AcpError::IdleTimeout (IdleKilled if the agent was killed). This is the only way a provider error the agent retries without reporting it, such as an HTTP 429, ends the turn.

ACP backend: not yet

compact returns Error::Unsupported.