Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/hive-sh4re/src/container.rs
atlas d94bc2188d topology: drop the parent field and the hierarchy it fed
`topology.json` was a map of `name -> parent | null`, and that value fed
the whole agent hierarchy: `<parent>` / `<children>` recipient sentinels,
the reparenting API (CLI verb, wire verb, dashboard endpoints, DAG node),
the dashboard tree, the rebuild depth sort, and an unconditional
bind-mount grant giving every agent RW on its direct children's state.

Per the operator's ruling the field goes, and with it all of the above.
The file survives as what remains once the value is gone: the roster of
agent names, which is the set `ManageRootAgent` grants mounts over. It is
now a JSON array; `read` still accepts the old map shape and keeps its
keys, so a hive that upgrades across this does not blank its roster (and
so no capability holder loses its mounts for the length of that window).

Two sites kept their behaviour under a different recipient rather than
losing it. Both addressed `<parent>`, which the broker already resolved to
`operator` for a root agent, and every agent is now what that fallback
called a root:

- the harness's turn-failure / plugin-failure notification
  (`Surface::send_to_parent` -> `send_to_operator`), and
- the send allow-list's always-permitted escape hatch, so an agent with a
  restrictive allow-list still has a way to say it is stuck.

What is NOT preserved, deliberately: an agent with no capability no longer
sees any other agent's dirs. `ManageRootAgent`'s own grant is unchanged --
still every agent in the roster, still state RW + config RO, still no
`harness`.

The dashboard's reparenting control (the M0V3 picker) is deleted with its
CSS. The tree rendering that reads `ContainerView.parent` is left for the
frontend owner -- it degrades to a flat list with the field gone.
2026-09-21 22:08:47 +02:00

102 lines
5.1 KiB
Rust

//! Container/agent-roster wire shapes: what `HostRequest::AgentStatus`
//! returns, plus the per-account matrix identity shape surfaced by
//! `GetAgentMeta`.
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
/// One row in a `HostRequest::AgentStatus` result — the operator-CLI
/// projection of the dashboard's per-agent `ContainerView`. Carries the
/// agent's running/health flags plus the technical state an operator
/// wants in a roster overview (`hivectl list-agents`).
//
// Four orthogonal, independently-observed facts about one agent, each
// rendered as its own column/token by `hivectl list-agents` and read
// individually by `--json` consumers. Any combination is meaningful
// (a stopped agent can be paused and need an update), so folding them
// into a state machine or nested flag structs would only add
// `serde(flatten)` indirection to preserve the same flat JSON. Same
// rationale as `LifecycleScope` in hive-host-sock.
#[allow(
clippy::struct_excessive_bools,
reason = "flat wire projection of independent per-agent flags"
)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentStatusRow {
/// Logical agent name (no `h-` prefix).
pub name: String,
/// Whether the container is currently running.
pub running: bool,
/// The container's unit is in systemd's `failed` state — it exhausted
/// its bounded restarts and gave up, as opposed to being stopped
/// deliberately. Orthogonal to `running`, like every other flag here:
/// a failed unit is not running, but a not-running unit is usually
/// just off.
#[serde(default)]
pub failed: bool,
/// Config commit is pending — the locked rev differs from the
/// agent's proposed/applied config (a rebuild would change it).
pub needs_update: bool,
/// The agent has no live claude session and is parked waiting for
/// the operator's re-auth flow.
pub needs_login: bool,
/// First 12 chars of the sha the meta flake currently has locked for
/// this agent's input. `None` when the agent has no locked rev yet.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub deployed_sha: Option<String>,
/// Count of this agent's pending reminders.
#[serde(default)]
pub pending_reminders: u64,
/// The agent's turn loop is parked (pause marker present in its
/// harness dir): the container may well be up and serving, it just
/// drives no turns. Orthogonal to `running` — an agent can be
/// paused while stopped, and pause survives a restart.
#[serde(default)]
pub paused: bool,
/// The Claude model the agent's harness is currently using. Mirrors
/// `container_view::ContainerView::active_model`: `None` when the
/// agent has never started a turn, the field is absent from its
/// harness state file, or the container isn't running.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub active_model: Option<String>,
/// The agent's current free-text status, set via the `set_status`
/// tool. `None` when unset or the container isn't running — a
/// stopped agent's on-disk status is a stale snapshot from before
/// the stop (same rule `container_view::read_agent_status_live`
/// applies for every other reader of this data).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status_text: Option<String>,
/// When `status_text` was last set, RFC 3339 UTC on the wire (see
/// `hive_sh4re::wire_time`). `None` exactly when `status_text` is
/// `None`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status_set_at: Option<DateTime<Utc>>,
/// This agent's own web UI, behind the gateway
/// (`https://<hive-domain>/agent/<name>/`) — the same URL the operator
/// dashboard's "open" action uses. `None` when the hive's domain isn't
/// configured (`HYPERHIVE_HIVE_DOMAIN` unset), same condition
/// `HostRequest::Urls`'s `HiveUrls::home` is `None` under. Present here
/// (not just derivable from `HiveUrls`) so a client never has to build
/// this path itself — see `hive-c0re/container_view.rs`'s `agent_url`
/// for why a client deriving it is the wrong shape to depend on.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
}
/// One matrix identity an agent can act as, surfaced in `GetAgentMeta`'s
/// `matrix_accounts`. Field names match the daemon's `matrix-accounts.json`
/// snapshot (written by `hive-matrix-mcp`'s account registry) so hive-c0re
/// deserializes the snapshot straight into `Vec<MatrixIdentity>`; the
/// snapshot's `live` / `is_primary` fields are ignored here (only live
/// accounts are listed).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MatrixIdentity {
/// Logical account name (the `account` arg on the matrix MCP tools).
pub name: String,
/// Matrix user id (`@user:server`). `None` if the session restored
/// without a known user id yet.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub user_id: Option<String>,
/// Homeserver base URL this account is on.
pub homeserver: String,
}