diff --git a/Cargo.lock b/Cargo.lock index 7d573164..759ac22d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1562,6 +1562,14 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "hive-agent-sock" +version = "0.1.0" +dependencies = [ + "hive-sh4re", + "serde", +] + [[package]] name = "hive-agent-wake" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 549abeb5..5ff5da9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "3" members = [ "hive-agent", "hive-agent-mcp", + "hive-agent-sock", "hive-core-agent-sock", "hive-agent-wake", "hive-bash-mcp", @@ -51,6 +52,7 @@ clap = { version = "4", features = ["derive"] } clap_complete = "4" indicatif = "0.18" hive-sh4re = { path = "hive-sh4re" } +hive-agent-sock = { path = "hive-agent-sock" } hive-core-agent-sock = { path = "hive-core-agent-sock" } hive-claude = { path = "hive-claude" } hive-host-sock = { path = "hive-host-sock" } diff --git a/hive-agent-sock/Cargo.toml b/hive-agent-sock/Cargo.toml new file mode 100644 index 00000000..7818923d --- /dev/null +++ b/hive-agent-sock/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "hive-agent-sock" +edition.workspace = true +version.workspace = true + +[lints] +workspace = true + +[dependencies] +serde.workspace = true +hive-sh4re.workspace = true diff --git a/hive-agent-sock/src/lib.rs b/hive-agent-sock/src/lib.rs new file mode 100644 index 00000000..dfe90539 --- /dev/null +++ b/hive-agent-sock/src/lib.rs @@ -0,0 +1,76 @@ +//! Wire types for the *in-agent* socket, served by the hive-agent harness +//! to the in-container producers (matrix / bash MCP daemons) and +//! forge_notify. Currently carries the loose-ends-v2 *todo* op family; +//! more in-agent request families may be added over time (the socket is +//! deliberately named for the agent, not the todos). +//! +//! Distinct from `hive-core-agent-sock`, the *host*-served core↔agent +//! protocol on `/run/hive/mcp.sock`: this socket never leaves the +//! container. The harness owns the todo store locally and signals its own +//! turn loop directly, so hive-c0re is not in the todo path — no broker +//! round-trip, no long-poll, no marker files. + +use serde::{Deserialize, Serialize}; + +use hive_sh4re::LooseEnd; + +/// In-container path of the harness-served in-agent socket. The harness +/// binds it on boot; the in-container producers dial it for todo ops. +/// (Placeholder default — the harness + producers resolve the real path +/// from config; kept here so a producer with no override has a sane one.) +pub const DEFAULT_AGENT_SOCKET: &str = "/run/hive/agent.sock"; + +/// A request on the in-agent socket. Serialised with a `cmd` tag so the +/// in-container producers can emit a plain JSON line without linking a +/// typed client (matrix/bash build the JSON by hand). +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "cmd", rename_all = "snake_case")] +pub enum Request { + /// Upsert a todo from an in-container subsystem (matrix / bash / + /// forge). `subsystem` is the producer marker; `key` the optional + /// subsystem-specific dedup key (a matrix room id, a bash task id). + /// A new-or-changed row signals the turn loop; an identical keyed + /// re-push is a silent no-op. Keyless todos always insert as one-offs. + UpsertTodo { + subsystem: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + key: Option, + summary: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + source: Option, + }, + /// Clear producer-resolved todo(s). `key = Some(k)` clears the one + /// keyed row; `key = None` clears the subsystem's keyless rows; `all + /// = true` wipes the producer's whole set (cancel-and-recreate on + /// daemon restart). + ClearTodo { + subsystem: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + key: Option, + #[serde(default)] + all: bool, + }, + /// List todos, optionally filtered to one `subsystem` (a producer + /// reconciling its own set). `None` = all. + ListTodos { + #[serde(default, skip_serializing_if = "Option::is_none")] + subsystem: Option, + }, + /// The agent marks one of its own todos done, by id. + MarkTodoDone { id: i64 }, +} + +/// A response on the in-agent socket. Serialised with a `kind` tag, +/// mirroring the core↔agent protocol's response shape. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum Response { + /// Op succeeded, no payload. + Ok, + /// Op succeeded and touched `count` rows (clear / mark-done). + Acked { count: u64 }, + /// `ListTodos` result. + LooseEnds { loose_ends: Vec }, + /// Op failed; `message` is operator-facing. + Err { message: String }, +}