rename: open_threads → loose_ends + cancel_thread → cancel_loose_end across wire / tools / web ui
This commit is contained in:
parent
b1d0a62cb9
commit
6e23d087d2
16 changed files with 152 additions and 139 deletions
|
|
@ -40,7 +40,7 @@ pub enum SocketReply {
|
|||
QuestionQueued(i64),
|
||||
Recent(Vec<hive_sh4re::InboxRow>),
|
||||
Logs(String),
|
||||
OpenThreads(Vec<hive_sh4re::OpenThread>),
|
||||
LooseEnds(Vec<hive_sh4re::LooseEnd>),
|
||||
PendingRemindersCount(u64),
|
||||
Whoami {
|
||||
name: String,
|
||||
|
|
@ -59,7 +59,7 @@ impl From<hive_sh4re::AgentResponse> for SocketReply {
|
|||
hive_sh4re::AgentResponse::Status { unread } => Self::Status(unread),
|
||||
hive_sh4re::AgentResponse::Recent { rows } => Self::Recent(rows),
|
||||
hive_sh4re::AgentResponse::QuestionQueued { id } => Self::QuestionQueued(id),
|
||||
hive_sh4re::AgentResponse::OpenThreads { threads } => Self::OpenThreads(threads),
|
||||
hive_sh4re::AgentResponse::LooseEnds { loose_ends } => Self::LooseEnds(loose_ends),
|
||||
hive_sh4re::AgentResponse::PendingRemindersCount { count } => {
|
||||
Self::PendingRemindersCount(count)
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ impl From<hive_sh4re::ManagerResponse> for SocketReply {
|
|||
hive_sh4re::ManagerResponse::QuestionQueued { id } => Self::QuestionQueued(id),
|
||||
hive_sh4re::ManagerResponse::Recent { rows } => Self::Recent(rows),
|
||||
hive_sh4re::ManagerResponse::Logs { content } => Self::Logs(content),
|
||||
hive_sh4re::ManagerResponse::OpenThreads { threads } => Self::OpenThreads(threads),
|
||||
hive_sh4re::ManagerResponse::LooseEnds { loose_ends } => Self::LooseEnds(loose_ends),
|
||||
hive_sh4re::ManagerResponse::PendingRemindersCount { count } => {
|
||||
Self::PendingRemindersCount(count)
|
||||
}
|
||||
|
|
@ -128,16 +128,16 @@ pub fn format_recv(resp: Result<SocketReply, anyhow::Error>) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
/// Format helper for `get_open_threads`: renders a short bulleted list
|
||||
/// Format helper for `get_loose_ends`: renders a short bulleted list
|
||||
/// of pending approvals + questions. Empty list collapses to a clear
|
||||
/// marker so claude doesn't go hunting for a payload that isn't there.
|
||||
pub fn format_open_threads(resp: Result<SocketReply, anyhow::Error>) -> String {
|
||||
pub fn format_loose_ends(resp: Result<SocketReply, anyhow::Error>) -> String {
|
||||
use std::fmt::Write as _;
|
||||
let threads = match resp {
|
||||
Ok(SocketReply::OpenThreads(t)) => t,
|
||||
Ok(SocketReply::Err(m)) => return format!("get_open_threads failed: {m}"),
|
||||
Ok(other) => return format!("get_open_threads unexpected response: {other:?}"),
|
||||
Err(e) => return format!("get_open_threads transport error: {e:#}"),
|
||||
Ok(SocketReply::LooseEnds(t)) => t,
|
||||
Ok(SocketReply::Err(m)) => return format!("get_loose_ends failed: {m}"),
|
||||
Ok(other) => return format!("get_loose_ends unexpected response: {other:?}"),
|
||||
Err(e) => return format!("get_loose_ends transport error: {e:#}"),
|
||||
};
|
||||
if threads.is_empty() {
|
||||
return "(no open threads)".to_owned();
|
||||
|
|
@ -145,7 +145,7 @@ pub fn format_open_threads(resp: Result<SocketReply, anyhow::Error>) -> String {
|
|||
let mut out = format!("{} open thread(s):\n", threads.len());
|
||||
for t in &threads {
|
||||
match t {
|
||||
hive_sh4re::OpenThread::Approval {
|
||||
hive_sh4re::LooseEnd::Approval {
|
||||
id,
|
||||
agent,
|
||||
commit_ref,
|
||||
|
|
@ -161,7 +161,7 @@ pub fn format_open_threads(resp: Result<SocketReply, anyhow::Error>) -> String {
|
|||
"- approval #{id} ({agent} @ {commit_ref}, {age_seconds}s old){desc}"
|
||||
);
|
||||
}
|
||||
hive_sh4re::OpenThread::Question {
|
||||
hive_sh4re::LooseEnd::Question {
|
||||
id,
|
||||
asker,
|
||||
target,
|
||||
|
|
@ -174,7 +174,7 @@ pub fn format_open_threads(resp: Result<SocketReply, anyhow::Error>) -> String {
|
|||
"- question #{id} ({asker} → {to}, {age_seconds}s old): {question}"
|
||||
);
|
||||
}
|
||||
hive_sh4re::OpenThread::Reminder {
|
||||
hive_sh4re::LooseEnd::Reminder {
|
||||
id,
|
||||
owner,
|
||||
message,
|
||||
|
|
@ -191,16 +191,16 @@ pub fn format_open_threads(resp: Result<SocketReply, anyhow::Error>) -> String {
|
|||
out
|
||||
}
|
||||
|
||||
/// Parse the user-facing `kind` string for `cancel_thread` into the
|
||||
/// 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).
|
||||
fn parse_cancel_kind(raw: &str) -> Result<hive_sh4re::CancelThreadKind, String> {
|
||||
fn parse_loose_end_kind(raw: &str) -> Result<hive_sh4re::CancelLooseEndKind, String> {
|
||||
match raw.trim().to_ascii_lowercase().as_str() {
|
||||
"question" | "q" => Ok(hive_sh4re::CancelThreadKind::Question),
|
||||
"reminder" | "r" => Ok(hive_sh4re::CancelThreadKind::Reminder),
|
||||
"question" | "q" => Ok(hive_sh4re::CancelLooseEndKind::Question),
|
||||
"reminder" | "r" => Ok(hive_sh4re::CancelLooseEndKind::Reminder),
|
||||
other => Err(format!(
|
||||
"cancel_thread: unknown kind '{other}' (expected \"question\" or \"reminder\")"
|
||||
"cancel_loose_end: unknown kind '{other}' (expected \"question\" or \"reminder\")"
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
@ -454,13 +454,13 @@ impl AgentServer {
|
|||
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 the relevant context. Each `question` or `reminder` row \
|
||||
can be cancelled by passing its id + kind to `cancel_thread`. Empty result \
|
||||
can be cancelled by passing its id + kind to `cancel_loose_end`. Empty result \
|
||||
is reported clearly."
|
||||
)]
|
||||
async fn get_open_threads(&self) -> String {
|
||||
run_tool_envelope("get_open_threads", String::new(), async move {
|
||||
let (resp, retries) = self.dispatch(hive_sh4re::AgentRequest::GetOpenThreads).await;
|
||||
annotate_retries(format_open_threads(resp), retries)
|
||||
async fn get_loose_ends(&self) -> String {
|
||||
run_tool_envelope("get_loose_ends", String::new(), async move {
|
||||
let (resp, retries) = self.dispatch(hive_sh4re::AgentRequest::GetLooseEnds).await;
|
||||
annotate_retries(format_loose_ends(resp), retries)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
|
@ -485,24 +485,24 @@ impl AgentServer {
|
|||
description = "Cancel an open thread you own — a `question` you asked (the \
|
||||
asker gets `[cancelled by <you>]` as the answer and unblocks) or a `reminder` \
|
||||
you scheduled (hard-deleted before it fires). `kind` is `\"question\"` or \
|
||||
`\"reminder\"`; `id` is the row id from the matching `get_open_threads` entry \
|
||||
`\"reminder\"`; `id` is the row id from the matching `get_loose_ends` entry \
|
||||
or the `question_queued` reply you got when you submitted. Auth: you can only \
|
||||
cancel rows where you're the asker / owner. Returns `ok` or an error string."
|
||||
)]
|
||||
async fn cancel_thread(&self, Parameters(args): Parameters<CancelThreadArgs>) -> String {
|
||||
async fn cancel_loose_end(&self, Parameters(args): Parameters<CancelLooseEndArgs>) -> String {
|
||||
let log = format!("{args:?}");
|
||||
let kind_label = args.kind.clone();
|
||||
let id = args.id;
|
||||
run_tool_envelope("cancel_thread", log, async move {
|
||||
let kind = match parse_cancel_kind(&args.kind) {
|
||||
run_tool_envelope("cancel_loose_end", log, async move {
|
||||
let kind = match parse_loose_end_kind(&args.kind) {
|
||||
Ok(k) => k,
|
||||
Err(e) => return e,
|
||||
};
|
||||
let (resp, retries) = self
|
||||
.dispatch(hive_sh4re::AgentRequest::CancelThread { kind, id })
|
||||
.dispatch(hive_sh4re::AgentRequest::CancelLooseEnd { kind, id })
|
||||
.await;
|
||||
annotate_retries(
|
||||
format_ack(resp, "cancel_thread", format!("cancelled {kind_label} {id}")),
|
||||
format_ack(resp, "cancel_loose_end", format!("cancelled {kind_label} {id}")),
|
||||
retries,
|
||||
)
|
||||
})
|
||||
|
|
@ -654,13 +654,13 @@ pub struct AnswerArgs {
|
|||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct CancelThreadArgs {
|
||||
pub struct CancelLooseEndArgs {
|
||||
/// Which kind of thread to cancel — `"question"` for an open
|
||||
/// `ask` that's still waiting on an answer, `"reminder"` for a
|
||||
/// scheduled `remind` that hasn't fired yet. Use the `kind`
|
||||
/// field straight off the `get_open_threads` row.
|
||||
/// field straight off the `get_loose_ends` row.
|
||||
pub kind: String,
|
||||
/// Row id from the matching `get_open_threads` entry (or the
|
||||
/// Row id from the matching `get_loose_ends` entry (or the
|
||||
/// `question_queued` reply when you submitted it).
|
||||
pub id: i64,
|
||||
}
|
||||
|
|
@ -995,14 +995,14 @@ impl ManagerServer {
|
|||
approvals stuck waiting on the operator, reminders piling up on an offline \
|
||||
agent, etc. No args. The sub-agent flavour only returns the agent's own \
|
||||
threads; the manager flavour is unfiltered. Cancel any question or reminder \
|
||||
row via `cancel_thread` (manager bypasses the owner check)."
|
||||
row via `cancel_loose_end` (manager bypasses the owner check)."
|
||||
)]
|
||||
async fn get_open_threads(&self) -> String {
|
||||
run_tool_envelope("get_open_threads", String::new(), async move {
|
||||
async fn get_loose_ends(&self) -> String {
|
||||
run_tool_envelope("get_loose_ends", String::new(), async move {
|
||||
let (resp, retries) = self
|
||||
.dispatch(hive_sh4re::ManagerRequest::GetOpenThreads)
|
||||
.dispatch(hive_sh4re::ManagerRequest::GetLooseEnds)
|
||||
.await;
|
||||
annotate_retries(format_open_threads(resp), retries)
|
||||
annotate_retries(format_loose_ends(resp), retries)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
|
@ -1025,24 +1025,24 @@ impl ManagerServer {
|
|||
description = "Cancel any open thread in the swarm — a `question` (cancels \
|
||||
with the operator-override sentinel so the asker unblocks) or a `reminder` \
|
||||
(hard-deleted before fire). `kind` is `\"question\"` or `\"reminder\"`; `id` \
|
||||
is the row id from `get_open_threads` or the original submission reply. \
|
||||
is the row id from `get_loose_ends` or the original submission reply. \
|
||||
Manager surface bypasses the owner check on the sub-agent flavour — use for \
|
||||
hive-wide cleanup of stuck or stale threads."
|
||||
)]
|
||||
async fn cancel_thread(&self, Parameters(args): Parameters<CancelThreadArgs>) -> String {
|
||||
async fn cancel_loose_end(&self, Parameters(args): Parameters<CancelLooseEndArgs>) -> String {
|
||||
let log = format!("{args:?}");
|
||||
let kind_label = args.kind.clone();
|
||||
let id = args.id;
|
||||
run_tool_envelope("cancel_thread", log, async move {
|
||||
let kind = match parse_cancel_kind(&args.kind) {
|
||||
run_tool_envelope("cancel_loose_end", log, async move {
|
||||
let kind = match parse_loose_end_kind(&args.kind) {
|
||||
Ok(k) => k,
|
||||
Err(e) => return e,
|
||||
};
|
||||
let (resp, retries) = self
|
||||
.dispatch(hive_sh4re::ManagerRequest::CancelThread { kind, id })
|
||||
.dispatch(hive_sh4re::ManagerRequest::CancelLooseEnd { kind, id })
|
||||
.await;
|
||||
annotate_retries(
|
||||
format_ack(resp, "cancel_thread", format!("cancelled {kind_label} {id}")),
|
||||
format_ack(resp, "cancel_loose_end", format!("cancelled {kind_label} {id}")),
|
||||
retries,
|
||||
)
|
||||
})
|
||||
|
|
@ -1092,8 +1092,8 @@ impl ManagerServer {
|
|||
any agent including yourself), `ask` (structured question to the operator or a \
|
||||
sub-agent — non-blocking, answer arrives later as a `question_answered` event), \
|
||||
`answer` (respond to a `question_asked` event directed at you), \
|
||||
`get_open_threads` (hive-wide loose ends — pending approvals + unanswered \
|
||||
questions + pending reminders across the swarm), `cancel_thread` (cancel any \
|
||||
`get_loose_ends` (hive-wide loose ends — pending approvals + unanswered \
|
||||
questions + pending reminders across the swarm), `cancel_loose_end` (cancel any \
|
||||
question or reminder row by id), `whoami` (self-introspection — canonical \
|
||||
name, role, current hyperhive rev). The manager's own config lives at \
|
||||
`/agents/hm1nd/config/agent.nix`."
|
||||
|
|
@ -1136,9 +1136,9 @@ pub fn allowed_mcp_tools(flavor: Flavor) -> Vec<String> {
|
|||
"ask",
|
||||
"answer",
|
||||
"remind",
|
||||
"get_open_threads",
|
||||
"get_loose_ends",
|
||||
"whoami",
|
||||
"cancel_thread",
|
||||
"cancel_loose_end",
|
||||
],
|
||||
Flavor::Manager => &[
|
||||
"send",
|
||||
|
|
@ -1152,10 +1152,10 @@ pub fn allowed_mcp_tools(flavor: Flavor) -> Vec<String> {
|
|||
"ask",
|
||||
"answer",
|
||||
"get_logs",
|
||||
"get_open_threads",
|
||||
"get_loose_ends",
|
||||
"remind",
|
||||
"whoami",
|
||||
"cancel_thread",
|
||||
"cancel_loose_end",
|
||||
],
|
||||
};
|
||||
let mut out: Vec<String> = names
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue