docs: update prompts for two-step spawn + in_reply_to; trim CLAUDE.md to pointer index
This commit is contained in:
parent
69604407a9
commit
bac7dd6cde
3 changed files with 28 additions and 453 deletions
471
CLAUDE.md
471
CLAUDE.md
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
Hey claude. This is your starting page. The detailed docs live in
|
Hey claude. This is your starting page. The detailed docs live in
|
||||||
[`docs/`](docs/) and are written for humans + you both — read them
|
[`docs/`](docs/) and are written for humans + you both — read them
|
||||||
when you need depth on a subsystem. This file is the index +
|
when you need depth on a subsystem. This file is the index.
|
||||||
scratchpad.
|
|
||||||
|
|
||||||
- High-level project intro: **[README.md](README.md)**.
|
- High-level project intro: **[README.md](README.md)**.
|
||||||
- Open work + backlog: the **[forge issue
|
- Open work + backlog: the **[forge issue
|
||||||
|
|
@ -25,8 +24,7 @@ hive-c0re/ host daemon + CLI (one binary, subcommand-dispatched)
|
||||||
src/agent_server.rs per-sub-agent socket listener (long-poll Recv)
|
src/agent_server.rs per-sub-agent socket listener (long-poll Recv)
|
||||||
src/broker.rs sqlite Message store + intra-process broadcast
|
src/broker.rs sqlite Message store + intra-process broadcast
|
||||||
channel (`MessageEvent`) for `recv_blocking_batch` +
|
channel (`MessageEvent`) for `recv_blocking_batch` +
|
||||||
the dashboard forwarder; hourly vacuum of
|
the dashboard forwarder; hourly vacuum of acked>30d
|
||||||
acked>30d
|
|
||||||
src/dashboard_events.rs unified wire-facing event channel feeding
|
src/dashboard_events.rs unified wire-facing event channel feeding
|
||||||
`/dashboard/stream`. Carries broker `Sent` /
|
`/dashboard/stream`. Carries broker `Sent` /
|
||||||
`Delivered` (mirrored by the forwarder task
|
`Delivered` (mirrored by the forwarder task
|
||||||
|
|
@ -111,7 +109,8 @@ hive-ag3nt/ in-container harness crate; produces TWO binaries
|
||||||
src/lib.rs re-exports + DEFAULT_SOCKET, DEFAULT_WEB_PORT
|
src/lib.rs re-exports + DEFAULT_SOCKET, DEFAULT_WEB_PORT
|
||||||
src/client.rs generic JSON-line request/response over unix socket
|
src/client.rs generic JSON-line request/response over unix socket
|
||||||
src/web_ui.rs per-container axum HTTP page (incl /api/cancel,
|
src/web_ui.rs per-container axum HTTP page (incl /api/cancel,
|
||||||
/api/compact, /api/model, /events/history)
|
/api/compact, /api/model, /events/history,
|
||||||
|
/screen, /screen/ws)
|
||||||
src/turn_stats.rs per-turn analytics sink (one sqlite row per
|
src/turn_stats.rs per-turn analytics sink (one sqlite row per
|
||||||
turn at /state/hyperhive-turn-stats.sqlite);
|
turn at /state/hyperhive-turn-stats.sqlite);
|
||||||
schema + best-effort writer
|
schema + best-effort writer
|
||||||
|
|
@ -121,14 +120,15 @@ hive-ag3nt/ in-container harness crate; produces TWO binaries
|
||||||
src/events.rs LiveEvent + broadcast Bus + sqlite-backed history
|
src/events.rs LiveEvent + broadcast Bus + sqlite-backed history
|
||||||
(/state/hyperhive-events.sqlite) + TurnState +
|
(/state/hyperhive-events.sqlite) + TurnState +
|
||||||
model selection (persisted at /state/hyperhive-model)
|
model selection (persisted at /state/hyperhive-model)
|
||||||
src/turn.rs claude --print + stream-json pump; --compact retry
|
src/turn.rs claude --print + stream-json pump; --compact retry;
|
||||||
|
proactive compaction + auto session-reset
|
||||||
src/mcp.rs embedded MCP server (rmcp): AgentServer + ManagerServer
|
src/mcp.rs embedded MCP server (rmcp): AgentServer + ManagerServer
|
||||||
src/login.rs probe /root/.claude/ for a valid session
|
src/login.rs probe /root/.claude/ for a valid session
|
||||||
src/login_session.rs drives `claude auth login` over stdio pipes
|
src/login_session.rs drives `claude auth login` over stdio pipes
|
||||||
src/bin/hive-ag3nt.rs sub-agent main (Serve + Mcp subcommands)
|
src/bin/hive-ag3nt.rs sub-agent main (Serve + Mcp subcommands)
|
||||||
src/bin/hive-m1nd.rs manager main (Serve + Mcp subcommands)
|
src/bin/hive-m1nd.rs manager main (Serve + Mcp subcommands)
|
||||||
assets/ index.html, agent.css, app.js, stats.html,
|
assets/ index.html, agent.css, app.js, stats.html,
|
||||||
stats.js (include_str!)
|
stats.js, screen.html (include_str!)
|
||||||
prompts/ static role/tools/settings for claude (include_str!):
|
prompts/ static role/tools/settings for claude (include_str!):
|
||||||
agent.md — sub-agent system prompt
|
agent.md — sub-agent system prompt
|
||||||
manager.md — manager system prompt
|
manager.md — manager system prompt
|
||||||
|
|
@ -141,22 +141,27 @@ nix/
|
||||||
modules/hive-c0re.nix systemd service + firewall + git wiring;
|
modules/hive-c0re.nix systemd service + firewall + git wiring;
|
||||||
imports hive-forge.nix
|
imports hive-forge.nix
|
||||||
modules/hive-forge.nix optional in-container Forgejo
|
modules/hive-forge.nix optional in-container Forgejo
|
||||||
(`hyperhive.forge.enable`, default on)
|
(`hyperhive.forge.enable`, default on);
|
||||||
|
Catppuccin Mocha theme via tmpfiles C+ copy
|
||||||
templates/harness-base.nix shared scaffolding for sub-agents + manager
|
templates/harness-base.nix shared scaffolding for sub-agents + manager
|
||||||
templates/agent-base.nix sub-agent nixosConfiguration
|
templates/agent-base.nix sub-agent nixosConfiguration
|
||||||
templates/manager.nix manager nixosConfiguration
|
templates/manager.nix manager nixosConfiguration
|
||||||
templates/weston-vnc.nix optional `hyperhive.gui.enable`
|
templates/weston-vnc.nix optional `hyperhive.gui.enable`
|
||||||
— weston + VNC backend systemd unit; writes
|
— weston + VNC backend systemd unit; writes
|
||||||
/etc/hyperhive/gui.json (vnc_port + auth) for
|
/etc/hyperhive/gui.json (vnc_port + auth) for
|
||||||
the harness WebSocket relay (issue #51)
|
the harness WebSocket relay (/screen/ws)
|
||||||
|
forge-theme/theme-catppuccin-vibec0re.css Catppuccin Mocha forge theme
|
||||||
|
|
||||||
docs/
|
docs/
|
||||||
conventions.md naming, identity=socket, async forms, commit style
|
conventions.md naming, identity=socket, async forms, commit style
|
||||||
gotchas.md NixOS / nspawn lessons learned the hard way
|
gotchas.md NixOS / nspawn quirks and lessons learned
|
||||||
web-ui.md dashboard + per-agent page layouts and endpoints
|
web-ui.md dashboard + per-agent page layouts and endpoints
|
||||||
turn-loop.md claude invocation, wake prompt, MCP tool surface
|
turn-loop.md claude invocation, wake prompt, MCP tool surface
|
||||||
approvals.md approval flow, manager policy, helper events
|
approvals.md approval flow, manager policy, helper events
|
||||||
persistence.md sqlite dbs, retention, state dir layout
|
persistence.md sqlite dbs, retention, state dir layout
|
||||||
|
terminal-rendering.md per-agent terminal row taxonomy (as built)
|
||||||
|
boundary.md operator/agent trust model rationale
|
||||||
|
damocles-migration.md future migration plan for damocles → hyperhive
|
||||||
```
|
```
|
||||||
|
|
||||||
## Reading paths
|
## Reading paths
|
||||||
|
|
@ -167,9 +172,7 @@ read them à la carte.
|
||||||
- **"What does the dashboard look like?"** →
|
- **"What does the dashboard look like?"** →
|
||||||
[`docs/web-ui.md`](docs/web-ui.md).
|
[`docs/web-ui.md`](docs/web-ui.md).
|
||||||
- **"How does the per-agent terminal classify + colour
|
- **"How does the per-agent terminal classify + colour
|
||||||
events?"** → [`docs/terminal-rendering.md`](docs/terminal-rendering.md)
|
events?"** → [`docs/terminal-rendering.md`](docs/terminal-rendering.md).
|
||||||
(as-built row taxonomy + layout contract + markdown +
|
|
||||||
extra-MCP fallback).
|
|
||||||
- **"How does claude get its prompt and what tools does it have?"** →
|
- **"How does claude get its prompt and what tools does it have?"** →
|
||||||
[`docs/turn-loop.md`](docs/turn-loop.md).
|
[`docs/turn-loop.md`](docs/turn-loop.md).
|
||||||
- **"How do config changes flow from manager to operator to
|
- **"How do config changes flow from manager to operator to
|
||||||
|
|
@ -196,439 +199,9 @@ read them à la carte.
|
||||||
- **Actions are factored** between admin socket and dashboard via
|
- **Actions are factored** between admin socket and dashboard via
|
||||||
`actions.rs` and `dashboard.rs::lifecycle_action`, so the two
|
`actions.rs` and `dashboard.rs::lifecycle_action`, so the two
|
||||||
surfaces never drift.
|
surfaces never drift.
|
||||||
|
- **Two-step spawn:** `request_init_config` → edit `agent.nix` →
|
||||||
## Scratchpad
|
`request_spawn`. Never submit a Spawn approval without first
|
||||||
|
reviewing the config template.
|
||||||
In-flight or recent context that hasn't earned a section yet.
|
- **Rate-limit sentinel:** `{state_dir}/hyperhive-rate-limited`
|
||||||
Prune freely.
|
is written by the harness on 429 and cleared on retry.
|
||||||
|
`ContainerView.rate_limited` reads it for the dashboard badge.
|
||||||
- **Just landed:** proactive context-size compaction. `turn::
|
|
||||||
drive_turn` now has two compaction paths. The old *reactive*
|
|
||||||
one (claude prints `Prompt is too long` → `/compact` + retry)
|
|
||||||
is unchanged — by then the session is already past the window
|
|
||||||
and no turn can run on it. The new *proactive* one fires after
|
|
||||||
a clean turn whose last-inference context
|
|
||||||
(`Bus::last_ctx_usage().context_tokens()`) crossed a watermark:
|
|
||||||
`drive_turn` injects one synthetic notes-checkpoint turn
|
|
||||||
(`CHECKPOINT_PROMPT` — "flush durable state into /state now")
|
|
||||||
then runs `/compact`, so the agent can persist in-flight state
|
|
||||||
before the detail collapses into a summary. Watermark is
|
|
||||||
`HIVE_COMPACT_WATERMARK_TOKENS` (default 150k, ~75% of a 200k
|
|
||||||
window; `0` disables). New `maybe_checkpoint_and_compact`
|
|
||||||
helper, all best-effort (a failed checkpoint/compact is a Note,
|
|
||||||
never fails the turn). Both ag3nt + m1nd loops get it for free
|
|
||||||
via the shared `drive_turn`. `docs/turn-loop.md` gained a
|
|
||||||
Compaction section.
|
|
||||||
- **Just landed:** dashboard side panel + forge-linked
|
|
||||||
config/approvals + per-bucket model stats. (a) Long content
|
|
||||||
(file previews, approval diffs, journald logs) opens in a
|
|
||||||
right-side slide-in panel instead of expanding inline;
|
|
||||||
markdown `.md` previews render through the vendored `marked`
|
|
||||||
bundle (`/static/marked.js` route added to the dashboard).
|
|
||||||
(b) the container-row `agent.nix` viewer + `/api/agent-config`
|
|
||||||
are gone — each row links to the agent's `agent-configs/<n>`
|
|
||||||
repo on the forge instead. (c) pending approvals render as a
|
|
||||||
card (identity / what-changed / decision actions) with a
|
|
||||||
`commit on forge` deep-link and a 3-way diff base toggle — vs
|
|
||||||
applied / vs last-approved / vs previous proposal — served by
|
|
||||||
the new `/api/approval-diff/{id}?base=` endpoint. (d) the
|
|
||||||
`/stats` page gained a per-bucket turns-by-model stacked bar
|
|
||||||
(`Snapshot.models` + `Bucket.model_counts`) since model
|
|
||||||
choice drives token cost. `/api/state` carries `forge_present`
|
|
||||||
so the links only show when the forge is up. Docs refreshed:
|
|
||||||
web-ui.md, approvals.md, turn-loop.md, agent.md, manager.md.
|
|
||||||
- **Just landed:** `hyperhive.forge.enable` (default **true**) —
|
|
||||||
the forge option moved out of `services.hive-forge.*` into the
|
|
||||||
`hyperhive.*` namespace, and `hive-c0re.nix` imports
|
|
||||||
`hive-forge.nix` so the forge ships with the standard core
|
|
||||||
install. Forgejo settings gained
|
|
||||||
`migrations.ALLOW_LOCALNETWORKS = true` so an in-hive mirror
|
|
||||||
of the hyperhive repo can import from a `localhost` source
|
|
||||||
(Forgejo blocks loopback/RFC-1918 migration sources by
|
|
||||||
default).
|
|
||||||
- **Just landed:** failed `nixos-container update` self-documents.
|
|
||||||
`lifecycle::run` now appends the tail (40 lines) of the target
|
|
||||||
container's own journal to the bail message when an `update`
|
|
||||||
fails. `nixos-container`'s own stderr on a reload-phase failure
|
|
||||||
is terse ("failed to reload container"); the real cause —
|
|
||||||
which unit failed `switch-to-configuration` — lives in the
|
|
||||||
*container* journal. Scoped to `update` (container's still up
|
|
||||||
on the old generation, so `journalctl -M` works); best-effort,
|
|
||||||
appends nothing if the journal can't be read. The manager's
|
|
||||||
`update` tool / rebuild errors now carry the failing-unit
|
|
||||||
detail without a second `get_logs` call.
|
|
||||||
- **Just landed:** `hyperhive.gui.enable` option (replaces
|
|
||||||
`hyperhive.westonRdp.enable`). New
|
|
||||||
`nix/templates/weston-vnc.nix` declares a per-agent bool;
|
|
||||||
enabling it runs weston with the VNC backend as a systemd
|
|
||||||
service (software/pixman render). Port is deterministic:
|
|
||||||
FNV-1a hash of the agent name (from hostname) in
|
|
||||||
[15900, 16799], mirroring `lifecycle::agent_web_port`.
|
|
||||||
The ExecStart wrapper script computes the port, writes
|
|
||||||
`/etc/hyperhive/gui.json` (`{ "vnc_port": N, "auth": "none" }`)
|
|
||||||
for the harness WebSocket relay (issue #51), then execs
|
|
||||||
weston. Imported by `harness-base.nix`; an agent opts in
|
|
||||||
from its own `agent.nix`. `Type = "simple"` so it can never
|
|
||||||
abort `nixos-container update`. A misconfigured weston
|
|
||||||
degrades to a restart loop in `journalctl`, not a blocked
|
|
||||||
rebuild. Old `weston-rdp.nix` / `westonRdp.enable` removed.
|
|
||||||
- **Just landed:** `get_logs` now resolves the machine name.
|
|
||||||
`journalctl -M` wants the *machine* name (`h-gui`), not the
|
|
||||||
logical agent name (`gui`) — `get_logs` was the one manager
|
|
||||||
verb that passed the name straight through instead of mapping
|
|
||||||
it via `lifecycle::container_name()` like Kill/Start/Restart/
|
|
||||||
Update do. Now consistent: pass the plain agent name, hive-c0re
|
|
||||||
resolves `h-<name>` (manager stays `hm1nd`). Tool description +
|
|
||||||
`GetLogs` wire doc updated.
|
|
||||||
- **Just landed:** applied config repos mirrored to the
|
|
||||||
forge. New private `agent-configs` Forgejo org (renamed
|
|
||||||
from the unused `agents` org in `SEEDED_ORGS`); core is the
|
|
||||||
only principal with access (site admin + private repos +
|
|
||||||
agents not members). `forge::push_config(name)` mirrors an
|
|
||||||
agent's hive-c0re-owned applied repo — `main` + every tag
|
|
||||||
(proposal/approved/building/deployed/failed/denied) — to
|
|
||||||
`agent-configs/<name>.git` via `git push --force`. The
|
|
||||||
tokenised URL is passed inline per push, never stored as a
|
|
||||||
named remote: the applied repo is RO-bind-mounted into the
|
|
||||||
manager at `/applied`, so a token in `.git/config` would
|
|
||||||
leak core's admin credential to an agent. Call sites:
|
|
||||||
`forge::ensure_all` (startup, per agent — catches migrate +
|
|
||||||
offline-forge drift), the spawn task in `actions::approve`
|
|
||||||
(+ `ensure_config_repo`), `actions::approve` ApplyCommit
|
|
||||||
branch, `actions::deny` ApplyCommit branch, and
|
|
||||||
`manager_server::submit_apply_commit`. All best-effort
|
|
||||||
(warn + continue). `ensure_repo` refactored to share a
|
|
||||||
`create_repo` helper with the new `ensure_org_repo`.
|
|
||||||
- **Just landed:** answer questions inline from the per-agent
|
|
||||||
web page. Question rows in the loose-ends section grew a
|
|
||||||
textarea + send button; the operator answers as operator by
|
|
||||||
POSTing cross-origin to the core dashboard's
|
|
||||||
`/answer-question/{id}` (CORS shim `with_cors` on that
|
|
||||||
route), never the per-agent socket — keeps the
|
|
||||||
operator-authority path off the agent's own socket. See
|
|
||||||
`docs/boundary.md` for the boundary rationale; the
|
|
||||||
deployment/gateway/privsep work is tracked as `area:ops`
|
|
||||||
forge issues.
|
|
||||||
- **Just landed:** sub-agents get a read-only view of their own
|
|
||||||
config repo. `set_nspawn_flags` now adds
|
|
||||||
`--bind-ro={proposed_dir}:/agents/<name>/config` for every
|
|
||||||
sub-agent container (manager unchanged — it already has the whole
|
|
||||||
`/agents` tree RW). The agent can read `agent.nix` + whatever
|
|
||||||
extra files the manager split the config into, so it can request
|
|
||||||
precise changes from the manager instead of guessing. RO is
|
|
||||||
load-bearing: config edits only ever flow through the manager's
|
|
||||||
proposed repo + the approval queue. `setup_proposed` seeds the
|
|
||||||
dir before spawn reaches `set_nspawn_flags`; a defensive
|
|
||||||
`create_dir_all` keeps a missing repo from becoming a
|
|
||||||
won't-boot container. Takes effect on next rebuild/restart of
|
|
||||||
each existing sub-agent. `agent.md` system prompt + `docs/
|
|
||||||
persistence.md` updated.
|
|
||||||
- **Just landed:** `request_apply_commit` fetch fix. The old
|
|
||||||
`git_fetch_to_tag` built a refspec `<sha>:refs/tags/proposal/<id>`
|
|
||||||
and ran `git fetch <proposed> <sha>:...` — but `git fetch` resolves
|
|
||||||
the left side of a refspec as a remote *ref name*, and a bare
|
|
||||||
commit sha is not one ("couldn't find remote ref ..."). Fetching
|
|
||||||
by sha would need a full 40-hex sha plus
|
|
||||||
`uploadpack.allow*SHA1InWant` on the remote. Surfaced on the first
|
|
||||||
real `request_apply_commit` (the `gui` agent bootstrap — initial
|
|
||||||
`deployed/0` seeding uses a different path). Fix: `git_fetch_to_tag`
|
|
||||||
now resolves the sha LOCALLY against the proposed repo
|
|
||||||
(`git rev-parse <sha>^{commit}`), fetches all of proposed's heads
|
|
||||||
into applied's object db (`+refs/heads/*:refs/remotes/proposal-src/*`),
|
|
||||||
then `git tag`s the resolved sha — all-local, no upload-pack
|
|
||||||
sha-want negotiation. Plus: `submit_apply_commit` now shape-checks
|
|
||||||
`commit_ref` is a 7-40 char hex sha (`validate_commit_ref`) and
|
|
||||||
rejects branch/tag names so the proposal always pins an immutable
|
|
||||||
commit. Tool description + `RequestApplyCommit` wire doc +
|
|
||||||
`docs/approvals.md` updated. 3 new tests in `manager_server::tests`.
|
|
||||||
- **Just landed:** inbox batching unified into `recv(max?)`.
|
|
||||||
No separate `recv_batch` tool — the existing `recv` tool
|
|
||||||
grew an optional `max: u32` arg (default 1, server-side
|
|
||||||
cap 32) so a single round-trip drains up to N popped rows
|
|
||||||
with the same delivery + ack bookkeeping per row
|
|
||||||
(`delivered_at = NOW`, `unacked_ids` list, redelivered
|
|
||||||
tag from `requeue_inflight`). `wait_seconds` still applies
|
|
||||||
to the FIRST message; once one lands the call drains up
|
|
||||||
to `max` in total — long-poll + drain compose. Wake
|
|
||||||
prompt's pending-inbox hint points at `recv(max: N)`.
|
|
||||||
Wire shape: `AgentRequest::Recv { wait_seconds, max }`
|
|
||||||
(added `max`), `AgentResponse::Messages { messages:
|
|
||||||
Vec<DeliveredMessage> }` (collapsed the old
|
|
||||||
`Message` + `Empty` + `Batch` trio into one always-list
|
|
||||||
variant — empty vec = idle). `DeliveredMessage` is a flat
|
|
||||||
shared struct in `hive-sh4re`. `format_recv` renders
|
|
||||||
single = the historical `from: X\n\nbody` block, multi =
|
|
||||||
`popped N message(s)` header with `---` separators +
|
|
||||||
per-message redelivery banners; empty = "(empty)". Broker
|
|
||||||
primitive: dropped the singular `recv`, kept just
|
|
||||||
`recv_batch(recipient, max)` and `recv_blocking_batch`
|
|
||||||
(which long-polls then drains via `recv_batch`). 4 new
|
|
||||||
broker tests on top of the existing 7 (recv_batch_*
|
|
||||||
family). Closes the "inbox batching hint" item from the
|
|
||||||
ergonomics wishlist with one tool instead of two; lower
|
|
||||||
context bloat in claude's prompt.
|
|
||||||
- **Just landed:** lease-style message delivery / no-drop
|
|
||||||
on turn fail. The `messages` table gained an `acked_at`
|
|
||||||
column (idempotent ALTER + backfill = `delivered_at` so
|
|
||||||
pre-migration delivered rows count as already-acked).
|
|
||||||
`Broker::recv` now returns `Delivery { id, redelivered,
|
|
||||||
message }` — the harness gets the row id back so
|
|
||||||
`AckTurn` can sweep every popped id at turn-end-OK. Two
|
|
||||||
new wire arms on both agent + manager surfaces:
|
|
||||||
`AckTurn` (drains the broker's per-recipient in-memory
|
|
||||||
`unacked_ids` list and stamps the rows `acked_at = NOW`)
|
|
||||||
and `RequeueInflight` (one-shot at harness boot: resets
|
|
||||||
`delivered_at = NULL` on every still-inflight row +
|
|
||||||
remembers each id so the next `Recv` carries
|
|
||||||
`redelivered: true`). Both bin loops call
|
|
||||||
`requeue_inflight` once before entering serve, and
|
|
||||||
`ack_turn` after every `TurnOutcome::Ok` (Failed +
|
|
||||||
PromptTooLong intentionally skip the ack so the popped
|
|
||||||
rows stay in-flight for the next boot's requeue).
|
|
||||||
`format_recv` + `format_wake_prompt` on both bins
|
|
||||||
surface a `[redelivered after harness restart — may
|
|
||||||
already be handled]` banner so claude knows the
|
|
||||||
side-effects of any previous handling may already have
|
|
||||||
happened. Lock order: `inflight` mutex first then
|
|
||||||
`conn` mutex in all three methods (`recv` / `ack_turn`
|
|
||||||
/ `requeue_inflight`) so a concurrent pop can't race
|
|
||||||
the requeue's DB update vs in-memory populate and
|
|
||||||
miss the redelivered tag. `vacuum_delivered` filter
|
|
||||||
flipped from `delivered_at < cutoff` to `acked_at IS
|
|
||||||
NOT NULL AND acked_at < cutoff` so unacked-but-
|
|
||||||
delivered rows survive vacuum (they're recoverable via
|
|
||||||
`requeue_inflight`). 7 new tests in `broker::tests`
|
|
||||||
cover happy path, crash recovery, idempotency, per-
|
|
||||||
recipient isolation, batch ack, vacuum preservation,
|
|
||||||
and FIFO ordering on requeue. Closes the "post-rebuild
|
|
||||||
system-message missed wake" bug class entirely (any
|
|
||||||
turn that wakes from a `delivered_at NOT NULL,
|
|
||||||
acked_at NULL` row resurfaces on next boot).
|
|
||||||
- **Just landed:** ctx + cost badges split. The per-agent
|
|
||||||
page now shows TWO chips — `ctx · N` (last inference's
|
|
||||||
prompt size = actual context window utilisation, parsed
|
|
||||||
from each `assistant` event's `.message.usage`; the
|
|
||||||
number to watch for compaction) and `cost · M` (sum
|
|
||||||
across every inference in the turn, the previous
|
|
||||||
behaviour now correctly labelled — tool-heavy turns
|
|
||||||
rebill the cached prefix per call and blow past the
|
|
||||||
model's window). Both fed by a single
|
|
||||||
`TokenUsageChanged { ctx, cost }` SSE event at turn-end
|
|
||||||
via `Bus::record_turn_usage`. `turn_stats` grew four
|
|
||||||
`last_*_tokens` columns (idempotent ALTER migration) so
|
|
||||||
cold-load seeds both badges from the most recent row.
|
|
||||||
Pre-migration rows yield no `ctx` seed (empty badge
|
|
||||||
until next turn) rather than a misleading zero.
|
|
||||||
- **Just landed:** per-agent terminal coherence pass.
|
|
||||||
Unified prefix column (padding-left + negative
|
|
||||||
text-indent so every row kind aligns); `<details>`
|
|
||||||
summaries drop the directional glyph and let CSS
|
|
||||||
`▸/▾` sit in the prefix column; turn boundaries
|
|
||||||
de-weighted (border-left rule only, no bold/margin/
|
|
||||||
tint); stderr lines render orange `!`,
|
|
||||||
operator-initiated notes mauve italic, catch-all `.sys`
|
|
||||||
escalated to orange so unrecognised stream-json
|
|
||||||
surfaces. Message-bearing tool calls
|
|
||||||
(`send`/`ask`/`answer`/`recv`) render default-open
|
|
||||||
with markdown bodies via vendored `marked` v4.0.2
|
|
||||||
(`hive-fr0nt::MARKED_JS`); assistant `text` rows also
|
|
||||||
markdown-rendered. Extra-MCP tools get a generic
|
|
||||||
args pretty-printer (`fmtArgsGeneric`) instead of
|
|
||||||
raw JSON. tool_use_id → name map carries through the
|
|
||||||
stream so `renderToolResult` knows when a result came
|
|
||||||
from `recv` and should default-open with markdown.
|
|
||||||
See `docs/terminal-rendering.md` for the as-built
|
|
||||||
taxonomy. Bonus: ctx badge seeded from `turn_stats`
|
|
||||||
on cold load via `Bus::seed_usage` so the chip paints
|
|
||||||
real numbers before the next turn finishes.
|
|
||||||
- **Just landed:** `open_threads` → `loose_ends` rename
|
|
||||||
(more honest about what the list is) + new
|
|
||||||
`cancel_loose_end(kind, id)` MCP tool on both
|
|
||||||
surfaces. `kind = "question"` posts a `[cancelled by
|
|
||||||
<self>]` answer to unblock the asker; `kind =
|
|
||||||
"reminder"` hard-deletes before fire. Auth: sub-agent
|
|
||||||
must own the row (`asker == self` / `owner == self`);
|
|
||||||
manager bypasses for hive-wide cleanup. `LooseEnd`
|
|
||||||
enum gained a `Reminder { id, owner, message, due_at,
|
|
||||||
age_seconds }` variant; sub-agent flavour filters by
|
|
||||||
owner, manager unfiltered. Shared dispatch in
|
|
||||||
`hive-c0re/src/questions.rs::handle_cancel_loose_end`.
|
|
||||||
Per-agent web UI's `/api/open-threads` →
|
|
||||||
`/api/loose-ends`. Closes the agent-side "I have no
|
|
||||||
way to cancel what I queued" friction from the
|
|
||||||
ergonomics wishlist.
|
|
||||||
- **Just landed:** `whoami` MCP tool on both surfaces —
|
|
||||||
returns `{ name, role, pronouns, hyperhive_rev }`.
|
|
||||||
Lets an agent self-identify without scraping its own
|
|
||||||
system prompt; pronouns are pulled from the
|
|
||||||
`HIVE_OPERATOR_PRONOUNS` env that the system prompt
|
|
||||||
also substitutes, so the two stay in sync.
|
|
||||||
- **Just landed:** reminder delivery failures persist +
|
|
||||||
surface. The 5s scheduler now records the failure
|
|
||||||
reason on the `reminders` row instead of silently
|
|
||||||
dropping; the web UI surfaces them in the loose-ends
|
|
||||||
section so the owner agent can see "this reminder
|
|
||||||
never landed because the target was destroyed" without
|
|
||||||
reading journald.
|
|
||||||
- **Just landed:** path linkify. The broker tags every
|
|
||||||
message body server-side with `file_refs` at ingest
|
|
||||||
via `/api/state-file/check` (validates each path,
|
|
||||||
attaches container→host resolution). Last segment
|
|
||||||
must look like `name.ext` so directory mentions don't
|
|
||||||
fire spurious links. Dashboard renders refs as
|
|
||||||
collapsible path-preview blocks below the message
|
|
||||||
body; per-agent terminal picks them up from the same
|
|
||||||
field. Server-side validation means the JS doesn't
|
|
||||||
re-walk allow-lists on every render.
|
|
||||||
- **Just landed:** tombstones + meta_inputs as
|
|
||||||
`DashboardEvent`s. Closes the last two refetch loops
|
|
||||||
on the dashboard side — `purge-tombstone` and
|
|
||||||
`meta-update` POSTs now flip to 200 with
|
|
||||||
`data-no-refresh`. The 5s `/api/state` poll is gone
|
|
||||||
entirely; everything event-driven.
|
|
||||||
- **Just landed:** per-agent UI gained an open-threads
|
|
||||||
section (questions + approvals + reminders pending
|
|
||||||
against this agent) + the container row on the
|
|
||||||
dashboard gained a `⏰ N` pending-reminder count
|
|
||||||
chip. task_started / task_notification stream-json
|
|
||||||
events now pretty-render in the terminal with the
|
|
||||||
`⌁` glyph so subagent (Task tool) activity is
|
|
||||||
visually distinct from main-session tool calls.
|
|
||||||
- **Just landed:** per-agent extra MCP servers via the
|
|
||||||
`hyperhive.extraMcpServers.<key>` NixOS option in
|
|
||||||
`agent.nix`. Declares `{ command, args, env,
|
|
||||||
allowedTools }`; the module writes the whole map to
|
|
||||||
`/etc/hyperhive/extra-mcp.json`; the harness reads that
|
|
||||||
file and merges each entry into both `--mcp-config`
|
|
||||||
and `--allowedTools` (mapped to `mcp__<key>__<pattern>`).
|
|
||||||
Unblocks matrix / bitburner / any agent with rich
|
|
||||||
domain tooling — the agent flake's `inputs` block pulls
|
|
||||||
the external flake, `agent.nix` references it via
|
|
||||||
`flakeInputs.<name>.packages.${pkgs.system}.default`.
|
|
||||||
- **Just landed:** per-turn analytics sink. New
|
|
||||||
`hive-ag3nt::turn_stats` writes one row per claude turn to
|
|
||||||
`/state/hyperhive-turn-stats.sqlite`: identity (model,
|
|
||||||
wake_from, result_kind), timing (started/ended_at,
|
|
||||||
duration_ms), cost (full token-usage breakdown), behaviour
|
|
||||||
(tool_call_count + per-tool JSON map), and post-turn snapshot
|
|
||||||
metrics (open_threads_count, open_reminders_count fetched via
|
|
||||||
the existing GetOpenThreads + new CountPendingReminders RPC).
|
|
||||||
Both ag3nt + m1nd bin loops capture, both Bus accumulates
|
|
||||||
tool_use blocks via observe_stream during the stdout pump.
|
|
||||||
Writes are best-effort. No host-side vacuum yet — TODO under
|
|
||||||
Telemetry; same shape as events_vacuum, target 90d retention.
|
|
||||||
- **Just landed:** agent web UI event-driven badges. New
|
|
||||||
`LiveEvent::StatusChanged / ModelChanged / TokenUsageChanged
|
|
||||||
/ TurnStateChanged` variants replace the per-agent page's
|
|
||||||
/api/state polling for the state row. Status/model/token/state
|
|
||||||
badges all update from SSE; /api/state only fetched on cold
|
|
||||||
load + during the login flow (session output isn't event-
|
|
||||||
shaped). Per-agent endpoints (`/api/cancel|compact|model|
|
|
||||||
new-session`, `/login/*`) all flip 303→200. New `alive-badge`
|
|
||||||
chip carries the harness reachability signal (replaces the
|
|
||||||
"● harness alive" paragraph); new `ctx-badge` mirrors Claude
|
|
||||||
Code's bottom-right "N tokens" indicator. Every chip carries
|
|
||||||
a `title=...` tooltip for hover detail.
|
|
||||||
- **Just landed:** events_vacuum simplified to age-only —
|
|
||||||
`KEEP_SECS = 7d`, no row cap. Chatty turn no longer evicts
|
|
||||||
a quiet day's history sooner than expected. Hourly sweep
|
|
||||||
unchanged.
|
|
||||||
- **Just landed:** Phase 6 container events. New
|
|
||||||
`DashboardEvent::ContainerStateChanged { container }` +
|
|
||||||
`ContainerRemoved { name }` close the last refetch loop on the
|
|
||||||
dashboard side. `Coordinator::rescan_containers_and_emit` builds a
|
|
||||||
fresh `container_view::build_all` snapshot, diffs it against a
|
|
||||||
cached `last_containers` map, and fires per-row events for the
|
|
||||||
delta. Called from every mutation site: `actions::approve`
|
|
||||||
(post-spawn), `actions::destroy`, the `lifecycle_action` wrapper
|
|
||||||
in `dashboard.rs` (start/stop/restart/rebuild), `auto_update::
|
|
||||||
rebuild_agent`, and the existing 10s `crash_watch` poll loop.
|
|
||||||
`ContainerView` extracted to its own module so coordinator +
|
|
||||||
dashboard can both build it. Dashboard endpoints (`/restart`,
|
|
||||||
`/destroy`, `/kill`, `/rebuild`, `/start`, `/update-all`,
|
|
||||||
`/meta-update`, `/purge-tombstone`) now return 200; matching
|
|
||||||
forms carry `data-no-refresh` where the event coverage is
|
|
||||||
complete (purge + meta-update keep the refetch since tombstones
|
|
||||||
+ meta_inputs aren't event-derived yet). Client drops the 5s
|
|
||||||
periodic `/api/state` poll entirely — initial cold load + SSE
|
|
||||||
for everything afterwards; pending overlay reads from
|
|
||||||
`transientsState` since the new event payload doesn't carry it.
|
|
||||||
- **Just landed:** dashboard event refactor. New `hive-fr0nt`
|
|
||||||
workspace crate hosts shared frontend assets (palette + terminal
|
|
||||||
CSS + `window.HiveTerminal.create` JS) so both the dashboard and
|
|
||||||
the per-agent web UI render their live panes through the same
|
|
||||||
code; the dashboard's `#msgflow` now feels like the agent's
|
|
||||||
terminal (sticky-bottom + pill + lit chrome). New unified
|
|
||||||
`DashboardEvent` channel on `Coordinator` (replaces the
|
|
||||||
broker-only `/messages/stream`); a background forwarder mirrors
|
|
||||||
broker traffic onto it as `Sent` / `Delivered` variants, and
|
|
||||||
the mutation-event variants
|
|
||||||
(`ApprovalAdded` / `ApprovalResolved`, `QuestionAdded` /
|
|
||||||
`QuestionResolved`, `TransientSet` / `TransientCleared`) cover
|
|
||||||
every in-process state change the dashboard cares about. Each
|
|
||||||
frame carries a monotonic per-process `seq`; snapshot endpoints
|
|
||||||
return their seq alongside the state, and the terminal's
|
|
||||||
open-buffer-then-fetch-history dance drops any buffered frame
|
|
||||||
with `seq <= history_seq` so an event landing between subscribe
|
|
||||||
and history-fetch is neither shown twice nor lost. Operator
|
|
||||||
inbox + approvals + questions + transients are now derived
|
|
||||||
client-side from the event stream (cold-loaded from
|
|
||||||
`/api/state` for first paint, mutated live from SSE
|
|
||||||
thereafter); `/op-send` + per-agent `/send` return 200 instead
|
|
||||||
of 303-and-refetch. Container-list events still pending —
|
|
||||||
`ContainerView` is sourced from external `nixos-container list`,
|
|
||||||
so the 5s `/api/state` poll continues to drive the containers
|
|
||||||
section. Approval diffs are now raw unified-diff text on the
|
|
||||||
wire (per-line classification happens in JS) so they fit in
|
|
||||||
SSE payloads without HTML escaping. Bug fix: `LiveEvent::Note`
|
|
||||||
was a newtype variant that serde silently failed to serialize
|
|
||||||
— converted to `Note { text: String }` (wire shape matches what
|
|
||||||
the JS already read).
|
|
||||||
- **Just landed:** `ask_operator` → `ask` rename + optional
|
|
||||||
`to: <agent>` param for agent-to-agent structured Q&A.
|
|
||||||
Recipient defaults to the operator (dashboard); peer
|
|
||||||
questions land in the target's inbox as `QuestionAsked`
|
|
||||||
events and the recipient replies via new `answer(id,
|
|
||||||
answer)` tool. Answer always flows back as
|
|
||||||
`QuestionAnswered { id, question, answer, answerer }`
|
|
||||||
(renamed from `OperatorAnswered`; `answerer` distinguishes
|
|
||||||
operator vs peer vs `ttl-watchdog`). Authorisation:
|
|
||||||
operator-targeted questions can only be answered by the
|
|
||||||
operator; agent-targeted by the named target (or the
|
|
||||||
operator as override). Self-ask rejected. Shared dispatch
|
|
||||||
lives in `hive-c0re/src/questions.rs`. Dashboard's
|
|
||||||
`pending()` filters on `target IS NULL` so peer questions
|
|
||||||
never leak into the operator's queue.
|
|
||||||
- **Just landed:** dashboard now has a terminal-style
|
|
||||||
compose textbox under the message-flow stream — `@name`
|
|
||||||
picks the recipient (sticky in localStorage, auto-
|
|
||||||
completed from `containers[]`), POSTs `/op-send`. New
|
|
||||||
per-agent `↻ new session` button drops `--continue` for
|
|
||||||
one turn. Claude spawns with `cwd = /state` so relative
|
|
||||||
paths in tool calls land in the durable dir.
|
|
||||||
- **Just landed (prior overhaul still underneath):** tag-
|
|
||||||
driven config-apply. Two-repo split (proposed = manager
|
|
||||||
RW, applied = core-only); `request_apply_commit` fetches
|
|
||||||
the manager's commit into applied and pins it as
|
|
||||||
`proposal/<id>`; approve / deny / build walk through
|
|
||||||
tags on the same commit; `applied/main` only fast-
|
|
||||||
forwards on `deployed/`. `failed/` + `denied/` are
|
|
||||||
annotated. See `docs/approvals.md`.
|
|
||||||
- **Recent (since last compaction):** inline +/- diffs on
|
|
||||||
Write/Edit, send full body via collapsed details, operator
|
|
||||||
cancel + ttl on questions, deny-with-reason, dashboard
|
|
||||||
back-link + last-turn timing + model chip, per-agent inbox
|
|
||||||
view, bind-retry + SO_REUSEADDR, journald viewer,
|
|
||||||
agent.nix viewer, server-side TurnState, recv(wait_seconds)
|
|
||||||
max 180s, runtime /model switch + persistence to /state,
|
|
||||||
crash watcher + ContainerCrash / NeedsLogin / LoggedIn /
|
|
||||||
NeedsUpdate events, manager `update` tool, pure-hash
|
|
||||||
agent_web_port + collision banner + spawn/rebuild preflight,
|
|
||||||
browser notifications, focus-preserving refresh, generalised
|
|
||||||
<details data-restore-key> survival, prompt-on-submit pattern.
|
|
||||||
- **Open threads:** two-step spawn, unprivileged
|
|
||||||
containers, Bash allow-list, xterm.js. The
|
|
||||||
deployment / gateway / privsep cluster is tracked as
|
|
||||||
`area:ops` forge issues. (Landed since this note was first written:
|
|
||||||
extra per-agent MCP servers, per-agent send allow-list,
|
|
||||||
telemetry + the `/stats` page.)
|
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ You are hyperhive agent `{label}` in a multi-agent system. The operator (recipie
|
||||||
Tools (hyperhive surface):
|
Tools (hyperhive surface):
|
||||||
|
|
||||||
- `mcp__hyperhive__recv(wait_seconds?, max?)` — drain inbox messages (returns `(empty)` if nothing pending). Without `wait_seconds` (or with `0`) it returns immediately — a cheap "anything pending?" peek you can sprinkle between tool calls. To **wait** for work when you have nothing else useful to do this turn, call with a long wait (e.g. `wait_seconds: 180`, the max) — incoming messages wake you instantly, otherwise the call returns empty at the timeout. That's strictly better than a fixed `sleep` shell command: lower latency on new work, no busy-loop. `max` (default 1, cap 32) drains several queued messages in one call — the wake prompt tells you the pending count.
|
- `mcp__hyperhive__recv(wait_seconds?, max?)` — drain inbox messages (returns `(empty)` if nothing pending). Without `wait_seconds` (or with `0`) it returns immediately — a cheap "anything pending?" peek you can sprinkle between tool calls. To **wait** for work when you have nothing else useful to do this turn, call with a long wait (e.g. `wait_seconds: 180`, the max) — incoming messages wake you instantly, otherwise the call returns empty at the timeout. That's strictly better than a fixed `sleep` shell command: lower latency on new work, no busy-loop. `max` (default 1, cap 32) drains several queued messages in one call — the wake prompt tells you the pending count.
|
||||||
- `mcp__hyperhive__send(to, body)` — message a peer (by their name) or the operator (recipient `operator`, surfaces in the dashboard). Use `to: "*"` to broadcast to all agents (they receive a hint that it's a broadcast and may not need action). Some agents have a per-agent allow-list (`hyperhive.allowedRecipients` in their `agent.nix`) — if so the tool refuses recipients outside the list with a clear error; route through the manager (`send(to: "manager", …)`) which is always reachable.
|
- `mcp__hyperhive__send(to, body, in_reply_to?)` — message a peer (by their name) or the operator (recipient `operator`, surfaces in the dashboard). Use `to: "*"` to broadcast to all agents (they receive a hint that it's a broadcast and may not need action). Optional `in_reply_to: <message-id>` threads this message under a prior one — the dashboard and per-agent inbox render it with a `↳ reply` link. Some agents have a per-agent allow-list (`hyperhive.allowedRecipients` in their `agent.nix`) — if so the tool refuses recipients outside the list with a clear error; route through the manager (`send(to: "manager", …)`) which is always reachable.
|
||||||
- (some agents only) **extra MCP tools** surfaced as `mcp__<server>__<tool>` — these are agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `hyperhive.extraMcpServers`. Treat them as first-class tools alongside the hyperhive surface; the operator already auto-approved them at deploy time.
|
- (some agents only) **extra MCP tools** surfaced as `mcp__<server>__<tool>` — these are agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `hyperhive.extraMcpServers`. Treat them as first-class tools alongside the hyperhive surface; the operator already auto-approved them at deploy time.
|
||||||
- `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_loose_ends()` — list your loose ends: unanswered questions where you're asker (waiting on someone) or target (owing a reply), plus reminders you've scheduled that haven't fired. No args, cheap server-side sweep. Useful at turn start to remember what's outstanding without scanning inbox archaeology.
|
- `mcp__hyperhive__get_loose_ends()` — list your loose ends: unanswered questions where you're asker (waiting on someone) or target (owing a reply), plus reminders you've scheduled that haven't fired. No args, cheap server-side sweep. Useful at turn start to remember what's outstanding without scanning inbox archaeology.
|
||||||
- `mcp__hyperhive__cancel_loose_end(kind, id)` — cancel one of your own open threads. `kind` is `"question"` (the asker — you, in this case — gets a `[cancelled by <you>]` answer so the waiter unblocks) or `"reminder"` (hard-deleted before it fires). `id` from the matching `get_loose_ends` row or the original submission reply.
|
- `mcp__hyperhive__cancel_loose_end(kind, id)` — cancel one of your own open threads. `kind` is `"question"` (the asker — you, in this case — gets a `[cancelled by <you>]` answer so the waiter unblocks) or `"reminder"` (hard-deleted before it fires). `id` from the matching `get_loose_ends` row or the original submission reply.
|
||||||
- `mcp__hyperhive__remind(message, delay_seconds? | at_unix_timestamp?, file_path?)` — schedule a message to land in your *own* inbox at a future time (sender shows as `reminder`). Set exactly one of `delay_seconds` (relative) or `at_unix_timestamp` (absolute). Use for self-paced follow-ups instead of blocking a whole turn on a long `recv` wait. A large `message` auto-spills to a file under `/agents/{label}/state/reminders/`; pass `file_path` to point at one yourself.
|
- `mcp__hyperhive__remind(message, delay_seconds? | at_unix_timestamp?, file_path?)` — schedule a message to land in your *own* inbox at a future time (sender shows as `reminder`). Set exactly one of `delay_seconds` (relative) or `at_unix_timestamp` (absolute). Use for self-paced follow-ups instead of blocking a whole turn on a long `recv` wait. A large `message` auto-spills to a file under `/agents/{label}/state/reminders/`; pass `file_path` to point at one yourself. Each agent's pending-reminder count is capped (default 50) — the tool will error if the cap is already reached.
|
||||||
- `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.
|
- `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.
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ Tools (hyperhive surface):
|
||||||
|
|
||||||
- `mcp__hyperhive__recv(wait_seconds?, max?)` — drain inbox messages. Without `wait_seconds` (or with `0`) it returns immediately — a cheap inbox peek you can drop between actions. To **wait** when you have nothing else to do, call with a long wait (e.g. `wait_seconds: 180`, the max) — you'll wake instantly on new work, otherwise return after the timeout. Use that instead of ending the turn or sleeping in a Bash command. `max` (default 1, cap 32) drains several queued messages in one call.
|
- `mcp__hyperhive__recv(wait_seconds?, max?)` — drain inbox messages. Without `wait_seconds` (or with `0`) it returns immediately — a cheap inbox peek you can drop between actions. To **wait** when you have nothing else to do, call with a long wait (e.g. `wait_seconds: 180`, the max) — you'll wake instantly on new work, otherwise return after the timeout. Use that instead of ending the turn or sleeping in a Bash command. `max` (default 1, cap 32) drains several queued messages in one call.
|
||||||
- `mcp__hyperhive__send(to, body)` — message an agent (by name), another peer, or the operator (`operator` surfaces in the dashboard). Use `to: "*"` to broadcast to all agents (they receive a hint that it's a broadcast and may not need action).
|
- `mcp__hyperhive__send(to, body)` — message an agent (by name), another peer, or the operator (`operator` surfaces in the dashboard). Use `to: "*"` to broadcast to all agents (they receive a hint that it's a broadcast and may not need action).
|
||||||
- `mcp__hyperhive__request_spawn(name, description?)` — queue a brand-new sub-agent for operator approval (≤9 char name). Pass an optional `description` and it appears on the dashboard approval card — no need to send a separate message explaining the request.
|
- `mcp__hyperhive__request_init_config(name, description?)` — **step 1 of a two-step spawn.** Queues an `InitConfig` approval (≤9 char name). On operator approve, hive-c0re seeds the proposed config repo at `/agents/<name>/config/` with a default `agent.nix` template and delivers a `config_ready` system event to your inbox. You then review, edit, and commit `agent.nix` before calling `request_spawn`.
|
||||||
|
- `mcp__hyperhive__request_spawn(name, description?)` — **step 2 of a two-step spawn.** Queues a Spawn approval; requires the proposed config repo to already exist (from a prior approved `request_init_config`). On operator approve, hive-c0re creates the container. Pass an optional `description` for the dashboard card.
|
||||||
- `mcp__hyperhive__kill(name)` — graceful stop on a sub-agent. No approval required.
|
- `mcp__hyperhive__kill(name)` — graceful stop on a sub-agent. No approval required.
|
||||||
- `mcp__hyperhive__start(name)` — start a stopped sub-agent. No approval required.
|
- `mcp__hyperhive__start(name)` — start a stopped sub-agent. No approval required.
|
||||||
- `mcp__hyperhive__restart(name)` — stop + start a sub-agent. No approval required.
|
- `mcp__hyperhive__restart(name)` — stop + start a sub-agent. No approval required.
|
||||||
|
|
@ -70,8 +71,9 @@ You're the policy gate between sub-agents and the operator's approval queue —
|
||||||
|
|
||||||
Two ways to talk to the operator: `send(to: "operator", ...)` for fire-and-forget status / pointers (surfaces in the operator inbox), or `ask(question, options?)` when you need a decision (omit `to`, or pass `to: "operator"`). `ask` is non-blocking — it queues the question and returns an id immediately; the answer arrives on a future turn as a `question_answered` system event. Prefer `ask` over an open-ended `send` for anything you actually need to wait on. Same primitive can target a sub-agent (`to: "<agent>"`) when you need a structured answer from a peer rather than free-form chat.
|
Two ways to talk to the operator: `send(to: "operator", ...)` for fire-and-forget status / pointers (surfaces in the operator inbox), or `ask(question, options?)` when you need a decision (omit `to`, or pass `to: "operator"`). `ask` is non-blocking — it queues the question and returns an id immediately; the answer arrives on a future turn as a `question_answered` system event. Prefer `ask` over an open-ended `send` for anything you actually need to wait on. Same primitive can target a sub-agent (`to: "<agent>"`) when you need a structured answer from a peer rather than free-form chat.
|
||||||
|
|
||||||
Messages from sender `system` are hyperhive helper events (JSON body, `event` field discriminates): `approval_resolved`, `spawned`, `rebuilt`, `killed`, `destroyed`, `container_crash`, `needs_login`, `logged_in`, `needs_update`, `question_asked`, `question_answered`. Use these to react to lifecycle changes:
|
Messages from sender `system` are hyperhive helper events (JSON body, `event` field discriminates): `approval_resolved`, `config_ready`, `spawned`, `rebuilt`, `killed`, `destroyed`, `container_crash`, `needs_login`, `logged_in`, `needs_update`, `question_asked`, `question_answered`. Use these to react to lifecycle changes:
|
||||||
|
|
||||||
|
- `config_ready` — the proposed config repo for a new agent was just seeded (post-`InitConfig` approval). Review and edit `/agents/<agent>/config/agent.nix`, commit your changes, then call `request_spawn` to proceed to the container-creation approval.
|
||||||
- `needs_login` — agent has no claude session yet. You can't help directly (login is interactive OAuth on the operator side); flag the operator if it's been long.
|
- `needs_login` — agent has no claude session yet. You can't help directly (login is interactive OAuth on the operator side); flag the operator if it's been long.
|
||||||
- `logged_in` — agent just completed login; first useful turn is imminent. Good time to brief them on what to do.
|
- `logged_in` — agent just completed login; first useful turn is imminent. Good time to brief them on what to do.
|
||||||
- `needs_update` — agent's flake rev is stale. Call `update(name)` to rebuild — it's idempotent and doesn't need approval.
|
- `needs_update` — agent's flake rev is stale. Call `update(name)` to rebuild — it's idempotent and doesn't need approval.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue