Watch
0
0
Fork
You've already forked hyperhive
0

hive-sh4re: split inbox, container, journal, and schedule wire shapes into their own modules

Closes the #3110 split — lib.rs is now just the crate doc comment and
the pub mod list.

journal.rs's new doc comment fixes a pre-existing bug: the old
JournalPriority doc text in lib.rs was actually half Capability's doc
(a leftover from an earlier reorder that moved the code but not the
comment above it).
This commit is contained in:
damocles 2026-08-10 23:06:57 +02:00 • committed by mara
commit 80f16094f1
30 changed files with 513 additions and 486 deletions

View file

@ -334,7 +334,7 @@ pub struct GetHostJournalArgs {
pub lines: Option<u32>,
/// Minimum syslog priority level.
#[serde(default)]
pub priority: Option<hive_sh4re::JournalPriority>,
pub priority: Option<hive_sh4re::journal::JournalPriority>,
/// Regex to match against log message fields (journalctl --grep).
#[serde(default)]
pub grep: Option<String>,

View file

@ -373,7 +373,7 @@ impl AgentServer {
let summary = format_matrix_summary(&unread_rooms);
loose_ends.insert(
0,
hive_sh4re::LooseEnd::UnreadMatrix {
hive_sh4re::inbox::LooseEnd::UnreadMatrix {
rooms: total,
summary,
},
@ -507,7 +507,7 @@ impl AgentServer {
// Reminders are harness-local — dial the in-agent socket
// directly instead of the broker; every other kind
// (question/approval) still lives in c0re.
if kind == hive_sh4re::CancelLooseEndKind::Reminder {
if kind == hive_sh4re::inbox::CancelLooseEndKind::Reminder {
return match dial_agent_socket(&hive_agent_sock::Request::CancelReminder { id })
.await
{
@ -527,7 +527,7 @@ impl AgentServer {
let (resp, retries) = self
.dispatch(hive_core_agent_sock::Request::CancelLooseEnd { kind, id })
.await;
if resp.is_ok() && kind == hive_sh4re::CancelLooseEndKind::Question {
if resp.is_ok() && kind == hive_sh4re::inbox::CancelLooseEndKind::Question {
// Best-effort — cancel is ownership-gated to the asker on
// the c0re side, so a successful cancel here always means
// *this* agent's own `asked` mirror row for `id`. Known gap
@ -627,8 +627,8 @@ impl AgentServer {
`at_unix_timestamp`"
.to_string();
}
(Some(s), None) => hive_sh4re::ReminderTiming::InSeconds { seconds: s },
(None, Some(t)) => hive_sh4re::ReminderTiming::At { unix_timestamp: t },
(Some(s), None) => hive_sh4re::inbox::ReminderTiming::InSeconds { seconds: s },
(None, Some(t)) => hive_sh4re::inbox::ReminderTiming::At { unix_timestamp: t },
};
// Reminders are harness-local — dial the in-agent socket
// directly instead of the broker.

View file

@ -66,8 +66,8 @@ pub fn format_recv(resp: Result<hive_core_agent_sock::Response, anyhow::Error>)
/// The synthetic single-message directive rendered for a fenced (graceful-stop)
/// inbox — see the `GracefulStop` arm of [`format_recv`].
fn graceful_stop_message() -> hive_sh4re::DeliveredMessage {
hive_sh4re::DeliveredMessage {
fn graceful_stop_message() -> hive_sh4re::inbox::DeliveredMessage {
hive_sh4re::inbox::DeliveredMessage {
from: "graceful-stop".into(),
body: "⛔ GRACEFUL STOP IN PROGRESS — the container shuts down as soon as this turn \
ends. Flush anything worth keeping to your durable /state files, then END \
@ -85,7 +85,10 @@ fn graceful_stop_message() -> hive_sh4re::DeliveredMessage {
/// depth; when non-zero a shared "(N more pending …)" hint (identical to the
/// wake prompt's) is appended so an in-turn drain knows more is queued. The
/// empty path never carries the hint (nothing was popped).
fn render_recv_messages(messages: &[hive_sh4re::DeliveredMessage], remaining: u64) -> String {
fn render_recv_messages(
messages: &[hive_sh4re::inbox::DeliveredMessage],
remaining: u64,
) -> String {
use std::fmt::Write as _;
if messages.is_empty() {
return "(empty)".to_owned();
@ -93,7 +96,7 @@ fn render_recv_messages(messages: &[hive_sh4re::DeliveredMessage], remaining: u6
let mut out = if messages.len() == 1 {
let m = &messages[0];
let banner = if m.redelivered {
hive_sh4re::REDELIVERY_HINT
hive_sh4re::inbox::REDELIVERY_HINT
} else {
""
};
@ -106,7 +109,7 @@ fn render_recv_messages(messages: &[hive_sh4re::DeliveredMessage], remaining: u6
out.push_str("\n---\n\n");
}
let banner = if m.redelivered {
hive_sh4re::REDELIVERY_HINT
hive_sh4re::inbox::REDELIVERY_HINT
} else {
""
};
@ -120,7 +123,7 @@ fn render_recv_messages(messages: &[hive_sh4re::DeliveredMessage], remaining: u6
}
out
};
out.push_str(&hive_sh4re::pending_hint(remaining));
out.push_str(&hive_sh4re::inbox::pending_hint(remaining));
out
}
@ -156,14 +159,14 @@ fn msg_id_tag(id: i64) -> String {
/// inbox messages are).
const MAX_RENDERED_TODOS: usize = 40;
/// Render one non-`Todo` [`hive_sh4re::LooseEnd`] variant onto `out`. Split
/// Render one non-`Todo` [`hive_sh4re::inbox::LooseEnd`] variant onto `out`. Split
/// out of `render_loose_ends` to keep that function under clippy's
/// `too_many_lines` limit — `Todo` stays inline there since it also needs
/// the shared `shown_todos` counter.
fn render_one_loose_end(out: &mut String, t: &hive_sh4re::LooseEnd) {
fn render_one_loose_end(out: &mut String, t: &hive_sh4re::inbox::LooseEnd) {
use std::fmt::Write as _;
match t {
hive_sh4re::LooseEnd::Approval {
hive_sh4re::inbox::LooseEnd::Approval {
id,
agent,
commit_ref,
@ -179,7 +182,7 @@ fn render_one_loose_end(out: &mut String, t: &hive_sh4re::LooseEnd) {
"- approval #{id} ({agent} @ {commit_ref}, {age_seconds}s old){desc}"
);
}
hive_sh4re::LooseEnd::Question {
hive_sh4re::inbox::LooseEnd::Question {
id,
asker,
target,
@ -192,7 +195,7 @@ fn render_one_loose_end(out: &mut String, t: &hive_sh4re::LooseEnd) {
"- question #{id} ({asker} → {to}, {age_seconds}s old): {question}"
);
}
hive_sh4re::LooseEnd::Reminder {
hive_sh4re::inbox::LooseEnd::Reminder {
id,
owner,
message,
@ -204,13 +207,13 @@ fn render_one_loose_end(out: &mut String, t: &hive_sh4re::LooseEnd) {
"- reminder #{id} ({owner}, scheduled {age_seconds}s ago, due_at={due_at}): {message}"
);
}
hive_sh4re::LooseEnd::PendingMessages { count } => {
hive_sh4re::inbox::LooseEnd::PendingMessages { count } => {
let _ = writeln!(
out,
"- {count} pending inbox message(s) — drain with recv (recv(max: {count}) to batch)"
);
}
hive_sh4re::LooseEnd::UnreadMatrix { rooms, summary } => {
hive_sh4re::inbox::LooseEnd::UnreadMatrix { rooms, summary } => {
let _ = write!(out, "- unread matrix messages in {rooms} room(s)");
if summary.is_empty() {
let _ = writeln!(
@ -228,13 +231,13 @@ fn render_one_loose_end(out: &mut String, t: &hive_sh4re::LooseEnd) {
);
}
}
hive_sh4re::LooseEnd::Todo { .. } => {
hive_sh4re::inbox::LooseEnd::Todo { .. } => {
// Handled inline by the caller (needs the shared shown-count).
}
}
}
/// Render one `Todo` [`hive_sh4re::LooseEnd`] onto `out`. Split out for the
/// Render one `Todo` [`hive_sh4re::inbox::LooseEnd`] onto `out`. Split out for the
/// same `too_many_lines` reason as [`render_one_loose_end`].
fn render_todo_loose_end(
out: &mut String,
@ -258,7 +261,7 @@ fn render_todo_loose_end(
/// Inner renderer for a `Vec<LooseEnd>` already extracted from the socket
/// reply. Called by the `get_loose_ends` handler, which injects the
/// `UnreadMatrix` entry before formatting.
pub(super) fn render_loose_ends(loose_ends: &[hive_sh4re::LooseEnd]) -> String {
pub(super) fn render_loose_ends(loose_ends: &[hive_sh4re::inbox::LooseEnd]) -> String {
use std::fmt::Write as _;
if loose_ends.is_empty() {
return "(no loose ends)".to_owned();
@ -267,7 +270,7 @@ pub(super) fn render_loose_ends(loose_ends: &[hive_sh4re::LooseEnd]) -> String {
let mut shown_todos = 0usize;
let mut hidden_todos = 0usize;
for t in loose_ends {
let hive_sh4re::LooseEnd::Todo {
let hive_sh4re::inbox::LooseEnd::Todo {
id,
subsystem,
subsystem_key,
@ -376,7 +379,7 @@ pub(super) async fn dial_agent_socket(
/// (loose-ends v2). Returns `None` when `HIVE_AGENT_SOCKET` is unset /
/// absent or the query fails — best-effort, like [`matrix_unread_summary`],
/// so an agent without the socket is not penalised.
pub(super) async fn local_todos() -> Option<Vec<hive_sh4re::LooseEnd>> {
pub(super) async fn local_todos() -> Option<Vec<hive_sh4re::inbox::LooseEnd>> {
match dial_agent_socket(&hive_agent_sock::Request::ListTodos { subsystem: None }).await? {
hive_agent_sock::Response::LooseEnds { loose_ends } => Some(loose_ends),
_ => None,
@ -386,7 +389,7 @@ pub(super) async fn local_todos() -> Option<Vec<hive_sh4re::LooseEnd>> {
/// Query the harness's in-agent socket for this agent's local pending
/// reminders — was a broker query before reminders moved in-container.
/// Same best-effort contract as [`local_todos`].
pub(super) async fn local_reminders() -> Option<Vec<hive_sh4re::LooseEnd>> {
pub(super) async fn local_reminders() -> Option<Vec<hive_sh4re::inbox::LooseEnd>> {
match dial_agent_socket(&hive_agent_sock::Request::ListReminders).await? {
hive_agent_sock::Response::LooseEnds { loose_ends } => Some(loose_ends),
_ => None,
@ -397,7 +400,7 @@ pub(super) async fn local_reminders() -> Option<Vec<hive_sh4re::LooseEnd>> {
/// (both roles — asked and answering). Same best-effort
/// contract as [`local_reminders`]; c0re stays the actual `Ask`/`Answer`
/// routing, this only mirrors the durable "still owed a reply" view.
pub(super) async fn local_questions() -> Option<Vec<hive_sh4re::LooseEnd>> {
pub(super) async fn local_questions() -> Option<Vec<hive_sh4re::inbox::LooseEnd>> {
match dial_agent_socket(&hive_agent_sock::Request::ListQuestions).await? {
hive_agent_sock::Response::LooseEnds { loose_ends } => Some(loose_ends),
_ => None,
@ -451,11 +454,13 @@ pub(super) fn format_matrix_summary(rooms: &[MatrixRoomUnread]) -> String {
/// wire enum. Accepts a small alias set so claude doesn't have to
/// remember the exact spelling (`"q"` / `"r"` shorthand falls out
/// for free).
pub(super) fn parse_loose_end_kind(raw: &str) -> Result<hive_sh4re::CancelLooseEndKind, String> {
pub(super) fn parse_loose_end_kind(
raw: &str,
) -> Result<hive_sh4re::inbox::CancelLooseEndKind, String> {
match raw.trim().to_ascii_lowercase().as_str() {
"question" | "q" => Ok(hive_sh4re::CancelLooseEndKind::Question),
"reminder" | "r" => Ok(hive_sh4re::CancelLooseEndKind::Reminder),
"approval" | "a" => Ok(hive_sh4re::CancelLooseEndKind::Approval),
"question" | "q" => Ok(hive_sh4re::inbox::CancelLooseEndKind::Question),
"reminder" | "r" => Ok(hive_sh4re::inbox::CancelLooseEndKind::Reminder),
"approval" | "a" => Ok(hive_sh4re::inbox::CancelLooseEndKind::Approval),
other => Err(format!(
"cancel_loose_end: unknown kind '{other}' \
(expected \"question\", \"reminder\", \"approval\", or \"todo\")"
@ -467,11 +472,11 @@ pub(super) fn parse_loose_end_kind(raw: &str) -> Result<hive_sh4re::CancelLooseE
/// the success ack so the caller always sees `"question"` /
/// `"reminder"` instead of whatever alias they passed in (`"q"` /
/// `"r"`).
pub(super) fn loose_end_kind_label(kind: hive_sh4re::CancelLooseEndKind) -> &'static str {
pub(super) fn loose_end_kind_label(kind: hive_sh4re::inbox::CancelLooseEndKind) -> &'static str {
match kind {
hive_sh4re::CancelLooseEndKind::Question => "question",
hive_sh4re::CancelLooseEndKind::Reminder => "reminder",
hive_sh4re::CancelLooseEndKind::Approval => "approval",
hive_sh4re::inbox::CancelLooseEndKind::Question => "question",
hive_sh4re::inbox::CancelLooseEndKind::Reminder => "reminder",
hive_sh4re::inbox::CancelLooseEndKind::Approval => "approval",
}
}
@ -595,8 +600,8 @@ pub fn annotate_retries(mut s: String, retries: u32) -> String {
mod tests {
use super::format_recv;
fn msg(id: i64, from: &str, body: &str) -> hive_sh4re::DeliveredMessage {
hive_sh4re::DeliveredMessage {
fn msg(id: i64, from: &str, body: &str) -> hive_sh4re::inbox::DeliveredMessage {
hive_sh4re::inbox::DeliveredMessage {
from: from.to_owned(),
body: body.to_owned(),
id,
@ -643,7 +648,7 @@ mod tests {
assert!(out.starts_with("popped 2 message(s):"));
assert_eq!(out.matches("more message(s) pending").count(), 1);
// `max` suggestion is clamped to the server-side recv cap.
let batch = 9u64.min(u64::from(hive_sh4re::RECV_BATCH_MAX));
let batch = 9u64.min(u64::from(hive_sh4re::inbox::RECV_BATCH_MAX));
assert!(out.contains(&format!("max: {batch}")));
}
}

View file

@ -16,7 +16,7 @@
use serde::{Deserialize, Serialize};
use hive_sh4re::LooseEnd;
use hive_sh4re::inbox::LooseEnd;
pub mod paths;
@ -80,7 +80,7 @@ pub enum Request {
/// harness persists an over-cap body instead of inlining it.
StoreReminder {
message: String,
timing: hive_sh4re::ReminderTiming,
timing: hive_sh4re::inbox::ReminderTiming,
#[serde(default, skip_serializing_if = "Option::is_none")]
file_path: Option<String>,
},

View file

@ -222,7 +222,7 @@ struct TurnControl {
/// set when this is the last todo wake before the miss-streak would hit
/// `TODO_MISS_PAUSE_THRESHOLD`, so the agent gets one unambiguous chance to
/// avoid being auto-paused.
fn synthetic_todo_message(stern: bool) -> hive_sh4re::DeliveredMessage {
fn synthetic_todo_message(stern: bool) -> hive_sh4re::inbox::DeliveredMessage {
let body = if stern {
"you have todos — call get_loose_ends NOW. you've skipped it on recent todo \
wakes in a row; if this turn doesn't call it, the harness will pause your \
@ -231,7 +231,7 @@ fn synthetic_todo_message(stern: bool) -> hive_sh4re::DeliveredMessage {
} else {
"you have todos — call get_loose_ends to see them".to_owned()
};
hive_sh4re::DeliveredMessage {
hive_sh4re::inbox::DeliveredMessage {
from: "todo".into(),
body,
id: 0,
@ -243,8 +243,8 @@ fn synthetic_todo_message(stern: bool) -> hive_sh4re::DeliveredMessage {
/// Synthetic message that drives the single stop-checkpoint turn when c0re
/// signals a graceful stop. The agent gets one final turn to flush durable
/// `/state` before the container is stopped; new inbound is already fenced.
fn graceful_stop_message() -> hive_sh4re::DeliveredMessage {
hive_sh4re::DeliveredMessage {
fn graceful_stop_message() -> hive_sh4re::inbox::DeliveredMessage {
hive_sh4re::inbox::DeliveredMessage {
from: "graceful-stop".into(),
body: "You are being gracefully stopped — the container will shut down after this turn, \
and new inbound messages are already fenced. This is your one checkpoint turn: \
@ -272,7 +272,7 @@ enum RecvOutcome {
/// body/id is per-row data the producer already resolved, so the
/// select arm wraps it straight into this variant — no dedicated
/// `LocalReminder` variant needed.
Message(hive_sh4re::DeliveredMessage),
Message(hive_sh4re::inbox::DeliveredMessage),
/// Long-poll timed out cleanly (empty `Messages` response). Caller
/// sleeps then retries.
Empty,
@ -529,7 +529,7 @@ async fn serve_main<S: Surface>(socket: &Path, poll_ms: u64) -> Result<()> {
let bus = Bus::new();
// Set by the web UI's `/api/cancel` on a successful SIGINT, read-and-
// cleared by `handle_turn` before building the next wake prompt — see
// `hive_sh4re::INTERRUPTED_HINT`. Shared between the web server task and
// `hive_sh4re::inbox::INTERRUPTED_HINT`. Shared between the web server task and
// the serve loop the same way `bus`/`todo_wake` are.
let interrupted = Arc::new(std::sync::atomic::AtomicBool::new(false));
let stats = TurnStats::open_default();
@ -654,7 +654,7 @@ async fn serve_loop<S: Surface>(
files: &turn::TurnFiles,
todo_wake: Arc<tokio::sync::Notify>,
todos_store: Option<Arc<todos::Todos>>,
mut reminder_rx: tokio::sync::mpsc::UnboundedReceiver<hive_sh4re::DeliveredMessage>,
mut reminder_rx: tokio::sync::mpsc::UnboundedReceiver<hive_sh4re::inbox::DeliveredMessage>,
interrupted: Arc<std::sync::atomic::AtomicBool>,
) -> Result<()> {
tracing::info!(socket = %socket.display(), "harness serve");
@ -849,7 +849,7 @@ async fn handle_turn<S: Surface>(
stats: Option<&TurnStats>,
files: &turn::TurnFiles,
session: &turn::AgentSession,
first: hive_sh4re::DeliveredMessage,
first: hive_sh4re::inbox::DeliveredMessage,
interrupted: &std::sync::atomic::AtomicBool,
) -> TurnControl {
let from = first.from;

View file

@ -1,6 +1,6 @@
//! Delivery half of the harness-local reminders store. Polls [`Reminders`]
//! for due rows and pushes each as a
//! [`hive_sh4re::DeliveredMessage`] down an mpsc channel the serve loop
//! [`hive_sh4re::inbox::DeliveredMessage`] down an mpsc channel the serve loop
//! races against the broker long-poll — mirrors `todo_server`'s `Notify`
//! wake, but a reminder fire carries real per-row data (message/id), so a
//! bare `Notify` doesn't fit; the channel carries the finished message
@ -60,7 +60,7 @@ fn remind_max_pending() -> u64 {
/// select), while cleanly disabling delivery.
pub async fn run(
store: Option<Arc<Reminders>>,
tx: mpsc::UnboundedSender<hive_sh4re::DeliveredMessage>,
tx: mpsc::UnboundedSender<hive_sh4re::inbox::DeliveredMessage>,
) {
let Some(store) = store else {
tracing::error!("reminders db unavailable — reminder delivery disabled");
@ -73,7 +73,7 @@ pub async fn run(
}
}
fn tick(store: &Reminders, tx: &mpsc::UnboundedSender<hive_sh4re::DeliveredMessage>) {
fn tick(store: &Reminders, tx: &mpsc::UnboundedSender<hive_sh4re::inbox::DeliveredMessage>) {
let now = chrono::Utc::now().timestamp();
let due = match store.due(now, REMINDER_BATCH_LIMIT) {
Ok(rows) => rows,
@ -84,7 +84,7 @@ fn tick(store: &Reminders, tx: &mpsc::UnboundedSender<hive_sh4re::DeliveredMessa
};
for r in due {
let body = prepare_body(&r.message, r.file_path.as_deref());
let dm = hive_sh4re::DeliveredMessage {
let dm = hive_sh4re::inbox::DeliveredMessage {
from: "reminder".into(),
body,
id: 0,
@ -109,7 +109,7 @@ fn tick(store: &Reminders, tx: &mpsc::UnboundedSender<hive_sh4re::DeliveredMessa
pub fn store(
store: &Reminders,
message: &str,
timing: &hive_sh4re::ReminderTiming,
timing: &hive_sh4re::inbox::ReminderTiming,
file_path: Option<&str>,
) -> Result<i64, String> {
let max = remind_max_pending();
@ -237,8 +237,8 @@ fn write_payload(path: &Path, message: &str) -> Result<(), String> {
}
/// Resolve the `due_at` unix timestamp for a `StoreReminder` request.
fn resolve_due_at(timing: &hive_sh4re::ReminderTiming) -> anyhow::Result<i64> {
use hive_sh4re::ReminderTiming;
fn resolve_due_at(timing: &hive_sh4re::inbox::ReminderTiming) -> anyhow::Result<i64> {
use hive_sh4re::inbox::ReminderTiming;
match timing {
ReminderTiming::InSeconds { seconds } => {
let now = std::time::SystemTime::now();
@ -268,14 +268,15 @@ mod tests {
#[test]
fn resolve_due_at_in_seconds_is_close_to_now_plus_n() {
let due = resolve_due_at(&hive_sh4re::ReminderTiming::InSeconds { seconds: 60 }).unwrap();
let due =
resolve_due_at(&hive_sh4re::inbox::ReminderTiming::InSeconds { seconds: 60 }).unwrap();
let now = chrono::Utc::now().timestamp();
assert!((due - now - 60).abs() <= 2, "due={due} now={now}");
}
#[test]
fn resolve_due_at_at_passes_through() {
let due = resolve_due_at(&hive_sh4re::ReminderTiming::At {
let due = resolve_due_at(&hive_sh4re::inbox::ReminderTiming::At {
unix_timestamp: 123_456,
})
.unwrap();

View file

@ -27,9 +27,9 @@ pub fn format_wake_prompt(
interrupted: bool,
) -> String {
let banner = if redelivered {
hive_sh4re::REDELIVERY_HINT
hive_sh4re::inbox::REDELIVERY_HINT
} else if interrupted {
hive_sh4re::INTERRUPTED_HINT
hive_sh4re::inbox::INTERRUPTED_HINT
} else {
""
};
@ -38,7 +38,7 @@ pub fn format_wake_prompt(
} else {
String::new()
};
let pending = hive_sh4re::pending_hint(unread);
let pending = hive_sh4re::inbox::pending_hint(unread);
format!("{banner}{tag}Incoming message from `{from}`:\n---\n{body}\n---{pending}")
}

View file

@ -30,7 +30,7 @@ use std::sync::Arc;
use anyhow::{Context, Result};
use hive_agent_sock::{Request, Response};
use hive_sh4re::LooseEnd;
use hive_sh4re::inbox::LooseEnd;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::{UnixListener, UnixStream};
use tokio::sync::Notify;
@ -248,7 +248,7 @@ fn dispatch(
fn store_reminder(
reminders: Option<&Reminders>,
message: &str,
timing: &hive_sh4re::ReminderTiming,
timing: &hive_sh4re::inbox::ReminderTiming,
file_path: Option<&str>,
) -> Response {
let Some(r) = reminders else {

View file

@ -60,7 +60,7 @@ struct AppState {
gui_vnc_port: Option<u16>,
/// Set by `post_cancel_turn` on a successful SIGINT; read-and-cleared
/// by the serve loop's next `handle_turn` to prepend
/// `hive_sh4re::INTERRUPTED_HINT` to that turn's wake prompt. Shared
/// `hive_sh4re::inbox::INTERRUPTED_HINT` to that turn's wake prompt. Shared
/// with the serve loop via the same `Arc` (see `serve_main`) — an
/// in-memory flag, not a marker file, since a `/cancel` from a prior
/// process lifetime isn't meaningful once the harness restarts.

View file

@ -124,7 +124,7 @@ pub(super) struct StateSnapshot {
/// Last N messages addressed to this agent, newest-first. Pulled
/// from the broker via the per-agent socket on each render.
/// Empty on transport failure.
inbox: Vec<hive_sh4re::InboxRow>,
inbox: Vec<hive_sh4re::inbox::InboxRow>,
/// Authoritative turn-loop state from the harness and the unix
/// timestamp the state was entered. The JS computes the age
/// client-side off this rather than tracking it from SSE events.
@ -363,7 +363,7 @@ struct ExtraLink {
/// Best-effort: pull the last 30 messages addressed to us via the
/// per-agent / manager socket. Empty list on any transport / decode
/// failure — the inbox section is decorative, not authoritative.
async fn recent_inbox(socket: &std::path::Path) -> Vec<hive_sh4re::InboxRow> {
async fn recent_inbox(socket: &std::path::Path) -> Vec<hive_sh4re::inbox::InboxRow> {
const LIMIT: u64 = 30;
// Deadline-bounded (via `broker_request`): `/api/state` must render even
// when hive-c0re is busy — an empty inbox section beats a hung snapshot.

View file

@ -654,16 +654,17 @@ impl Coordinator {
/// after each tick that fires or rearms a row) so the dashboard's
/// scheduled-prompts tab updates live without polling.
pub fn emit_schedules_snapshot(self: &Arc<Self>) {
let mut schedules: Vec<hive_sh4re::WireSchedule> = match self.scheduled_prompts.list() {
Ok(rows) => rows
.into_iter()
.map(crate::socket_server::schedule_to_wire_public)
.collect(),
Err(e) => {
tracing::warn!(error = ?e, "emit_schedules_snapshot: list failed");
return;
}
};
let mut schedules: Vec<hive_sh4re::schedule::WireSchedule> =
match self.scheduled_prompts.list() {
Ok(rows) => rows
.into_iter()
.map(crate::socket_server::schedule_to_wire_public)
.collect(),
Err(e) => {
tracing::warn!(error = ?e, "emit_schedules_snapshot: list failed");
return;
}
};
// Strip ghost targets (destroyed agents) so the dashboard doesn't
// render dead columns. Best-effort: if the roster cache is
// momentarily contended we emit unfiltered rather than block this
@ -1055,7 +1056,7 @@ impl Coordinator {
let old_label = old_parent.as_deref().unwrap_or("<root>");
let new_label = new_parent.unwrap_or("<root>");
if let Some(op) = old_parent.as_deref() {
let _ = self.broker.send(&hive_sh4re::Message {
let _ = self.broker.send(&hive_sh4re::inbox::Message {
from: hive_sh4re::manager::trusted_sender(hive_sh4re::manager::SYSTEM_SENDER),
to: op.to_owned(),
body: format!("{child} moved out of your subtree to {new_label}"),
@ -1063,7 +1064,7 @@ impl Coordinator {
});
}
if let Some(np) = new_parent {
let _ = self.broker.send(&hive_sh4re::Message {
let _ = self.broker.send(&hive_sh4re::inbox::Message {
from: hive_sh4re::manager::trusted_sender(hive_sh4re::manager::SYSTEM_SENDER),
to: np.to_owned(),
body: format!(
@ -1344,7 +1345,7 @@ impl Coordinator {
`--continue` session is intact, so prior context is \
still in your window."
);
if let Err(e) = self.broker.send(&hive_sh4re::Message {
if let Err(e) = self.broker.send(&hive_sh4re::inbox::Message {
from: hive_sh4re::manager::trusted_sender(hive_sh4re::manager::SYSTEM_SENDER),
to: name.to_owned(),
body,
@ -1492,7 +1493,7 @@ impl Coordinator {
return;
}
};
if let Err(e) = self.broker.send(&hive_sh4re::Message {
if let Err(e) = self.broker.send(&hive_sh4re::inbox::Message {
from: hive_sh4re::manager::trusted_sender(from),
to: agent.to_owned(),
body,
@ -1514,7 +1515,7 @@ impl Coordinator {
if agent_name == from {
continue;
}
if let Err(e) = self.broker.send(&hive_sh4re::Message {
if let Err(e) = self.broker.send(&hive_sh4re::inbox::Message {
from: hive_sh4re::manager::trusted_sender(from),
to: agent_name.clone(),
body: broadcast_body.clone(),

View file

@ -227,7 +227,7 @@ pub(super) async fn post_op_send(
errors.join("; ")
));
}
} else if let Err(e) = state.coord.broker.send(&hive_sh4re::Message {
} else if let Err(e) = state.coord.broker.send(&hive_sh4re::inbox::Message {
from: hive_sh4re::manager::trusted_sender(hive_sh4re::manager::OPERATOR_RECIPIENT),
to: to.clone(),
body,

View file

@ -23,7 +23,7 @@ use super::{AppState, error_problem, error_response};
///
/// Returns the wire shape directly so the frontend can render
/// without an extra translation layer.
// `hive_sh4re::WireSchedule` (the actual body) has no `ToSchema` — adding
// `hive_sh4re::schedule::WireSchedule` (the actual body) has no `ToSchema` — adding
// one would pull `utoipa` into the wire-types crate for a single dashboard
// endpoint. `serde_json::Value` placeholder; see the batch report.
#[utoipa::path(
@ -38,7 +38,7 @@ use super::{AppState, error_problem, error_response};
pub(super) async fn api_schedules(State(state): State<AppState>) -> Response {
match state.coord.scheduled_prompts.list() {
Ok(rows) => {
let mut wire: Vec<hive_sh4re::WireSchedule> = rows
let mut wire: Vec<hive_sh4re::schedule::WireSchedule> = rows
.into_iter()
.map(crate::socket_server::schedule_to_wire_public)
.collect();

View file

@ -239,7 +239,7 @@ pub enum DashboardEvent {
/// naturally re-derived from the full list.
SchedulesChanged {
seq: u64,
schedules: Vec<hive_sh4re::WireSchedule>,
schedules: Vec<hive_sh4re::schedule::WireSchedule>,
},
/// Full snapshot of capability grants (per-agent `Vec<cap_name>`).
/// Emitted from the rebuild-queue worker after a `PermChange`

View file

@ -21,7 +21,7 @@
use anyhow::Result;
use chrono::Utc;
use hive_sh4re::LooseEnd;
use hive_sh4re::inbox::LooseEnd;
use crate::coordinator::Coordinator;

View file

@ -152,11 +152,11 @@ pub fn handle_answer(
pub fn handle_cancel_loose_end(
coord: &Arc<Coordinator>,
canceller: &str,
kind: hive_sh4re::CancelLooseEndKind,
kind: hive_sh4re::inbox::CancelLooseEndKind,
id: i64,
) -> Result<(), String> {
match kind {
hive_sh4re::CancelLooseEndKind::Question => {
hive_sh4re::inbox::CancelLooseEndKind::Question => {
// Agent-socket path: never privileged — an agent may only cancel
// its own question (ownership). The operator's cancel-anything
// path goes through a separate handler with `privileged = true`.
@ -185,7 +185,7 @@ pub fn handle_cancel_loose_end(
coord.emit_question_resolved(id, &sentinel, canceller, true, target.as_deref());
Ok(())
}
hive_sh4re::CancelLooseEndKind::Reminder => {
hive_sh4re::inbox::CancelLooseEndKind::Reminder => {
// Reminders are now agent-local (in-container store) — the
// agent-mcp `cancel_loose_end` tool branches on this kind and
// dials the agent's own socket directly, never forwarding to
@ -196,7 +196,7 @@ pub fn handle_cancel_loose_end(
not by hive-c0re"
))
}
hive_sh4re::CancelLooseEndKind::Approval => {
hive_sh4re::inbox::CancelLooseEndKind::Approval => {
// Withdrawing an approval needs the grantable `approvals`
// tool-group (held by any approval-submitting orchestrator)
// AND ownership: only the agent that submitted the approval

View file

@ -354,7 +354,7 @@ async fn handle_agent_status(coord: &Arc<Coordinator>) -> HostResponse {
let rows = crate::container_view::build_all(&coord.hive_env())
.await
.into_iter()
.map(|v| hive_sh4re::AgentStatusRow {
.map(|v| hive_sh4re::container::AgentStatusRow {
name: v.name,
running: v.running,
needs_update: v.needs_update,

View file

@ -189,7 +189,7 @@ pub(super) async fn handle_list_descendants(coord: &Arc<Coordinator>, agent: &st
// rather than erroring — matches the old membership-check's
// default-false behavior for an unknown name.
let running = running_by_name.get(name.as_str()).copied().unwrap_or(false);
hive_sh4re::ContainerInfo { name, running }
hive_sh4re::container::ContainerInfo { name, running }
})
.collect();
Response::Containers { containers }

View file

@ -14,7 +14,7 @@ use std::sync::Arc;
use anyhow::{Context, Result};
use hive_core_agent_sock::{Request, Response};
use hive_sh4re::Message;
use hive_sh4re::inbox::Message;
use hive_sh4re::manager::MANAGER_AGENT;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::{UnixListener, UnixStream};
@ -169,9 +169,9 @@ async fn serve(stream: UnixStream, agent: String, coord: Arc<Coordinator>) -> Re
pub(crate) const RECV_LONG_POLL_MAX: std::time::Duration = std::time::Duration::from_mins(3);
/// Server-side hard cap on `Recv.max` — canonical value lives in
/// `hive_sh4re::RECV_BATCH_MAX` so the harness's wake-prompt hint and
/// `hive_sh4re::inbox::RECV_BATCH_MAX` so the harness's wake-prompt hint and
/// this enforcement site can't drift apart.
pub(crate) const RECV_BATCH_MAX: u32 = hive_sh4re::RECV_BATCH_MAX;
pub(crate) const RECV_BATCH_MAX: u32 = hive_sh4re::inbox::RECV_BATCH_MAX;
pub(crate) fn recv_timeout(wait_seconds: Option<u64>) -> std::time::Duration {
match wait_seconds {
@ -314,7 +314,7 @@ async fn handle_recv(
hive_core_agent_sock::Response::Messages {
messages: deliveries
.into_iter()
.map(|d| hive_sh4re::DeliveredMessage {
.map(|d| hive_sh4re::inbox::DeliveredMessage {
from: d.message.from.to_string(),
body: d.message.body,
id: d.id,
@ -458,11 +458,13 @@ async fn handle_get_agent_meta(
/// or the daemon not up yet) yields an empty list. The `MatrixIdentity`
/// serde shape matches the snapshot entries; the snapshot's `live` field is
/// ignored (only live accounts are written).
fn read_agent_matrix_identities(agent: &hive_types::Ident) -> Vec<hive_sh4re::MatrixIdentity> {
fn read_agent_matrix_identities(
agent: &hive_types::Ident,
) -> Vec<hive_sh4re::container::MatrixIdentity> {
let path = Coordinator::agent_notes_dir(agent).join("matrix-accounts.json");
std::fs::read_to_string(&path)
.ok()
.and_then(|s| serde_json::from_str::<Vec<hive_sh4re::MatrixIdentity>>(&s).ok())
.and_then(|s| serde_json::from_str::<Vec<hive_sh4re::container::MatrixIdentity>>(&s).ok())
.unwrap_or_default()
}
@ -825,7 +827,7 @@ pub struct HostJournalArgs<'a> {
pub unit: &'a Option<String>,
pub container: &'a Option<String>,
pub lines: &'a Option<u32>,
pub priority: &'a Option<hive_sh4re::JournalPriority>,
pub priority: &'a Option<hive_sh4re::journal::JournalPriority>,
pub grep: &'a Option<String>,
pub since: &'a Option<String>,
pub until: &'a Option<String>,

View file

@ -290,7 +290,9 @@ fn cancel_authorized(requester: &str, owner: &str) -> bool {
/// Public alias `schedule_to_wire_public` re-exports for
/// `dashboard.rs::api_schedules` without crossing the module
/// boundary into the socket-server file.
pub fn schedule_to_wire_public(s: crate::scheduled_prompts::Schedule) -> hive_sh4re::WireSchedule {
pub fn schedule_to_wire_public(
s: crate::scheduled_prompts::Schedule,
) -> hive_sh4re::schedule::WireSchedule {
schedule_to_wire(s)
}
@ -306,7 +308,7 @@ pub fn schedule_to_wire_public(s: crate::scheduled_prompts::Schedule) -> hive_sh
/// schedule rows keep every target, so a re-spawned agent's targets
/// reappear on their own.
pub(crate) fn filter_ghost_schedule_targets(
schedules: &mut [hive_sh4re::WireSchedule],
schedules: &mut [hive_sh4re::schedule::WireSchedule],
live: &std::collections::HashSet<String>,
) {
for s in schedules.iter_mut() {
@ -316,8 +318,8 @@ pub(crate) fn filter_ghost_schedule_targets(
}
}
fn schedule_to_wire(s: crate::scheduled_prompts::Schedule) -> hive_sh4re::WireSchedule {
hive_sh4re::WireSchedule {
fn schedule_to_wire(s: crate::scheduled_prompts::Schedule) -> hive_sh4re::schedule::WireSchedule {
hive_sh4re::schedule::WireSchedule {
id: s.id,
owner: s.owner,
body: s.body,
@ -326,10 +328,10 @@ fn schedule_to_wire(s: crate::scheduled_prompts::Schedule) -> hive_sh4re::WireSc
created_at_unix: s.created_at_unix,
source: match s.source {
crate::scheduled_prompts::ScheduleSource::Operator => {
hive_sh4re::WireScheduleSource::Operator
hive_sh4re::schedule::WireScheduleSource::Operator
}
crate::scheduled_prompts::ScheduleSource::Approval { id } => {
hive_sh4re::WireScheduleSource::Approval { id }
hive_sh4re::schedule::WireScheduleSource::Approval { id }
}
},
cancelled_at_unix: s.cancelled_at_unix,
@ -338,7 +340,7 @@ fn schedule_to_wire(s: crate::scheduled_prompts::Schedule) -> hive_sh4re::WireSc
targets: s
.targets
.into_iter()
.map(|t| hive_sh4re::WireScheduleTarget {
.map(|t| hive_sh4re::schedule::WireScheduleTarget {
target: t.target,
cancelled_at_unix: t.cancelled_at_unix,
last_fired_at_unix: t.last_fired_at_unix,
@ -352,8 +354,8 @@ fn schedule_to_wire(s: crate::scheduled_prompts::Schedule) -> hive_sh4re::WireSc
mod tests {
use super::*;
fn target(name: &str) -> hive_sh4re::WireScheduleTarget {
hive_sh4re::WireScheduleTarget {
fn target(name: &str) -> hive_sh4re::schedule::WireScheduleTarget {
hive_sh4re::schedule::WireScheduleTarget {
target: name.to_owned(),
cancelled_at_unix: None,
last_fired_at_unix: None,
@ -361,15 +363,15 @@ mod tests {
}
}
fn schedule(targets: &[&str]) -> hive_sh4re::WireSchedule {
hive_sh4re::WireSchedule {
fn schedule(targets: &[&str]) -> hive_sh4re::schedule::WireSchedule {
hive_sh4re::schedule::WireSchedule {
id: 1,
owner: "operator".to_owned(),
body: "ping".to_owned(),
interval_seconds: None,
next_fire_at_unix: hive_sh4re::wire_time::from_secs(0),
created_at_unix: hive_sh4re::wire_time::from_secs(0),
source: hive_sh4re::WireScheduleSource::Operator,
source: hive_sh4re::schedule::WireScheduleSource::Operator,
cancelled_at_unix: None,
paused_at_unix: None,
description: None,

Some files were not shown because too many files have changed in this diff Show more