docs/tools: the bash and matrix pages say what the tools do around the edges, not how to call them
Each page opens with its audience (the operator). The per-parameter tool listings, which repeat the tools' own MCP descriptions, become short summaries of the behaviour an operator needs: the completion todo, what status clears, the process-group kill, the unread guard, how invites resolve. The wiring and architecture sections are unchanged.
This commit is contained in:
parent
209a286d7c
commit
2d1b6a4e1a
2 changed files with 56 additions and 137 deletions
|
|
@ -1,5 +1,8 @@
|
||||||
# Bash execution tools
|
# Bash execution tools
|
||||||
|
|
||||||
|
For the operator: this page covers the daemon as deployed and what the tools do
|
||||||
|
around the edges. How an agent calls them is in the tools' own MCP descriptions.
|
||||||
|
|
||||||
Background shell execution via `hive-bash-daemon`. Tools land as
|
Background shell execution via `hive-bash-daemon`. Tools land as
|
||||||
`mcp__bash__<tool>` (the MCP server name is `bash`, not `hyperhive`).
|
`mcp__bash__<tool>` (the MCP server name is `bash`, not `hyperhive`).
|
||||||
`nix/agent-modules/mcp.nix` registers bash into `services.hyperhive.agent.extraMcpServers`
|
`nix/agent-modules/mcp.nix` registers bash into `services.hyperhive.agent.extraMcpServers`
|
||||||
|
|
@ -16,78 +19,30 @@ so a change applies at the next one. See "Relationship to the
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
### `run(cmd, timeout_secs?, wait_seconds?, name?)`
|
Each tool's parameters and return values are in its own MCP description, which
|
||||||
|
is what an agent reads at call time. This section says what the tools do around
|
||||||
|
the edges.
|
||||||
|
|
||||||
Submit a shell command for background execution (runs via `bash`).
|
- **`run`** submits a shell command (through `bash`) as a background task. Its
|
||||||
Stdout and stderr stream to `harness/bash-tasks/<id>.{out,err}`.
|
stdout and stderr stream to `harness/bash-tasks/<id>.{out,err}`. When the
|
||||||
When the task completes (or times out, or the process errors), it
|
task finishes, times out or its process errors, it surfaces as a todo in the
|
||||||
surfaces as a todo in the agent's loose-ends (via `get_loose_ends`),
|
agent's loose-ends, with the exit code and a pointer to the captured output.
|
||||||
carrying the exit code and a `Read(<path>)` pointer to the captured
|
`run` waits up to 3 seconds (30 at most) for a result before it returns; a
|
||||||
output. Handle it on a future turn — unless `wait_seconds` already
|
result delivered that way makes no todo. A caller-chosen `name` replaces the
|
||||||
delivered the terminal result inline, in which case the daemon creates
|
generated id. The daemon accepts the name again once its task has finished,
|
||||||
no todo (see `status` below).
|
and rejects it while the task is still pending or running.
|
||||||
|
- **`status`** polls a task: its state (`pending`, `running`, `done`,
|
||||||
- `timeout_secs` — kill the task after N seconds and mark it
|
`timed_out`, `interrupted` or `killed`), the exit code, the run time and the last
|
||||||
`timed_out`. Omit for no timeout (runs until natural exit).
|
4 KiB of each stream. A call that sees a finished task clears its completion
|
||||||
- `wait_seconds` — inline poll before returning (capped at 30).
|
todo, since the agent already has the result; in a narrow race both can still
|
||||||
When the task finishes within the window the full status is
|
appear. A task marked `interrupted` had its process killed by a harness
|
||||||
returned immediately and the daemon creates no todo; when the window expires
|
restart, and a best-effort todo is still made so the agent isn't blocked
|
||||||
the task keeps running and the daemon returns the normal
|
silently.
|
||||||
`task started: id=<id>` response. **Defaults to 3** — pass `wait_seconds: 0`
|
- **`kill`** stops a running or pending task and returns without waiting. The
|
||||||
to disable inline waiting and always get the immediate response.
|
completion surfaces as a todo like any other. It signals the task's whole
|
||||||
- `name` — optional caller-chosen task id. When set it replaces the
|
process group, so children the shell started stop too: SIGINT by default,
|
||||||
autogenerated hex id, so it surfaces in `status(<name>)` lookups and
|
SIGKILL with `force`. The daemon cancels a task still pending before it
|
||||||
the loose-ends list — a memorable label instead of an opaque id. A name
|
starts, and the task ends as `killed`.
|
||||||
is **reusable once its previous task has finished**; the daemon
|
|
||||||
rejects a name whose task is still `pending`/`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` / `killed`
|
|
||||||
- `exit_code` — set when done
|
|
||||||
- run duration
|
|
||||||
- last 4 KiB of stdout and stderr (full output in the `.out` / `.err` files)
|
|
||||||
|
|
||||||
`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
|
## Namespace note
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
# Matrix MCP tools and extra MCP servers
|
# Matrix MCP tools and extra MCP servers
|
||||||
|
|
||||||
|
For the operator: this page covers how the harness adds the matrix tools and what
|
||||||
|
they do around the edges. How an agent calls them is in the tools' own MCP
|
||||||
|
descriptions.
|
||||||
|
|
||||||
## Built-in matrix MCP (`mcp__matrix__*`)
|
## Built-in matrix MCP (`mcp__matrix__*`)
|
||||||
|
|
||||||
When the agent has at least one `services.hyperhive.agent.matrixAccounts`
|
When the agent has at least one `services.hyperhive.agent.matrixAccounts`
|
||||||
|
|
@ -9,71 +13,31 @@ see Architecture below). No `matrix.enable` switch exists: the account
|
||||||
set *is* the switch, and the module declares the hive-internal `main`
|
set *is* the switch, and the module declares the hive-internal `main`
|
||||||
account for you as soon as `services.hyperhive.agent.matrix.url` is
|
account for you as soon as `services.hyperhive.agent.matrix.url` is
|
||||||
non-null — which hive-c0re fills in per agent whenever the operator
|
non-null — which hive-c0re fills in per agent whenever the operator
|
||||||
configures the host-level matrix tuwunel. Tools land as `mcp__matrix__<name>`:
|
configures the host-level matrix tuwunel. Tools land as `mcp__matrix__<name>`.
|
||||||
|
|
||||||
### Messaging
|
Each tool's parameters and return values are in its own MCP description, which
|
||||||
|
is what an agent reads at call time. They fall into four groups:
|
||||||
|
|
||||||
**Unread guard**: `send_message`, `send_dm`, `send_file`, and
|
- **Messaging:** `send_message`, `send_dm`, `send_reply`, `send_reaction`,
|
||||||
`send_reply` are all rejected if the room has unread messages — call
|
`send_file`, `send_redact` and `open_dm`. A room is `!id:server` or
|
||||||
`read_room` then `mark_read` on the latest event before sending to a
|
`#alias:server`, and the daemon resolves either. `open_dm` finds or creates the
|
||||||
room you haven't read yet.
|
DM room and returns its id without sending anything, for a case where
|
||||||
|
`send_dm`'s body is inconvenient, such as a file attachment. `send_file` caps
|
||||||
|
an upload at 50 MiB. Redacting someone else's event needs moderator power.
|
||||||
|
- **Reading:** `read_room`, `download_file`, `list_rooms` and
|
||||||
|
`list_room_members`. `read_room` shows media as `[file: …]`, `[image: …]`,
|
||||||
|
`[audio: …]` or `[video: …]` markers, and the event id of one goes to
|
||||||
|
`download_file`.
|
||||||
|
- **Room membership:** `invite_user`, `list_invites`, `resolve_invite` and
|
||||||
|
`join_room`. `resolve_invite` accepts or rejects a pending invite and is the
|
||||||
|
path for invites; `join_room` is for a public room the agent wasn't invited to.
|
||||||
|
`list_invites` gives the inviter and the topic, so an agent can judge an
|
||||||
|
invite before it answers.
|
||||||
|
- **Receipts:** `mark_read` advances the read receipt.
|
||||||
|
|
||||||
- `send_message(room, body)` — send a markdown message; `room`
|
**Unread guard.** `send_message`, `send_dm`, `send_file` and `send_reply` fail
|
||||||
accepts `!id:server` or `#alias:server`, daemon resolves either
|
while the room has unread messages. The agent calls `read_room` and then
|
||||||
- `send_dm(user_id, body)` — open (or reuse) the DM room with
|
`mark_read` on the latest event before sending to a room it hasn't read.
|
||||||
`user_id` and post `body` to it
|
|
||||||
- `open_dm(user_id)` — resolve (find-or-create) the DM room with
|
|
||||||
`user_id` and return its room id **without sending anything**.
|
|
||||||
Use the returned id with room-based tools (`send_message`,
|
|
||||||
`send_file`, …) to deliver into a DM when `send_dm`'s body
|
|
||||||
parameter is inconvenient (for example for file attachments)
|
|
||||||
- `send_reply(room, event_id, body)` — threaded reply to a specific
|
|
||||||
event
|
|
||||||
- `send_reaction(room, event_id, key)` — react to a message with an
|
|
||||||
emoji key
|
|
||||||
- `send_file(room, path, caption?)` — upload a local file and post it
|
|
||||||
as a room attachment, inferring the MIME type from the file extension
|
|
||||||
(50 MiB cap); an optional `caption` follows as a separate text message
|
|
||||||
- `send_redact(room, event_id, reason?)` — redact (delete) a specific
|
|
||||||
event; works on your own events, redacting others' requires moderator
|
|
||||||
power level
|
|
||||||
|
|
||||||
### Reading
|
|
||||||
|
|
||||||
- `read_room(room, limit?)` — recent timeline events; media attachments
|
|
||||||
appear inline as `[file: name]`, `[image: name]`, `[audio: name]`,
|
|
||||||
or `[video: name]` markers — pass the event id to `download_file`
|
|
||||||
to retrieve the attachment
|
|
||||||
- `download_file(room, event_id, dest_path?)` — download a media
|
|
||||||
attachment from a message event to a local file and return its path;
|
|
||||||
pair with `read_room` which surfaces attachments as the markers above
|
|
||||||
- `list_rooms()` — enumerate joined rooms
|
|
||||||
(`{ room_id, canonical_alias, name, member_count }` per room)
|
|
||||||
- `list_room_members(room)` — members of a room
|
|
||||||
|
|
||||||
### Room membership
|
|
||||||
|
|
||||||
<!-- vale write-good.Passive = NO -->
|
|
||||||
- `invite_user(room, user_id)` — invite `@user:server` into a room
|
|
||||||
you're already in; you must have a high enough power level.
|
|
||||||
The invitee sees a pending invite and resolves it via
|
|
||||||
`resolve_invite`.
|
|
||||||
- `resolve_invite(room, action)` — accept or reject a pending invite.
|
|
||||||
`action` is `"accept"` (join the room) or `"reject"` (decline and
|
|
||||||
leave). This is the path for invites; `join_room` is for joining a
|
|
||||||
public room you weren't invited to.
|
|
||||||
- `join_room(room)` — join a public room by id or alias. Also accepts
|
|
||||||
a pending invite if one exists, but prefer `resolve_invite` for
|
|
||||||
invites (it can reject too).
|
|
||||||
- `list_invites()` — rooms this agent has been invited to but not
|
|
||||||
yet joined (`{ room_id, canonical_alias, name, inviter, topic }` per room —
|
|
||||||
`inviter` and `topic` are there so an agent has enough to judge an
|
|
||||||
invite before `resolve_invite`ing it, not just a bare room id).
|
|
||||||
<!-- vale write-good.Passive = YES -->
|
|
||||||
|
|
||||||
### Receipts
|
|
||||||
|
|
||||||
- `mark_read(room, event_id)` — advance the read receipt
|
|
||||||
|
|
||||||
## Multiple accounts
|
## Multiple accounts
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue