whoami: drop operator_pronouns (redundant — already in system prompts at boot)

This commit is contained in:
damocles 2026-05-17 23:49:34 +02:00
parent 3c66cb6707
commit d395bdc945
7 changed files with 19 additions and 31 deletions

View file

@ -57,7 +57,7 @@ how often the friction bites in normal use.
and `cancel_ask(id)` on the agent surface, plus `list_my_reminders()` and `cancel_ask(id)` on the agent surface, plus `list_my_reminders()`
/ `cancel_reminder(id)`. Bounded by `asker == self` and `reminder.owner / `cancel_reminder(id)`. Bounded by `asker == self` and `reminder.owner
== self` so no cross-agent meddling. == self` so no cross-agent meddling.
- ~~**`whoami` introspection tool**~~ ✓ landed — new `mcp__hyperhive__whoami` on both agent + manager surfaces. Returns `{ name, role, operator_pronouns, hyperhive_rev }` from coord state (socket identity for `name`, hard-coded per surface for `role`, `coord.operator_pronouns`, `auto_update::current_flake_rev`). `model` + `started_at` deferred — those live in the harness process not the coord, would need extra plumbing for marginal value. - ~~**`whoami` introspection tool**~~ ✓ landed — new `mcp__hyperhive__whoami` on both agent + manager surfaces. Returns `{ name, role, hyperhive_rev }` from coord state (socket identity for `name`, hard-coded per surface for `role`, `auto_update::current_flake_rev`). `operator_pronouns` deliberately omitted — already substituted into every agent's system prompt at boot, so returning it again was duplicate data. `model` + `started_at` deferred — those live in the harness process not the coord, would need extra plumbing for marginal value.
- **Optional `in_reply_to: <msg_id>` on send** — pure wire addition; no - **Optional `in_reply_to: <msg_id>` on send** — pure wire addition; no
behavioural change. The dashboard could render conversation threads behavioural change. The dashboard could render conversation threads
(already wants this for the agent-to-agent question UI in the (already wants this for the agent-to-agent question UI in the

View file

@ -8,7 +8,7 @@ Tools (hyperhive surface):
- `mcp__hyperhive__ask(question, options?, multi?, ttl_seconds?, to?)` — surface a structured question to the human operator (default, or `to: "operator"`) OR a peer agent (`to: "<agent-name>"`). Returns immediately with a question id — do NOT wait inline. When the recipient answers, a system message with event `question_answered { id, question, answer, answerer }` lands in your inbox; handle it on a future turn. Use this for clarifications, permission for risky actions, choice between options, or peer Q&A without burning regular inbox slots. `options` is advisory: a short fixed-choice list when applicable, otherwise leave empty for free text. `multi: true` lets the answerer pick multiple (checkboxes), answer comes back comma-joined. `ttl_seconds` auto-cancels with answer `[expired]` (and `answerer: "ttl-watchdog"`) when the decision becomes moot. - `mcp__hyperhive__ask(question, options?, multi?, ttl_seconds?, to?)` — surface a structured question to the human operator (default, or `to: "operator"`) OR a peer agent (`to: "<agent-name>"`). Returns immediately with a question id — do NOT wait inline. When the recipient answers, a system message with event `question_answered { id, question, answer, answerer }` lands in your inbox; handle it on a future turn. Use this for clarifications, permission for risky actions, choice between options, or peer Q&A without burning regular inbox slots. `options` is advisory: a short fixed-choice list when applicable, otherwise leave empty for free text. `multi: true` lets the answerer pick multiple (checkboxes), answer comes back comma-joined. `ttl_seconds` auto-cancels with answer `[expired]` (and `answerer: "ttl-watchdog"`) when the decision becomes moot.
- `mcp__hyperhive__answer(id, answer)` — answer a question that was routed to YOU. You'll see one in your inbox as a `question_asked { id, asker, question, options, multi }` system event when a peer or the manager calls `ask(to: "<your-name>", ...)`. The answer surfaces in the asker's inbox as a `question_answered` event. Strict authorisation: you can only answer questions where you are the declared target. - `mcp__hyperhive__answer(id, answer)` — answer a question that was routed to YOU. You'll see one in your inbox as a `question_asked { id, asker, question, options, multi }` system event when a peer or the manager calls `ask(to: "<your-name>", ...)`. The answer surfaces in the asker's inbox as a `question_answered` event. Strict authorisation: you can only answer questions where you are the declared target.
- `mcp__hyperhive__get_open_threads()` — list your loose ends: unanswered questions where you're asker (waiting on someone) or target (owing a reply). No args, cheap server-side sweep. Useful at turn start to remember what's outstanding without scanning inbox archaeology. - `mcp__hyperhive__get_open_threads()` — list your loose ends: unanswered questions where you're asker (waiting on someone) or target (owing a reply). No args, cheap server-side sweep. Useful at turn start to remember what's outstanding without scanning inbox archaeology.
- `mcp__hyperhive__whoami()` — self-introspection: returns your canonical agent name (from socket identity, not the prompt-substituted label), role, operator pronouns, 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. - `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.
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. 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

@ -13,7 +13,7 @@ Tools (hyperhive surface):
- `mcp__hyperhive__ask(question, options?, multi?, ttl_seconds?, to?)` — surface a structured question to the operator (default, or `to: "operator"`) OR a sub-agent (`to: "<agent-name>"`). Returns immediately with a question id; the answer arrives later as a system `question_answered { id, question, answer, answerer }` event in your inbox. Options are advisory: the dashboard always lets the operator type a free-text answer in addition. Set `multi: true` to render options as checkboxes (operator can pick multiple); the answer comes back as `, `-separated. Set `ttl_seconds` to auto-cancel after a deadline (capped at 6h server-side) — on expiry the answer is `[expired]` and `answerer` is `"ttl-watchdog"`. Do not poll inside the same turn — finish the current work and react when the event lands. - `mcp__hyperhive__ask(question, options?, multi?, ttl_seconds?, to?)` — surface a structured question to the operator (default, or `to: "operator"`) OR a sub-agent (`to: "<agent-name>"`). Returns immediately with a question id; the answer arrives later as a system `question_answered { id, question, answer, answerer }` event in your inbox. Options are advisory: the dashboard always lets the operator type a free-text answer in addition. Set `multi: true` to render options as checkboxes (operator can pick multiple); the answer comes back as `, `-separated. Set `ttl_seconds` to auto-cancel after a deadline (capped at 6h server-side) — on expiry the answer is `[expired]` and `answerer` is `"ttl-watchdog"`. Do not poll inside the same turn — finish the current work and react when the event lands.
- `mcp__hyperhive__answer(id, answer)` — answer a question that was routed to YOU (a sub-agent did `ask(to: "manager", ...)`). The triggering event in your inbox is `question_asked { id, asker, question, options, multi }`. The answer surfaces in the asker's inbox as a `question_answered` event. - `mcp__hyperhive__answer(id, answer)` — answer a question that was routed to YOU (a sub-agent did `ask(to: "manager", ...)`). The triggering event in your inbox is `question_asked { id, asker, question, options, multi }`. The answer surfaces in the asker's inbox as a `question_answered` event.
- `mcp__hyperhive__get_open_threads()` — hive-wide loose ends: every pending approval + every unanswered question across the swarm. Cheap server-side sweep, no args. Use to find stalled threads (sub-agent A asked B something three days ago and B never answered) before they rot. - `mcp__hyperhive__get_open_threads()` — hive-wide loose ends: every pending approval + every unanswered question across the swarm. Cheap server-side sweep, no args. Use to find stalled threads (sub-agent A asked B something three days ago and B never answered) before they rot.
- `mcp__hyperhive__whoami()` — self-introspection: canonical name (`manager`), role, operator pronouns, current hyperhive rev. No args. Useful for boot announcements and cross-agent attribution that won't drift across config reloads. - `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.
Approval boundary: lifecycle ops on *existing* sub-agents (`kill`, `start`, `restart`) are at your discretion — no operator approval. *Creating* a new agent (`request_spawn`) and *changing* any agent's config (`request_apply_commit`) still go through the approval queue. The operator only signs off on changes; you run the day-to-day. Approval boundary: lifecycle ops on *existing* sub-agents (`kill`, `start`, `restart`) are at your discretion — no operator approval. *Creating* a new agent (`request_spawn`) and *changing* any agent's config (`request_apply_commit`) still go through the approval queue. The operator only signs off on changes; you run the day-to-day.

View file

@ -45,7 +45,6 @@ pub enum SocketReply {
Whoami { Whoami {
name: String, name: String,
role: String, role: String,
operator_pronouns: String,
hyperhive_rev: Option<String>, hyperhive_rev: Option<String>,
}, },
} }
@ -67,12 +66,10 @@ impl From<hive_sh4re::AgentResponse> for SocketReply {
hive_sh4re::AgentResponse::Whoami { hive_sh4re::AgentResponse::Whoami {
name, name,
role, role,
operator_pronouns,
hyperhive_rev, hyperhive_rev,
} => Self::Whoami { } => Self::Whoami {
name, name,
role, role,
operator_pronouns,
hyperhive_rev, hyperhive_rev,
}, },
} }
@ -97,12 +94,10 @@ impl From<hive_sh4re::ManagerResponse> for SocketReply {
hive_sh4re::ManagerResponse::Whoami { hive_sh4re::ManagerResponse::Whoami {
name, name,
role, role,
operator_pronouns,
hyperhive_rev, hyperhive_rev,
} => Self::Whoami { } => Self::Whoami {
name, name,
role, role,
operator_pronouns,
hyperhive_rev, hyperhive_rev,
}, },
} }
@ -192,13 +187,10 @@ pub fn format_whoami(resp: Result<SocketReply, anyhow::Error>) -> String {
Ok(SocketReply::Whoami { Ok(SocketReply::Whoami {
name, name,
role, role,
operator_pronouns,
hyperhive_rev, hyperhive_rev,
}) => { }) => {
let rev = hyperhive_rev.as_deref().unwrap_or("<unknown>"); let rev = hyperhive_rev.as_deref().unwrap_or("<unknown>");
format!( format!("name: {name}\nrole: {role}\nhyperhive_rev: {rev}")
"name: {name}\nrole: {role}\noperator_pronouns: {operator_pronouns}\nhyperhive_rev: {rev}"
)
} }
Ok(SocketReply::Err(m)) => format!("whoami failed: {m}"), Ok(SocketReply::Err(m)) => format!("whoami failed: {m}"),
Ok(other) => format!("whoami unexpected response: {other:?}"), Ok(other) => format!("whoami unexpected response: {other:?}"),
@ -447,11 +439,11 @@ impl AgentServer {
#[tool( #[tool(
description = "Self-introspection: returns your own canonical agent name (the \ description = "Self-introspection: returns your own canonical agent name (the \
socket-identity name, NOT the prompt-substituted label), role (`agent`), the \ socket-identity name, NOT the prompt-substituted label), role (`agent`), and \
operator's pronouns, and the current hyperhive rev hive-c0re is running against. \ the current hyperhive rev hive-c0re is running against. No args. Useful when \
No args. Useful when you want a trustworthy identity stamp for state files / \ you want a trustworthy identity stamp for state files / commit messages / \
commit messages / cross-agent attribution that won't drift across renames or \ cross-agent attribution that won't drift across renames or session-continue \
session-continue boundaries where the system-prompt label could be stale." boundaries where the system-prompt label could be stale."
)] )]
async fn whoami(&self) -> String { async fn whoami(&self) -> String {
run_tool_envelope("whoami", String::new(), async move { run_tool_envelope("whoami", String::new(), async move {
@ -947,9 +939,9 @@ impl ManagerServer {
#[tool( #[tool(
description = "Self-introspection for the manager: returns canonical name \ description = "Self-introspection for the manager: returns canonical name \
(`manager`), role (`manager`), operator pronouns, and the current hyperhive rev. \ (`manager`), role (`manager`), and the current hyperhive rev. Same shape as \
Same shape as the agent flavour; useful for cross-agent attribution / boot \ the agent flavour; useful for cross-agent attribution / boot announcements / \
announcements / state-file headers without trusting prompt substitution." state-file headers without trusting prompt substitution."
)] )]
async fn whoami(&self) -> String { async fn whoami(&self) -> String {
run_tool_envelope("whoami", String::new(), async move { run_tool_envelope("whoami", String::new(), async move {
@ -1004,8 +996,8 @@ impl ManagerServer {
`answer` (respond to a `question_asked` event directed at you), \ `answer` (respond to a `question_asked` event directed at you), \
`get_open_threads` (hive-wide loose ends pending approvals + unanswered \ `get_open_threads` (hive-wide loose ends pending approvals + unanswered \
questions across the swarm), `whoami` (self-introspection canonical name, \ questions across the swarm), `whoami` (self-introspection canonical name, \
role, operator pronouns, current hyperhive rev). The manager's own config \ role, current hyperhive rev). The manager's own config lives at \
lives at `/agents/hm1nd/config/agent.nix`." `/agents/hm1nd/config/agent.nix`."
)] )]
impl ServerHandler for ManagerServer {} impl ServerHandler for ManagerServer {}

View file

@ -191,7 +191,6 @@ async fn dispatch(req: &AgentRequest, agent: &str, coord: &Arc<Coordinator>) ->
AgentRequest::Whoami => AgentResponse::Whoami { AgentRequest::Whoami => AgentResponse::Whoami {
name: agent.to_owned(), name: agent.to_owned(),
role: "agent".to_owned(), role: "agent".to_owned(),
operator_pronouns: coord.operator_pronouns.clone(),
hyperhive_rev: crate::auto_update::current_flake_rev(&coord.hyperhive_flake), hyperhive_rev: crate::auto_update::current_flake_rev(&coord.hyperhive_flake),
}, },
} }

View file

@ -346,7 +346,6 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc<Coordinator>) -> ManagerResp
ManagerRequest::Whoami => ManagerResponse::Whoami { ManagerRequest::Whoami => ManagerResponse::Whoami {
name: MANAGER_AGENT.to_owned(), name: MANAGER_AGENT.to_owned(),
role: "manager".to_owned(), role: "manager".to_owned(),
operator_pronouns: coord.operator_pronouns.clone(),
hyperhive_rev: crate::auto_update::current_flake_rev(&coord.hyperhive_flake), hyperhive_rev: crate::auto_update::current_flake_rev(&coord.hyperhive_flake),
}, },
} }

View file

@ -345,15 +345,14 @@ pub enum AgentResponse {
OpenThreads { threads: Vec<OpenThread> }, OpenThreads { threads: Vec<OpenThread> },
/// `CountPendingReminders` result. /// `CountPendingReminders` result.
PendingRemindersCount { count: u64 }, PendingRemindersCount { count: u64 },
/// `Whoami` result: identity + role + operator pronouns + the /// `Whoami` result: identity + role + the current hyperhive rev
/// current hyperhive rev hive-c0re is running against. `role` /// hive-c0re is running against. `role` is `"agent"` for
/// is `"agent"` for sub-agents (the only path that reaches this /// sub-agents (the only path that reaches this variant of the
/// variant of the response). `hyperhive_rev` is `None` only /// response). `hyperhive_rev` is `None` only when the configured
/// when the configured flake URL has no canonical path. /// flake URL has no canonical path.
Whoami { Whoami {
name: String, name: String,
role: String, role: String,
operator_pronouns: String,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
hyperhive_rev: Option<String>, hyperhive_rev: Option<String>,
}, },
@ -673,7 +672,6 @@ pub enum ManagerResponse {
Whoami { Whoami {
name: String, name: String,
role: String, role: String,
operator_pronouns: String,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
hyperhive_rev: Option<String>, hyperhive_rev: Option<String>,
}, },