diff --git a/.prettierignore b/.prettierignore index 8c27f02e..0baa578a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -16,6 +16,6 @@ docs/persistence.md docs/terminal-rendering.md docs/tools/forge.md docs/tools/matrix.md -docs/turn-loop/README.md +docs/turn-loop.md docs/web-ui/agent.md docs/web-ui/dashboard.md diff --git a/CLAUDE.md b/CLAUDE.md index dd5c38a6..20201153 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -179,7 +179,7 @@ read them à la carte. - **"How does the per-agent terminal classify + colour events?"** → [`docs/terminal-rendering.md`](docs/terminal-rendering.md). - **"How does claude get its prompt and what tools does it have?"** → - [`docs/turn-loop/`](docs/turn-loop/README.md) (index: the loop, binary shape, + [`docs/turn-loop.md`](docs/turn-loop.md) (index: the loop, binary shape, turn outcomes; sub-pages: [`claude-invocation`](docs/turn-loop/claude-invocation.md), [`config`](docs/turn-loop/config.md), [`mcp`](docs/turn-loop/mcp.md)). @@ -239,7 +239,7 @@ The docs below own the details — this section just points at them. - **NixOS / nspawn quirks** (bind mounts, conf flags, etc.): → [`docs/gotchas.md`](docs/gotchas.md). - **Turn loop, sentinels (rate-limit, auth-failed), context - window:** → [`docs/turn-loop/`](docs/turn-loop/README.md). + window:** → [`docs/turn-loop.md`](docs/turn-loop.md). - **Two-step spawn, approval flow, flake.lock validation:** → [`docs/approvals.md`](docs/approvals.md). - **Pre-push lint hook** (catches tracker-tag and comment-block failures diff --git a/README.md b/README.md index 41afa1aa..f069ea8c 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Depth lives in [`docs/`](docs/) — pick the one matching your task: | reading path | doc | | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | dashboard layout + endpoints | [`docs/web-ui.md`](docs/web-ui.md) ([shape](docs/web-ui/shape.md) · [dashboard](docs/web-ui/dashboard.md) · [agent](docs/web-ui/agent.md)) | -| claude turn loop + MCP tools | [`docs/turn-loop/`](docs/turn-loop/README.md) | +| claude turn loop + MCP tools | [`docs/turn-loop.md`](docs/turn-loop.md) | | config-edit + approval state machine | [`docs/approvals.md`](docs/approvals.md) | | what survives destroy / purge / restart | [`docs/persistence.md`](docs/persistence.md) | | naming, wire protocol, commit style | [`docs/conventions.md`](docs/conventions.md) | @@ -103,6 +103,46 @@ older/newer channel hits breakage hyperhive's CI doesn't catch. For the full list of host and agent NixOS options see the **[options reference](https://hyperhive.darkest.space/options/)**. +## Agent configuration + +Per-agent config lives in each agent's `agent.nix` (proposed, operator-approved, deployed as git commits). Key options: + +### Multi-account Matrix support + +`hyperhive.matrixAccounts` declares _additional_ matrix accounts for an agent, beyond the hive-internal one. Each entry is keyed by account name and specifies: + +- `tokenFile` — path to the matrix bearer token (provisioned out-of-band) +- `sessionDir` — path to the per-account matrix-sdk sqlite state (crypto keys + cache) +- `homeserver` — optional homeserver URL (defaults to `hyperhive.matrix.url`) + +**Example:** + +```nix +hyperhive.matrixAccounts = { + external-public = { + tokenFile = "/agents/myagent/state/matrix-token-external"; + sessionDir = "/agents/myagent/state/matrix-sdk-state-external"; + homeserver = "https://matrix.org"; + }; +}; +``` + +The hive-internal account is always named `main` (synthesized from `hyperhive.matrix.url` + agent state). This option only declares _extras_; the `main` name is reserved and cannot be used here. Requires `hyperhive.matrix.enable = true`. + +For more details see [`docs/matrix.md`](docs/matrix.md). + +### GitHub account + +Every agent gets a managed GitHub identity — a `gh` CLI wrapper and `git push` over HTTPS — on by default (`hyperhive.github.enable`), inert until a PAT is provisioned. There is nothing per-agent to declare: paste an operator-supplied personal access token into the agent's dashboard **credentials** tab (or `hivectl github set-token --token-stdin`) and it works. The `gh` wrapper + git credential helper read the token live (git auths as `x-access-token` + the PAT; github.com only), so a rotated PAT takes effect with no rebuild. + +Turn the integration off for the whole hive with the host option: + +```nix +services.hyperhive.github.enable = false; +``` + +The PAT value is never in nix — only the enable flag. For more details see [`docs/github.md`](docs/github.md). + ## Operator CLI `hivectl` is the operator-facing host CLI for ad-hoc administration that diff --git a/docs/persistence.md b/docs/persistence.md index 7f3030eb..4d138404 100644 --- a/docs/persistence.md +++ b/docs/persistence.md @@ -239,7 +239,7 @@ Survives destroy/recreate, gone on `--purge`. Empty marker file. Its presence parks the agent's turn loop: the harness keeps serving its web UI and MCP daemons but drives no turns, and inbox messages queue unacked until it's removed (see -[turn loop](turn-loop/README.md#the-loop)). +[turn loop](turn-loop.md#the-loop)). Unusually, it's read and written from **both** sides of the harness bind-mount, and that's the whole design: the harness stats it diff --git a/docs/tools/README.md b/docs/tools/README.md deleted file mode 100644 index 67d988eb..00000000 --- a/docs/tools/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Tools - -`hivectl` is *your* tool — the operator's own host CLI. Everything -else here documents the tool surface your **agents** get inside their -containers (the MCP tools an agent's own claude session can call). -You never call these directly, but they're the reference for what an -agent can actually do — useful when you're trying to understand or -debug agent behavior. - -## For the operator - -- **[hivectl](hivectl.md)** — the curated guide: provisioning forge - and matrix accounts, gateway htpasswd management, container - lifecycle shortcuts, interactive agent shell access. -- **[hivectl-cli](hivectl-cli.md)** — the exhaustive, auto-generated - flag-by-flag reference, kept in lockstep with the binary by CI. - -## What your agents can do - -- **[bash](bash.md)** — background shell execution (`mcp__bash__*`), - available on every agent unconditionally. -- **[forge](forge.md)** — the `hive-forge` Forgejo CLI every agent has - for issues, PRs, and comments. Not an MCP tool — a binary agents - shell out to instead of ad-hoc curl. -- **[lifecycle](lifecycle.md)** — kill/start/restart/update for an - agent's own direct children, plus the approval-gated config-change - tools. -- **[matrix](matrix.md)** — the matrix MCP tool surface - (`mcp__matrix__*`) for agents with a matrix account, multiple - accounts per agent, and declaring extra MCP servers generally. -- **[scheduling](scheduling.md)** — scheduled prompts (operator - approval required) and the diagnostics tools (`get_logs`, - `get_host_journal`). diff --git a/docs/tools/matrix.md b/docs/tools/matrix.md index 896e742b..014274c7 100644 --- a/docs/tools/matrix.md +++ b/docs/tools/matrix.md @@ -67,23 +67,6 @@ room you haven't read yet. - `mark_read(room, event_id)` — advance the read receipt -## Multiple accounts - -`hyperhive.matrixAccounts` (declared in `agent.nix`) gives an agent -*additional* matrix identities beyond the hive-internal one — e.g. an -external-facing account alongside the internal one. Each entry is -keyed by account name and specifies `tokenFile` (bearer token, -provisioned out-of-band; basename must start with `matrix-token`), -`sessionDir` (per-account matrix-sdk sqlite state — crypto keys + -cache), and an optional `homeserver` (defaults to -`hyperhive.matrix.url`). The hive-internal account is always named -`main`, synthesized from `hyperhive.matrix.url` + agent state — this -option only declares extras, and `main` is a reserved key here. -Requires `hyperhive.matrix.enable = true`. - -Every matrix tool above takes an optional `account` parameter (a name -from this map) to act as that identity instead of the primary one. - ## Architecture `hive-matrix-daemon` is a single long-running process (one per agent diff --git a/docs/tools/scheduling.md b/docs/tools/scheduling.md index 1b02918c..9d92a2ca 100644 --- a/docs/tools/scheduling.md +++ b/docs/tools/scheduling.md @@ -89,6 +89,6 @@ lifecycle events, or another container's boot log. ## See also - `remind` (no-approval self-wake path) — documented in - [`docs/turn-loop/`](../turn-loop/README.md). + [`docs/turn-loop.md`](../turn-loop.md). - [`docs/approvals.md`](../approvals.md) — approval flow for `request_schedule_prompt`. diff --git a/docs/turn-loop/README.md b/docs/turn-loop.md similarity index 93% rename from docs/turn-loop/README.md rename to docs/turn-loop.md index d73a2b42..6e5bf6ea 100644 --- a/docs/turn-loop/README.md +++ b/docs/turn-loop.md @@ -14,7 +14,7 @@ agents) runs: queued and unacked, so a resume drains the backlog instead of losing it; reminders and todo wakes buffer in their channels. Set it with `hivectl agent pause` or the dashboard toggle; see - [persistence](../persistence.md#-harnesspaused-per-agent). + [persistence](persistence.md#-harnesspaused-per-agent). 1. Long-poll `Recv` on its socket. The host-side broker (`broker.rs::recv_blocking_batch`) returns immediately if there's a pending message, otherwise waits up to 30 s for a broker `Sent` @@ -31,7 +31,7 @@ agents) runs: hard failure. The outcome drives the post-turn action (see [Turn outcomes](#turn-outcomes)); compaction is handled inside the session (see - [Compaction](claude-invocation.md#compaction)). Rate-limit + [Compaction](turn-loop/claude-invocation.md#compaction)). Rate-limit and auth-failure detection is described [below](#failure-detection-and-login). 7. Emit `LiveEvent::TurnEnd { ok, note }`. Sleep `poll_ms` to avoid tight loops on transient failures. @@ -94,12 +94,12 @@ loop (`serve_loop` / `handle_turn`) has no per-role branches. flake sets it unconditionally for any container-deployed agent; see `docs/conventions.md::Hive identity` for the env stack), opens turn-stats sqlite, prepares the on-boot files (see -[claude-invocation](claude-invocation.md#on-boot-files)), +[claude-invocation](turn-loop/claude-invocation.md#on-boot-files)), installs claude plugins, spawns `web_ui::serve` + `vacuum::run`, and either drops into `serve_loop` directly (`Online`) or parks on the login flow first (`NeedsLogin`). (The forge notification poller used to be spawned here too; it is its own process now — -`hive-forge-notify`, see [`forge.md`](../forge.md).) +`hive-forge-notify`, see [`forge.md`](forge.md).) `spawn_todo_socket` opens the todos store and, alongside `todo_server::run` (the socket the out-of-process producers dial), @@ -153,17 +153,17 @@ harness between turns. ## Sub-pages -The rest lives alongside this page, in three topic files: +The rest lives in three topic pages under [`turn-loop/`](turn-loop/): -- **[claude-invocation.md](claude-invocation.md)** — how the harness +- **[claude-invocation.md](turn-loop/claude-invocation.md)** — how the harness spawns `claude --print` each turn, the two-pronged compaction (reactive + proactive), and the on-boot files it materialises (`--mcp-config`, `--system-prompt-file`). -- **[config.md](config.md)** — the optional per-agent knobs the meta +- **[config.md](turn-loop/config.md)** — the optional per-agent knobs the meta flake wires in (reference docs, icon, passwordless sudo, dashboard links, custom static files, connectivity overrides, claude plugins, cargo message filtering). -- **[mcp.md](mcp.md)** — the MCP tool surface claude sees: core tools, +- **[mcp.md](turn-loop/mcp.md)** — the MCP tool surface claude sees: core tools, privileged tool groups, self-wake, authoritative state, the tool envelope, and the built-in tool whitelist. diff --git a/docs/turn-loop/claude-invocation.md b/docs/turn-loop/claude-invocation.md index f5b3df9a..b9d2f26c 100644 --- a/docs/turn-loop/claude-invocation.md +++ b/docs/turn-loop/claude-invocation.md @@ -116,7 +116,7 @@ window with two triggers baked into its `run`: *still* overflows, `run` surfaces `Error::PromptTooLong`; `drive_turn` then archives the session (session lifecycle stays hive-side) and the serve loop requeues the message so it redelivers into a fresh session - (see [Turn outcomes](README.md#turn-outcomes) — the wake prompt itself is tiny, so + (see [Turn outcomes](../turn-loop.md#turn-outcomes) — the wake prompt itself is tiny, so the overflow was the accumulated context the archive clears). - **Proactive** — a turn finishes cleanly but the last inference's context size crossed the policy watermark. While the session is still healthy it diff --git a/hive-agent/Cargo.toml b/hive-agent/Cargo.toml index 3b6ffff4..a4029b5c 100644 --- a/hive-agent/Cargo.toml +++ b/hive-agent/Cargo.toml @@ -41,4 +41,4 @@ tempfile = "3" # The sibling MCP server is its own bin crate now (`hive-agent-mcp`). # Privilege boundary is enforced server-side at the socket (tool # groups / manager surface). -# See `docs/turn-loop/README.md::Harness binary shape`. +# See `docs/turn-loop.md::Harness binary shape`. diff --git a/hive-agent/README.md b/hive-agent/README.md index 2fadfb8f..05f7406d 100644 --- a/hive-agent/README.md +++ b/hive-agent/README.md @@ -15,7 +15,7 @@ understand or change: what happens between "a message lands in the inbox" and "claude produces a reply", how login/auth is bootstrapped, how the per-agent web UI is served, or how turn/event stats get recorded. Architecture detail lives in -[`docs/turn-loop/`](../docs/turn-loop/README.md); this README is just the +[`docs/turn-loop.md`](../docs/turn-loop.md); this README is just the map of the module tree. ## Shape @@ -48,4 +48,4 @@ map of the module tree. Sibling: **`hive-agent-mcp`** (the MCP server this loop points claude at every turn). Both are described together in -[`docs/turn-loop/::Harness binary shape`](../docs/turn-loop/README.md). +[`docs/turn-loop.md::Harness binary shape`](../docs/turn-loop.md). diff --git a/hive-agent/src/login.rs b/hive-agent/src/login.rs index b6f028a8..f5302e77 100644 --- a/hive-agent/src/login.rs +++ b/hive-agent/src/login.rs @@ -118,7 +118,7 @@ impl LoginState { /// infinite-401 loop a bare-existence check would produce when stale /// credentials are already on disk. Mtime-snapshot resumption rationale /// and `DirSnapshot` two-axis design: see -/// [`docs/turn-loop/::The loop`](../../docs/turn-loop/README.md). +/// [`docs/turn-loop.md::The loop`](../../docs/turn-loop.md). /// /// # Panics /// diff --git a/hive-agent/src/main.rs b/hive-agent/src/main.rs index 7ac23fe9..8ac8460e 100644 --- a/hive-agent/src/main.rs +++ b/hive-agent/src/main.rs @@ -4,7 +4,7 @@ //! generic and testable. Sibling: `hive-agent-mcp` (the MCP server this //! loop points claude at). //! Architecture lives in -//! [`docs/turn-loop/::Harness binary shape`](../../../docs/turn-loop/README.md). +//! [`docs/turn-loop.md::Harness binary shape`](../../../docs/turn-loop.md). //! //! Single bin crate: the module tree below (formerly this crate's `lib.rs`, //! before lib + bin were collapsed into one) plus the serve loop. @@ -510,7 +510,7 @@ fn spawn_todo_socket( /// Boot — wires up the web UI, login state, stats, plugins, forge /// notifier, and either drops into `serve_loop` directly (`Online`) or /// parks on the login flow first (`NeedsLogin`). See -/// `docs/turn-loop/README.md::Boot wiring`. +/// `docs/turn-loop.md::Boot wiring`. async fn serve_main(socket: &Path, poll_ms: u64) -> Result<()> { let port = std::env::var("HIVE_PORT") .ok()