hive-sh4re: lift TaskFile + TaskStatus from hive-bash-mcp

Move the bash-task on-disk schema (TaskFile + TaskStatus) into hive-sh4re,
the shared wire-types crate, and re-export them from hive-bash-mcp::protocol
so existing in-crate imports keep compiling. This gives hive-ag3nt's agent
web UI a canonical type to deserialize when reading the bash-tasks dir for a
running-tasks panel, instead of a parallel struct that would silently drift
from the daemon's persisted format. Both crates already depend on hive-sh4re,
so no new dependency edges.
This commit is contained in:
iris 2026-06-19 12:37:14 +02:00 committed by mara
commit b70209836e
2 changed files with 59 additions and 41 deletions

View file

@ -8,47 +8,13 @@ use serde::{Deserialize, Serialize};
// Task state (shared between runner and protocol)
// ---------------------------------------------------------------------------
/// Lifecycle state of a bash task.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum TaskStatus {
Pending,
Running,
Done,
TimedOut,
/// Daemon was restarted while the task was running; process is gone.
Interrupted,
/// Killed on request via `BashKill` (SIGINT or SIGKILL to the task's
/// process group). Distinct from `Interrupted` (daemon-restart) and
/// `TimedOut` (exceeded `timeout_secs`).
Killed,
}
/// Task metadata + result written to `<id>.json` under the tasks dir.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskFile {
pub id: String,
pub cmd: String,
/// Kill timeout in seconds. `None` means no timeout — task runs until
/// natural exit. Old task files with a numeric value are still readable
/// (serde coerces `u64` → `Some(u64)` is handled by the caller).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeout_secs: Option<u64>,
pub status: TaskStatus,
pub created_at: i64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub started_at: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub completed_at: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exit_code: Option<i32>,
/// Last [`crate::runner::SUMMARY_BYTES`] of stdout.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stdout_tail: Option<String>,
/// Last [`crate::runner::SUMMARY_BYTES`] of stderr.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stderr_tail: Option<String>,
}
// `TaskFile` + `TaskStatus` are the bash-task on-disk schema. They live in
// `hive-sh4re` (the shared wire-types crate) so the agent web UI in
// `hive-ag3nt` can deserialize the same canonical type when reading the
// tasks dir for its running-tasks panel — no parallel copy to drift. Both
// are re-exported here so existing `crate::protocol::{TaskFile, TaskStatus}`
// imports across this crate keep compiling unchanged.
pub use hive_sh4re::{TaskFile, TaskStatus};
// ---------------------------------------------------------------------------
// Request / response

View file

@ -380,6 +380,58 @@ pub enum CancelLooseEndKind {
Approval,
}
// ---------------------------------------------------------------------------
// Bash-task on-disk schema (shared with `hive-bash-mcp`)
// ---------------------------------------------------------------------------
/// Lifecycle state of a bash task.
///
/// Canonical home for the bash-task persisted schema: `hive-bash-mcp`
/// (the daemon that writes the files) re-exports these from its
/// `protocol` module, and `hive-ag3nt` (the agent web UI that reads
/// them back for the running-tasks panel) deserializes the same type, so
/// the on-disk shape can't drift between writer and reader.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum TaskStatus {
Pending,
Running,
Done,
TimedOut,
/// Daemon was restarted while the task was running; process is gone.
Interrupted,
/// Killed on request via `BashKill` (SIGINT or SIGKILL to the task's
/// process group). Distinct from `Interrupted` (daemon-restart) and
/// `TimedOut` (exceeded `timeout_secs`).
Killed,
}
/// Task metadata + result written to `<id>.json` under the bash-tasks dir.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskFile {
pub id: String,
pub cmd: String,
/// Kill timeout in seconds. `None` means no timeout — task runs until
/// natural exit. Old task files with a numeric value are still readable
/// (serde coerces `u64` → `Some(u64)` is handled by the caller).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeout_secs: Option<u64>,
pub status: TaskStatus,
pub created_at: i64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub started_at: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub completed_at: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exit_code: Option<i32>,
/// Last `SUMMARY_BYTES` of stdout (see `hive-bash-mcp` runner).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stdout_tail: Option<String>,
/// Last `SUMMARY_BYTES` of stderr (see `hive-bash-mcp` runner).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stderr_tail: Option<String>,
}
/// Unified request enum for both agent and manager sockets. The agent's
/// identity is the socket it arrived on. Privileged variants are marked
/// `*(privileged)*` — an agent socket returns `Err` for them server-side.