set_status: add get_agent_meta tool for inter-agent status queries
This commit is contained in:
parent
8e8e8a771f
commit
77fdaf0d1e
5 changed files with 153 additions and 2 deletions
|
|
@ -11,7 +11,8 @@ Tools (hyperhive surface):
|
|||
- `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. `"reviewing PR #42"`, `"fixing #319 model priority"`, `"idle"`). Pass an empty string to clear. Persists across harness restarts.
|
||||
- `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__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.
|
||||
|
|
|
|||
|
|
@ -56,6 +56,11 @@ pub enum SocketReply {
|
|||
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 {
|
||||
|
|
@ -85,6 +90,15 @@ impl From<hive_sh4re::AgentResponse> for SocketReply {
|
|||
status_text,
|
||||
status_set_at,
|
||||
},
|
||||
hive_sh4re::AgentResponse::AgentMeta {
|
||||
name,
|
||||
status_text,
|
||||
status_set_at,
|
||||
} => Self::AgentMeta {
|
||||
name,
|
||||
status_text,
|
||||
status_set_at,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -117,6 +131,15 @@ impl From<hive_sh4re::ManagerResponse> for SocketReply {
|
|||
status_text,
|
||||
status_set_at,
|
||||
},
|
||||
hive_sh4re::ManagerResponse::AgentMeta {
|
||||
name,
|
||||
status_text,
|
||||
status_set_at,
|
||||
} => Self::AgentMeta {
|
||||
name,
|
||||
status_text,
|
||||
status_set_at,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -305,6 +328,40 @@ pub fn format_whoami(resp: Result<SocketReply, anyhow::Error>) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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(),
|
||||
Some(s) => {
|
||||
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))
|
||||
});
|
||||
match age {
|
||||
Some(a) => format!("{s} (set {a} ago)"),
|
||||
None => s,
|
||||
}
|
||||
}
|
||||
};
|
||||
format!("agent: {name}\nstatus: {status}")
|
||||
}
|
||||
Ok(SocketReply::Err(m)) => format!("get_agent_meta failed: {m}"),
|
||||
Ok(other) => format!("get_agent_meta unexpected response: {other:?}"),
|
||||
Err(e) => format!("get_agent_meta transport error: {e:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Format a duration in seconds as a human-readable age string.
|
||||
fn format_age_secs(secs: u64) -> String {
|
||||
if secs < 60 {
|
||||
|
|
@ -607,7 +664,7 @@ impl AgentServer {
|
|||
#[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 \
|
||||
`\"processing matrix messages\"`, `\"fixing bitburner crash\"`, `\"idle\"`). Pass an empty \
|
||||
string to clear. The status is shown on your dashboard card and persists across \
|
||||
harness restarts."
|
||||
)]
|
||||
|
|
@ -620,6 +677,28 @@ impl AgentServer {
|
|||
.await
|
||||
}
|
||||
|
||||
#[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."
|
||||
)]
|
||||
async fn get_agent_meta(
|
||||
&self,
|
||||
Parameters(args): Parameters<GetAgentMetaArgs>,
|
||||
) -> String {
|
||||
let log = args.name.clone();
|
||||
run_tool_envelope("get_agent_meta", log, async move {
|
||||
let (resp, retries) = self
|
||||
.dispatch(hive_sh4re::AgentRequest::GetAgentMeta { name: args.name })
|
||||
.await;
|
||||
annotate_retries(format_agent_meta(resp), 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` \
|
||||
|
|
@ -786,6 +865,12 @@ pub struct SetStatusArgs {
|
|||
pub text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct GetAgentMetaArgs {
|
||||
/// Logical name of the agent to query (e.g. `"iris"`, `"manager"`).
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct StartArgs {
|
||||
/// Sub-agent name (without the `h-` container prefix).
|
||||
|
|
@ -1335,6 +1420,27 @@ impl ManagerServer {
|
|||
.await
|
||||
}
|
||||
|
||||
#[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."
|
||||
)]
|
||||
async fn get_agent_meta(
|
||||
&self,
|
||||
Parameters(args): Parameters<GetAgentMetaArgs>,
|
||||
) -> String {
|
||||
let log = args.name.clone();
|
||||
run_tool_envelope("get_agent_meta", log, async move {
|
||||
let (resp, retries) = self
|
||||
.dispatch(hive_sh4re::ManagerRequest::GetAgentMeta { name: args.name })
|
||||
.await;
|
||||
annotate_retries(format_agent_meta(resp), 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` \
|
||||
|
|
@ -1455,6 +1561,7 @@ pub fn allowed_mcp_tools(flavor: Flavor) -> Vec<String> {
|
|||
"get_loose_ends",
|
||||
"whoami",
|
||||
"set_status",
|
||||
"get_agent_meta",
|
||||
"cancel_loose_end",
|
||||
],
|
||||
Flavor::Manager => &[
|
||||
|
|
@ -1474,6 +1581,7 @@ pub fn allowed_mcp_tools(flavor: Flavor) -> Vec<String> {
|
|||
"remind",
|
||||
"whoami",
|
||||
"set_status",
|
||||
"get_agent_meta",
|
||||
"cancel_loose_end",
|
||||
],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -254,6 +254,15 @@ async fn dispatch(req: &AgentRequest, agent: &str, coord: &Arc<Coordinator>) ->
|
|||
Err(e) => AgentResponse::Err { message: format!("set_status write failed: {e}") },
|
||||
}
|
||||
}
|
||||
AgentRequest::GetAgentMeta { name } => {
|
||||
let (status_text, status_set_at) =
|
||||
crate::container_view::read_agent_status(name);
|
||||
AgentResponse::AgentMeta {
|
||||
name: name.clone(),
|
||||
status_text,
|
||||
status_set_at,
|
||||
}
|
||||
}
|
||||
AgentRequest::CancelLooseEnd { kind, id } => crate::questions::handle_cancel_loose_end(
|
||||
coord, agent, *kind, *id,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -512,6 +512,15 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc<Coordinator>) -> ManagerResp
|
|||
Err(e) => ManagerResponse::Err { message: format!("set_status write failed: {e}") },
|
||||
}
|
||||
}
|
||||
ManagerRequest::GetAgentMeta { name } => {
|
||||
let (status_text, status_set_at) =
|
||||
crate::container_view::read_agent_status(name);
|
||||
ManagerResponse::AgentMeta {
|
||||
name: name.clone(),
|
||||
status_text,
|
||||
status_set_at,
|
||||
}
|
||||
}
|
||||
ManagerRequest::CancelLooseEnd { kind, id } => crate::questions::handle_cancel_loose_end(
|
||||
coord,
|
||||
MANAGER_AGENT,
|
||||
|
|
|
|||
|
|
@ -447,6 +447,10 @@ pub enum AgentRequest {
|
|||
/// to `{state_dir}/hyperhive-status` so it survives harness restarts.
|
||||
/// Pass an empty string to clear the status.
|
||||
SetStatus { text: String },
|
||||
/// Fetch the current status of another agent by name. Returns
|
||||
/// `AgentResponse::AgentMeta` with the target's status fields.
|
||||
/// If the agent does not exist or has never set a status, fields are `None`.
|
||||
GetAgentMeta { name: String },
|
||||
/// Cancel an open thread the agent owns: a `Question` they asked
|
||||
/// (returns `[cancelled by <self>]` as the answer to the asker)
|
||||
/// or a `Reminder` they scheduled (hard-deletes the row).
|
||||
|
|
@ -525,6 +529,16 @@ pub enum AgentResponse {
|
|||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
status_set_at: Option<i64>,
|
||||
},
|
||||
/// `GetAgentMeta` result: status metadata for a named agent.
|
||||
/// `status_text` / `status_set_at` are `None` when the agent has
|
||||
/// not set a status or the agent name is unknown.
|
||||
AgentMeta {
|
||||
name: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
status_text: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
status_set_at: Option<i64>,
|
||||
},
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -856,6 +870,8 @@ pub enum ManagerRequest {
|
|||
Whoami,
|
||||
/// Mirror of `AgentRequest::SetStatus` on the manager surface.
|
||||
SetStatus { text: String },
|
||||
/// Mirror of `AgentRequest::GetAgentMeta` on the manager surface.
|
||||
GetAgentMeta { name: String },
|
||||
/// Cancel an open thread (question or reminder). Manager surface
|
||||
/// can cancel any row (no owner check) — same dispatch as
|
||||
/// `AgentRequest::CancelLooseEnd` but with privileged auth.
|
||||
|
|
@ -943,4 +959,12 @@ pub enum ManagerResponse {
|
|||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
status_set_at: Option<i64>,
|
||||
},
|
||||
/// Mirror of `AgentResponse::AgentMeta` on the manager surface.
|
||||
AgentMeta {
|
||||
name: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
status_text: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
status_set_at: Option<i64>,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue