hive-c0re: remove the approvals subsystem
Nothing submits an approval since scheduled prompts left hive-c0re, so the
queue only acted on rows written before that.
- hive-c0re: the approvals store and its migrations, approve/deny/
resolve actions, the ResolveApproval tail node (and meta_update's
approval_id), POST /api/approve and /api/deny, the approval views on
/api/state, the approval_resolved dashboard event, the orphan GC,
destroy/purge failing pending rows, and the approval rows in
get_loose_ends. DestroyBookkeeping loses the purge flag, which only
worded the approval-failure note.
- The CancelLooseEnd request goes: its only hive-c0re arm was the
approval one. cancel_loose_end handles reminder and todo in the harness.
- Wire types: hive_sh4re::approvals (ReminderStats moves to inbox),
LooseEnd::Approval, CancelLooseEndKind, HelperEvent::ApprovalResolved,
ToolGroup::Approvals, HostRequest::{Pending,Approve,Deny} and
HostResponse.approvals. HOST_SOCK_VERSION goes to 2 for the removed
field.
- hivectl loses the approvals verbs; docs/tools/hivectl-cli.md is
regenerated.
- The system prompt drops approval_resolved.
- The approvals table stays in broker.sqlite, unread, the same way the
operator_questions table was left when its store was removed.
- An `approvals` entry still in tool-groups.json is skipped with a warning
by ToolGroup::parse_list, like any unknown group.
- docs/agent-lifecycle/approvals.md is renamed to config-changes.md so the
code's doc pointers resolve; its content follows in the docs commit.
This commit is contained in:
parent
99bd912ae0
commit
58ef299c3c
60 changed files with 153 additions and 1828 deletions
|
|
@ -94,7 +94,7 @@ pub struct CompactArgs {
|
|||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Privileged tool arg types (lifecycle, approvals)
|
||||
// Privileged tool arg types (lifecycle)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
|
|
@ -115,8 +115,7 @@ pub struct GetAgentMetaArgs {
|
|||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct CancelLooseEndArgs {
|
||||
/// Which kind of thread to cancel — `"reminder"` for a scheduled
|
||||
/// `remind` that hasn't fired yet, `"approval"` for a pending
|
||||
/// approval you submitted, or `"todo"` for a loose-ends-v2 todo
|
||||
/// `remind` that hasn't fired yet, or `"todo"` for a loose-ends-v2 todo
|
||||
/// (bash/matrix/forge). Use the `kind` field straight off the
|
||||
/// `get_loose_ends` row.
|
||||
pub kind: String,
|
||||
|
|
|
|||
|
|
@ -30,9 +30,8 @@ pub use args::{
|
|||
pub use render::{annotate_retries, format_ack, format_agent_meta, format_recv};
|
||||
|
||||
use render::{
|
||||
dial_agent_socket, format_matrix_summary, local_reminders, local_todos, loose_end_kind_label,
|
||||
mark_local_todo_done, mark_local_todos_done, matrix_unread_summary, parse_loose_end_kind,
|
||||
render_loose_ends, reply_err,
|
||||
dial_agent_socket, format_matrix_summary, local_reminders, local_todos, mark_local_todo_done,
|
||||
mark_local_todos_done, matrix_unread_summary, render_loose_ends, reply_err,
|
||||
};
|
||||
|
||||
/// Write (or remove) the status file in the agent's own `state/` directory.
|
||||
|
|
@ -231,9 +230,7 @@ impl AgentServer {
|
|||
|
||||
#[tool(
|
||||
description = "List loose ends pending against this agent: pending reminders \
|
||||
you scheduled, plus — for the manager only — pending \
|
||||
approvals you submitted that the operator hasn't acted on yet (agents with the \
|
||||
`approvals` tool group also see their own pending approvals). Also lists active \
|
||||
you scheduled. Also lists active \
|
||||
local tasks published by external MCP daemons (e.g. running bash tasks). Cheap sweep, no args. Useful \
|
||||
at turn start to remember what you owe / what's owed to you without scrolling \
|
||||
inbox history. Output is a short bulleted list with ids, ages in seconds, and \
|
||||
|
|
@ -346,9 +343,6 @@ impl AgentServer {
|
|||
you scheduled (hard-deleted before it fires). `kind` is `\"reminder\"`; \
|
||||
`id` is the row id from the matching `get_loose_ends` entry. Auth: you can only \
|
||||
cancel rows where you're the owner. Returns `ok` or an error string.\n\
|
||||
`kind` may also be `\"approval\"` to withdraw a pending approval you submitted \
|
||||
(before the operator acts on it) — root agent (`ruth`) only; the server rejects \
|
||||
`approval` kind for all other callers.\n\
|
||||
`kind` may also be `\"todo\"` to clear one of your own loose-ends-v2 todos (the id \
|
||||
from a `get_loose_ends` `todo #N` line) — dials the in-agent socket directly, safe \
|
||||
to call repeatedly.")]
|
||||
|
|
@ -373,42 +367,26 @@ impl AgentServer {
|
|||
.to_owned(),
|
||||
};
|
||||
}
|
||||
let kind = match parse_loose_end_kind(&args.kind) {
|
||||
Ok(k) => k,
|
||||
Err(e) => return e,
|
||||
};
|
||||
let kind_label = loose_end_kind_label(kind);
|
||||
// Reminders are harness-local — dial the in-agent socket
|
||||
// directly instead of the broker; the other kind (approval)
|
||||
// still lives in c0re.
|
||||
if kind == hive_sh4re::inbox::CancelLooseEndKind::Reminder {
|
||||
return match dial_agent_socket(&hive_agent_sock::Request::CancelReminder { id })
|
||||
.await
|
||||
{
|
||||
Some(hive_agent_sock::Response::Acked { count }) if count > 0 => {
|
||||
format!("cancelled {kind_label} {id}")
|
||||
}
|
||||
Some(hive_agent_sock::Response::Acked { .. }) => {
|
||||
format!("cancel_loose_end failed: no pending {kind_label} {id}")
|
||||
}
|
||||
Some(hive_agent_sock::Response::Err { message }) => {
|
||||
format!("cancel_loose_end failed: {message}")
|
||||
}
|
||||
Some(other) => format!("cancel_loose_end unexpected response: {other:?}"),
|
||||
None => "cancel_loose_end failed: in-agent socket unavailable".to_owned(),
|
||||
};
|
||||
let kind = args.kind.trim().to_ascii_lowercase();
|
||||
if !matches!(kind.as_str(), "reminder" | "r") {
|
||||
return format!(
|
||||
"cancel_loose_end: unknown kind '{kind}' (expected \"reminder\" or \"todo\")"
|
||||
);
|
||||
}
|
||||
// Reminders are harness-local — dial the in-agent socket directly.
|
||||
match dial_agent_socket(&hive_agent_sock::Request::CancelReminder { id }).await {
|
||||
Some(hive_agent_sock::Response::Acked { count }) if count > 0 => {
|
||||
format!("cancelled reminder {id}")
|
||||
}
|
||||
Some(hive_agent_sock::Response::Acked { .. }) => {
|
||||
format!("cancel_loose_end failed: no pending reminder {id}")
|
||||
}
|
||||
Some(hive_agent_sock::Response::Err { message }) => {
|
||||
format!("cancel_loose_end failed: {message}")
|
||||
}
|
||||
Some(other) => format!("cancel_loose_end unexpected response: {other:?}"),
|
||||
None => "cancel_loose_end failed: in-agent socket unavailable".to_owned(),
|
||||
}
|
||||
let (resp, retries) = self
|
||||
.dispatch(hive_core_agent_sock::Request::CancelLooseEnd { kind, id })
|
||||
.await;
|
||||
annotate_retries(
|
||||
format_ack(
|
||||
resp,
|
||||
"cancel_loose_end",
|
||||
format!("cancelled {kind_label} {id}"),
|
||||
),
|
||||
retries,
|
||||
)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,8 +152,8 @@ fn msg_id_tag(id: i64) -> String {
|
|||
}
|
||||
|
||||
/// Hard cap on how many individual `Todo` lines `render_loose_ends` will
|
||||
/// emit. Approvals/reminders stay naturally bounded (they're
|
||||
/// triaged interactively and don't self-multiply), but todos are pushed by
|
||||
/// emit. Reminders stay naturally bounded (they're triaged interactively
|
||||
/// and don't self-multiply), but todos are pushed by
|
||||
/// unattended producers — matrix/bash/forge are the built-in ones, but any
|
||||
/// user-configured MCP server can push its own too — an agent that goes a long
|
||||
/// stretch without calling `get_loose_ends`, or whose producers outpace its
|
||||
|
|
@ -178,22 +178,6 @@ const MAX_RENDERED_TODOS: usize = 40;
|
|||
fn render_one_loose_end(out: &mut String, t: &hive_sh4re::inbox::LooseEnd) {
|
||||
use std::fmt::Write as _;
|
||||
match t {
|
||||
hive_sh4re::inbox::LooseEnd::Approval {
|
||||
id,
|
||||
agent,
|
||||
commit_ref,
|
||||
description,
|
||||
age_seconds,
|
||||
} => {
|
||||
let desc = description
|
||||
.as_deref()
|
||||
.map(|d| format!(" — {d}"))
|
||||
.unwrap_or_default();
|
||||
let _ = writeln!(
|
||||
out,
|
||||
"- approval #{id} ({agent} @ {commit_ref}, {age_seconds}s old){desc}"
|
||||
);
|
||||
}
|
||||
hive_sh4re::inbox::LooseEnd::Reminder {
|
||||
id,
|
||||
owner,
|
||||
|
|
@ -438,33 +422,6 @@ pub(super) fn format_matrix_summary(rooms: &[MatrixRoomUnread]) -> String {
|
|||
out
|
||||
}
|
||||
|
||||
/// Parse the user-facing `kind` string for `cancel_loose_end` into the
|
||||
/// 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::inbox::CancelLooseEndKind, String> {
|
||||
match raw.trim().to_ascii_lowercase().as_str() {
|
||||
"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 \"reminder\", \"approval\", or \"todo\")"
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Canonical user-facing label for a `CancelLooseEndKind` — used in
|
||||
/// the success ack so the caller always sees `"reminder"` instead of
|
||||
/// whatever alias they passed in (`"r"`).
|
||||
pub(super) fn loose_end_kind_label(kind: hive_sh4re::inbox::CancelLooseEndKind) -> &'static str {
|
||||
match kind {
|
||||
hive_sh4re::inbox::CancelLooseEndKind::Reminder => "reminder",
|
||||
hive_sh4re::inbox::CancelLooseEndKind::Approval => "approval",
|
||||
}
|
||||
}
|
||||
|
||||
/// Format helper for `get_agent_meta`: renders an agent's identity +
|
||||
/// current status as a short human-readable block. `name`,
|
||||
/// `hyperhive_rev`, and `running` are always shown; `status` only
|
||||
|
|
|
|||
Loading…
Reference in a new issue