mara on #5170: "dont split caps and perms. its all perms. / dont include the now empty tool groups / nothing set -> default / also add perms to agent creation screen". - hive-sh4re: one `Permission` enum (messaging, meta, inbox, execution, web_tools, manage_root_agent) replaces `ToolGroup` and `Capability`; `lifecycle`/`forge` are gone. `Permission::default_for` is the default for an agent the swarm sets nothing for. - The record is `{ "permissions": [...] }`. Missing key, unset field or no successful read yet = default; `[]` = none; a failed read after a good one keeps the last value. - hive-agent reads its own key every 10s and builds each turn's --tools, --allowedTools and MCP config (rewritten before every turn) from it; the subagent daemon reads it on each start/continue. Agents get a read-only grant on their own key. - hive-c0re restarts an agent only when its manage_root_agent changes. The import now merges meta/capabilities.json and meta/tool-groups.json into one list per agent and deletes both; the "import pending, don't read" decision is `ImportOutcome::read_allowed`, table-tested. - swarm-controller: `GET /api/permissions` (names + default) and an optional `permissions` on `POST /api/agents`, stored before the job is queued. swarm-ui's create-agent form gets a permissions field. - Deleted: HIVE_TOOL_GROUPS and its forward to the subagent daemon, tool_groups.rs, ruth's tool-group seed, the permissions rebuild job (PermPayload/WritePermFile/perm_change), the dashboard permissions tab and its stale-entries panel. Part of #5170.
6.6 KiB
Bash execution tools
Background shell execution via hive-bash-daemon. Tools land as
mcp__bash__<tool> (the MCP server name is bash, not hyperhive).
nix/agent-modules/mcp.nix registers bash into services.hyperhive.agent.extraMcpServers
by default (lib.mkDefault, with allowedTools = ["*"]) — an agent.nix
can override or drop the entry, so this is a default, not an
unconditional grant. Even where the entry is present, the server is only
actually rendered into an agent's claude config when the agent also
holds the execution permission: extra_server_required_permission in
hive-agent/src/mcp_config.rs suppresses it entirely without that
permission, so an agent lacking execution gets no mcp__bash__* tools at
all, not just an unused grant. The harness rewrites the config each turn,
so a change applies at the next one. See "Relationship to the
execution permission" below.
Tools
run(cmd, timeout_secs?, wait_seconds?, name?)
Submit a shell command for background execution (runs via bash).
Stdout and stderr stream to harness/bash-tasks/<id>.{out,err}.
When the task completes (or times out, or the process errors), it
surfaces as a todo in the agent's loose-ends (via get_loose_ends),
carrying the exit code and a Read(<path>) pointer to the captured
output. Handle it on a future turn — unless wait_seconds already
delivered the terminal result inline, in which case the daemon creates
no todo (see status below).
timeout_secs— kill the task after N seconds and mark ittimed_out. Omit for no timeout (runs until natural exit).wait_seconds— inline poll before returning (capped at 30). When the task finishes within the window the full status is returned immediately and the daemon creates no todo; when the window expires the task keeps running and the daemon returns the normaltask started: id=<id>response. Defaults to 3 — passwait_seconds: 0to disable inline waiting and always get the immediate response.name— optional caller-chosen task id. When set it replaces the autogenerated hex id, so it surfaces instatus(<name>)lookups and the loose-ends list — a memorable label instead of an opaque id. A name is reusable once its previous task has finished; the daemon rejects a name whose task is stillpending/running. Allowed characters:[a-z0-9-](a valid identifier — lowercase, digits, hyphen; max 63). Omit for the autogenerated id.
Exposed as mcp__bash__run.
status(id, wait_seconds?)
Poll the status of a task submitted with run. Returns:
status—pending/running/done/timed_out/interrupted/killedexit_code— set when done- run duration
- last 4 KiB of stdout and stderr (full output in the
.out/.errfiles)
wait_seconds — optional inline poll (capped at 30): when the task
finishes within the window the call returns the full status immediately.
Useful to avoid a separate round-trip when you expect the task to
finish soon.
Any status call (waited or not) that observes a terminal task clears
that task's completion todo — you already have the result in this
response, so no redundant loose-end follows. Narrow best-effort race: a
status/run inline wait that resolves in the same instant the task
actually finishes can still occasionally get both.
Tasks marked interrupted had their process killed by a harness
restart; a best-effort todo is still surfaced so the agent isn't
silently blocked.
Exposed as mcp__bash__status.
kill(id, force?)
Stop a running or pending task by its ID (from run). Fire-and-forget:
sends the signal and returns without waiting — the completion surfaces
in the agent's loose-ends; handle it on a future turn.
force: false(default) — SIGINT to the task's process group (graceful; lets the process clean up). The whole process group is signalled, so children spawned by the shell (cargo, nix, etc.) are also stopped.force: true— SIGKILL.
If a SIGINT'd task doesn't exit, call kill again with force: true.
The daemon cancels a still-pending task before it starts. The task ends as
killed and surfaces in the loose-ends like any completion.
Exposed as mcp__bash__kill.
Namespace note
run and status live in the bash MCP server, not hyperhive. The
tool names in claude are mcp__bash__run and mcp__bash__status.
hyperhive blocks the Bash built-in tool — all shell execution goes through
this structured path so tasks get task-id tracking and structured output.
Architecture
hive-bash-daemon is a single long-running process (one per agent
container, systemd service in nix/agent-modules/mcp.nix) — no stdio
bridge. It owns subprocess management, output file writing, todo delivery
on the harness's in-agent socket, and serves the run/status/kill
MCP tools directly over streamable-http on services.hyperhive.agent.mcp.bashHttpPort
(declared in services.hyperhive.agent.extraMcpServers.bash as { type = "http"; url = ...; }). Same shape as the built-in hyperhive surface (hive-mcp-http)
— claude reconnects to the stable URL every turn instead of respawning a
stdio child, so there's no per-turn MCP re-registration race and no
round-trip socket hop for tool calls.
Completion as a todo (loose-ends v2)
When a bash task changes state, hive-bash-daemon upserts a single keyed
todo (key = task id) on the harness's in-agent socket (HIVE_AGENT_SOCKET)
— "running" at start, then the completion summary when it finishes. The
summary change signals the harness turn loop directly (in-process, no broker
round-trip), so the harness drives a turn for the agent to handle it via
get_loose_ends, then clears the todo with cancel_loose_end(kind: "todo", id: N) (dials the
in-container socket directly — no bash task involved, so clearing doesn't
spawn another todo). Same mechanism the matrix daemon uses for
unread rooms. An inline wait_seconds / status observation that already
delivered the result instead clears the keyed todo, so no redundant
loose-end follows.
Relationship to the execution permission
Permission::Execution (in Permission::DEFAULT) is the real gate on bash:
extra_server_required_permission (in hive-agent/src/mcp_config.rs)
controls whether the bash MCP server reaches the agent's claude
config at all, suppressing every mcp__bash__* tool
when absent — not just leaving them unused. Its own tools() returns
&[]: mcp__bash__run/status/kill live on the separate bash MCP
server that extra_server_required_permission gates, not on the
mcp__hyperhive__* allowlist tools() builds. Removing execution
from an agent's permissions does remove bash availability.