hive-c0re stops owning scheduled prompts; agents lose the schedule tools
Scheduled prompts are swarm-level now (swarmctl schedule, #4936). The hive keeps the rows it has only so the operator can copy them over. - hive-c0re no longer fires anything: the scheduled-prompts worker is gone, and with it every create/edit/cancel/pause/resume/fire path (dashboard endpoints, agent-socket verbs, the SchedulePrompt approval kind and its approve handler). The store keeps open + list; GET /api/schedules stays. - A v6 approvals migration drops schedule_prompt rows. - Nothing submits approvals any more, so the submit path goes too: Approvals::submit_kind (now a test fixture), the ApprovalAdded dashboard event and its emitter. - Coordinator::push_todo, whose only caller was the worker, goes, and with it hive_host_sock::agent_todo_socket and hive-c0re's hive-agent-sock / hive-sock-client deps. - The schedules_changed dashboard event goes; nothing mutates the rows. - Agents lose request_schedule_prompt, cancel_schedule, fire_schedule_now, edit_schedule and list_schedules, the `scheduling` tool group, their system-prompt line and their transcript formatters. `remind` is unchanged.
This commit is contained in:
parent
7d80f5146d
commit
c3a54bbdd0
31 changed files with 273 additions and 3124 deletions
|
|
@ -15,8 +15,8 @@ pub struct Approval {
|
|||
pub id: i64,
|
||||
pub agent: Ident,
|
||||
pub kind: ApprovalKind,
|
||||
/// Kind-specific payload (git sha / inputs array / schedule
|
||||
/// payload / empty). See the Approval struct doc.
|
||||
/// Kind-specific payload (git sha / inputs array / empty). See the
|
||||
/// Approval struct doc.
|
||||
pub commit_ref: String,
|
||||
pub requested_at: DateTime<Utc>,
|
||||
pub status: ApprovalStatus,
|
||||
|
|
@ -40,8 +40,6 @@ pub enum ApprovalKind {
|
|||
/// Run `nix flake update [inputs...]` on the meta flake and commit
|
||||
/// the resulting lock changes.
|
||||
UpdateMetaInputs,
|
||||
/// Add a scheduled prompt to the broker queue.
|
||||
SchedulePrompt,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
//! Manager socket — `/run/hyperhive/manager/mcp.sock` on the host,
|
||||
//! bind-mounted into the manager container at `/run/hive/mcp.sock`.
|
||||
//! Reserved recipient names/senders, the out-of-band `HelperEvent`
|
||||
//! payload hive-c0re pushes into the manager's inbox, and the
|
||||
//! schedule-prompt submission payload.
|
||||
//! Reserved recipient names/senders and the out-of-band `HelperEvent`
|
||||
//! payload hive-c0re pushes into the manager's inbox.
|
||||
|
||||
use hive_types::Ident;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -66,30 +65,6 @@ pub enum HelperEvent {
|
|||
},
|
||||
}
|
||||
|
||||
/// Submission payload for `RequestSchedulePrompt`. Lives outside the
|
||||
/// enum so it can also serialize into the approval row's `commit_ref`
|
||||
/// (the dispatcher re-parses it on approve and inserts the schedule).
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct SchedulePromptPayload {
|
||||
/// Names of recipient agents. Operator + `root` allowed.
|
||||
pub targets: Vec<String>,
|
||||
/// Message body delivered to each target's inbox at fire time.
|
||||
/// Same size budget as `Send.body` — soft cap at the broker level.
|
||||
pub body: String,
|
||||
/// Absolute unix timestamp (seconds) for the FIRST fire. For
|
||||
/// recurring schedules the worker then re-arms in
|
||||
/// `interval_seconds` steps.
|
||||
pub first_fire_at_unix: i64,
|
||||
/// `None` = one-shot. `Some(n > 0)` = recurring every `n` seconds.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub interval_seconds: Option<u64>,
|
||||
/// Optional description shown on the dashboard approval card AND
|
||||
/// stored on the resulting schedule row for the operator's
|
||||
/// "what is this?" reference later.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod reserved_name_tests {
|
||||
use super::{MANAGER_AGENT, OPERATOR_RECIPIENT, SYSTEM_SENDER};
|
||||
|
|
|
|||
|
|
@ -162,9 +162,6 @@ pub enum ToolGroup {
|
|||
/// server-side gate: `cancel_loose_end`'s approval-cancel arm
|
||||
/// requires it (`socket_server::check_can_cancel_approval`).
|
||||
Approvals,
|
||||
/// `request_schedule_prompt`, `fire_schedule_now`, `cancel_schedule`,
|
||||
/// `edit_schedule`, `list_schedules` - *(privileged)*
|
||||
Scheduling,
|
||||
/// Gates no tool today — `create_repo`, its only member, was removed
|
||||
/// with no replacement. Kept so existing `meta/capabilities.json`
|
||||
/// grants still parse; `tools()` returns `&[]`.
|
||||
|
|
@ -230,13 +227,6 @@ impl ToolGroup {
|
|||
Self::Messaging => &["send", "recv", "ack_until"],
|
||||
Self::Meta => &["get_agent_meta"],
|
||||
Self::Inbox => &["get_loose_ends", "cancel_loose_end", "remind"],
|
||||
Self::Scheduling => &[
|
||||
"request_schedule_prompt",
|
||||
"fire_schedule_now",
|
||||
"cancel_schedule",
|
||||
"edit_schedule",
|
||||
"list_schedules",
|
||||
],
|
||||
// All five empty, for different reasons — see each
|
||||
// variant's own doc comment above. `Execution` grants the
|
||||
// out-of-process `bash` MCP server
|
||||
|
|
@ -308,7 +298,6 @@ impl ToolGroup {
|
|||
Self::Inbox,
|
||||
Self::Lifecycle,
|
||||
Self::Approvals,
|
||||
Self::Scheduling,
|
||||
Self::Execution,
|
||||
];
|
||||
|
||||
|
|
@ -321,7 +310,6 @@ impl ToolGroup {
|
|||
Self::Inbox,
|
||||
Self::Lifecycle,
|
||||
Self::Approvals,
|
||||
Self::Scheduling,
|
||||
Self::Forge,
|
||||
Self::Execution,
|
||||
Self::WebTools,
|
||||
|
|
@ -340,9 +328,6 @@ impl ToolGroup {
|
|||
Self::Approvals => {
|
||||
"no tools — grants cancel_loose_end's approval-cancel arm (privileged)"
|
||||
}
|
||||
Self::Scheduling => {
|
||||
"request_schedule_prompt and related — operator-visible scheduled prompts (privileged)"
|
||||
}
|
||||
Self::Forge => "no tools — vestigial since create_repo was removed",
|
||||
Self::Execution => {
|
||||
"run, status — run shell commands via mcp__bash__run / mcp__bash__status"
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
//! Scheduled-prompt row shape on the wire, shared by the dashboard and
|
||||
//! agent surfaces (mirrors `hive-c0re`'s internal `scheduled_prompts::Schedule`).
|
||||
//! Scheduled-prompt row shape on the wire, served by the dashboard's
|
||||
//! read-only schedule list (mirrors `hive-c0re`'s internal `scheduled_prompts::Schedule`).
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Schedule row shape on the wire — mirror of
|
||||
/// `scheduled_prompts::Schedule` but in the public crate so the
|
||||
/// dashboard and agent surfaces can deserialize without depending
|
||||
/// on hive-c0re-internal types. Kept structurally identical to the
|
||||
/// in-process type; the conversion is field-by-field in
|
||||
/// `manager_server` / `dashboard`.
|
||||
/// `scheduled_prompts::Schedule` but in the public crate so a client
|
||||
/// can deserialize it without depending on hive-c0re-internal types.
|
||||
/// Kept structurally identical to the in-process type; the conversion
|
||||
/// is field-by-field in the dashboard.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WireSchedule {
|
||||
pub id: i64,
|
||||
|
|
@ -22,10 +21,7 @@ pub struct WireSchedule {
|
|||
pub source: WireScheduleSource,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub cancelled_at_unix: Option<DateTime<Utc>>,
|
||||
/// Set while the schedule is paused. Worker skips paused rows;
|
||||
/// they keep their `next_fire_at_unix` so resuming at any time
|
||||
/// fires at the next intended instant (no catch-up clamp needed
|
||||
/// — a paused schedule simply slips its next fire).
|
||||
/// Set if the row is paused.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub paused_at_unix: Option<DateTime<Utc>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
|
|
|||
|
|
@ -645,13 +645,6 @@ fn fmt_hyperhive_tool(name: &str, short: &str, input: &Value) -> String {
|
|||
format!("{short} ≤{up_to}")
|
||||
}
|
||||
"mcp__hyperhive__remind" => fmt_hyperhive_remind(short, input),
|
||||
"mcp__hyperhive__list_schedules"
|
||||
| "mcp__hyperhive__cancel_schedule"
|
||||
| "mcp__hyperhive__fire_schedule_now"
|
||||
| "mcp__hyperhive__edit_schedule"
|
||||
| "mcp__hyperhive__request_schedule_prompt" => {
|
||||
fmt_hyperhive_schedule_tool(name, short, input)
|
||||
}
|
||||
"mcp__hyperhive__set_status" => {
|
||||
format!("{short} \"{}\"", trim_str(&sv(input, "text"), 60))
|
||||
}
|
||||
|
|
@ -739,91 +732,6 @@ fn fmt_hyperhive_remind(short: &str, input: &Value) -> String {
|
|||
format!("{short}{when_part}{msg_part}")
|
||||
}
|
||||
|
||||
fn fmt_hyperhive_edit_schedule(short: &str, input: &Value) -> String {
|
||||
let id = input
|
||||
.get("id")
|
||||
.and_then(Value::as_u64)
|
||||
.map_or_else(|| "?".to_owned(), |n| n.to_string());
|
||||
let mut parts = vec![format!("#{id}")];
|
||||
if input.get("body").is_some() {
|
||||
parts.push("body".to_owned());
|
||||
}
|
||||
if input.get("interval_seconds").is_some() {
|
||||
parts.push("interval".to_owned());
|
||||
}
|
||||
if input.get("next_fire_at_unix").is_some() {
|
||||
parts.push("next".to_owned());
|
||||
}
|
||||
if let Some(a) = input.get("targets_add").and_then(Value::as_array)
|
||||
&& !a.is_empty()
|
||||
{
|
||||
parts.push(format!("+{} tgt", a.len()));
|
||||
}
|
||||
if let Some(r) = input.get("targets_remove").and_then(Value::as_array)
|
||||
&& !r.is_empty()
|
||||
{
|
||||
parts.push(format!("-{} tgt", r.len()));
|
||||
}
|
||||
format!("{short} {}", parts.join(" · "))
|
||||
}
|
||||
|
||||
/// Schedule-management hyperhive tools (list/cancel/fire/edit/request).
|
||||
fn fmt_hyperhive_schedule_tool(name: &str, short: &str, input: &Value) -> String {
|
||||
match name {
|
||||
"mcp__hyperhive__list_schedules" => format!("{short}()"),
|
||||
"mcp__hyperhive__cancel_schedule" => {
|
||||
let id = input
|
||||
.get("id")
|
||||
.and_then(Value::as_u64)
|
||||
.map_or_else(|| "?".to_owned(), |n| n.to_string());
|
||||
let tgts = match input.get("targets").and_then(Value::as_array) {
|
||||
Some(arr) if !arr.is_empty() => {
|
||||
let names: Vec<&str> = arr.iter().filter_map(Value::as_str).collect();
|
||||
format!(" [{}]", names.join(", "))
|
||||
}
|
||||
_ => " all".to_owned(),
|
||||
};
|
||||
format!("{short} #{id}{tgts}")
|
||||
}
|
||||
"mcp__hyperhive__fire_schedule_now" => {
|
||||
let id = input
|
||||
.get("id")
|
||||
.and_then(Value::as_u64)
|
||||
.map_or_else(|| "?".to_owned(), |n| n.to_string());
|
||||
format!("{short} #{id}")
|
||||
}
|
||||
"mcp__hyperhive__edit_schedule" => fmt_hyperhive_edit_schedule(short, input),
|
||||
"mcp__hyperhive__request_schedule_prompt" => {
|
||||
let tgts = match input.get("targets").and_then(Value::as_array) {
|
||||
Some(arr) => arr
|
||||
.iter()
|
||||
.filter_map(Value::as_str)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
None => "?".to_owned(),
|
||||
};
|
||||
let when = input
|
||||
.get("first_fire_at_unix")
|
||||
.and_then(Value::as_u64)
|
||||
.map_or_else(
|
||||
|| "?".to_owned(),
|
||||
|ts| {
|
||||
let h = (ts % 86_400) / 3_600;
|
||||
let m = (ts % 3_600) / 60;
|
||||
format!("{h:02}:{m:02}Z")
|
||||
},
|
||||
);
|
||||
let recur = input
|
||||
.get("interval_seconds")
|
||||
.and_then(Value::as_u64)
|
||||
.map(|s| format!(" +{s}s"))
|
||||
.unwrap_or_default();
|
||||
format!("{short} → {tgts} at {when}{recur}")
|
||||
}
|
||||
_ => fmt_args_generic(short, input),
|
||||
}
|
||||
}
|
||||
|
||||
/// `mcp__bash__*` tools.
|
||||
fn fmt_bash_tool(name: &str, short: &str, input: &Value) -> String {
|
||||
match name {
|
||||
|
|
|
|||
Loading…
Reference in a new issue