docs(turn-loop): facts + structure pass
Frame the turn loop runtime-neutrally: every turn runs through hive-runtime, on claude (default) or an ACP agent. The loop steps, harness binary shape and hive-agent README now say so; claude-only failure detection gets its own heading; claude-invocation.md opens with its scope and links the ACP side to hive-runtime/README.md. Fact fixes: on-boot file paths (/run/hive-config, not /run/hive), hive-claude is a crates.io dependency with no README here, hive-agent has no client.rs or forge_notify.rs, the claude launch-config layer is hive-agent's mcp_config.rs, agent forge/matrix accounts are swarm-controller's, hive-c0re's dashboard is dashboard/, and the deprecated hyperhive.gui.enable / hyperhive.extraMcpServers spellings. Refs #3902
This commit is contained in:
parent
816d0d5d3c
commit
fd74cbd495
4 changed files with 74 additions and 48 deletions
|
|
@ -1,7 +1,20 @@
|
|||
# Turn loop + MCP
|
||||
|
||||
How the harness wakes up, what it asks claude to do, and what tools
|
||||
claude has access to in return.
|
||||
How the harness wakes up, what it asks the agent's runtime to do, and what
|
||||
tools the agent has access to in return.
|
||||
|
||||
## Runtimes
|
||||
|
||||
Every turn runs through `hive-runtime`, on the runtime
|
||||
`services.hyperhive.agent.runtime` selects:
|
||||
|
||||
- **`claude`** (default) — one `claude --print` process per turn.
|
||||
→ [claude-invocation.md](claude-invocation.md)
|
||||
- **`acp`** — any Agent Client Protocol agent, run as one long-lived child
|
||||
of the harness. → [`hive-runtime/README.md`](../../hive-runtime/README.md)
|
||||
|
||||
Both report the turn in claude's `stream-json` shape, so the loop below is
|
||||
the same for either.
|
||||
|
||||
## The loop
|
||||
|
||||
|
|
@ -22,21 +35,22 @@ agents) runs:
|
|||
2. Pop one message. Peek the remaining inbox depth with `Status`.
|
||||
3. Emit `LiveEvent::TurnStart { from, body, unread }` onto the SSE
|
||||
bus.
|
||||
4. Spawn claude (one process per turn) and pipe the wake prompt
|
||||
over stdin.
|
||||
5. Stream stdout (JSON lines) into the bus as
|
||||
4. Hand the wake prompt to the runtime: on `claude`, spawn
|
||||
`claude --print` and pipe it over stdin; on `acp`, send it as a
|
||||
prompt on the agent's session.
|
||||
5. Stream the turn's `stream-json` lines into the bus as
|
||||
`LiveEvent::Stream(value)`. Pump stderr as `Note`.
|
||||
6. Wait for claude to exit and classify the turn's outcome from the
|
||||
stream + exit — success, compaction, rate-limit, auth-failure, or
|
||||
hard failure. The outcome drives the post-turn action (see
|
||||
6. Wait for the turn to end and classify its outcome from the
|
||||
stream + result — success, compaction, rate-limit, auth-failure, stall,
|
||||
or hard failure. The outcome drives the post-turn action (see
|
||||
[Turn outcomes](#turn-outcomes)); the session handles compaction
|
||||
internally (see
|
||||
[Compaction](claude-invocation.md#compaction)). This page describes
|
||||
rate-limit and auth-failure detection [below](#failure-detection-and-login).
|
||||
rate-limit and auth-failure detection [below](#failure-detection-and-login-claude).
|
||||
7. Emit `LiveEvent::TurnEnd { ok, note }`. Sleep `poll_ms` to avoid
|
||||
tight loops on transient failures.
|
||||
|
||||
### Failure detection and login
|
||||
### Failure detection and login (claude)
|
||||
|
||||
- **Rate limit** — a `429` / `rate_limit` marker on stderr, or a parsed
|
||||
`{"type":"error"}` rate-limit event on stdout (conversation-text
|
||||
|
|
@ -65,13 +79,16 @@ agents) runs:
|
|||
|
||||
## Harness binary shape
|
||||
|
||||
Two sibling crates, both role-agnostic (there is one role: agent —
|
||||
the privilege boundary lives server-side at the broker socket
|
||||
(`/run/hive/mcp.sock`), which refuses privileged `Request` variants
|
||||
regardless of who sends them):
|
||||
Two sibling binaries over one runtime library, all role-agnostic (there
|
||||
is one role: agent — the privilege boundary lives server-side at the
|
||||
broker socket (`/run/hive/mcp.sock`), which refuses privileged `Request`
|
||||
variants regardless of who sends them):
|
||||
|
||||
- `hive-agent` — long-running harness loop (the inbox poll +
|
||||
claude-pump + ack/requeue cycle described above).
|
||||
runtime turn + ack/requeue cycle described above).
|
||||
- `hive-runtime` — the library every turn runs through, one `Runtime`
|
||||
impl per runtime (`ClaudeRuntime`, `AcpRuntime`); `hive-subagent-mcp`
|
||||
drives its nested sessions through it too.
|
||||
- `hive-agent-mcp` — MCP server for the built-in `hyperhive` surface.
|
||||
Run with `--http <addr>` as a persistent streamable-HTTP daemon (the
|
||||
`hive-mcp-http` systemd unit, on `services.hyperhive.agent.mcp.httpPort`, default
|
||||
|
|
@ -130,8 +147,8 @@ else a `TurnError`) drives the post-claude branch:
|
|||
| `Err(Failed(err))` | route `[system] \`<qualified-label>\` claude turn failed:\n<err>` to `operator` via `send_to_operator` |
|
||||
|
||||
`ApiStall` catches an Anthropic API stall — a multi-retry connection storm where
|
||||
the stream goes silent for minutes. The idle watchdog lives in `hive-claude`'s
|
||||
driver (`Config::idle_timeout`, enforced around `child.wait()`): the timer
|
||||
the stream goes silent for minutes. On `claude` the idle watchdog lives in
|
||||
`hive-claude`'s driver (`Config::idle_timeout`, enforced around `child.wait()`): the timer
|
||||
resets on every stdout line, so a large but still-streaming turn is never cut —
|
||||
only complete output silence for the window trips it. The harness sets the
|
||||
window from `HIVE_TURN_IDLE_SECS` (`0` disables) and maps the driver's
|
||||
|
|
|
|||
Loading…
Reference in a new issue