set_status: consolidate whoami into get_agent_meta with optional name

This commit is contained in:
damocles 2026-05-23 10:57:27 +02:00 committed by Mara
parent 77fdaf0d1e
commit 73871f18c3
10 changed files with 118 additions and 204 deletions

View file

@ -10,9 +10,8 @@ Tools (hyperhive surface):
- `mcp__hyperhive__get_loose_ends()` — list your loose ends: unanswered questions where you're asker (waiting on someone) or target (owing a reply), plus reminders you've scheduled that haven't fired. No args, cheap server-side sweep. Useful at turn start to remember what's outstanding without scanning inbox archaeology.
- `mcp__hyperhive__cancel_loose_end(kind, id)` — cancel one of your own open threads. `kind` is `"question"` (the asker — you, in this case — gets a `[cancelled by <you>]` answer so the waiter unblocks) or `"reminder"` (hard-deleted before it fires). `id` from the matching `get_loose_ends` row or the original submission reply.
- `mcp__hyperhive__remind(message, delay_seconds? | at_unix_timestamp?, file_path?)` — schedule a message to land in your *own* inbox at a future time (sender shows as `reminder`). Set exactly one of `delay_seconds` (relative) or `at_unix_timestamp` (absolute). Use for self-paced follow-ups instead of blocking a whole turn on a long `recv` wait. A large `message` auto-spills to a file under `/agents/{label}/state/reminders/`; pass `file_path` to point at one yourself. Each agent's pending-reminder count is capped (default 50) — the tool will error if the cap is already reached.
- `mcp__hyperhive__whoami()` — self-introspection: returns your canonical agent name (from socket identity, not the prompt-substituted label), role, and current hyperhive rev. No args. Use it when you want a trustworthy identity stamp for state files, commit messages, or cross-agent attribution that won't drift across renames or session-continue boundaries where the system-prompt label could be stale.
- `mcp__hyperhive__set_status(text)` — set a free-text status visible on the operator dashboard. **Call this at the start of every task** to say what you're working on (e.g. `"processing matrix messages"`, `"fixing #319 model priority"`, `"idle"`). Pass an empty string to clear. Persists across harness restarts.
- `mcp__hyperhive__get_agent_meta(name)` — fetch another agent's current status (set via `set_status`). Returns their status text and how long ago it was set. Useful for checking whether a peer is idle before sending a request.
- `mcp__hyperhive__get_agent_meta(name?)` — fetch identity + status metadata for an agent: canonical `name`, `role` (`agent` / `manager`), current `hyperhive_rev`, plus self-reported `status` text (set via `set_status`) and how long ago it was set. Pass `name` to query a peer (e.g. check whether iris is idle before pinging them). Omit `name` to get your own trustworthy identity stamp — useful 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.
- `mcp__hyperhive__request_next_turn()` — ask the harness to start another turn immediately after this one ends, even if the inbox is empty. Use for multi-turn tasks (long builds, sequential steps) where you want to continue without waiting for an external message. The next turn starts with `from: "self"` and `body: "continue"`. No-op if new inbox messages arrive before this turn ends (the harness already loops immediately on pending messages). No args.
Need new packages, env vars, or other NixOS config for yourself? You can't edit your own config directly — message the manager (recipient `manager`) describing what you need + why. The manager evaluates the request (it doesn't rubber-stamp), edits `/agents/{label}/config/agent.nix` on your behalf, commits, and submits an approval that the operator can accept on the dashboard; on approve hive-c0re rebuilds your container with the new config.

View file

@ -17,7 +17,8 @@ Tools (hyperhive surface):
- `mcp__hyperhive__get_loose_ends(agent?)` — loose ends. Omit `agent` for your own: pending approvals you submitted + unanswered questions where you are asker/target + your own pending reminders. Pass `agent: "*"` for a hive-wide sweep — every pending approval, unanswered question, and reminder across the swarm — to find stalled threads (sub-agent A asked B something three days ago and B never answered) before they rot. Pass `agent: "<name>"` to inspect one agent's threads. Cheap server-side query.
- `mcp__hyperhive__cancel_loose_end(kind, id)` — cancel any question or reminder in the swarm (manager bypasses the owner check used on sub-agents). Use for hive-wide cleanup when a sub-agent is offline / can't withdraw its own ask / reminder.
- `mcp__hyperhive__remind(message, delay_seconds? | at_unix_timestamp?, file_path?)` — schedule a message to land in your own inbox at a future time (sender shows as `reminder`). Set exactly one of `delay_seconds` (relative) or `at_unix_timestamp` (absolute). Good for deadline follow-ups — "check whether agent X answered the question I relayed". Large payloads auto-spill to a file under `/state/reminders/`; pass `file_path` to control the destination.
- `mcp__hyperhive__whoami()` — self-introspection: canonical name (`manager`), role, current hyperhive rev. No args. Useful for boot announcements and cross-agent attribution that won't drift across config reloads.
- `mcp__hyperhive__set_status(text)` — set a free-text status visible on the operator dashboard. **Call this at the start of every task** to say what you're doing (e.g. `"reviewing argus's #341 proposal"`, `"approving lifecycle changes"`, `"idle"`). Pass an empty string to clear. Persists across harness restarts.
- `mcp__hyperhive__get_agent_meta(name?)` — fetch identity + status metadata for an agent: canonical `name`, `role` (`agent` / `manager`), current `hyperhive_rev`, plus the target's self-reported `status` text (set via `set_status`) and how long ago it was set. Pass `name` to check on a sub-agent (idle? still working on the task you assigned?) without scrolling the dashboard. Omit `name` for your own identity stamp — useful for boot announcements, state-file headers, cross-agent attribution that won't drift across config reloads.
Approval boundary: lifecycle ops on *existing* sub-agents (`kill`, `start`, `restart`) are at your discretion — no operator approval. *Creating* a new agent (two-step: `request_init_config` + `request_apply_commit`) and *changing* any agent's config (`request_apply_commit`) both go through the approval queue. The operator only signs off on changes; you run the day-to-day.

View file

@ -194,7 +194,7 @@ async fn serve(
| AgentResponse::LooseEnds { .. }
| AgentResponse::PendingRemindersCount { .. }
| AgentResponse::ReminderRollup { .. }
| AgentResponse::Whoami { .. },
| AgentResponse::AgentMeta { .. },
) => {
tracing::warn!("recv produced unexpected response kind");
}

View file

@ -160,7 +160,7 @@ async fn serve(
| ManagerResponse::LooseEnds { .. }
| ManagerResponse::PendingRemindersCount { .. }
| ManagerResponse::ReminderRollup { .. }
| ManagerResponse::Whoami { .. },
| ManagerResponse::AgentMeta { .. },
) => {
tracing::warn!("recv produced unexpected response kind");
}

View file

@ -49,18 +49,13 @@ pub enum SocketReply {
LooseEnds(Vec<hive_sh4re::LooseEnd>),
PendingRemindersCount(u64),
ReminderRollup(hive_sh4re::ReminderStats),
Whoami {
AgentMeta {
name: String,
role: String,
hyperhive_rev: Option<String>,
status_text: Option<String>,
status_set_at: Option<i64>,
},
AgentMeta {
name: String,
status_text: Option<String>,
status_set_at: Option<i64>,
},
}
impl From<hive_sh4re::AgentResponse> for SocketReply {
@ -77,25 +72,16 @@ impl From<hive_sh4re::AgentResponse> for SocketReply {
Self::PendingRemindersCount(count)
}
hive_sh4re::AgentResponse::ReminderRollup(stats) => Self::ReminderRollup(stats),
hive_sh4re::AgentResponse::Whoami {
name,
role,
hyperhive_rev,
status_text,
status_set_at,
} => Self::Whoami {
name,
role,
hyperhive_rev,
status_text,
status_set_at,
},
hive_sh4re::AgentResponse::AgentMeta {
name,
role,
hyperhive_rev,
status_text,
status_set_at,
} => Self::AgentMeta {
name,
role,
hyperhive_rev,
status_text,
status_set_at,
},
@ -118,25 +104,16 @@ impl From<hive_sh4re::ManagerResponse> for SocketReply {
Self::PendingRemindersCount(count)
}
hive_sh4re::ManagerResponse::ReminderRollup(stats) => Self::ReminderRollup(stats),
hive_sh4re::ManagerResponse::Whoami {
name,
role,
hyperhive_rev,
status_text,
status_set_at,
} => Self::Whoami {
name,
role,
hyperhive_rev,
status_text,
status_set_at,
},
hive_sh4re::ManagerResponse::AgentMeta {
name,
role,
hyperhive_rev,
status_text,
status_set_at,
} => Self::AgentMeta {
name,
role,
hyperhive_rev,
status_text,
status_set_at,
},
@ -292,13 +269,14 @@ fn loose_end_kind_label(kind: hive_sh4re::CancelLooseEndKind) -> &'static str {
}
}
/// Format helper for `whoami`: renders the identity block as a short
/// human-readable string. Skips fields that are `None` so the output
/// doesn't carry dead placeholders.
/// Format helper for `get_agent_meta`: renders an agent's identity +
/// current status as a short human-readable block. `name`, `role`, and
/// `hyperhive_rev` are always shown; `status` only appears when one is
/// set, otherwise the line reads `status: <none>`.
#[must_use]
pub fn format_whoami(resp: Result<SocketReply, anyhow::Error>) -> String {
pub fn format_agent_meta(resp: Result<SocketReply, anyhow::Error>) -> String {
match resp {
Ok(SocketReply::Whoami {
Ok(SocketReply::AgentMeta {
name,
role,
hyperhive_rev,
@ -307,38 +285,8 @@ pub fn format_whoami(resp: Result<SocketReply, anyhow::Error>) -> String {
}) => {
let rev = hyperhive_rev.as_deref().unwrap_or("<unknown>");
let mut out = format!("name: {name}\nrole: {role}\nhyperhive_rev: {rev}");
if let Some(s) = status_text {
let age = status_set_at.and_then(|ts| {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH).ok()?.as_secs();
let secs = now.saturating_sub(ts as u64);
Some(format_age_secs(secs))
});
if let Some(a) = age {
out.push_str(&format!("\nstatus: {s} (set {a} ago)"));
} else {
out.push_str(&format!("\nstatus: {s}"));
}
}
out
}
Ok(SocketReply::Err(m)) => format!("whoami failed: {m}"),
Ok(other) => format!("whoami unexpected response: {other:?}"),
Err(e) => format!("whoami transport error: {e:#}"),
}
}
/// Format the result of a `get_agent_meta` call.
#[must_use]
pub fn format_agent_meta(resp: Result<SocketReply, anyhow::Error>) -> String {
match resp {
Ok(SocketReply::AgentMeta {
name,
status_text,
status_set_at,
}) => {
let status = match status_text {
None => "no status set".to_owned(),
match status_text {
None => out.push_str("\nstatus: <none>"),
Some(s) => {
let age = status_set_at.and_then(|ts| {
let now = std::time::SystemTime::now()
@ -349,12 +297,12 @@ pub fn format_agent_meta(resp: Result<SocketReply, anyhow::Error>) -> String {
Some(format_age_secs(secs))
});
match age {
Some(a) => format!("{s} (set {a} ago)"),
None => s,
Some(a) => out.push_str(&format!("\nstatus: {s} (set {a} ago)")),
None => out.push_str(&format!("\nstatus: {s}")),
}
}
};
format!("agent: {name}\nstatus: {status}")
}
out
}
Ok(SocketReply::Err(m)) => format!("get_agent_meta failed: {m}"),
Ok(other) => format!("get_agent_meta unexpected response: {other:?}"),
@ -644,23 +592,6 @@ impl AgentServer {
.await
}
#[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. 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."
)]
async fn whoami(&self) -> String {
run_tool_envelope("whoami", String::new(), async move {
let (resp, retries) = self.dispatch(hive_sh4re::AgentRequest::Whoami).await;
annotate_retries(format_whoami(resp), retries)
})
.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. \
@ -678,18 +609,21 @@ impl AgentServer {
}
#[tool(
description = "Fetch the current status of another agent by name. Returns the \
agent's self-reported status text (set via `set_status`) and how long ago it \
was set. Useful for checking whether a peer is idle before sending a request, \
or for the manager to get a quick overview of what each agent is doing. \
Returns `no status set` when the agent has never called `set_status` or has \
cleared it."
description = "Fetch identity + status metadata for an agent. Returns canonical \
`name`, `role` (`agent` / `manager`), the current `hyperhive_rev` hive-c0re is \
running against, and the target's self-reported `status` text (set via \
`set_status`) plus how long ago it was set. Pass `name` to query a peer (e.g. \
check whether iris is idle before pinging them); omit `name` to get your own \
identity stamp handy 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. Status reads `<none>` when the \
target has never called `set_status` or has cleared it."
)]
async fn get_agent_meta(
&self,
Parameters(args): Parameters<GetAgentMetaArgs>,
) -> String {
let log = args.name.clone();
let log = args.name.clone().unwrap_or_else(|| "<self>".to_owned());
run_tool_envelope("get_agent_meta", log, async move {
let (resp, retries) = self
.dispatch(hive_sh4re::AgentRequest::GetAgentMeta { name: args.name })
@ -868,7 +802,10 @@ pub struct SetStatusArgs {
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct GetAgentMetaArgs {
/// Logical name of the agent to query (e.g. `"iris"`, `"manager"`).
pub name: String,
/// Omit to query your own identity + status — replaces the
/// previous `whoami` self-introspection tool.
#[serde(default)]
pub name: Option<String>,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
@ -1391,21 +1328,6 @@ impl ManagerServer {
.await
}
#[tool(
description = "Self-introspection for the manager: returns canonical name \
(`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."
)]
async fn whoami(&self) -> String {
run_tool_envelope("whoami", String::new(), async move {
let (resp, retries) = self.dispatch(hive_sh4re::ManagerRequest::Whoami).await;
annotate_retries(format_whoami(resp), retries)
})
.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. \
@ -1421,17 +1343,20 @@ impl ManagerServer {
}
#[tool(
description = "Fetch the current status of another agent by name. Returns the \
agent's self-reported status text (set via `set_status`) and how long ago it \
was set. Useful for the manager to get a quick overview of what each agent is \
doing without querying the dashboard. Returns `no status set` when the agent \
has never called `set_status` or has cleared it."
description = "Fetch identity + status metadata for an agent. Returns canonical \
`name`, `role` (`agent` / `manager`), the current `hyperhive_rev` hive-c0re is \
running against, and the target's self-reported `status` text (set via \
`set_status`) plus how long ago it was set. Pass `name` to query a sub-agent or \
peer manager; omit `name` for the manager's own identity stamp useful for \
boot announcements, state-file headers, or cross-agent attribution that won't \
drift across renames. Status reads `<none>` when the target has never called \
`set_status` or has cleared it."
)]
async fn get_agent_meta(
&self,
Parameters(args): Parameters<GetAgentMetaArgs>,
) -> String {
let log = args.name.clone();
let log = args.name.clone().unwrap_or_else(|| "<self>".to_owned());
run_tool_envelope("get_agent_meta", log, async move {
let (resp, retries) = self
.dispatch(hive_sh4re::ManagerRequest::GetAgentMeta { name: args.name })
@ -1516,8 +1441,9 @@ impl ManagerServer {
`answer` (respond to a `question_asked` event directed at you), \
`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 \
question or reminder row by id), `set_status` / `get_agent_meta` (publish your \
own status text + query identity/status of any agent `get_agent_meta` with \
no arg replaces the old `whoami` self-introspection). The manager's own config lives at \
`/agents/hm1nd/config/agent.nix`."
)]
impl ServerHandler for ManagerServer {}
@ -1559,7 +1485,6 @@ pub fn allowed_mcp_tools(flavor: Flavor) -> Vec<String> {
"answer",
"remind",
"get_loose_ends",
"whoami",
"set_status",
"get_agent_meta",
"cancel_loose_end",
@ -1579,7 +1504,6 @@ pub fn allowed_mcp_tools(flavor: Flavor) -> Vec<String> {
"get_logs",
"get_loose_ends",
"remind",
"whoami",
"set_status",
"get_agent_meta",
"cancel_loose_end",