feat(#2569): migrate matrix producer to the in-agent todo socket (unread + invites); drop dead mcp.sock/wake plumbing
This commit is contained in:
parent
a2d44c7eb6
commit
21f1569a04
5 changed files with 113 additions and 166 deletions
|
|
@ -1,17 +1,14 @@
|
|||
//! Wake-signal writer: notifies the hyperhive harness when an incoming
|
||||
//! matrix event arrives so claude drives a new turn.
|
||||
//! Todo writer: pushes matrix *todos* (loose-ends v2) to the harness's
|
||||
//! in-agent socket (`HIVE_AGENT_SOCKET`) when rooms have unread messages
|
||||
//! or pending invites, so claude drives a turn to handle them. One JSON
|
||||
//! line per op (`upsert_todo` / `clear_todo`), keyed by room id so
|
||||
//! re-pushing an unchanged item is an idempotent no-op and resolving one
|
||||
//! clears it. The harness owns the todo store locally and signals its own
|
||||
//! turn loop — no hive-c0re round-trip.
|
||||
//!
|
||||
//! 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 `Request::Wake { from, body }`.
|
||||
//! The agent harness's `agent_server` parses it and treats it as a
|
||||
//! `Wake` from the matrix subsystem.
|
||||
//!
|
||||
//! Per the operator's call (phase 3): the body is a SHORT TEASER, not
|
||||
//! the full message — the agent then reads the unmarked event via
|
||||
//! the `read_room` MCP tool. Truncation to ~100 chars keeps the wake
|
||||
//! prompt focused (`forge_notify` embeds longer excerpts because the
|
||||
//! agent doesn't have a follow-up read-the-original tool for forge).
|
||||
//! Todo summaries stay short: a SHORT TEASER, not the full message — the
|
||||
//! agent then reads the unmarked event via the `read_room` MCP tool.
|
||||
//! Truncation to ~100 chars keeps the summary focused.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
|
|
@ -19,65 +16,59 @@ use anyhow::{Context, Result};
|
|||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::UnixStream;
|
||||
|
||||
/// Send an `Request::Wake { from: "matrix", body }` to the hyperhive
|
||||
/// control socket at `socket`. Best-effort: returns Err on any plumbing
|
||||
/// failure; callers log + ignore so a wake delivery hiccup doesn't tear
|
||||
/// down the matrix sync loop.
|
||||
///
|
||||
/// Wire format matches `hive_core_agent_sock::Request` tagged with `"cmd"` per
|
||||
/// `#[serde(tag = "cmd", rename_all = "snake_case")]`. Must be `"cmd"`,
|
||||
/// not `"kind"` — the harness deserialises against the hive-sh4re type
|
||||
/// and silently discards requests that don't match.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error on socket connect failure, serialisation failure,
|
||||
/// or I/O error writing to or reading from the socket.
|
||||
pub async fn send_wake(socket: &Path, body: impl AsRef<str>) -> Result<()> {
|
||||
let payload = serde_json::json!({
|
||||
"cmd": "wake",
|
||||
"from": "matrix",
|
||||
"body": body.as_ref(),
|
||||
"transient": true,
|
||||
});
|
||||
send_line(socket, &payload).await
|
||||
/// The harness-served in-agent socket (`HIVE_AGENT_SOCKET`) where todo ops
|
||||
/// go — distinct from the host-served control socket used by [`send_wake`].
|
||||
/// `None` when unset/empty, in which case todo sends are a best-effort
|
||||
/// no-op (a standalone daemon without the harness socket).
|
||||
fn agent_socket() -> Option<std::path::PathBuf> {
|
||||
std::env::var_os("HIVE_AGENT_SOCKET")
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(std::path::PathBuf::from)
|
||||
}
|
||||
|
||||
/// Upsert a matrix-subsystem *todo* (loose-ends v2) on the
|
||||
/// hyperhive control socket — the replacement for a direct wake. `key` is
|
||||
/// the room id (the dedup key); hive-c0re coalesces a wake iff the todo is
|
||||
/// new or its `summary` changed. Best-effort like [`send_wake`].
|
||||
/// Upsert a matrix-subsystem *todo* (loose-ends v2) on the harness's
|
||||
/// in-agent socket — the replacement for a direct wake. `key` is the room
|
||||
/// id (the dedup key); the harness signals a turn iff the todo is new or
|
||||
/// its `summary` changed. Best-effort: a no-op when `HIVE_AGENT_SOCKET`
|
||||
/// isn't configured.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error on socket connect failure, serialisation failure,
|
||||
/// or I/O error writing to or reading from the socket.
|
||||
pub async fn send_todo_upsert(socket: &Path, key: &str, summary: impl AsRef<str>) -> Result<()> {
|
||||
pub async fn send_todo_upsert(key: &str, summary: impl AsRef<str>) -> Result<()> {
|
||||
let Some(socket) = agent_socket() else {
|
||||
return Ok(());
|
||||
};
|
||||
let payload = serde_json::json!({
|
||||
"cmd": "upsert_todo",
|
||||
"subsystem": "matrix",
|
||||
"key": key,
|
||||
"summary": summary.as_ref(),
|
||||
});
|
||||
send_line(socket, &payload).await
|
||||
send_line(&socket, &payload).await
|
||||
}
|
||||
|
||||
/// Clear matrix-subsystem todos. `key = Some(room)` clears one room's
|
||||
/// todo (it was read); `all = true` wipes the whole matrix set
|
||||
/// (cancel-and-recreate on daemon restart). Best-effort.
|
||||
/// Clear matrix-subsystem todos on the harness's in-agent socket. `key =
|
||||
/// Some(room)` clears one room's todo (it was read); `all = true` wipes the
|
||||
/// whole matrix set (cancel-and-recreate on daemon restart). Best-effort:
|
||||
/// a no-op when `HIVE_AGENT_SOCKET` isn't configured.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error on socket connect failure, serialisation failure,
|
||||
/// or I/O error writing to or reading from the socket.
|
||||
pub async fn send_todo_clear(socket: &Path, key: Option<&str>, all: bool) -> Result<()> {
|
||||
pub async fn send_todo_clear(key: Option<&str>, all: bool) -> Result<()> {
|
||||
let Some(socket) = agent_socket() else {
|
||||
return Ok(());
|
||||
};
|
||||
let payload = serde_json::json!({
|
||||
"cmd": "clear_todo",
|
||||
"subsystem": "matrix",
|
||||
"key": key,
|
||||
"all": all,
|
||||
});
|
||||
send_line(socket, &payload).await
|
||||
send_line(&socket, &payload).await
|
||||
}
|
||||
|
||||
/// Write one JSON request line to the hyperhive control socket and drain
|
||||
|
|
|
|||
Loading…
Reference in a new issue