feat: add set_status MCP tool and status field to whoami/dashboard (closes #325)

This commit is contained in:
damocles 2026-05-23 01:04:49 +02:00 committed by Mara
parent 6f3b56ad84
commit fe2933b213
8 changed files with 170 additions and 16 deletions

View file

@ -53,6 +53,7 @@ pub enum SocketReply {
name: String,
role: String,
hyperhive_rev: Option<String>,
status_text: Option<String>,
},
}
@ -74,10 +75,12 @@ impl From<hive_sh4re::AgentResponse> for SocketReply {
name,
role,
hyperhive_rev,
status_text,
} => Self::Whoami {
name,
role,
hyperhive_rev,
status_text,
},
}
}
@ -102,10 +105,12 @@ impl From<hive_sh4re::ManagerResponse> for SocketReply {
name,
role,
hyperhive_rev,
status_text,
} => Self::Whoami {
name,
role,
hyperhive_rev,
status_text,
},
}
}
@ -269,9 +274,14 @@ pub fn format_whoami(resp: Result<SocketReply, anyhow::Error>) -> String {
name,
role,
hyperhive_rev,
status_text,
}) => {
let rev = hyperhive_rev.as_deref().unwrap_or("<unknown>");
format!("name: {name}\nrole: {role}\nhyperhive_rev: {rev}")
let mut out = format!("name: {name}\nrole: {role}\nhyperhive_rev: {rev}");
if let Some(s) = status_text {
out.push_str(&format!("\nstatus: {s}"));
}
out
}
Ok(SocketReply::Err(m)) => format!("whoami failed: {m}"),
Ok(other) => format!("whoami unexpected response: {other:?}"),
@ -551,8 +561,9 @@ impl AgentServer {
#[tool(
description = "Self-introspection: returns your own canonical agent name (the \
socket-identity name, NOT the prompt-substituted label), role (`agent`), and \
the current hyperhive rev hive-c0re is running against. No args. Useful when \
you want a trustworthy identity stamp for state files / commit messages / \
the current hyperhive rev hive-c0re is running against. Also returns the \
current `status` text if one has been set via `set_status`. No args. Useful \
when you want a trustworthy identity stamp for state files / commit messages / \
cross-agent attribution that won't drift across renames or session-continue \
boundaries where the system-prompt label could be stale."
)]
@ -564,6 +575,22 @@ impl AgentServer {
.await
}
#[tool(
description = "Set a free-text status string visible on the operator dashboard. \
Call this at the START of every task to describe what you're working on (e.g. \
`\"reviewing PR #42\"`, `\"fixing bitburner crash\"`, `\"idle\"`). Pass an empty \
string to clear. The status is shown on your dashboard card and persists across \
harness restarts."
)]
async fn set_status(&self, Parameters(args): Parameters<SetStatusArgs>) -> String {
run_tool_envelope("set_status", args.text.clone(), async move {
let (resp, retries) =
self.dispatch(hive_sh4re::AgentRequest::SetStatus { text: args.text }).await;
annotate_retries(format_ack(resp, "set_status", "status updated".to_owned()), retries)
})
.await
}
#[tool(
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` \
@ -724,6 +751,12 @@ pub struct KillArgs {
pub name: String,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct SetStatusArgs {
/// Status text to display on the dashboard card. Pass an empty string to clear.
pub text: String,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct StartArgs {
/// Sub-agent name (without the `h-` container prefix).
@ -1246,7 +1279,8 @@ impl ManagerServer {
#[tool(
description = "Self-introspection for the manager: returns canonical name \
(`manager`), role (`manager`), and the current hyperhive rev. Same shape as \
(`manager`), role (`manager`), and the current hyperhive rev. Also returns \
the current `status` text if one has been set via `set_status`. Same shape as \
the agent flavour; useful for cross-agent attribution / boot announcements / \
state-file headers without trusting prompt substitution."
)]
@ -1258,6 +1292,20 @@ impl ManagerServer {
.await
}
#[tool(
description = "Set a free-text status string visible on the operator dashboard. \
Call this at the START of every task to describe what you're working on. \
Pass an empty string to clear. Persists across harness restarts."
)]
async fn set_status(&self, Parameters(args): Parameters<SetStatusArgs>) -> String {
run_tool_envelope("set_status", args.text.clone(), async move {
let (resp, retries) =
self.dispatch(hive_sh4re::ManagerRequest::SetStatus { text: args.text }).await;
annotate_retries(format_ack(resp, "set_status", "status updated".to_owned()), retries)
})
.await
}
#[tool(
description = "Cancel any open thread in the swarm — a `question` (cancels \
with the operator-override sentinel so the asker unblocks) or a `reminder` \
@ -1377,6 +1425,7 @@ pub fn allowed_mcp_tools(flavor: Flavor) -> Vec<String> {
"remind",
"get_loose_ends",
"whoami",
"set_status",
"cancel_loose_end",
],
Flavor::Manager => &[
@ -1395,6 +1444,7 @@ pub fn allowed_mcp_tools(flavor: Flavor) -> Vec<String> {
"get_loose_ends",
"remind",
"whoami",
"set_status",
"cancel_loose_end",
],
};