refactor(#2464): rename hive-ag3nt crate to hive-agent, collapse lib into main
This commit is contained in:
parent
7b54e7aa50
commit
3f1643c594
57 changed files with 101 additions and 130 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1460,7 +1460,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||
|
||||
[[package]]
|
||||
name = "hive-ag3nt"
|
||||
name = "hive-agent"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[workspace]
|
||||
resolver = "3"
|
||||
members = [
|
||||
"hive-ag3nt",
|
||||
"hive-agent",
|
||||
"hive-agent-mcp",
|
||||
"hive-agent-wake",
|
||||
"hive-bash-mcp",
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
//! Shared in-container harness code for the sibling `hive-agent` /
|
||||
//! `hive-agent-mcp` / `hive-agent-wake` binaries, which serve every
|
||||
//! agent role (the manager is just an agent role, not a separate set
|
||||
//! of binaries).
|
||||
|
||||
pub mod client;
|
||||
pub mod events;
|
||||
pub mod forge_notify;
|
||||
pub mod harness_state;
|
||||
pub mod identity;
|
||||
pub mod login;
|
||||
pub mod login_session;
|
||||
pub mod mcp_config;
|
||||
pub mod paths;
|
||||
pub mod plugins;
|
||||
pub mod prompt;
|
||||
pub mod serve_common;
|
||||
pub mod stats;
|
||||
pub mod turn;
|
||||
pub mod turn_stats;
|
||||
pub mod vacuum;
|
||||
pub mod web_ui;
|
||||
|
||||
/// Default socket path inside the container — bind-mounted by `hive-c0re`.
|
||||
pub const DEFAULT_SOCKET: &str = "/run/hive/mcp.sock";
|
||||
|
||||
/// Default web UI port — used when `HIVE_PORT` env is unset.
|
||||
pub const DEFAULT_WEB_PORT: u16 = 8042;
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
//!
|
||||
//! Standalone bin crate: the MCP surface (`mcp/`) plus its small support
|
||||
//! modules (socket client, send allow-list, loose-end scanner, path
|
||||
//! resolver) live here rather than in the `hive-ag3nt` harness lib, so the
|
||||
//! resolver) live here rather than in the `hive-agent` harness lib, so the
|
||||
//! server binary doesn't link the whole turn loop.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
//!
|
||||
//! Standalone bin crate: it dials the per-agent MCP socket directly and
|
||||
//! carries its own copy of the retrying request client (below), so it
|
||||
//! does not link the whole `hive-ag3nt` harness lib — a helper author
|
||||
//! does not link the whole `hive-agent` harness lib — a helper author
|
||||
//! wiring up an `extraMcpServers` binary only needs this one small crate.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
|
@ -66,7 +66,7 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
/// Self-contained retrying unix-socket JSON request client. A trimmed
|
||||
/// copy of `hive_ag3nt::client` (no `request_retried` variant — the wake
|
||||
/// copy of `hive_agent::client` (no `request_retried` variant — the wake
|
||||
/// CLI never needs the retry-count) so this bin does not link the harness
|
||||
/// lib. Keeping the retry matters: the socket can be briefly absent while
|
||||
/// hive-c0re restarts under an operator redeploy, and a helper firing a
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "hive-ag3nt"
|
||||
name = "hive-agent"
|
||||
edition.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
|
|
@ -34,8 +34,8 @@ tracing-subscriber.workspace = true
|
|||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
|
||||
# Three sibling harness binaries for all agents (auto-discovered from
|
||||
# `src/bin/`): `hive-agent` (serve loop), `hive-agent-mcp` (MCP
|
||||
# server), `hive-agent-wake` (wake CLI). Privilege boundary is
|
||||
# Single harness serve-loop binary: `hive-agent` (from `src/main.rs`).
|
||||
# The sibling MCP server + external wake CLI are their own bin crates
|
||||
# now (`hive-agent-mcp`, `hive-agent-wake`). Privilege boundary is
|
||||
# enforced server-side at the socket (tool groups / manager surface).
|
||||
# See `docs/turn-loop.md::Harness binary shape`.
|
||||
|
|
@ -184,11 +184,6 @@ impl EventStore {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn recent(&self, limit: usize) -> rusqlite::Result<Vec<StoredEvent>> {
|
||||
let (events, _, _) = self.page(None, limit)?;
|
||||
Ok(events)
|
||||
}
|
||||
|
||||
/// Fetch up to `limit` events with id < `before_id` (or the most recent
|
||||
/// `limit` events when `before_id` is `None`). Returns
|
||||
/// `(events_oldest_first, min_row_id, has_more)`.
|
||||
|
|
@ -818,17 +813,6 @@ impl Bus {
|
|||
self.tx.subscribe()
|
||||
}
|
||||
|
||||
/// Most recent events, oldest first, capped at `HISTORY_CAPACITY`.
|
||||
/// Drives the terminal pre-fill when the operator opens the agent
|
||||
/// page; without a store (db open failed) this is empty.
|
||||
#[must_use]
|
||||
pub fn history(&self) -> Vec<StoredEvent> {
|
||||
let Some(store) = &self.store else {
|
||||
return Vec::new();
|
||||
};
|
||||
store.recent(HISTORY_CAPACITY).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Paginated history: up to `limit` events before `before_id`
|
||||
/// (or the most recent `limit` when `before_id` is `None`).
|
||||
/// Returns `(events_oldest_first, min_row_id, has_more)`.
|
||||
|
|
@ -48,25 +48,6 @@ pub fn swarm_name() -> Option<String> {
|
|||
non_empty_env("HYPERHIVE_SWARM_NAME")
|
||||
}
|
||||
|
||||
/// One peer hive in the same swarm. Parsed from `HYPERHIVE_PEERS`.
|
||||
#[derive(Debug, Clone, serde::Deserialize)]
|
||||
pub struct PeerHive {
|
||||
pub domain: String,
|
||||
pub cert_fingerprint: Option<String>,
|
||||
}
|
||||
|
||||
/// Peer hives in the same swarm, parsed from `HYPERHIVE_PEERS` env var
|
||||
/// (JSON array of `{domain, cert_fingerprint}` objects, emitted by the
|
||||
/// c0re NixOS module from `services.hyperhive.swarm.peers`). Returns
|
||||
/// empty vec on single-hive deploys (env var absent).
|
||||
#[must_use]
|
||||
pub fn peers() -> Vec<PeerHive> {
|
||||
env::var("HYPERHIVE_PEERS")
|
||||
.ok()
|
||||
.and_then(|s| serde_json::from_str(&s).ok())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Hive-qualified agent identity. When the hive domain is configured, returns
|
||||
/// `${label}@${domain}` (e.g. `iris@darkest.space`); when not, returns just
|
||||
/// the short label so callers can render a single string regardless of
|
||||
|
|
@ -5,17 +5,43 @@
|
|||
//! loop points claude at) and `hive-agent-wake` (external wake CLI).
|
||||
//! Architecture lives in
|
||||
//! [`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.
|
||||
|
||||
mod client;
|
||||
mod events;
|
||||
mod forge_notify;
|
||||
mod harness_state;
|
||||
mod identity;
|
||||
mod login;
|
||||
mod login_session;
|
||||
mod mcp_config;
|
||||
mod paths;
|
||||
mod plugins;
|
||||
mod prompt;
|
||||
mod serve_common;
|
||||
mod stats;
|
||||
mod turn;
|
||||
mod turn_stats;
|
||||
mod vacuum;
|
||||
mod web_ui;
|
||||
|
||||
/// Default socket path inside the container — bind-mounted by `hive-c0re`.
|
||||
const DEFAULT_SOCKET: &str = "/run/hive/mcp.sock";
|
||||
|
||||
/// Default web UI port — used when `HIVE_PORT` env is unset.
|
||||
const DEFAULT_WEB_PORT: u16 = 8042;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::events::{Bus, LiveEvent, TurnState};
|
||||
use crate::login::LoginState;
|
||||
use crate::turn_stats::TurnStats;
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use hive_ag3nt::events::{Bus, LiveEvent, TurnState};
|
||||
use hive_ag3nt::login::{self, LoginState};
|
||||
use hive_ag3nt::turn_stats::TurnStats;
|
||||
use hive_ag3nt::{DEFAULT_SOCKET, DEFAULT_WEB_PORT, client, plugins, serve_common, turn, web_ui};
|
||||
use hive_sh4re::{AgentRequest, AgentResponse, HelperEvent, SYSTEM_SENDER};
|
||||
|
||||
#[derive(Parser)]
|
||||
|
|
@ -72,7 +98,7 @@ fn log_system_event(bus: &Bus, from: &str, body: &str) {
|
|||
/// Falls back to `<unknown>` when `HIVE_LABEL` is missing so a
|
||||
/// misconfigured harness still produces a parseable line.
|
||||
fn format_turn_failure(err: &anyhow::Error) -> String {
|
||||
let who = hive_ag3nt::identity::qualified_label();
|
||||
let who = crate::identity::qualified_label();
|
||||
let who = if who.is_empty() {
|
||||
"<unknown>".to_owned()
|
||||
} else {
|
||||
|
|
@ -87,7 +113,7 @@ fn format_turn_failure(err: &anyhow::Error) -> String {
|
|||
/// the role-specific `Wake` request — the sentinel itself is wire-
|
||||
/// agnostic so this helper lives outside both surfaces.
|
||||
fn consume_continue_sentinel() -> bool {
|
||||
let sentinel = hive_ag3nt::paths::state_dir().join("hyperhive-continue");
|
||||
let sentinel = crate::paths::state_dir().join("hyperhive-continue");
|
||||
if !sentinel.exists() {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -374,11 +400,11 @@ async fn serve_main<S: Surface>(socket: &Path, poll_ms: u64) -> Result<()> {
|
|||
for failure in plugins::install_configured().await {
|
||||
S::send_to_parent(socket, failure).await;
|
||||
}
|
||||
tokio::spawn(hive_ag3nt::forge_notify::run(socket.to_path_buf()));
|
||||
tokio::spawn(crate::forge_notify::run(socket.to_path_buf()));
|
||||
// Agent-side cleanup of this agent's own harness artifacts (completed
|
||||
// bash-task files + verbose event rows). Runs here, not host-side in
|
||||
// hive-c0re, because the files are agent-owned — see `vacuum` module docs.
|
||||
tokio::spawn(hive_ag3nt::vacuum::run());
|
||||
tokio::spawn(crate::vacuum::run());
|
||||
// Log web_ui::serve's error instead of dropping it. A bare
|
||||
// `tokio::spawn(web_ui::serve(...))` discards the JoinHandle, so
|
||||
// any Err (e.g. EACCES from `bind_unix` when HIVE_WEB_SOCKET points
|
||||
|
|
@ -171,7 +171,7 @@ mod tests {
|
|||
// no file open at compile, so this still doesn't pull
|
||||
// `prompts/` into the build hash.
|
||||
// The combined effect is that the flake's `cleanSrc` no longer
|
||||
// unions `./hive-ag3nt/prompts` — tweaks to system.md don't bust
|
||||
// unions `./hive-agent/prompts` — tweaks to system.md don't bust
|
||||
// the cargo cache anymore.
|
||||
static PRODUCTION_TEMPLATE: LazyLock<String> = LazyLock::new(|| {
|
||||
let path = match std::env::var("HIVE_ASSETS_DIR") {
|
||||
|
|
@ -185,11 +185,19 @@ pub struct KeyCount {
|
|||
pub count: u64,
|
||||
}
|
||||
|
||||
// Field names drop the `_ms` unit suffix (satisfies `clippy::struct_field_names`
|
||||
// once this crate is a bin — pub structs lose the lib API-name exemption), but
|
||||
// the serialized keys keep `_ms` via `serde(rename)` so the `/api/stats` JSON
|
||||
// contract the agent web UI reads (`frontend/packages/agent/src/stats.js`) is
|
||||
// unchanged.
|
||||
#[derive(Debug, Default, Serialize)]
|
||||
pub struct DurationSummary {
|
||||
pub avg_ms: f64,
|
||||
pub p50_ms: f64,
|
||||
pub p95_ms: f64,
|
||||
#[serde(rename = "avg_ms")]
|
||||
pub avg: f64,
|
||||
#[serde(rename = "p50_ms")]
|
||||
pub p50: f64,
|
||||
#[serde(rename = "p95_ms")]
|
||||
pub p95: f64,
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
|
@ -531,9 +539,9 @@ fn summarize_durations(all: &mut [i64]) -> DurationSummary {
|
|||
)]
|
||||
let len_f = all.len() as f64;
|
||||
DurationSummary {
|
||||
avg_ms: sum_f / len_f,
|
||||
p50_ms: percentile(all, 50),
|
||||
p95_ms: percentile(all, 95),
|
||||
avg: sum_f / len_f,
|
||||
p50: percentile(all, 50),
|
||||
p95: percentile(all, 95),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -686,9 +694,9 @@ mod tests {
|
|||
assert_eq!(model_totals.get("opus").copied(), Some(2));
|
||||
assert_eq!(model_totals.get("sonnet").copied(), Some(1));
|
||||
// Durations: [5000, 10000, 20000] → avg ≈ 11666.67, p50 = 10000, p95 ~ 20000
|
||||
assert!((s.duration_summary.avg_ms - 11_666.666_666_666_666).abs() < 1.0);
|
||||
assert!((s.duration_summary.p50_ms - 10_000.0).abs() < 1.0);
|
||||
assert!((s.duration_summary.p95_ms - 20_000.0).abs() < 1.0);
|
||||
assert!((s.duration_summary.avg - 11_666.666_666_666_666).abs() < 1.0);
|
||||
assert!((s.duration_summary.p50 - 10_000.0).abs() < 1.0);
|
||||
assert!((s.duration_summary.p95 - 20_000.0).abs() < 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -48,7 +48,7 @@ async fn round_trip(req: DaemonRequest) -> Result<DaemonResponse> {
|
|||
}
|
||||
|
||||
/// Format a `TaskFile` JSON value as a human-readable status string.
|
||||
/// Mirrors `format_bash_status` in the old hive-ag3nt, adapted to work
|
||||
/// Mirrors `format_bash_status` in the old hive-agent, adapted to work
|
||||
/// from the daemon's JSON payload.
|
||||
fn format_task(id: &str, task: &serde_json::Value) -> String {
|
||||
let status = task["status"].as_str().unwrap_or("unknown");
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
// `TaskFile` + `TaskStatus` are the bash-task on-disk schema. They live in
|
||||
// `hive-sh4re` (the shared wire-types crate) so the agent web UI in
|
||||
// `hive-ag3nt` can deserialize the same canonical type when reading the
|
||||
// `hive-agent` can deserialize the same canonical type when reading the
|
||||
// tasks dir for its running-tasks panel — no parallel copy to drift. Both
|
||||
// are re-exported here so existing `crate::protocol::{TaskFile, TaskStatus}`
|
||||
// imports across this crate keep compiling unchanged.
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ pub fn check_size(label: &str, body: &str) -> Result<(), String> {
|
|||
/// payload. Cap at 200 chars to fit the chip plus a little
|
||||
/// descriptive padding without forcing the operator to read a
|
||||
/// scrolling chunk.
|
||||
/// NOTE: `hive-ag3nt/src/mcp.rs::write_status_file` mirrors this constant
|
||||
/// NOTE: `hive-agent/src/mcp.rs::write_status_file` mirrors this constant
|
||||
/// client-side so invalid text is caught before the file is written.
|
||||
/// Keep in sync if this value changes.
|
||||
pub const STATUS_MAX_CHARS: usize = 200;
|
||||
|
|
|
|||
|
|
@ -1178,7 +1178,7 @@ fn choom(name: &str, resume_session: Option<&str>) -> Result<()> {
|
|||
// right `$HOME/.claude`.
|
||||
let target = format!("{name}@{container}");
|
||||
let claude = "/run/current-system/sw/bin/claude";
|
||||
// Bind-mounted state dir; matches `hive-ag3nt::paths::state_dir()`.
|
||||
// Bind-mounted state dir; matches `hive-agent::paths::state_dir()`.
|
||||
let state_dir = format!("/agents/{name}/state");
|
||||
// Per-turn config the harness writes; matches `paths::config_dir()`.
|
||||
let cfg = "/run/hive-config";
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ pub async fn build_all(coord: &Coordinator) -> Vec<ContainerView> {
|
|||
out
|
||||
}
|
||||
|
||||
/// Host-side mirror of `hive_ag3nt::login::has_session`. Returns true
|
||||
/// Host-side mirror of `hive_agent::login::has_session`. Returns true
|
||||
/// if the agent's bound `~/.claude/` dir on disk contains any regular
|
||||
/// file. Reads each `build_all()` so a login driven from the agent's
|
||||
/// own web UI reflects on the next snapshot.
|
||||
|
|
@ -225,7 +225,7 @@ fn read_active_model(name: &str) -> Option<String> {
|
|||
/// Host-side hive + swarm display names, read from the c0re service's
|
||||
/// own process env. The `hive-c0re.nix` module sets these from
|
||||
/// `services.hyperhive.{hiveName, swarmName}`. The agent-side
|
||||
/// `hive-ag3nt::identity::{hive_name, swarm_name}` accessors read the
|
||||
/// `hive-agent::identity::{hive_name, swarm_name}` accessors read the
|
||||
/// same env vars after they're forwarded into each sub-agent's
|
||||
/// harness service environment by `meta::render_flake`; surfacing
|
||||
/// them here from c0re's own env keeps the manager + agent
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use crate::lifecycle;
|
|||
|
||||
#[derive(Deserialize)]
|
||||
pub(super) struct JournalQuery {
|
||||
/// Optional systemd unit filter — e.g. `hive-ag3nt.service`. When
|
||||
/// Optional systemd unit filter — e.g. `hive-agent.service`. When
|
||||
/// omitted, returns the full machine journal.
|
||||
#[serde(default)]
|
||||
unit: Option<String>,
|
||||
|
|
@ -57,8 +57,8 @@ pub(super) async fn get_journal(
|
|||
let lines = q.lines.unwrap_or(500).min(5000);
|
||||
let unit = match q.unit.as_deref().filter(|s| !s.is_empty()) {
|
||||
Some(u) => {
|
||||
// accept hive-ag3nt[.service] — anything else refused.
|
||||
let allowed = ["hive-ag3nt.service"];
|
||||
// accept hive-agent[.service] — anything else refused.
|
||||
let allowed = ["hive-agent.service"];
|
||||
let unit = if u.ends_with(".service") {
|
||||
u.to_owned()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -495,7 +495,7 @@ async fn cmd_serve(
|
|||
}
|
||||
});
|
||||
// Per-agent events.sqlite + bash-tasks file cleanup now runs
|
||||
// agent-side in the harness (`hive_ag3nt::vacuum`): the files are
|
||||
// agent-side in the harness (`hive_agent::vacuum`): the files are
|
||||
// agent-owned, so host-side deletes hit PermissionDenied / readonly-db
|
||||
// under privsep. See issue tracker "perms borked".
|
||||
// (turn-stats.sqlite has no vacuum — it's one tiny row per turn,
|
||||
|
|
|
|||
|
|
@ -959,7 +959,7 @@ where
|
|||
then hyperhive.nixosConfigurations.ruth
|
||||
else hyperhive.nixosConfigurations.agent-base;
|
||||
input = inputs."agent-${name}";
|
||||
service = "hive-ag3nt";
|
||||
service = "hive-agent";
|
||||
parentEnv = if parent == null then {} else { HIVE_PARENT = parent; };
|
||||
toolGroupsEnv = if toolGroups == null then {} else { HIVE_TOOL_GROUPS = toolGroups; };
|
||||
capabilitiesEnv = if capabilities == null then {} else { HIVE_CAPABILITIES = capabilities; };
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
//! swarm totals + a per-agent rollup + model mix + a *labelled*
|
||||
//! cost estimate.
|
||||
//!
|
||||
//! Why re-read the rows here instead of reusing `hive-ag3nt`'s
|
||||
//! Why re-read the rows here instead of reusing `hive-agent`'s
|
||||
//! `stats.rs`: that module lives in a different crate (the agent
|
||||
//! harness) which hive-c0re can't import. The stable contract is the
|
||||
//! turn-stats *schema*, so we run a focused query against it. If we
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! Wake-signal writer: notifies the hyperhive harness when an incoming
|
||||
//! matrix event arrives so claude drives a new turn.
|
||||
//!
|
||||
//! Same wire shape as `hive-ag3nt::forge_notify`'s wake: a single JSON
|
||||
//! Same wire shape as `hive-agent::forge_notify`'s wake: a single JSON
|
||||
//! line written to the hyperhive control socket (`/run/hive/mcp.sock`
|
||||
//! by default) carrying an `AgentRequest::Wake { from, body }`.
|
||||
//! The agent harness's `agent_server` parses it and treats it as a
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ pub fn config_org_avatar_png() -> PathBuf {
|
|||
}
|
||||
|
||||
/// `$HIVE_ASSETS_DIR/prompts/system.md` — the claude system prompt
|
||||
/// template. `hive-ag3nt::prompt::render` filters the role markers
|
||||
/// template. `hive-agent::prompt::render` filters the role markers
|
||||
/// inside it per agent / manager flavor.
|
||||
#[must_use]
|
||||
pub fn prompt_template() -> PathBuf {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ pub mod wire_time;
|
|||
/// above the cap silently clamps. 5 keeps individual turns small — a big
|
||||
/// backlog is drained over several recv calls instead of one giant pop.
|
||||
/// Lives here so both the enforcing side (hive-c0re's `socket_server`) and
|
||||
/// the hinting side (hive-ag3nt's wake prompt + tool docs) reference one
|
||||
/// the hinting side (hive-agent's wake prompt + tool docs) reference one
|
||||
/// constant instead of a scattered magic value.
|
||||
pub const RECV_BATCH_MAX: u32 = 5;
|
||||
|
||||
|
|
@ -294,7 +294,7 @@ pub enum CancelLooseEndKind {
|
|||
///
|
||||
/// Canonical home for the bash-task persisted schema: `hive-bash-mcp`
|
||||
/// (the daemon that writes the files) re-exports these from its
|
||||
/// `protocol` module, and `hive-ag3nt` (the agent web UI that reads
|
||||
/// `protocol` module, and `hive-agent` (the agent web UI that reads
|
||||
/// them back for the running-tasks panel) deserializes the same type, so
|
||||
/// the on-disk shape can't drift between writer and reader.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# The hive-ag3nt harness service itself, plus the per-agent knobs it
|
||||
# The hive-agent harness service itself, plus the per-agent knobs it
|
||||
# reads from its environment: model selection, effort level,
|
||||
# compaction watermark, and the extra reverse-proxies of the per-agent
|
||||
# web UI.
|
||||
|
|
@ -176,7 +176,7 @@ in
|
|||
# docs/agent-hierarchy.md::Harness systemd unit shape. PATH /bin
|
||||
# auto-append behaviour: docs/gotchas.md::systemd.services.*.path
|
||||
# appends /bin to every entry.
|
||||
systemd.services.hive-ag3nt =
|
||||
systemd.services.hive-agent =
|
||||
let
|
||||
binary = "hive-agent";
|
||||
in
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ in
|
|||
{
|
||||
description = "Inject the OTEL auth header into the agent's claude user settings";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
before = [ "hive-ag3nt.service" ];
|
||||
before = [ "hive-agent.service" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
|
|
@ -341,7 +341,7 @@ in
|
|||
systemd.services.hive-claude-onboarding = {
|
||||
description = "Seed claude onboarding + project-trust so choom skips the walkthrough";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
before = [ "hive-ag3nt.service" ];
|
||||
before = [ "hive-agent.service" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
via `claude --add-dir`, so the markdown is readable at
|
||||
`$HIVE_DOCS_DIR/`, and appends a single pointer sentence to the agent's
|
||||
system prompt so it knows the docs exist (see
|
||||
`hive-ag3nt::prompt::render`). Default-on for the root/manager agent
|
||||
`hive-agent::prompt::render`). Default-on for the root/manager agent
|
||||
(see `../templates/ruth.nix`), off elsewhere; any agent can flip it from its
|
||||
`agent.nix`.
|
||||
'';
|
||||
|
|
@ -40,10 +40,10 @@
|
|||
# The harness reads HIVE_DOCS_DIR and passes it to claude as
|
||||
# `--add-dir` so the docs are readable, and appends a single
|
||||
# pointer sentence to the system prompt
|
||||
# (hive-ag3nt::prompt::render) telling the agent the docs exist.
|
||||
# (hive-agent::prompt::render) telling the agent the docs exist.
|
||||
# Source is `hyperhive.docs.source` (the narrow `hyperhive-docs`
|
||||
# meta-flake input, or `pkgs.hyperhive-docs` for standalone
|
||||
# builds). See hive-ag3nt::turn.
|
||||
# builds). See hive-agent::turn.
|
||||
HIVE_DOCS_DIR = "${config.hyperhive.docs.source}";
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -212,14 +212,14 @@ in
|
|||
# per-turn MCP registration race). It dials the control socket
|
||||
# (`/run/hive/mcp.sock`, the harness binaries' default) fresh on every
|
||||
# tool call, so a host-side hive-c0re restart is transparent.
|
||||
# `before = hive-ag3nt` so the URL is already listening by the time
|
||||
# `before = hive-agent` so the URL is already listening by the time
|
||||
# the harness renders the first turn's config; the harness/claude also
|
||||
# reconnect on their own, so ordering is a latency nicety not a hard
|
||||
# correctness dep.
|
||||
systemd.services.hive-mcp-http = {
|
||||
description = "persistent streamable-http MCP daemon for the hyperhive surface";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
before = [ "hive-ag3nt.service" ];
|
||||
before = [ "hive-agent.service" ];
|
||||
environment.RUST_LOG = "info";
|
||||
serviceConfig = {
|
||||
ExecStart = "${config.hyperhive.packages.hive-agent-mcp}/bin/hive-agent-mcp --http 127.0.0.1:${toString config.hyperhive.mcp.httpPort}";
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "local-fs.target" ];
|
||||
# Ordered before every network consumer that does DNS on first
|
||||
# boot. `hive-ag3nt` (the harness) is the load-bearing one: its
|
||||
# boot. `hive-agent` (the harness) is the load-bearing one: its
|
||||
# first-turn api.anthropic.com lookup must not race the resolv.conf
|
||||
# rewrite (it only declares `after network.target`, so without this
|
||||
# edge the harness can start before we've fixed resolv.conf and the
|
||||
|
|
@ -52,7 +52,7 @@
|
|||
before = [
|
||||
"network-online.target"
|
||||
"tea-login.service"
|
||||
"hive-ag3nt.service"
|
||||
"hive-agent.service"
|
||||
"hive-matrix-daemon.service"
|
||||
];
|
||||
unitConfig.ConditionPathExists = "/etc/hyperhive-bridge-dns";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
}:
|
||||
let
|
||||
# GUI processes run as the agent's own non-root user — the same user
|
||||
# hive-ag3nt runs as (declared + home-chowned by harness-base.nix) — so
|
||||
# hive-agent runs as (declared + home-chowned by harness-base.nix) — so
|
||||
# weston, the wayland client, and the agent share one user session.
|
||||
# `hyperhive.user.name` is set per-agent by the meta-flake renderer.
|
||||
userName = config.hyperhive.user.name;
|
||||
|
|
|
|||
|
|
@ -39,11 +39,11 @@ in
|
|||
};
|
||||
|
||||
# `cargo test --workspace` lifted out of the package builds so the
|
||||
# `hyperhive-assets` dep (which `hive-ag3nt::prompt::tests`
|
||||
# `hyperhive-assets` dep (which `hive-agent::prompt::tests`
|
||||
# needs via `HIVE_ASSETS_DIR` to assert against the actual
|
||||
# production prompt template) is scoped to this one check
|
||||
# instead of bleeding into the binary derivations' input
|
||||
# hash. Net: editing `hive-ag3nt/prompts/system.md` still
|
||||
# hash. Net: editing `hive-agent/prompts/system.md` still
|
||||
# rebuilds this test check (correct — the tests assert
|
||||
# against its wording), but `packages.default` and the
|
||||
# per-container toplevels stay fully cached.
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ in
|
|||
}
|
||||
// lib.optionalAttrs (config.services.hyperhive.swarm.peers != { }) {
|
||||
# Peer hives serialised as a JSON array of {domain, cert_fingerprint,
|
||||
# wireguard_address?} objects. Consumed by hive-ag3nt::identity::peers()
|
||||
# wireguard_address?} objects. Consumed by hive-agent::identity::peers()
|
||||
# + the dashboard's peer_hives StateSnapshot field (P33RS tab). Domain
|
||||
# is the attrset key; cert_fingerprint is null for CA-trusted peers;
|
||||
# wireguard_address is omitted when not part of the mesh.
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
first boot and drives the gateway/forge/agent URLs, with no safe
|
||||
default; changing it later is destructive). Exposed to agents as
|
||||
`HYPERHIVE_HIVE_DOMAIN`; consumed by
|
||||
`hive-ag3nt::identity::hive_domain()` for `<name>@<domain>`
|
||||
`hive-agent::identity::hive_domain()` for `<name>@<domain>`
|
||||
qualified labels.
|
||||
'';
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,16 +19,16 @@
|
|||
stdenv.mkDerivation {
|
||||
pname = "hyperhive-assets";
|
||||
version = "0.1.0";
|
||||
# Narrow `srcs` (branding/ + hive-ag3nt/prompts/) is what decouples
|
||||
# Narrow `srcs` (branding/ + hive-agent/prompts/) is what decouples
|
||||
# this derivation's input hash from the rest of the tree.
|
||||
srcs = [
|
||||
../../branding
|
||||
../../hive-ag3nt/prompts
|
||||
../../hive-agent/prompts
|
||||
];
|
||||
unpackPhase = ''
|
||||
runHook preUnpack
|
||||
cp -r ${../../branding} branding
|
||||
cp -r ${../../hive-ag3nt/prompts} prompts
|
||||
cp -r ${../../hive-agent/prompts} prompts
|
||||
chmod -R u+w branding prompts
|
||||
runHook postUnpack
|
||||
'';
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ let
|
|||
#
|
||||
# Tests are kept in the separate `checks.cargo-test` derivation
|
||||
# (carries the hyperhive-assets build input for the prompt-template
|
||||
# assertions in hive-ag3nt::prompt::tests). Keeping them out of this
|
||||
# assertions in hive-agent::prompt::tests). Keeping them out of this
|
||||
# derivation means a prompt edit doesn't bust the cargo cache.
|
||||
workspaceBuild = craneLib.buildPackage {
|
||||
src = cleanSrc;
|
||||
|
|
@ -160,7 +160,7 @@ in
|
|||
# `preBuildAgentTemplates` option on the hive-c0re module —
|
||||
# see nix/host-modules/hive-c0re.nix). Speeds up the first agent
|
||||
# spawn dramatically because the heavy lifting (nixpkgs +
|
||||
# claude-code + hive-ag3nt binary) is already in the store
|
||||
# claude-code + hive-agent binary) is already in the store
|
||||
# when the meta evaluator goes to build the container.
|
||||
#
|
||||
# nixosConfigurations are pinned to x86_64-linux (nixos-
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
# $out/share/icons/hicolor/<N>x<N>/apps/hyperhive.png (N = 16,32,48,64,128,256)
|
||||
# $out/share/pixmaps/hyperhive.png (48px fallback)
|
||||
# $out/share/applications/hive-c0re.desktop
|
||||
# $out/share/applications/hive-ag3nt.desktop
|
||||
# $out/share/applications/hive-agent.desktop
|
||||
stdenv.mkDerivation {
|
||||
pname = "hive-xdg-icons";
|
||||
version = "0.1.0";
|
||||
|
|
@ -62,11 +62,11 @@ stdenv.mkDerivation {
|
|||
NoDisplay=true
|
||||
StartupNotify=false
|
||||
EOF
|
||||
cat > "$out/share/applications/hive-ag3nt.desktop" <<EOF
|
||||
cat > "$out/share/applications/hive-agent.desktop" <<EOF
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=hyperhive agent
|
||||
GenericName=hive-ag3nt
|
||||
GenericName=hive-agent
|
||||
Comment=hyperhive per-agent harness (claude turn loop + MCP server)
|
||||
Exec=hive serve
|
||||
Icon=hyperhive
|
||||
|
|
|
|||
Loading…
Reference in a new issue