manager: add optional agent param to GetLooseEnds

GetLooseEnds now takes agent: Option<String>:
- None   = manager's own loose ends (default; the bug fix)
- Some("*")    = hive-wide view (every approval/question/reminder)
- Some("name") = that agent's loose ends

The get_loose_ends MCP tool exposes this as an optional agent arg, so
the manager can still scan the whole swarm on demand. The web UI and
post-turn counts pass None (manager's own).
This commit is contained in:
iris 2026-05-20 21:42:21 +02:00 committed by Mara
parent 873d5a083d
commit d348ce885f
5 changed files with 50 additions and 22 deletions

View file

@ -334,7 +334,7 @@ fn now_unix() -> i64 {
async fn fetch_manager_post_turn_counts(socket: &Path) -> (Option<u64>, Option<u64>) {
let threads = match client::request::<_, ManagerResponse>(
socket,
&ManagerRequest::GetLooseEnds,
&ManagerRequest::GetLooseEnds { agent: None },
)
.await
{

View file

@ -756,6 +756,18 @@ pub struct CancelLooseEndArgs {
pub id: i64,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct GetLooseEndsArgs {
/// Whose loose ends to list. Omit (or `null`) for your own — the
/// manager's: approvals you submitted + questions where you are
/// asker/target + your own pending reminders. Pass `"*"` for a
/// hive-wide view of EVERY pending approval, unanswered question,
/// and reminder across the swarm. Pass a specific agent name to
/// inspect just that agent's threads.
#[serde(default)]
pub agent: Option<String>,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct RequestApplyCommitArgs {
/// Agent whose config repo the commit lives in (use `"hm1nd"` for the
@ -1126,18 +1138,19 @@ impl ManagerServer {
}
#[tool(
description = "Hive-wide loose ends: EVERY pending approval + EVERY unanswered \
question + EVERY pending reminder across the swarm. Use to scan for stalled \
coordination questions sub-agents asked each other that nobody's answering, \
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 \
description = "List loose ends. By default returns your OWN — the manager's: \
pending approvals you submitted + unanswered questions where you are \
asker/target + your own pending reminders. Pass `agent: \"*\"` for a \
hive-wide scan (EVERY pending approval, unanswered question, and reminder \
across the swarm) use it to spot stalled coordination, e.g. questions \
sub-agents asked each other that nobody's answering. Pass `agent: \
\"<name>\"` to inspect one agent's threads. Cancel any question or reminder \
row via `cancel_loose_end` (manager bypasses the owner check)."
)]
async fn get_loose_ends(&self) -> String {
async fn get_loose_ends(&self, Parameters(args): Parameters<GetLooseEndsArgs>) -> String {
run_tool_envelope("get_loose_ends", String::new(), async move {
let (resp, retries) = self
.dispatch(hive_sh4re::ManagerRequest::GetLooseEnds)
.dispatch(hive_sh4re::ManagerRequest::GetLooseEnds { agent: args.agent })
.await;
annotate_retries(format_loose_ends(resp), retries)
})

View file

@ -409,7 +409,9 @@ async fn api_loose_ends(State(state): State<AppState>) -> Response {
Flavor::Manager => {
match client::request::<_, hive_sh4re::ManagerResponse>(
&state.socket,
&hive_sh4re::ManagerRequest::GetLooseEnds,
// Manager's own loose ends — the web page is the
// manager's page, not a hive-wide console.
&hive_sh4re::ManagerRequest::GetLooseEnds { agent: None },
)
.await
{