docs: document the independent subagent daemon (start/continue/interrupt)
This commit is contained in:
parent
c280664d74
commit
e64639c4d0
6 changed files with 165 additions and 14 deletions
|
|
@ -98,6 +98,14 @@ hand-maintained per-file tree drifts out of sync with the code.
|
||||||
directly over streamable-http (no stdio bridge), writes task files
|
directly over streamable-http (no stdio bridge), writes task files
|
||||||
under `/harness/bash-tasks/`, and records the favorite-tools
|
under `/harness/bash-tasks/`, and records the favorite-tools
|
||||||
`bash_commands` stat into turn-stats.sqlite.
|
`bash_commands` stat into turn-stats.sqlite.
|
||||||
|
- **`hive-subagent-mcp/`** — per-agent claude-subagent runner daemon
|
||||||
|
(`hive-subagent-daemon`); spawns nested claude sessions on request and
|
||||||
|
serves `start`/`continue`/`interrupt` directly over streamable-http (no
|
||||||
|
stdio bridge). Independent of `hive-bash-mcp` (a subagent is a much
|
||||||
|
heavier capability than a bash command). No task files — the daemon's
|
||||||
|
only state is an in-memory map of currently-running processes, live
|
||||||
|
only as long as the process is; the actual claude session survives a
|
||||||
|
daemon restart independently (see `session.rs`'s module doc).
|
||||||
- **`hive-sh4re/`** — shared wire types (Agent / Manager request +
|
- **`hive-sh4re/`** — shared wire types (Agent / Manager request +
|
||||||
response, `Message`, `Approval`, `HelperEvent`) used across the unix
|
response, `Message`, `Approval`, `HelperEvent`) used across the unix
|
||||||
sockets. Host-admin-socket and hive-priv-socket wire types have been
|
sockets. Host-admin-socket and hive-priv-socket wire types have been
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ name: claude-subagents
|
||||||
description: Spin up a short-lived headless `claude` sub-instance to grind through a well-scoped, mechanical batch (bulk relabeling, a repetitive find/replace, a mechanical migration) instead of burning your own context on it inline. Use this when a task has a clear, describable recipe and is either big enough to eat your context or long enough that you'd rather not babysit it. Not for judgement-heavy work, anything needing operator back-and-forth, or a task whose blast radius you can't bound up front.
|
description: Spin up a short-lived headless `claude` sub-instance to grind through a well-scoped, mechanical batch (bulk relabeling, a repetitive find/replace, a mechanical migration) instead of burning your own context on it inline. Use this when a task has a clear, describable recipe and is either big enough to eat your context or long enough that you'd rather not babysit it. Not for judgement-heavy work, anything needing operator back-and-forth, or a task whose blast radius you can't bound up front.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Ephemeral Sub-Agents via the Claude CLI
|
# Ephemeral Sub-Agents
|
||||||
|
|
||||||
`claude` is on `PATH` in your container, and a sub-instance you spawn
|
A sub-instance you spawn inherits the same filesystem and credentials you
|
||||||
inherits the same filesystem and credentials you have. This is a
|
have. This is a first-class tool for offloading a bounded, mechanical
|
||||||
first-class tool for offloading a bounded, mechanical batch - not a hack.
|
batch - not a hack.
|
||||||
|
|
||||||
## When to use it
|
## When to use it
|
||||||
|
|
||||||
|
|
@ -18,7 +18,36 @@ first-class tool for offloading a bounded, mechanical batch - not a hack.
|
||||||
- **No:** judgement-heavy work, anything needing back-and-forth with a
|
- **No:** judgement-heavy work, anything needing back-and-forth with a
|
||||||
human, or a task whose blast radius you can't bound up front.
|
human, or a task whose blast radius you can't bound up front.
|
||||||
|
|
||||||
## The spawn command
|
## The `subagent` MCP tools
|
||||||
|
|
||||||
|
Your container's `subagent` MCP server (`hive-subagent-daemon`) is this
|
||||||
|
skill's recipe as MCP calls — no manual shell-wrapping, no hand-rolled
|
||||||
|
backgrounding:
|
||||||
|
|
||||||
|
```
|
||||||
|
start(name, prompt_file, model?, trigger?)
|
||||||
|
continue(name, prompt, model?)
|
||||||
|
interrupt(name, force?)
|
||||||
|
```
|
||||||
|
|
||||||
|
`name` ↔ `--name`, `model` ↔ `--model`, `prompt_file` ↔
|
||||||
|
`--append-system-prompt-file`, `trigger`/`prompt` ↔ `-p`. `start` returns
|
||||||
|
once the process is confirmed running, not once it finishes — a
|
||||||
|
completion lands as a todo (`get_loose_ends`), same as any other producer.
|
||||||
|
There's no `status` poll: check on a subagent by `continue`-ing it (refused
|
||||||
|
while it's still running, which is itself the "still going" signal) or by
|
||||||
|
reading its completion todo. `interrupt` genuinely stops a running turn
|
||||||
|
(`force: true` for SIGKILL) — unlike bash tasks' `kill`, this isn't
|
||||||
|
limited to a still-pending state.
|
||||||
|
|
||||||
|
Everything else in this skill (model choice, prompt hygiene, splitting
|
||||||
|
big batches, verify-then-report) applies unchanged; only the launch
|
||||||
|
mechanics differ from a raw shell invocation.
|
||||||
|
|
||||||
|
No `subagent` MCP server on this container? Fall back to the manual
|
||||||
|
command below — same recipe, you drive the backgrounding yourself.
|
||||||
|
|
||||||
|
## The spawn command (manual fallback)
|
||||||
|
|
||||||
Put the task recipe in a file and pass it as a path, with a short `-p`
|
Put the task recipe in a file and pass it as a path, with a short `-p`
|
||||||
trigger:
|
trigger:
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@ debug agent behavior.
|
||||||
|
|
||||||
- **[bash](bash.md)** — background shell execution (`mcp__bash__*`),
|
- **[bash](bash.md)** — background shell execution (`mcp__bash__*`),
|
||||||
available on every agent unconditionally.
|
available on every agent unconditionally.
|
||||||
|
- **[subagent](subagent.md)** — spawn nested headless claude sessions
|
||||||
|
(`mcp__subagent__{start,continue,interrupt}`), shipped default-on for
|
||||||
|
every agent today alongside `bash` (expected to become a real opt-in
|
||||||
|
capability later).
|
||||||
- **[forge](forge.md)** — the `hive-forge` Forgejo CLI every agent has
|
- **[forge](forge.md)** — the `hive-forge` Forgejo CLI every agent has
|
||||||
for issues, PRs, and comments. Not an MCP tool — a binary agents
|
for issues, PRs, and comments. Not an MCP tool — a binary agents
|
||||||
shell out to instead of ad-hoc curl.
|
shell out to instead of ad-hoc curl.
|
||||||
|
|
|
||||||
|
|
@ -93,15 +93,20 @@ this structured path so tasks get task-id tracking and structured output.
|
||||||
|
|
||||||
`hive-bash-daemon` is a single long-running process (one per agent
|
`hive-bash-daemon` is a single long-running process (one per agent
|
||||||
container, systemd service in `nix/agent-modules/mcp.nix`) — no stdio
|
container, systemd service in `nix/agent-modules/mcp.nix`) — no stdio
|
||||||
bridge, no separate bin. It owns subprocess management, output file
|
bridge. It owns subprocess management, output file writing, todo delivery
|
||||||
writing, todo delivery on the harness's in-agent socket, **and** serves
|
on the harness's in-agent socket, **and** serves the `run`/`status`/`kill`
|
||||||
the `run`/`status`/`kill` MCP tools directly over streamable-http on
|
MCP tools directly over streamable-http on `hyperhive.mcp.bashHttpPort`
|
||||||
`hyperhive.mcp.bashHttpPort` (declared in `hyperhive.extraMcpServers.bash`
|
(declared in `hyperhive.extraMcpServers.bash` as `{ type = "http"; url =
|
||||||
as `{ type = "http"; url = ...; }`). Same shape as the built-in
|
...; }`). Same shape as the built-in `hyperhive` surface (`hive-mcp-http`)
|
||||||
`hyperhive` surface (`hive-mcp-http`) — claude reconnects to the stable
|
— claude reconnects to the stable URL every turn instead of respawning a
|
||||||
URL every turn instead of respawning a stdio child, so there's no
|
stdio child, so there's no per-turn MCP re-registration race and no
|
||||||
per-turn MCP re-registration race and no round-trip socket hop for tool
|
round-trip socket hop for tool calls.
|
||||||
calls.
|
|
||||||
|
Independent daemon (own crate, own process/systemd unit/MCP server
|
||||||
|
`subagent`): `hive-subagent-daemon` — see [`subagent.md`](subagent.md).
|
||||||
|
Spawns nested claude sessions rather than shell commands, so it keeps no
|
||||||
|
task files here or anywhere else; a subagent task never shows up in this
|
||||||
|
daemon's `status`/`kill` or the running-tasks panel.
|
||||||
|
|
||||||
### Completion as a todo (loose-ends v2)
|
### Completion as a todo (loose-ends v2)
|
||||||
|
|
||||||
|
|
|
||||||
102
docs/tools/subagent.md
Normal file
102
docs/tools/subagent.md
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
# Subagent tools
|
||||||
|
|
||||||
|
Spawns nested headless `claude` sessions via `hive-subagent-daemon`. Tools
|
||||||
|
land as `mcp__subagent__<tool>` (the MCP server name is `subagent`, not
|
||||||
|
`hyperhive`). Shipped default-on for every agent today —
|
||||||
|
`nix/agent-modules/mcp.nix` always injects `subagent` into
|
||||||
|
`hyperhive.extraMcpServers` (with `allowedTools = ["*"]`), same as `bash`.
|
||||||
|
The operator's own framing: shipped default-on for now, expected to become
|
||||||
|
a real opt-in capability later (not built yet).
|
||||||
|
|
||||||
|
See the `base:claude-subagents` skill for _when_ to reach for this
|
||||||
|
(mechanical, bounded batches) versus doing the work inline.
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
### `start(name, prompt_file, model?, trigger?)`
|
||||||
|
|
||||||
|
Start a fresh subagent session under `name`, running in the background.
|
||||||
|
Returns as soon as the process is confirmed running — not once it
|
||||||
|
finishes.
|
||||||
|
|
||||||
|
- `name` — session display name: claude's own `--name`/`--resume` session
|
||||||
|
title, and this daemon's tracking key while the process is alive.
|
||||||
|
`[a-z0-9-]`, max 63 chars. Reusable once a prior session under that name
|
||||||
|
has _finished_; refused while one is still running.
|
||||||
|
- `prompt_file` — path to a file passed as `--append-system-prompt-file`,
|
||||||
|
the subagent's actual task instructions. A file, not an inline string,
|
||||||
|
to avoid `ARG_MAX` on a large recipe.
|
||||||
|
- `model` — `--model` for the subagent's own claude invocation. Omit for
|
||||||
|
claude's own default.
|
||||||
|
- `trigger` — written to the subagent's stdin as its first turn's prompt.
|
||||||
|
Defaults to a generic "carry out your instructions" nudge.
|
||||||
|
|
||||||
|
A prior _finished_ session under the same name is archived first (renamed
|
||||||
|
`.jsonl.archived`, not deleted) — a real fresh start, not a silent resume
|
||||||
|
of old history.
|
||||||
|
|
||||||
|
Exposed as `mcp__subagent__start`.
|
||||||
|
|
||||||
|
### `continue(name, prompt, model?)`
|
||||||
|
|
||||||
|
Give an existing named session a new turn — whether that means "the
|
||||||
|
previous turn finished, here's a follow-up instruction" or "the daemon
|
||||||
|
restarted, reattaching to a session that survived it independently."
|
||||||
|
Returns as soon as confirmed running, same as `start`. Refused for a name
|
||||||
|
with no session on disk at all, or one currently running.
|
||||||
|
|
||||||
|
- `name` — the existing session's name (from a prior `start`).
|
||||||
|
- `prompt` — the new turn's prompt.
|
||||||
|
- `model` — `--model` for this turn; doesn't have to match whatever
|
||||||
|
`start` used.
|
||||||
|
|
||||||
|
Exposed as `mcp__subagent__continue`.
|
||||||
|
|
||||||
|
### `interrupt(name, force?)`
|
||||||
|
|
||||||
|
Signal `name`'s currently-running process. Only works while it's actually
|
||||||
|
running — there's no queued/pending state to cancel pre-emptively.
|
||||||
|
|
||||||
|
- `force: false` (default) — SIGINT, letting claude shut down cleanly if
|
||||||
|
it's mid-response.
|
||||||
|
- `force: true` — SIGKILL.
|
||||||
|
|
||||||
|
Exposed as `mcp__subagent__interrupt`.
|
||||||
|
|
||||||
|
Always runs with `--dangerously-skip-permissions --strict-mcp-config` (no
|
||||||
|
`--mcp-config` override — a safety property, not a knob).
|
||||||
|
|
||||||
|
## Checking on a subagent
|
||||||
|
|
||||||
|
There's no `status` tool. A subagent's own claude session — not a
|
||||||
|
parallel log this daemon writes — is the record of what it did; read that
|
||||||
|
the same way you'd catch up on any other agent, by asking it directly
|
||||||
|
(`continue` with a prompt). The daemon pushes exactly one todo per
|
||||||
|
session lifetime: when a turn finishes, its completion summary lands via
|
||||||
|
`get_loose_ends` like any other producer's todo. `continue` on a name
|
||||||
|
that's still running is refused (with that as the tell it's still going)
|
||||||
|
rather than queuing behind it.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
`hive-subagent-daemon` (own crate, `hive-subagent-mcp`) is independent of
|
||||||
|
`hive-bash-daemon` — a subagent spawns a full nested `claude` process, a
|
||||||
|
materially heavier capability than a bash command, worth its own
|
||||||
|
deployable/restartable unit. Own systemd unit, own streamable-http
|
||||||
|
listener on `hyperhive.mcp.subagentHttpPort`.
|
||||||
|
|
||||||
|
**No task files.** The daemon's only state is an in-memory map of
|
||||||
|
currently-running processes (`name -> Cancel`), live only as long as the
|
||||||
|
process is — a restart stops whatever's running rather than adopting it.
|
||||||
|
The durable record of a subagent's existence is claude's own on-disk
|
||||||
|
session, found again by name via `hive_claude::SessionStore`; that's what
|
||||||
|
`continue` reattaches to, restart or not.
|
||||||
|
|
||||||
|
**No mid-turn compaction.** Built on `hive_claude::Claude::spawn` +
|
||||||
|
`RunningClaude::wait` directly, not `InfiniteSession::run` — the latter
|
||||||
|
has no cancel handle to reach in from the outside, which is what would
|
||||||
|
make `interrupt` a no-op. The trade: a turn that overflows the context
|
||||||
|
window surfaces as a plain error instead of self-healing via reactive
|
||||||
|
compaction. Subagents are meant to be bounded, single-batch work (see the
|
||||||
|
`base:claude-subagents` skill), not sessions long-lived enough to need
|
||||||
|
in-place compaction — a real follow-up if that assumption stops holding.
|
||||||
|
|
@ -128,6 +128,9 @@ hive_name?, swarm_name?, matrix_accounts? }`. `matrix_accounts` is a
|
||||||
|
|
||||||
- **Bash execution** (`execution`) — background shell tasks. See
|
- **Bash execution** (`execution`) — background shell tasks. See
|
||||||
[`docs/tools/bash.md`](../tools/bash.md).
|
[`docs/tools/bash.md`](../tools/bash.md).
|
||||||
|
- **Subagent spawning** — headless claude sub-instances as background
|
||||||
|
tasks, shipped default-on like bash execution (no tool group gates it
|
||||||
|
yet). See [`docs/tools/subagent.md`](../tools/subagent.md).
|
||||||
- **Lifecycle + config** (`lifecycle`, `approvals`) — manage child
|
- **Lifecycle + config** (`lifecycle`, `approvals`) — manage child
|
||||||
agents, spawn new ones, apply config commits. See
|
agents, spawn new ones, apply config commits. See
|
||||||
[`docs/tools/lifecycle.md`](../tools/lifecycle.md).
|
[`docs/tools/lifecycle.md`](../tools/lifecycle.md).
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue