diff --git a/hive-c0re/src/container_view.rs b/hive-c0re/src/container_view.rs index f0639e9e..f6a84b2c 100644 --- a/hive-c0re/src/container_view.rs +++ b/hive-c0re/src/container_view.rs @@ -8,7 +8,6 @@ use std::collections::HashMap; use std::path::Path; -use chrono::{DateTime, Utc}; use serde::Serialize; use crate::coordinator::Coordinator; @@ -64,17 +63,6 @@ pub struct ContainerView { /// for stopped containers. #[serde(default, skip_serializing_if = "Option::is_none")] pub active_model: Option, - /// The agent's current free-text status (`set_status`). `None` when - /// unset, or when the container isn't running — see - /// [`read_agent_status_live`] for why a stopped agent's on-disk - /// status is treated as stale rather than surfaced. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub status_text: Option, - /// 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>, /// The agent's turn loop is parked: the container is up and still /// serving its web UI / MCP daemons, but it drives no turns and its /// inbox messages queue unacked until it resumes. Sourced from the @@ -167,17 +155,6 @@ pub async fn build_all(hive: &crate::coordinator::HiveEnv) -> Vec } else { None }; - // Same running-gate as `active_model` just above, and the same - // rule `read_agent_status_live` applies for its other callers — - // duplicated inline rather than calling that helper because it - // would re-derive `running` with its own `lifecycle::is_running` - // shell-out when this loop already has it for free. - let (status_text, status_set_at) = if running { - let (text, set_at) = read_agent_status(&logical); - (text, set_at.map(hive_sh4re::wire_time::from_secs)) - } else { - (None, None) - }; let paused = Coordinator::is_paused(&logical); let (cpu_quota, memory_max) = crate::resource_limits::effective_from( &limits, @@ -196,8 +173,6 @@ pub async fn build_all(hive: &crate::coordinator::HiveEnv) -> Vec deployed_sha, parent, active_model, - status_text, - status_set_at, paused, cpu_quota, memory_max, diff --git a/hive-c0re/src/dashboard/state_snapshot.rs b/hive-c0re/src/dashboard/state_snapshot.rs index 5b7fc68b..da62d746 100644 --- a/hive-c0re/src/dashboard/state_snapshot.rs +++ b/hive-c0re/src/dashboard/state_snapshot.rs @@ -817,8 +817,6 @@ mod tests { deployed_sha: None, parent: None, active_model: None, - status_text: None, - status_set_at: None, paused: false, cpu_quota: "200%".to_owned(), memory_max: "4G".to_owned(), diff --git a/hive-c0re/src/server.rs b/hive-c0re/src/server.rs index 9eaba30e..85a80cde 100644 --- a/hive-c0re/src/server.rs +++ b/hive-c0re/src/server.rs @@ -384,9 +384,6 @@ async fn handle_agent_status(coord: &Arc) -> HostResponse { pending_reminders: 0, parent: v.parent, paused: v.paused, - active_model: v.active_model, - status_text: v.status_text, - status_set_at: v.status_set_at, }) .collect(); HostResponse::agent_statuses(rows) diff --git a/hive-c0re/src/stats/host_stats.rs b/hive-c0re/src/stats/host_stats.rs index 56459f0f..2047e25c 100644 --- a/hive-c0re/src/stats/host_stats.rs +++ b/hive-c0re/src/stats/host_stats.rs @@ -254,8 +254,6 @@ mod tests { deployed_sha: None, parent: None, active_model: None, - status_text: None, - status_set_at: None, paused: false, cpu_quota: "200%".to_owned(), memory_max: "4G".to_owned(), diff --git a/hive-host-sock/src/lib.rs b/hive-host-sock/src/lib.rs index 29dc777c..62a5bc99 100644 --- a/hive-host-sock/src/lib.rs +++ b/hive-host-sock/src/lib.rs @@ -517,27 +517,8 @@ pub struct HiveUrls { pub matrix: Option, } -/// Wire-schema version for [`HostResponse`]. Bump only for a breaking -/// change — removing, renaming, or retyping an existing field; adding a -/// new `Option`/`skip_serializing_if` field (the pattern every field on -/// this struct already follows) is not breaking and needs no bump. A -/// client compares this against its own compiled-in copy of the constant -/// and warns (does not fail the request) on mismatch — see -/// `hivectl::client::version_mismatch_hint`, the one place that reads it. -pub const HOST_SOCK_VERSION: u32 = 1; - -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct HostResponse { - /// Wire-schema version this response was built against, see - /// [`HOST_SOCK_VERSION`]. `#[serde(default)]` so a pre-version daemon - /// (or a hand-built test fixture) deserializes as `0` rather than - /// failing to parse — `0` reads as "older than any real version" to a - /// mismatch check, which is the correct answer for wire predating - /// this field. Always populated on the write side: every constructor - /// below routes through `Self::default()`, whose `Default` impl (not - /// derived — see below) sets this to the current version. - #[serde(default)] - pub version: u32, pub ok: bool, #[serde(default, skip_serializing_if = "Option::is_none")] pub error: Option, @@ -587,31 +568,6 @@ pub struct HostResponse { pub quota: Option>, } -/// Hand-written rather than `#[derive(Default)]` for one reason: -/// `version` must default to [`HOST_SOCK_VERSION`], not `u32::default()` -/// (`0`). Every field below it is the same all-`None`/`false`/empty shape -/// `derive(Default)` would have produced — only `version` differs — and -/// every constructor in `impl HostResponse` builds through `..Self::default()`, -/// so this is the one place that has to get it right. -impl Default for HostResponse { - fn default() -> Self { - Self { - version: HOST_SOCK_VERSION, - ok: false, - error: None, - agents: None, - approvals: None, - urls: None, - agent_statuses: None, - agent_exists: None, - queued_dags: None, - nodes: None, - messages: Vec::new(), - quota: None, - } - } -} - impl HostResponse { #[must_use] pub fn success() -> Self { diff --git a/hive-sh4re/src/container.rs b/hive-sh4re/src/container.rs index 27ace066..59755a14 100644 --- a/hive-sh4re/src/container.rs +++ b/hive-sh4re/src/container.rs @@ -2,7 +2,6 @@ //! `HostRequest::AgentStatus` return, plus the per-account matrix //! identity shape surfaced by `GetAgentMeta`. -use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; /// One entry in a `ListDescendants` result. @@ -65,24 +64,6 @@ pub struct AgentStatusRow { /// Parent in the topology tree. `None` marks a root-level agent. #[serde(default, skip_serializing_if = "Option::is_none")] pub parent: Option, - /// 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, - /// 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, - /// 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>, } /// One matrix identity an agent can act as, surfaced in `GetAgentMeta`'s diff --git a/hivectl/src/client.rs b/hivectl/src/client.rs index 54a434f1..14bd4427 100644 --- a/hivectl/src/client.rs +++ b/hivectl/src/client.rs @@ -69,53 +69,12 @@ pub async fn request(socket: &Path, req: HostRequest) -> Result { bail!("server closed connection without responding"); } let resp: HostResponse = serde_json::from_str(line.trim())?; - if let Some(hint) = version_mismatch_hint(resp.version) { - eprintln!("{hint}"); - } Ok(resp) } -/// Message to print (stderr, non-fatal) when the daemon's `HostResponse` -/// wire version differs from what this `hivectl` build was compiled -/// against. `None` when they match. Split out from the print site so it's -/// unit-testable, same pattern as `connect_hint` above — a mismatch is not -/// a request failure (most fields still decode fine across a one-version -/// skew), just something the operator should know before trusting a -/// response that might be missing a field this build expects. -fn version_mismatch_hint(daemon_version: u32) -> Option { - (daemon_version != hive_host_sock::HOST_SOCK_VERSION).then(|| { - format!( - "warning: hive-c0re host.sock wire version {daemon_version} does not match \ - hivectl's {} — upgrade hivectl or the daemon; some response fields may be \ - missing or misread", - hive_host_sock::HOST_SOCK_VERSION - ) - }) -} - #[cfg(test)] mod tests { - use super::{ErrorKind, Path, connect_hint, version_mismatch_hint}; - - /// A daemon on the same version this `hivectl` was built against gets - /// no warning — the common case, and the only one that must stay silent. - #[test] - fn matching_version_has_no_hint() { - assert!(version_mismatch_hint(hive_host_sock::HOST_SOCK_VERSION).is_none()); - } - - /// A mismatch names both versions, so the operator knows which side - /// (hivectl or the daemon) is behind, not just that they disagree. - #[test] - fn mismatched_version_names_both_numbers() { - let other = hive_host_sock::HOST_SOCK_VERSION + 1; - let h = version_mismatch_hint(other).expect("versions differ"); - assert!(h.contains(&other.to_string()), "{h}"); - assert!( - h.contains(&hive_host_sock::HOST_SOCK_VERSION.to_string()), - "{h}" - ); - } + use super::{ErrorKind, Path, connect_hint}; fn hint(kind: ErrorKind) -> String { connect_hint(kind, Path::new("/run/hyperhive/host.sock"))