prompts+docs: the lifecycle tools reach the whole subtree, not just direct children

`require_descendant` (`socket_server/mod.rs:666`) authorises
kill/start/restart/update/get_logs with `topology::is_descendant_of` — the
caller's whole subtree, itself included. That has been true since `53b4e752`
(#1865), whose message says "a parent owns its whole subtree; the root covers
every agent as a consequence, no positional privilege", and two tests pin it
(`is_descendant_of_in_grandchild`, `is_descendant_of_in_self_is_true`).

The prose never followed. The four lifecycle tool descriptions, their
`// IMPORTANT:` comments, `docs/tools/lifecycle.md`, the tools README,
hive-agent-mcp's README and the system prompt every agent is rendered from all
still said "direct children only" — while `list_containers`, four tools away in
the same file, said "direct children + their subtrees".

`lifecycle.md` also taught the model #1865 deleted: "Privileged agents (for
example ruth) may operate on any sub-agent — the topology scope applies to all
others." There is no privileged class to belong to; ruth reaches every agent
because the check is transitive and everything sits under it.

Same drift on the state-query side: `resolve_agent_state_target` is
subtree-scoped by the same commit, so `get_loose_ends`' argument doc, the
`QueryAgentState` capability doc and `docs/turn-loop/mcp.md` were all telling a
parent it needs a capability to read a grandchild's threads.

Two smaller corrections found on the way:

* `list_containers` returns the caller itself. `is_descendant_of` is true for
  `candidate == ancestor` and `handle_list_descendants` filters the topology
  with it; called from a leaf agent it answers one row, that agent.
* `request_init_config` accepts any unused name — the requester becomes its
  parent — or an existing agent already in the caller's subtree, not "a direct
  child". The editing surface is narrower than the guard, though: only direct
  children's config repos are bind-mounted, so re-seeding deeper in the subtree
  leaves no local copy to edit. `lifecycle.md` now says so.

The prompt's other stale claim, the dead `request_apply_commit`, is #4226 and
was fixed independently by damocles in #4227 while this was being gated. This
branch keeps only the scope wording on that line.

Closes #4225.
This commit is contained in:
atlas 2026-09-11 15:20:47 +02:00
commit 0a80f21003
10 changed files with 93 additions and 66 deletions

View file

@ -38,9 +38,9 @@ debug agent behavior.
- **[forge-cli](forge-cli.md)** — the exhaustive, autogenerated - **[forge-cli](forge-cli.md)** — the exhaustive, autogenerated
flag-by-flag reference for `hive-forge`, kept in lockstep with the flag-by-flag reference for `hive-forge`, kept in lockstep with the
binary by CI the same way `hivectl-cli.md` is. binary by CI the same way `hivectl-cli.md` is.
- **[lifecycle](lifecycle.md)** — kill/start/restart/update for an - **[lifecycle](lifecycle.md)** — kill/start/restart/update for the
agent's own direct children, plus the approval-gated config-change agents in a caller's own subtree, plus the approval-gated
tools. config-change tools.
- **[matrix](matrix.md)** — the matrix MCP tool surface - **[matrix](matrix.md)** — the matrix MCP tool surface
(`mcp__matrix__*`) for agents with a matrix account, multiple (`mcp__matrix__*`) for agents with a matrix account, multiple
accounts per agent, and declaring extra MCP servers generally. accounts per agent, and declaring extra MCP servers generally.

View file

@ -1,14 +1,15 @@
# Lifecycle and approvals tools # Lifecycle and approvals tools
Two tool groups govern agent lifecycle management and config changes. Two tool groups govern agent lifecycle management and config changes.
The server scopes both to **direct children only** (topology-enforced: it The server scopes both to the caller's **own subtree** (topology-enforced
rejects any name that's not a direct child of the calling per `topology.json`: a child, a child's child, every agent below them —
agent per `topology.json`). Privileged agents (for example ruth) may operate plus the caller itself). No privileged class exists to belong to; the
on any sub-agent — the topology scope applies to all others. root agent reaches every agent purely because the check is transitive and
everything sits under it.
## `lifecycle` tool group ## `lifecycle` tool group
No operator approval required. Direct children only. No operator approval required. The caller's own subtree.
### `kill(name)` ### `kill(name)`
@ -31,17 +32,19 @@ to `needs_update` system events.
### `list_containers()` ### `list_containers()`
List all **descendant** containers (children + their subtrees) with List the caller's whole **subtree** with running status — children,
running status. Topology-scoped to descendants only. their children, every agent below them. The calling agent is part of its
own subtree, so it appears in its own listing; a leaf agent gets a
one-row answer naming itself.
## `approvals` tool group ## `approvals` tool group
Config changes and new-agent spawns route through the operator Config changes and new-agent spawns route through the operator
approval queue. Direct children only (topology-enforced). approval queue. Topology-enforced the same way.
### `request_init_config(name, description?)` ### `request_init_config(name, description?)`
Step 1 of spawning a new direct child agent. Queues an `InitConfig` Step 1 of spawning a new sub-agent. Queues an `InitConfig`
approval; on operator approve, hive-c0re seeds the proposed config approval; on operator approve, hive-c0re seeds the proposed config
repo at `/agents/<name>/config/agent.nix` with a default template and repo at `/agents/<name>/config/agent.nix` with a default template and
delivers a `config_ready` system event. Then edit `agent.nix`, commit, delivers a `config_ready` system event. Then edit `agent.nix`, commit,
@ -54,6 +57,17 @@ Subsequent config changes go through a **forge PR** on the agent's
open/update — no MCP tool involved), not a tool call. See open/update — no MCP tool involved), not a tool call. See
`docs/agent-lifecycle/approvals.md`. `docs/agent-lifecycle/approvals.md`.
`name` must be either unused — in which case the caller becomes its
parent on approval — or an agent already in the caller's subtree whose
config is being re-seeded. The server refuses a name that exists outside
that subtree, so one agent can't hijack another's.
Only **direct** children's config repos are bind-mounted into a parent's
container, though (`bind_child_agent_dirs`, driven by
`topology::children_of`). The server accepts re-seeding an agent further
down the subtree, and that still leaves the caller without a local copy
to edit afterwards.
Fails if a proposed config repo for `name` already exists. Fails if a proposed config repo for `name` already exists.
`name` is ≤ 9 characters. `name` is ≤ 9 characters.
@ -69,12 +83,12 @@ agents after the approval resolves.
## Boundary summary ## Boundary summary
| Operation | Requires approval? | Scope | | Operation | Requires approval? | Scope |
| --------------------------------------- | ------------------ | --------------------- | | --------------------------------------- | ------------------ | ---------------------------------- |
| `kill` / `start` / `restart` / `update` | No | Direct children | | `kill` / `start` / `restart` / `update` | No | Own subtree |
| `list_containers` | No | All descendants | | `list_containers` | No | Own subtree, caller included |
| `request_init_config` | Yes (InitConfig) | New direct child only | | `request_init_config` | Yes (InitConfig) | Unused name, or one in own subtree |
| `request_update_meta_inputs` | Yes (MetaUpdate) | Meta flake (global) | | `request_update_meta_inputs` | Yes (MetaUpdate) | Meta flake (global) |
## See also ## See also

View file

@ -81,8 +81,9 @@ at_unix_timestamp?)`.
external MCP daemons (for example running bash tasks from external MCP daemons (for example running bash tasks from
`hive-bash-daemon`). Each row carries an id + kind for `hive-bash-daemon`). Each row carries an id + kind for
`cancel_loose_end`. Omit `agent` to list your own threads. Pass `cancel_loose_end`. Omit `agent` to list your own threads. Pass
`agent: "<name>"` to inspect a direct child agent (always accessible `agent: "<name>"` to inspect any agent in your subtree — a child, a
per topology enforcement); non-children require the child's child, every agent below them (always accessible per topology
enforcement); a name outside the subtree requires the
`query_agent_state` capability. The `"*"` hive-wide query isn't `query_agent_state` capability. The `"*"` hive-wide query isn't
available on the agent socket. available on the agent socket.
- `cancel_loose_end` — hard-delete a `reminder`, cancel a pending - `cancel_loose_end` — hard-delete a `reminder`, cancel a pending

View file

@ -10,8 +10,8 @@ would hit. HTTP is the sole transport; there is no stdio mode here.
This is where the core hyperhive tool surface lives: `send`, `recv`, This is where the core hyperhive tool surface lives: `send`, `recv`,
`remind`, `get_loose_ends`, `set_status`, `remind`, `get_loose_ends`, `set_status`,
`get_agent_meta`, lifecycle (`kill`/`start`/`restart`/`update` on `get_agent_meta`, lifecycle (`kill`/`start`/`restart`/`update` on the
direct children), scheduling, and the approval-request tools. Reach agent's own subtree), scheduling, and the approval-request tools. Reach
for this crate when you're adding or changing a built-in tool rather for this crate when you're adding or changing a built-in tool rather
than an `extraMcpServers` add-on — those are separate stdio bridges than an `extraMcpServers` add-on — those are separate stdio bridges
(see `hive-bash-mcp`, `hive-matrix-mcp`) that dial the harness socket (see `hive-bash-mcp`, `hive-matrix-mcp`) that dial the harness socket

View file

@ -172,7 +172,8 @@ pub struct CancelLooseEndArgs {
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)] #[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct AgentGetLooseEndsArgs { pub struct AgentGetLooseEndsArgs {
/// Whose loose ends to list. Omit (or `null`) for your own. You may /// Whose loose ends to list. Omit (or `null`) for your own. You may
/// also pass a direct child agent's name without any extra capability. /// also pass the name of any agent in your subtree — a child, a child's
/// child, and so on down — without any extra capability.
/// Pass any other agent name to inspect their threads — requires the /// Pass any other agent name to inspect their threads — requires the
/// `query_agent_state` capability; without it the request is rejected /// `query_agent_state` capability; without it the request is rejected
/// with an error. The `"*"` hive-wide value is not available on the /// with an error. The `"*"` hive-wide value is not available on the

View file

@ -560,11 +560,12 @@ impl AgentServer {
// IMPORTANT: this tool is only available when the `lifecycle` tool group // IMPORTANT: this tool is only available when the `lifecycle` tool group
// is granted to this agent. hive-c0re enforces the topology check // is granted to this agent. hive-c0re enforces the topology check
// server-side: the call is rejected unless `name` is a direct child. // server-side: the call is rejected unless `name` is in the caller's
// subtree.
#[tool( #[tool(
description = "Restart a direct child sub-agent container (stop + start). \ description = "Restart a sub-agent container (stop + start). Only succeeds if `name` \
Only succeeds if `name` is a direct child of this agent in the topology \ is somewhere in this agent's subtree a child, a child's child, and so on down \
tree the server enforces this. No approval required." the topology tree which the server enforces. No approval required."
)] )]
async fn restart(&self, Parameters(args): Parameters<RestartArgs>) -> String { async fn restart(&self, Parameters(args): Parameters<RestartArgs>) -> String {
let log = format!("{args:?}"); let log = format!("{args:?}");
@ -583,11 +584,14 @@ impl AgentServer {
// IMPORTANT: this tool is only available when the `lifecycle` tool group // IMPORTANT: this tool is only available when the `lifecycle` tool group
// is granted to this agent. hive-c0re enforces the topology check // is granted to this agent. hive-c0re enforces the topology check
// server-side: the call is rejected unless `name` is a direct child. // server-side: the call is rejected unless `name` is in the caller's
#[tool(description = "Stop a direct child sub-agent container (graceful). \ // subtree.
Only succeeds if `name` is a direct child of this agent in the topology \ #[tool(
tree the server enforces this. No approval required. \ description = "Stop a sub-agent container (graceful). Only succeeds if `name` \
State dir is kept; recreating the agent reuses prior config + credentials.")] is somewhere in this agent's subtree a child, a child's child, and so on down \
the topology tree which the server enforces. No approval required. \
State dir is kept; recreating the agent reuses prior config + credentials."
)]
async fn kill(&self, Parameters(args): Parameters<KillArgs>) -> String { async fn kill(&self, Parameters(args): Parameters<KillArgs>) -> String {
let log = format!("{args:?}"); let log = format!("{args:?}");
let name = args.name.clone(); let name = args.name.clone();
@ -602,12 +606,14 @@ impl AgentServer {
// IMPORTANT: this tool is only available when the `lifecycle` tool group // IMPORTANT: this tool is only available when the `lifecycle` tool group
// is granted to this agent. hive-c0re enforces the topology check // is granted to this agent. hive-c0re enforces the topology check
// server-side: the call is rejected unless `name` is a direct child. // server-side: the call is rejected unless `name` is in the caller's
// subtree.
#[tool( #[tool(
description = "Rebuild a direct child sub-agent: re-applies the current hyperhive \ description = "Rebuild a sub-agent: re-applies the current hyperhive flake + agent.nix \
flake + agent.nix and restarts the container. Only succeeds if `name` is a direct \ and restarts the container. Only succeeds if `name` is somewhere in this agent's \
child of this agent in the topology tree the server enforces this. \ subtree a child, a child's child, and so on down the topology tree which the \
No approval required. Idempotent use when a child needs its config reapplied." server enforces. No approval required. Idempotent use when an agent needs its \
config reapplied."
)] )]
async fn update(&self, Parameters(args): Parameters<UpdateArgs>) -> String { async fn update(&self, Parameters(args): Parameters<UpdateArgs>) -> String {
let log = format!("{args:?}"); let log = format!("{args:?}");
@ -625,11 +631,12 @@ impl AgentServer {
} }
// IMPORTANT: this tool is only available when the `lifecycle` tool group // IMPORTANT: this tool is only available when the `lifecycle` tool group
// is granted to this agent. Returns all topological descendants of the // is granted to this agent. Returns the calling agent's whole subtree,
// calling agent with their running status. // itself included, with running status.
#[tool( #[tool(
description = "List all containers that are topological descendants of this agent \ description = "List this agent's whole subtree — children, their children, and so on \
(direct children + their subtrees). Requires the `lifecycle` tool group. \ down with running status. The calling agent is part of its own subtree, so it \
appears in the listing too. Requires the `lifecycle` tool group. \
Returns every known descendant regardless of running state check the `running` \ Returns every known descendant regardless of running state check the `running` \
field to distinguish live from stopped containers. Ordered by topology depth \ field to distinguish live from stopped containers. Ordered by topology depth \
(parents before children), then alphabetically within each tier." (parents before children), then alphabetically within each tier."
@ -705,20 +712,20 @@ impl AgentServer {
// IMPORTANT: this tool is only available when the `approvals` tool group // IMPORTANT: this tool is only available when the `approvals` tool group
// is configured for the agent (`HIVE_TOOL_GROUPS` contains `approvals`). // is configured for the agent (`HIVE_TOOL_GROUPS` contains `approvals`).
// hive-c0re performs a topology check server-side: only direct children // hive-c0re performs a topology check server-side: an unused `name` is
// of the calling agent are accepted; all other names are rejected. // accepted from any caller (the requester becomes its parent); an existing
#[tool( // agent is accepted only from inside the caller's subtree.
description = "Create a brand-new direct child agent's config repo and queue an \ #[tool(description = "Create a new agent's config repo and queue an \
`InitConfig` approval for the operator to review. Requires the `approvals` tool \ `InitConfig` approval for the operator to review. Requires the `approvals` tool \
group. `name` must be a direct child of this agent in the topology tree. Fails if a \ group. `name` must be either unused in which case you become its parent or an \
config repo for that child already exists. This tool **creates the repo** and \ agent already in your subtree, whose config you are re-seeding. Fails if a \
config repo for that agent already exists. This tool **creates the repo** and \
nothing else: on approval hive-c0re seeds it with a default `agent.nix`, and \ nothing else: on approval hive-c0re seeds it with a default `agent.nix`, and \
approving the follow-up `Spawn` creates the container. \ approving the follow-up `Spawn` creates the container. \
Every config change the child's first one included goes through a PR on its \ Every config change the child's first one included goes through a PR on its \
config repo, made from a clone you take yourself, reviewed + approved by the \ config repo, made from a clone you take yourself, reviewed + approved by the \
operator. `/agents/<name>/config` is a **read-only copy** for reading a config, \ operator. `/agents/<name>/config` is a **read-only copy** for reading a config, \
never an editing surface." never an editing surface.")]
)]
async fn request_init_config( async fn request_init_config(
&self, &self,
Parameters(args): Parameters<RequestInitConfigArgs>, Parameters(args): Parameters<RequestInitConfigArgs>,
@ -746,10 +753,13 @@ impl AgentServer {
// IMPORTANT: this tool is only available when the `lifecycle` tool group // IMPORTANT: this tool is only available when the `lifecycle` tool group
// is granted to this agent. hive-c0re enforces the topology check // is granted to this agent. hive-c0re enforces the topology check
// server-side: the call is rejected unless `name` is a direct child. // server-side: the call is rejected unless `name` is in the caller's
#[tool(description = "Start a stopped direct child sub-agent container. \ // subtree.
Only succeeds if `name` is a direct child of this agent in the topology \ #[tool(
tree the server enforces this. No approval required.")] description = "Start a stopped sub-agent container. Only succeeds if `name` \
is somewhere in this agent's subtree a child, a child's child, and so on down \
the topology tree which the server enforces. No approval required."
)]
async fn start(&self, Parameters(args): Parameters<StartArgs>) -> String { async fn start(&self, Parameters(args): Parameters<StartArgs>) -> String {
let log = format!("{args:?}"); let log = format!("{args:?}");
let name = args.name.clone(); let name = args.name.clone();

View file

@ -4,7 +4,7 @@ Tools (hyperhive surface). Full signature + behavior for each comes from the too
- **Inbox / messaging** (always available): `mcp__hyperhive__recv`, `mcp__hyperhive__ack_until`, `mcp__hyperhive__send`, `mcp__hyperhive__get_loose_ends`, `mcp__hyperhive__cancel_loose_end`, `mcp__hyperhive__mark_todos_done`, `mcp__hyperhive__remind`, `mcp__hyperhive__set_status`, `mcp__hyperhive__get_agent_meta`. One habit worth internalizing beyond the tool descriptions themselves: prefer ending the turn over repeatedly polling `recv` when idle (only turn-boundaries observe in-container todo wakes — bash-task completions, matrix unread, forge activity — and ending the turn is also your checkpoint). For a large todo backlog (`get_loose_ends` caps at 40 rows), clear reviewed ids in bulk with `mark_todos_done` rather than cancelling one at a time — there's no blind range-clear, only ids you've actually looked at. - **Inbox / messaging** (always available): `mcp__hyperhive__recv`, `mcp__hyperhive__ack_until`, `mcp__hyperhive__send`, `mcp__hyperhive__get_loose_ends`, `mcp__hyperhive__cancel_loose_end`, `mcp__hyperhive__mark_todos_done`, `mcp__hyperhive__remind`, `mcp__hyperhive__set_status`, `mcp__hyperhive__get_agent_meta`. One habit worth internalizing beyond the tool descriptions themselves: prefer ending the turn over repeatedly polling `recv` when idle (only turn-boundaries observe in-container todo wakes — bash-task completions, matrix unread, forge activity — and ending the turn is also your checkpoint). For a large todo backlog (`get_loose_ends` caps at 40 rows), clear reviewed ids in bulk with `mark_todos_done` rather than cancelling one at a time — there's no blind range-clear, only ids you've actually looked at.
- **Extra MCP tools** (some agents only): `mcp__<server>__<tool>` — agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `hyperhive.extraMcpServers`. First-class tools, already operator-approved at deploy time. - **Extra MCP tools** (some agents only): `mcp__<server>__<tool>` — agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `hyperhive.extraMcpServers`. First-class tools, already operator-approved at deploy time.
- **Lifecycle** (_requires `lifecycle` tool group_, direct children only, no approval needed): `restart`, `kill`, `start`, `update`, `list_containers`. - **Lifecycle** (_requires `lifecycle` tool group_, your own subtree — children, their children, and so on down, no approval needed): `restart`, `kill`, `start`, `update`, `list_containers`.
- **Approvals** (_requires `approvals` tool group_, queues an operator approval): `request_init_config`, `request_update_meta_inputs`. - **Approvals** (_requires `approvals` tool group_, queues an operator approval): `request_init_config`, `request_update_meta_inputs`.
- **Scheduling** (_requires `scheduling` tool group_): `request_schedule_prompt` (queues an approval), `cancel_schedule`, `fire_schedule_now`, `edit_schedule`, `list_schedules` (these four don't need approval — you can manage schedules you own or that a sub-agent in your subtree owns). - **Scheduling** (_requires `scheduling` tool group_): `request_schedule_prompt` (queues an approval), `cancel_schedule`, `fire_schedule_now`, `edit_schedule`, `list_schedules` (these four don't need approval — you can manage schedules you own or that a sub-agent in your subtree owns).
- **Diagnostics**: `get_logs` (_requires `diagnostics` tool group_), `get_host_journal` (_requires `read_host_journal` capability_). - **Diagnostics**: `get_logs` (_requires `diagnostics` tool group_), `get_host_journal` (_requires `read_host_journal` capability_).
@ -13,7 +13,7 @@ Need new packages, env vars, or other NixOS config for yourself? You can't edit
Your config repo is mounted **read-only** at `/agents/{label}/config/``agent.nix` plus whatever extra files define you (declared packages, env vars, MCP servers). Read it to see exactly what defines you before asking for a change, so you can point at the precise file and line. Your config repo is mounted **read-only** at `/agents/{label}/config/``agent.nix` plus whatever extra files define you (declared packages, env vars, MCP servers). Read it to see exactly what defines you before asking for a change, so you can point at the precise file and line.
Approval boundary: lifecycle ops on _existing_ direct children (`kill`, `start`, `restart`) are at your discretion — no operator approval needed (requires `lifecycle` tool group). _Creating_ a new agent starts with `request_init_config` (requires `approvals` tool group), then the operator spawns it from the dashboard. _Changing_ any agent's config is not a tool call at all — it's a forge PR on the agent's `agent-configs/<name>` repo, which queues a `MergeConfigPr` approval on open/update. The operator only signs off on changes; you run the day-to-day. Approval boundary: lifecycle ops on _existing_ agents in your subtree (`kill`, `start`, `restart`) are at your discretion — no operator approval needed (requires `lifecycle` tool group). _Creating_ a new agent starts with `request_init_config` (requires `approvals` tool group), then the operator spawns it from the dashboard. _Changing_ any agent's config is not a tool call at all — it's a forge PR on the agent's `agent-configs/<name>` repo, which queues a `MergeConfigPr` approval on open/update. The operator only signs off on changes; you run the day-to-day.
Messages from sender `system` are hyperhive helper events (JSON body, `event` field discriminates): `approval_resolved`, `container_crash`, `needs_update`. Use these to react to lifecycle changes: Messages from sender `system` are hyperhive helper events (JSON body, `event` field discriminates): `approval_resolved`, `container_crash`, `needs_update`. Use these to react to lifecycle changes:

View file

@ -698,10 +698,10 @@ fn require_group(agent: &str, group: &str, action: &str) -> Option<Response> {
/// Topology guard for `request_init_config`, which may legitimately target a /// Topology guard for `request_init_config`, which may legitimately target a
/// child that does not exist *yet* (seeding a brand-new sub-agent's config /// child that does not exist *yet* (seeding a brand-new sub-agent's config
/// repo). The caller may act on a /// repo). The caller may act on a
/// `target` that is EITHER already its direct child (re-init / config /// `target` that is EITHER already in its subtree (re-init / config
/// update of an existing child) OR brand-new (absent from the topology /// update of an agent it owns) OR brand-new (absent from the topology
/// tree — the requester becomes its parent). A name that already /// tree — the requester becomes its parent). A name that already
/// belongs to a *different* parent (or is a root agent) is refused so /// exists outside the caller's subtree is refused so
/// one agent can't hijack another's sub-tree. /// one agent can't hijack another's sub-tree.
/// ///
/// Also re-runs the agent-name format check (a traversal / malformed name /// Also re-runs the agent-name format check (a traversal / malformed name

View file

@ -193,10 +193,10 @@ pub enum Request {
}, },
/// *(privileged)* List every schedule in the queue. /// *(privileged)* List every schedule in the queue.
ListSchedules, ListSchedules,
/// List all containers that are topological descendants of the calling /// List the calling agent's subtree — children, their children, and so
/// agent (direct children + their subtrees). Scoped to the caller's /// on down, plus the caller itself, which is part of its own subtree.
/// subtree; gated by the `lifecycle` tool group. The result includes all /// Gated by the `lifecycle` tool group. The result includes every known
/// known descendants regardless of whether the container is currently /// member regardless of whether the container is currently
/// running — use `running` to distinguish. /// running — use `running` to distinguish.
ListDescendants, ListDescendants,
/// *(privileged)* Fire a scheduled prompt out of band immediately. /// *(privileged)* Fire a scheduled prompt out of band immediately.

View file

@ -215,11 +215,12 @@ pub enum Capability {
/// MCP tool `get_host_journal` is only registered in the harness /// MCP tool `get_host_journal` is only registered in the harness
/// when this capability is present. /// when this capability is present.
ReadHostJournal, ReadHostJournal,
/// Agent can query non-child agents via `GetLooseEnds`, /// Agent can query agents outside its own subtree via `GetLooseEnds`,
/// `CountPendingReminders`, and `ReminderRollup` on the agent /// `CountPendingReminders`, and `ReminderRollup` on the agent
/// socket. Without this capability, targeting a non-child agent is /// socket. Without this capability, targeting an agent outside the
/// rejected with an error (direct children are always accessible /// caller's subtree is rejected with an error (the caller itself and
/// without any capability). The `"*"` hive-wide value is not /// every descendant are always accessible without any capability).
/// The `"*"` hive-wide value is not
/// available on the agent socket even with this capability — use the /// available on the agent socket even with this capability — use the
/// manager socket for swarm-wide scans. /// manager socket for swarm-wide scans.
QueryAgentState, QueryAgentState,