diff --git a/CLAUDE.md b/CLAUDE.md index 37322c3a..9b52b3f4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -32,9 +32,8 @@ hand-maintained per-file tree drifts out of sync with the code. the meta flake, lifecycle (`nixos-container` shellouts), gateway / forge / matrix provisioning, per-container stats, and the axum operator dashboard (`dashboard.rs`). Largest crate. -- **`hive-ag3nt/`** — in-container harness; three sibling binaries for - every agent (`hive-agent` serve loop, `hive-agent-mcp`, - `hive-agent-wake`). Turn-loop *policy* layer (`turn.rs`) over the `hive-claude` +- **`hive-ag3nt/`** — in-container harness; one `hive` binary for every + agent. Turn-loop *policy* layer (`turn.rs`) over the `hive-claude` driver, embedded MCP server (`mcp.rs`) + its claude launch-config layer (`mcp_config.rs`: tool-group/capability → `--allowedTools`, `--mcp-config` render), per-agent web UI (`web_ui/` module dir), event + turn-stats diff --git a/Cargo.lock b/Cargo.lock index ebf70b9c..2e4e59c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -630,16 +630,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation" version = "0.10.1" @@ -872,7 +862,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", - "serde_core", ] [[package]] @@ -935,15 +924,6 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -1077,27 +1057,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "forgejo-api" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c51fffa047d5836a4d89179a20b21b7e41ad97e713104752169d819b83c9158" -dependencies = [ - "base64ct", - "bytes", - "futures", - "reqwest", - "serde", - "serde_json", - "soft_assert", - "thiserror 1.0.69", - "time", - "tokio", - "url", - "urlencoding", - "zeroize", -] - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1379,7 +1338,6 @@ dependencies = [ "anyhow", "axum", "clap", - "forgejo-api", "futures-util", "hive-claude", "hive-sh4re", @@ -1390,13 +1348,11 @@ dependencies = [ "serde", "serde_json", "tempfile", - "time", "tokio", "tokio-stream", "tower-http", "tracing", "tracing-subscriber", - "url", ] [[package]] @@ -1428,7 +1384,6 @@ dependencies = [ "clap", "clap-markdown", "clap_complete", - "forgejo-api", "hive-sh4re", "libc", "listenfd", @@ -1443,7 +1398,6 @@ dependencies = [ "tokio-stream", "tracing", "tracing-subscriber", - "url", ] [[package]] @@ -1463,12 +1417,9 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "forgejo-api", "reqwest", "serde", "serde_json", - "time", - "url", ] [[package]] @@ -1680,11 +1631,9 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", - "system-configuration", "tokio", "tower-service", "tracing", - "windows-registry", ] [[package]] @@ -3069,7 +3018,6 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64", "bytes", - "encoding_rs", "futures-channel", "futures-core", "futures-util", @@ -3082,7 +3030,6 @@ dependencies = [ "hyper-util", "js-sys", "log", - "mime", "mime_guess", "percent-encoding", "pin-project-lite", @@ -3510,7 +3457,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags", - "core-foundation 0.10.1", + "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", @@ -3742,12 +3689,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "soft_assert" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5097ec7ea7218135541ad96348f1441d0c616537dd4ed9c47205920c35d7d97" - [[package]] name = "spki" version = "0.7.3" @@ -3845,27 +3786,6 @@ dependencies = [ "syn", ] -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tempfile" version = "3.27.0" @@ -4676,17 +4596,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - [[package]] name = "windows-result" version = "0.4.1" diff --git a/Cargo.toml b/Cargo.toml index 10737252..873c0434 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,14 +68,6 @@ reqwest = { version = "0.12", default-features = false, features = [ "json", "rustls-tls", ] } -forgejo-api = { version = "0.11", default-features = false, features = [ - "rustls-tls", -] } -url = "2" -time = { version = "0.3", default-features = false, features = [ - "formatting", - "parsing", -] } petgraph = { version = "0.8", default-features = false, features = ["std"] } matrix-sdk = { version = "0.14", default-features = false, features = [ "rustls-tls", diff --git a/docs/agent-hierarchy.md b/docs/agent-hierarchy.md index 038a52c3..a11ac9a6 100644 --- a/docs/agent-hierarchy.md +++ b/docs/agent-hierarchy.md @@ -214,8 +214,7 @@ nspawn agent. Open questions, not yet wired: ## Harness systemd unit shape -One harness serve binary (`hive-agent`, with its `hive-agent-mcp` / -`hive-agent-wake` siblings), one `harness-base.nix` template, one +One harness binary (`hive`), one `harness-base.nix` template, one service unit (`systemd.services.hive-ag3nt`) for all agents. There is no longer a separate manager service name or role distinction in the harness — privilege differences live server-side in the broker @@ -260,8 +259,7 @@ bit set` regardless of `hyperhive.user.passwordlessSudo`. ### `serviceConfig` highlights -- `ExecStart = pkgs.hyperhive/bin/hive-agent` — same binary for - every agent. +- `ExecStart = pkgs.hyperhive/bin/hive serve` — single binary. - `Restart = on-failure`, `RestartSec = 2` — keeps the harness resilient across transient crashes without thundering retries. - `RuntimeDirectory = "hive-config"` → `/run/hive-config/` owned by diff --git a/docs/forge.md b/docs/forge.md index 83a2481c..093b5860 100644 --- a/docs/forge.md +++ b/docs/forge.md @@ -71,12 +71,7 @@ Two things live in the `agent-configs` Forgejo organization: The `hive-forge` CLI (separate workspace crate, see [`README.md`](../README.md) file map) wraps the Forgejo REST API with the per-agent token; agents call it for issue / PR / comment -ops as if it were a peer. All REST calls across the workspace -(`hive-forge` verbs, hive-c0re provisioning, this poller) go through -the typed `forgejo-api` crate; only non-`/api/v1` web-router routes -(attachment / artifact downloads, log streaming) and the poller's -enrichment fetches of server-provided subject URLs stay on raw -reqwest. +ops as if it were a peer. ## Notification poller (`hive-ag3nt/src/forge_notify.rs`) @@ -151,14 +146,11 @@ one path still marked-read directly (no read-before-comment value). The poller starts disabled and stays that way for any of: -- `HIVE_FORGE_URL` not set (no forge configured for this hive), or - not parseable as a URL. +- `HIVE_FORGE_URL` not set (no forge configured for this hive). - `/forge-token` missing or empty (agent has no forge account — pre-provisioning or destroy-without-purge race). -- Initial client construction fails (the typed `forgejo-api` client - for the API calls, or the plain reqwest client kept for the - best-effort enrichment fetches of server-provided subject URLs; - both extremely unlikely; treated as fatal-to-the-task only). +- Initial `reqwest::Client::builder` fails (extremely unlikely; + treated as fatal-to-the-task only). Disabled = the spawned task returns immediately. All other failure modes (HTTP errors, parse errors, mark-read failures) are diff --git a/docs/turn-loop.md b/docs/turn-loop.md index 2a16037e..30b5390f 100644 --- a/docs/turn-loop.md +++ b/docs/turn-loop.md @@ -5,8 +5,7 @@ claude has access to in return. ## The loop -Each agent harness (`hive-agent` — one serve-loop binary for all -agents) runs: +Each agent harness (`hive serve` — one binary for all agents) runs: 1. Long-poll `Recv` on its socket. The host-side broker (`broker.rs::recv_blocking_batch`) returns immediately if there's @@ -52,37 +51,38 @@ agents) runs: ## Harness binary shape -Three sibling binaries out of the one `hive-ag3nt` crate, all -role-agnostic. (The earlier split into `hive-ag3nt` + `hive-m1nd` -was collapsed because the privilege boundary lives server-side at -the broker socket (`/run/hive/mcp.sock`): `ManagerRequest` calls are -refused by the standard agent socket regardless of who sends them.) +One `hive` binary for all agents. The earlier split into +`hive-ag3nt` + `hive-m1nd` was collapsed because the privilege +boundary lives server-side at the broker socket +(`/run/hive/mcp.sock`): `ManagerRequest` calls are refused by the +standard agent socket regardless of who sends them. -- `hive-agent` — long-running harness loop (the inbox poll + +Three subcommands: + +- `serve` — long-running harness loop (the inbox poll + claude-pump + ack/requeue cycle described above). -- `hive-agent-mcp` — MCP server. Default: stdio child claude spawns - via `--mcp-config` per turn (the serve loop renders the config to - point at this sibling of its own `/proc/self/exe`). With - `--http `, runs as a persistent streamable-HTTP daemon - instead (used by the `hive-mcp-http` systemd unit when - `hyperhive.mcp.httpPort` is set). -- `hive-agent-wake --from --body ` — push a message into - our own inbox so the next turn fires with the given body. Used by - co-process helpers (scrapers, webhook listeners) to nudge claude on - external events. `--body -` reads from stdin. +- `mcp` — MCP server. Default: stdio child claude spawns via + `--mcp-config` per turn. With `--http `, runs as a persistent + streamable-HTTP daemon instead (used by the `hive-mcp-http` + systemd unit when `hyperhive.mcp.httpPort` is set). +- `wake --from --body ` — push a message into our own + inbox so the next turn fires with the given body. Used by + co-process daemons (matrix bridge, scraper, webhook listeners) + to nudge claude on external events. `--body -` reads from stdin. ### `Surface` trait + zero-sized type tags `AgentRequest` / `AgentResponse` (= `ManagerRequest` / `ManagerResponse` — type aliases) are the wire types. There is one role: agent. -`bin/hive-agent.rs` factors the turn loop through a `Surface` trait -with one zero-sized impl (`AgentSurface`) wrapping: +`bin/hive.rs` factors the turn loop through a `Surface` trait with one +zero-sized impl (`AgentSurface`) wrapping: - One async method per wire op: `ack_turn`, `requeue_inflight`, - `inbox_unread`, `post_turn_counts`, `send_to_parent`, `recv_next`. + `inbox_unread`, `post_turn_counts`, `send_to_parent`, + `recv_next`, `wake_external`. `main()` calls `serve_main::` for all roles. The turn -loop (`serve_loop` / `handle_turn`) has no per-role branches. +loop (`serve_loop` / `handle_turn` / `wake`) has no per-role branches. ### Boot wiring diff --git a/docs/turn-loop/mcp.md b/docs/turn-loop/mcp.md index 2552e64a..02d707ed 100644 --- a/docs/turn-loop/mcp.md +++ b/docs/turn-loop/mcp.md @@ -138,7 +138,7 @@ External MCP servers (and any other in-container process) can inject a wake-up event into the agent's inbox via the per-agent socket at `/run/hive/mcp.sock`. Two equivalent paths: -- **Shell out to `hive-agent-wake --from