docs: the hive has no approval queue
config-changes.md (was approvals.md) describes config PRs, the meta flake and helper events; the approval queue, its kinds, the withdraw path, the job-queue dispatch and ApprovalResolved go. Every other page drops the approvals table, endpoints, event, tool group, hivectl verbs, dashboard section and the loose-end/cancel kind, and links to the renamed page.
This commit is contained in:
parent
ac1caef972
commit
e4b170f8c6
18 changed files with 122 additions and 327 deletions
12
CLAUDE.md
12
CLAUDE.md
|
|
@ -31,11 +31,11 @@ hand-maintained per-file tree drifts out of sync with the code.
|
||||||
user). `src/main.rs` is the `hive-c0re` binary — **daemon-only**
|
user). `src/main.rs` is the `hive-c0re` binary — **daemon-only**
|
||||||
(`serve` + the periodic vacuum/sweep loops); the operator CLI lives in
|
(`serve` + the periodic vacuum/sweep loops); the operator CLI lives in
|
||||||
the separate `hivectl` crate, which talks to the daemon over the host
|
the separate `hivectl` crate, which talks to the daemon over the host
|
||||||
admin socket. Owns the sqlite broker, approval + reminder
|
admin socket. Owns the sqlite broker, the reminder queue, the meta
|
||||||
queues, the meta flake, lifecycle (`nixos-container`
|
flake, lifecycle (`nixos-container` shellouts), gateway / forge /
|
||||||
shellouts), gateway / forge / matrix wiring (agent forge and matrix
|
matrix wiring (agent forge and matrix accounts are
|
||||||
accounts are `swarm-controller`'s), per-container stats, and the axum
|
`swarm-controller`'s), per-container stats, and the axum operator
|
||||||
operator dashboard (`dashboard/`). Largest crate.
|
dashboard (`dashboard/`). Largest crate.
|
||||||
- **`hivectl/`** — standalone operator CLI (`hivectl` binary). Talks to
|
- **`hivectl/`** — standalone operator CLI (`hivectl` binary). Talks to
|
||||||
the `hive-c0re` daemon over the host admin socket (`hive-host-sock`
|
the `hive-c0re` daemon over the host admin socket (`hive-host-sock`
|
||||||
wire types) — does NOT link `hive-c0re`. Full, always-current verb
|
wire types) — does NOT link `hive-c0re`. Full, always-current verb
|
||||||
|
|
@ -128,7 +128,7 @@ hand-maintained per-file tree drifts out of sync with the code.
|
||||||
the process is; the actual claude session survives a daemon restart
|
the process is; the actual claude session survives a daemon restart
|
||||||
independently (see `session.rs`'s module doc).
|
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`, `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
|
||||||
split out into their own crates (below) so `hivectl` and `hive-priv`
|
split out into their own crates (below) so `hivectl` and `hive-priv`
|
||||||
don't need to pull in the rest of `hive-sh4re`.
|
don't need to pull in the rest of `hive-sh4re`.
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ the swarm — one of each, each on whichever host you choose
|
||||||
|
|
||||||
the hives — the substrate, as many as you like (deploy.hive-controller.enable)
|
the hives — the substrate, as many as you like (deploy.hive-controller.enable)
|
||||||
│
|
│
|
||||||
├── hive-c0re runs the agent containers: lifecycle, approvals, broker,
|
├── hive-c0re runs the agent containers: lifecycle, broker,
|
||||||
│ auto-rebuild, the per-hive dashboard; unprivileged
|
│ auto-rebuild, the per-hive dashboard; unprivileged
|
||||||
├── hive-priv root helper, socket-activated, does bind-mounts + nsenter
|
├── hive-priv root helper, socket-activated, does bind-mounts + nsenter
|
||||||
├── hive-gateway nginx + dnsmasq on :80/:443, fronts every agent's web UI
|
├── hive-gateway nginx + dnsmasq on :80/:443, fronts every agent's web UI
|
||||||
|
|
@ -180,7 +180,7 @@ newer channel breaks in ways hyperhive's CI doesn't catch.
|
||||||
- **`swarmctl`** — the swarm's CLI, on the swarm-controller host: create
|
- **`swarmctl`** — the swarm's CLI, on the swarm-controller host: create
|
||||||
agents and mint their identities, manage SSO users, make forge admins.
|
agents and mint their identities, manage SSO users, make forge admins.
|
||||||
→ [reference](docs/tools/swarmctl-cli.md)
|
→ [reference](docs/tools/swarmctl-cli.md)
|
||||||
- **per-hive** — each hive's dashboard (approvals, container state,
|
- **per-hive** — each hive's dashboard (container state,
|
||||||
rebuild queue) and `hivectl` for host-level administration and
|
rebuild queue) and `hivectl` for host-level administration and
|
||||||
container shells. → [`docs/web-ui/`](docs/web-ui/README.md) ·
|
container shells. → [`docs/web-ui/`](docs/web-ui/README.md) ·
|
||||||
[hivectl](docs/tools/hivectl.md)
|
[hivectl](docs/tools/hivectl.md)
|
||||||
|
|
@ -197,7 +197,7 @@ pick the page for your task rather than reading front to back. Some
|
||||||
common entry points:
|
common entry points:
|
||||||
|
|
||||||
- connecting hives into a swarm → [`swarm/`](docs/swarm/README.md)
|
- connecting hives into a swarm → [`swarm/`](docs/swarm/README.md)
|
||||||
- how config changes flow → [`agent-lifecycle/approvals.md`](docs/agent-lifecycle/approvals.md)
|
- how config changes flow → [`agent-lifecycle/config-changes.md`](docs/agent-lifecycle/config-changes.md)
|
||||||
- trust boundary + threat model → [`trust-boundary/`](docs/trust-boundary/boundary.md)
|
- trust boundary + threat model → [`trust-boundary/`](docs/trust-boundary/boundary.md)
|
||||||
- what an agent's turn looks like → [`turn-loop/`](docs/turn-loop/README.md)
|
- what an agent's turn looks like → [`turn-loop/`](docs/turn-loop/README.md)
|
||||||
- every `services.hyperhive.*` option → [options reference](https://hyperhive.darkest.space/options/)
|
- every `services.hyperhive.*` option → [options reference](https://hyperhive.darkest.space/options/)
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ declarations.
|
||||||
## Agent lifecycle
|
## Agent lifecycle
|
||||||
|
|
||||||
- **How do config changes flow from manager to operator to container?** →
|
- **How do config changes flow from manager to operator to container?** →
|
||||||
[`agent-lifecycle/approvals.md`](agent-lifecycle/approvals.md) (approval kinds, approval
|
[`agent-lifecycle/config-changes.md`](agent-lifecycle/config-changes.md)
|
||||||
state machine, `flake.lock` validation).
|
(config PRs, the meta flake, helper events).
|
||||||
- **What state survives destroy / purge / restart?** →
|
- **What state survives destroy / purge / restart?** →
|
||||||
[`agent-lifecycle/persistence.md`](agent-lifecycle/persistence.md).
|
[`agent-lifecycle/persistence.md`](agent-lifecycle/persistence.md).
|
||||||
- **Who can do what to whom — the agent roster and privilege?** →
|
- **Who can do what to whom — the agent roster and privilege?** →
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,8 @@
|
||||||
# Approvals + helper events
|
# Config changes + helper events
|
||||||
|
|
||||||
The approval queue is where an agent asks the operator for something it
|
Config changes are pull requests on the agent's config repo, which an
|
||||||
can't do itself. Nothing submits a new approval today: the one kind
|
operator merges on the forge; that merge deploys them. Helper events are
|
||||||
left, a meta-input bump, only reads back from existing rows. Config
|
how agents stay informed about lifecycle changes that need a reaction.
|
||||||
changes don't go through this queue: they're pull requests
|
|
||||||
on the agent's config repo, which an operator merges on the forge, and
|
|
||||||
that merge deploys them. The submitting agent — any agent with the
|
|
||||||
`approvals` tool group, which manages the config of its **direct
|
|
||||||
children** (the root agent for top-level agents; a sub-manager for its
|
|
||||||
own subtree) — is the policy gate in front of both; helper events are how
|
|
||||||
it stays informed about what happens after a decision lands.
|
|
||||||
|
|
||||||
## For operators
|
## For operators
|
||||||
|
|
||||||
|
|
@ -17,17 +10,6 @@ it stays informed about what happens after a decision lands.
|
||||||
config (or its own, via a sub-manager) as a pull request on
|
config (or its own, via a sub-manager) as a pull request on
|
||||||
`agent-configs/<agent>`. Review it on the forge like any other PR, and
|
`agent-configs/<agent>`. Review it on the forge like any other PR, and
|
||||||
merge it there to deploy it. See [Config changes](#config-changes).
|
merge it there to deploy it. See [Config changes](#config-changes).
|
||||||
- **Meta/flake update** (`UpdateMetaInputs`) — an existing row reads
|
|
||||||
back and you can approve it. Approving runs the update and commits
|
|
||||||
the lock change; it doesn't rebuild anything by itself.
|
|
||||||
|
|
||||||
Don't want to approve something? **Deny it** (`DENY` on the dashboard
|
|
||||||
card, or `hivectl approvals deny <id>`) — nothing runs. Either way the
|
|
||||||
submitting agent is always notified that the operator denied their request; what's
|
|
||||||
optional is only the reason text, which you can add on the dashboard's
|
|
||||||
prompt (cancelling that prompt aborts the whole deny, not just the
|
|
||||||
reason) but not from the CLI. Denying is final: a denied approval
|
|
||||||
can't be re-approved later, the agent has to submit a fresh request.
|
|
||||||
|
|
||||||
Everything below this point is the implementation detail behind that
|
Everything below this point is the implementation detail behind that
|
||||||
flow.
|
flow.
|
||||||
|
|
@ -38,8 +20,7 @@ Config changes flow through a **forge pull request** on the agent's
|
||||||
`agent-configs/<name>` repo — the same surface agents use for code PRs.
|
`agent-configs/<name>` repo — the same surface agents use for code PRs.
|
||||||
No MCP tool exists for config changes: opening the PR IS the request.
|
No MCP tool exists for config changes: opening the PR IS the request.
|
||||||
|
|
||||||
1. The submitting agent (the child's parent, holding the `approvals`
|
1. The submitting agent **clones** `agent-configs/<name>`, edits it there (any
|
||||||
tool group) **clones** `agent-configs/<name>`, edits it there (any
|
|
||||||
tracked path, but `agent.nix` is the contract entry point), commits
|
tracked path, but `agent.nix` is the contract entry point), commits
|
||||||
with its own git identity, and pushes a branch + opens a PR with
|
with its own git identity, and pushes a branch + opens a PR with
|
||||||
`hive-forge` — the same way it would change any other repo.
|
`hive-forge` — the same way it would change any other repo.
|
||||||
|
|
@ -77,46 +58,17 @@ failed rebuild shows on the hive like any other and isn't commented on
|
||||||
the PR. A merge whose webhook delivery never reaches the controller
|
the PR. A merge whose webhook delivery never reaches the controller
|
||||||
deploys nothing.
|
deploys nothing.
|
||||||
|
|
||||||
## Approval queue
|
Creating a brand-new agent is swarm-level (`swarmctl agent create`,
|
||||||
|
`POST /api/agents` on the swarm controller), and no hive can originate
|
||||||
### Withdrawing a pending approval
|
an agent. The swarm controller's `InitAgentConfigRepo` job seeds the
|
||||||
|
agent's config repo with a default `agent.nix` template, then asks the
|
||||||
The submitting agent can call `cancel_loose_end(kind: "approval", id)` to
|
target hive to deploy it.
|
||||||
withdraw an approval the operator hasn't acted on yet.
|
|
||||||
The row transitions to `ApprovalStatus::Cancelled` (distinct from
|
|
||||||
`Denied`/`Failed`), the dashboard pulls the card out of the
|
|
||||||
pending pane, and `ApprovalResolved { status: "cancelled" }` fires
|
|
||||||
on the root agent + dashboard channels. Approvals that the operator
|
|
||||||
(or a lifecycle failure) has already approved, denied, or failed return an error — the resolution is
|
|
||||||
final once acted on.
|
|
||||||
|
|
||||||
The socket refuses the `approval` kind with a clear error for any
|
|
||||||
agent that lacks the `approvals` tool group: only an agent with that
|
|
||||||
group submits approvals (for its direct children), so an agent
|
|
||||||
without it has nothing of its own to withdraw.
|
|
||||||
|
|
||||||
Creating a brand-new agent isn't an approval: it's swarm-level
|
|
||||||
(`swarmctl agent create`, `POST /api/agents` on the swarm controller),
|
|
||||||
and no hive can originate an agent. The swarm controller's
|
|
||||||
`InitAgentConfigRepo` job seeds the agent's config repo with a default
|
|
||||||
`agent.nix` template, then asks the target hive to deploy it.
|
|
||||||
|
|
||||||
Changing what the template seeded isn't a special case: like every
|
Changing what the template seeded isn't a special case: like every
|
||||||
later change, it's a PR on that config repo, made from a clone and
|
later change, it's a PR on that config repo, made from a clone and
|
||||||
merged by an operator on the forge (see [Config changes](#config-changes)).
|
merged by an operator on the forge (see [Config changes](#config-changes)).
|
||||||
|
|
||||||
### Approval kinds (wire shapes)
|
## Agent lifecycle
|
||||||
|
|
||||||
`ApprovalKind` carries one variant, and it overloads `commit_ref` as
|
|
||||||
its payload carrier.
|
|
||||||
|
|
||||||
<!-- vale write-good.Passive = NO -->
|
|
||||||
- `UpdateMetaInputs` — `commit_ref` stores the JSON-encoded inputs
|
|
||||||
array (`"[]"` = all inputs, `"[\"nixpkgs\"]"` = just nixpkgs,
|
|
||||||
etc.). hive-c0re sets the `agent` field to the requesting root agent.
|
|
||||||
On approve hive-c0re runs `nix flake update [inputs...]` on the
|
|
||||||
meta flake and commits the resulting lock changes.
|
|
||||||
<!-- vale write-good.Passive = YES -->
|
|
||||||
|
|
||||||
### Reminder delivery: file-path semantics
|
### Reminder delivery: file-path semantics
|
||||||
|
|
||||||
|
|
@ -144,10 +96,10 @@ body strings before calling it.
|
||||||
|
|
||||||
### Destroy semantics
|
### Destroy semantics
|
||||||
|
|
||||||
`HostRequest::Destroy { name, purge }` is the lifecycle tear-down,
|
`HostRequest::Destroy { name, purge }` is the lifecycle tear-down.
|
||||||
not an approval. Stops + removes the nspawn container, drops the
|
Stops + removes the nspawn container and drops the systemd drop-in.
|
||||||
systemd drop-in, fails any pending approvals. Persistent state
|
Persistent state (proposed/applied repos, claude credentials, `/state/`
|
||||||
(proposed/applied repos, claude credentials, `/state/` notes) is
|
notes) is
|
||||||
**kept by default** — recreating the agent with the same name
|
**kept by default** — recreating the agent with the same name
|
||||||
reuses prior config + login. With `purge = true` the agent's
|
reuses prior config + login. With `purge = true` the agent's
|
||||||
`/var/lib/hyperhive/{agents,applied}/<name>/` trees are also
|
`/var/lib/hyperhive/{agents,applied}/<name>/` trees are also
|
||||||
|
|
@ -252,41 +204,6 @@ forge and meta's lock commits record it. A config PR nobody merges
|
||||||
carries no extra state on the forge side: the PR stays open, and
|
carries no extra state on the forge side: the PR stays open, and
|
||||||
the submitter pushes again (or closes it) to retry.
|
the submitter pushes again (or closes it) to retry.
|
||||||
|
|
||||||
### Dispatch via the job queue
|
|
||||||
|
|
||||||
Long-running approval work — `UpdateMetaInputs` — runs as a DAG on the
|
|
||||||
global job queue (`docs/scheduler/coordinator.md::Job queue`), submitted
|
|
||||||
by the approval handler rather than run inline:
|
|
||||||
|
|
||||||
| `ApprovalKind` | DAG submitted | source |
|
|
||||||
|---|---|---|
|
|
||||||
| `UpdateMetaInputs` | `meta_update` (`MetaLock` + rebuild fan-out) | `approval` |
|
|
||||||
|
|
||||||
The DAG carries the originating `approval_id`. **Every** queued kind
|
|
||||||
resolves through `actions::resolve_approval_dag` when its DAG settles
|
|
||||||
terminal: the DAG's own terminal state is the authoritative outcome.
|
|
||||||
That hook fires the matching `HelperEvent::*` via `finish_approval`.
|
|
||||||
|
|
||||||
Two visible consequences:
|
|
||||||
|
|
||||||
- **Operator dashboard**: after selecting APPR0VE the work-in-progress
|
|
||||||
shows up on the *rebuild queue* card (`GET /api/jobq/graph`, refetched
|
|
||||||
on every `rebuild_queue_changed` tick), not on the approvals panel
|
|
||||||
(which already moved the row to "approved"). A long meta-update
|
|
||||||
cascade renders as a parent DAG with one child rebuild per affected
|
|
||||||
agent — see `docs/web-ui/dashboard.md` for the layout.
|
|
||||||
- **Cancellation**: the dashboard's *× cancel* button on a still-queued
|
|
||||||
DAG calls `POST /api/rebuild-queue/{id}/cancel`, which flips it to
|
|
||||||
`Cancelled` before any node runs (and fails the approval row instead
|
|
||||||
of leaving it dangling). Returns `{"cancelled": true}` on success,
|
|
||||||
`{"cancelled": false}` once any node started — terminal states can't
|
|
||||||
be retroactively rewritten.
|
|
||||||
|
|
||||||
The `approval` source + `approval_id` mean a tail-end build failure
|
|
||||||
surfaces back as a failed approval row, not just a silent queue
|
|
||||||
entry. `manual` (dashboard ↻ R3BU1LD) and `auto_update` (boot
|
|
||||||
reconcile) DAGs use the same queue but skip the approval plumbing.
|
|
||||||
|
|
||||||
### Forge mirror
|
### Forge mirror
|
||||||
|
|
||||||
The bundled `hive-forge` container runs on the swarm's forge host
|
The bundled `hive-forge` container runs on the swarm's forge host
|
||||||
|
|
@ -323,10 +240,10 @@ calls `bind_child_agent_dirs` for each entry in
|
||||||
current config — not an editing surface. An agent without the
|
current config — not an editing surface. An agent without the
|
||||||
capability sees no other agent's config at all.
|
capability sees no other agent's config at all.
|
||||||
|
|
||||||
An agent with the `approvals` tool group submits a change the same way
|
An agent submits a change to another agent's config the same way it
|
||||||
it makes any other change: **clone the target agent's config repo from the
|
makes any other change: **clone the target agent's config repo from the
|
||||||
forge into its own state dir, commit on a branch, open a PR**, and let
|
forge into its own state dir, commit on a branch, open a PR**, and let
|
||||||
the operator review and approve it. By design, no second, mount-shaped
|
the operator review and merge it. By design, no second, mount-shaped
|
||||||
path reaches the same file without the review.
|
path reaches the same file without the review.
|
||||||
|
|
||||||
Agents holding the `manage_root_agent` capability (granted per agent in
|
Agents holding the `manage_root_agent` capability (granted per agent in
|
||||||
|
|
@ -401,7 +318,7 @@ hive-c0re creates it. The root agent's flake lives at
|
||||||
`/var/lib/hyperhive/applied/ruth/`; its proposed config at
|
`/var/lib/hyperhive/applied/ruth/`; its proposed config at
|
||||||
`/var/lib/hyperhive/agents/ruth/config/`. The root agent can edit its own
|
`/var/lib/hyperhive/agents/ruth/config/`. The root agent can edit its own
|
||||||
`agent.nix` (visible inside the container at `/agents/ruth/config/`)
|
`agent.nix` (visible inside the container at `/agents/ruth/config/`)
|
||||||
and open a config PR on `agent-configs/ruth` for operator approval,
|
and open a config PR on `agent-configs/ruth` for operator review,
|
||||||
same as any other agent.
|
same as any other agent.
|
||||||
|
|
||||||
Differences from sub-agents:
|
Differences from sub-agents:
|
||||||
|
|
@ -415,8 +332,6 @@ Differences from sub-agents:
|
||||||
and `/var/lib/hyperhive/applied` → `/applied` (RO) so the root agent
|
and `/var/lib/hyperhive/applied` → `/applied` (RO) so the root agent
|
||||||
can `git fetch` deployed/failed/denied tags from any agent's
|
can `git fetch` deployed/failed/denied tags from any agent's
|
||||||
authoritative applied repo (see "Root-agent view of applied" below).
|
authoritative applied repo (see "Root-agent view of applied" below).
|
||||||
- First-deploy spawn bypasses the approval queue (the root agent is
|
|
||||||
required infrastructure).
|
|
||||||
- `socket_server::start_manager` binds the root agent's socket,
|
- `socket_server::start_manager` binds the root agent's socket,
|
||||||
pure transport with no dedicated helpers — it uses the same
|
pure transport with no dedicated helpers — it uses the same
|
||||||
per-agent runtime dir as any other agent (`/run/hyperhive/agents/ruth/`),
|
per-agent runtime dir as any other agent (`/run/hyperhive/agents/ruth/`),
|
||||||
|
|
@ -432,26 +347,19 @@ The system prompt (`hive-agent/prompts/system.md`, rendered by
|
||||||
`hive-agent/src/prompt.rs`) is the **same for every agent**; what
|
`hive-agent/src/prompt.rs`) is the **same for every agent**; what
|
||||||
varies is which MCP tools it surfaces (gated by tool groups and
|
varies is which MCP tools it surfaces (gated by tool groups and
|
||||||
capabilities in `agent.nix`). No `role:manager` block renders only
|
capabilities in `agent.nix`). No `role:manager` block renders only
|
||||||
for the root agent. The root agent's approval-gating
|
for the root agent. The root agent's role-specific
|
||||||
behaviour comes from its CLAUDE.md / agent-specific instructions, not
|
behaviour comes from its CLAUDE.md / agent-specific instructions, not
|
||||||
the system prompt template.
|
the system prompt template.
|
||||||
|
|
||||||
## Helper events to the submitting agent
|
## Helper events
|
||||||
|
|
||||||
`Coordinator::notify_submitter(approval_id, &HelperEvent)` routes the
|
`Coordinator::notify_agent(agent, &HelperEvent)` pushes the event into
|
||||||
event to the agent that originally submitted the approval (looked up from
|
the agent's inbox, and the harness delivers it as a regular `system`
|
||||||
the `submitter` column on the `approvals` table). The harness delivers it
|
inbox message so it drives a normal claude turn. A "FYI, check when
|
||||||
as a regular `system` inbox message so it drives a normal claude turn.
|
convenient" event doesn't need a message — hive-c0re publishes those as
|
||||||
`finish_approval` fires an `ApprovalResolved` HelperEvent this way for
|
swarm lifecycle notices instead (`swarm_notices::notify`). Variants
|
||||||
**every** approval kind's terminal state. A
|
(`hive_sh4re::manager::HelperEvent`):
|
||||||
"FYI, check when convenient" event doesn't need a message — hive-c0re publishes those as swarm
|
|
||||||
lifecycle notices instead (`swarm_notices::notify`). Legacy
|
|
||||||
approval rows that predate the submitter column fall back to the
|
|
||||||
root agent. Variants (`hive_sh4re::manager::HelperEvent`):
|
|
||||||
|
|
||||||
- `ApprovalResolved { id, agent, commit_ref, status, note }` —
|
|
||||||
fired by `actions::approve` + `actions::deny` whenever an
|
|
||||||
approval transitions to its terminal state.
|
|
||||||
- `ContainerCrash { agent, note }` — `crash_watch`: a previously-
|
- `ContainerCrash { agent, note }` — `crash_watch`: a previously-
|
||||||
running container went away with no operator-initiated transient
|
running container went away with no operator-initiated transient
|
||||||
state (Stopping / Restarting / Destroying / Rebuilding) AND nothing
|
state (Stopping / Restarting / Destroying / Rebuilding) AND nothing
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,6 @@ keeps its state, purging it doesn't.**
|
||||||
|
|
||||||
Beyond that:
|
Beyond that:
|
||||||
|
|
||||||
- **hive-c0re keeps approvals forever** — they're an audit trail, not a
|
|
||||||
cache. Nothing about them ever ages out.
|
|
||||||
- **Broker messages**: acked ones vacuum after 30 days; anything
|
- **Broker messages**: acked ones vacuum after 30 days; anything
|
||||||
undelivered or delivered-but-not-yet-acked is always kept, however
|
undelivered or delivered-but-not-yet-acked is always kept, however
|
||||||
old.
|
old.
|
||||||
|
|
@ -40,8 +38,8 @@ schemas, file layouts, and internal migration mechanics.
|
||||||
|
|
||||||
### `/var/lib/hyperhive/db/broker.sqlite` (host)
|
### `/var/lib/hyperhive/db/broker.sqlite` (host)
|
||||||
|
|
||||||
Six tables, all in one file — two queues, a small key/value table,
|
Five tables, all in one file — the message queue, a small key/value
|
||||||
the read-only schedule header/targets split, and the per-agent
|
table, the read-only schedule header/targets split, and the per-agent
|
||||||
power-intent registry:
|
power-intent registry:
|
||||||
|
|
||||||
- `messages` — every inter-agent / operator-bound message.
|
- `messages` — every inter-agent / operator-bound message.
|
||||||
|
|
@ -56,8 +54,6 @@ power-intent registry:
|
||||||
per-agent store — see [`/harness/` contents
|
per-agent store — see [`/harness/` contents
|
||||||
below](#state-dirs-per-agent) for where reminders (and todos)
|
below](#state-dirs-per-agent) for where reminders (and todos)
|
||||||
live.
|
live.
|
||||||
- `approvals` — the queue. `agent / kind (update_meta_inputs) /
|
|
||||||
commit_ref / requested_at / status / resolved_at / note`.
|
|
||||||
- `scheduled_prompts` — the scheduled prompts left from before they
|
- `scheduled_prompts` — the scheduled prompts left from before they
|
||||||
moved to swarm level ([`swarm/schedules.md`](../swarm/schedules.md)).
|
moved to swarm level ([`swarm/schedules.md`](../swarm/schedules.md)).
|
||||||
`owner / body / interval_seconds (NULL = one-shot) /
|
`owner / body / interval_seconds (NULL = one-shot) /
|
||||||
|
|
@ -86,8 +82,6 @@ Retention:
|
||||||
rows are always kept — the harness `ack_turn`s only after a
|
rows are always kept — the harness `ack_turn`s only after a
|
||||||
successful turn, so `requeue_inflight` can still requeue
|
successful turn, so `requeue_inflight` can still requeue
|
||||||
an unacked row on a crash.
|
an unacked row on a crash.
|
||||||
- hive-c0re keeps approvals indefinitely — an audit trail. `actions::destroy`
|
|
||||||
rows stay visible to anything that queries by id.
|
|
||||||
- Scheduled prompts: nothing deletes them; the rows stay as they are.
|
- Scheduled prompts: nothing deletes them; the rows stay as they are.
|
||||||
- `agent_power` rows live until the operator destroys the agent (one row per
|
- `agent_power` rows live until the operator destroys the agent (one row per
|
||||||
agent — nothing to vacuum).
|
agent — nothing to vacuum).
|
||||||
|
|
@ -362,10 +356,10 @@ unless it holds the capability, nothing else.
|
||||||
|
|
||||||
Under `/var/lib/hyperhive/applied/<name>/` — the hive-c0re-only
|
Under `/var/lib/hyperhive/applied/<name>/` — the hive-c0re-only
|
||||||
applied repo. Tracks `flake.nix` (module-only boilerplate; never
|
applied repo. Tracks `flake.nix` (module-only boilerplate; never
|
||||||
edited after first spawn) + `agent.nix` (the actual config; the
|
edited after first spawn) + `agent.nix` (the actual config; edits land
|
||||||
root agent's edits land here via the approval flow) + any other
|
here from merged config PRs) + any other files those PRs commit.
|
||||||
files committed via the approval flow. `.git/` carries the proposal /
|
`.git/` carries the proposal / approved / building / deployed / failed /
|
||||||
approved / building / deployed / failed / denied tag history.
|
denied tag history.
|
||||||
|
|
||||||
Under `/var/lib/hyperhive/meta/` — the swarm-wide deploy flake plus
|
Under `/var/lib/hyperhive/meta/` — the swarm-wide deploy flake plus
|
||||||
system-level config files. Single git repo for the whole host; hive-c0re
|
system-level config files. Single git repo for the whole host; hive-c0re
|
||||||
|
|
@ -411,18 +405,17 @@ hive-c0re renders containers onto `meta#<n>` at creation.
|
||||||
See [For operators](#for-operators) above for what each action does to
|
See [For operators](#for-operators) above for what each action does to
|
||||||
an agent's state. The mechanics, for completeness:
|
an agent's state. The mechanics, for completeness:
|
||||||
|
|
||||||
- `DESTR0Y` also drops the systemd drop-in and fails any pending
|
- `DESTR0Y` also drops the systemd drop-in; reviving the agent is
|
||||||
approvals; reviving the agent is `swarmctl agent create` with the same
|
`swarmctl agent create` with the same name and hive, which reuses the
|
||||||
name and hive, which reuses the kept state.
|
kept state.
|
||||||
- `PURG3` wipes `/var/lib/hyperhive/{agents,applied}/<name>/` — the
|
- `PURG3` wipes `/var/lib/hyperhive/{agents,applied}/<name>/` — the
|
||||||
union of everything `DESTR0Y` left behind.
|
union of everything `DESTR0Y` left behind.
|
||||||
|
|
||||||
`actions::destroy` implements the root/bootstrap agent's specialness as a soft policy
|
`actions::destroy` implements the root/bootstrap agent's specialness as a soft policy
|
||||||
guard that refuses to destroy it, backstopped by
|
guard that refuses to destroy it, backstopped by
|
||||||
`auto_update::ensure_root_agent`, which recreates it on the next
|
`auto_update::ensure_root_agent`, which recreates it on the next
|
||||||
hive-c0re startup if it's ever absent (bypassing the approval queue,
|
hive-c0re startup if it's ever absent — so even without the guard,
|
||||||
as required infrastructure) — so even without the guard, destroying it
|
destroying it would only be transient.
|
||||||
would only be transient.
|
|
||||||
|
|
||||||
### btrfs subvolumes for `/var/lib/hyperhive/agents/<name>`
|
### btrfs subvolumes for `/var/lib/hyperhive/agents/<name>`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ swarm UI's job view for progress.
|
||||||
|
|
||||||
Later config changes are PRs on `agent-configs/iris`; you merge them on the
|
Later config changes are PRs on `agent-configs/iris`; you merge them on the
|
||||||
forge, and the merge deploys them. →
|
forge, and the merge deploys them. →
|
||||||
[`agent-lifecycle/approvals.md`](../agent-lifecycle/approvals.md#config-changes)
|
[`agent-lifecycle/config-changes.md`](../agent-lifecycle/config-changes.md#config-changes)
|
||||||
|
|
||||||
## Optional · Lock the hive dashboard
|
## Optional · Lock the hive dashboard
|
||||||
|
|
||||||
|
|
@ -150,7 +150,6 @@ hivectl list-agents # this hive's agents, status, rev
|
||||||
hivectl agent <name> restart # stop + start, no rebuild
|
hivectl agent <name> restart # stop + start, no rebuild
|
||||||
hivectl agent <name> watch # follow its live event stream
|
hivectl agent <name> watch # follow its live event stream
|
||||||
hivectl agent <name> choom # interactive claude session in its container
|
hivectl agent <name> choom # interactive claude session in its container
|
||||||
hivectl approvals pending # what's waiting on you
|
|
||||||
hivectl open # this hive's dashboard (or: forge, matrix)
|
hivectl open # this hive's dashboard (or: forge, matrix)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ Two things live in the `agent-configs` Forgejo organization:
|
||||||
allowlists are the `operators` team, and the agent can neither push
|
allowlists are the `operators` team, and the agent can neither push
|
||||||
`main` directly nor self-merge. An operator's merge in the Forgejo UI
|
`main` directly nor self-merge. An operator's merge in the Forgejo UI
|
||||||
deploys the merged commit (see
|
deploys the merged commit (see
|
||||||
[approvals.md § Config changes](../agent-lifecycle/approvals.md#config-changes)).
|
[config-changes.md § Config changes](../agent-lifecycle/config-changes.md#config-changes)).
|
||||||
hive-c0re never force-pushes: the `push_config` mirror pushes the
|
hive-c0re never force-pushes: the `push_config` mirror pushes the
|
||||||
add-only status tags and `main` without force, and treats a refused
|
add-only status tags and `main` without force, and treats a refused
|
||||||
`main` push as expected.
|
`main` push as expected.
|
||||||
|
|
@ -75,7 +75,7 @@ Two things live in the `agent-configs` Forgejo organization:
|
||||||
agent's config. (Agents remain read-only collaborators on `core/meta`.)
|
agent's config. (Agents remain read-only collaborators on `core/meta`.)
|
||||||
hive-c0re also references this repo as the agent's **persistent meta
|
hive-c0re also references this repo as the agent's **persistent meta
|
||||||
flake input** (`agent-<n>.url = git+http://<forge>/agent-configs/<n>.git`;
|
flake input** (`agent-<n>.url = git+http://<forge>/agent-configs/<n>.git`;
|
||||||
see [approvals.md § Meta flake](../agent-lifecycle/approvals.md)), fetching it as the `core`
|
see [config-changes.md § Meta flake](../agent-lifecycle/config-changes.md#meta-flake)), fetching it as the `core`
|
||||||
user via a git credential helper that reads the live forge-core token —
|
user via a git credential helper that reads the live forge-core token —
|
||||||
so the config lives on the forge, not a hand-synced local checkout.
|
so the config lives on the forge, not a hand-synced local checkout.
|
||||||
- The dashboard links each container's "config" anchor to this
|
- The dashboard links each container's "config" anchor to this
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ A failed or missing login gets `401` with a styled `unauthorized.html` naming th
|
||||||
|
|
||||||
nginx is the one external entry point. The per-agent web-port range (`8100`–`8999`) stays closed: agents serve their UI on a unix socket ([Per-agent unix-socket upstream](#per-agent-unix-socket-upstream)), and the hashed TCP port (`lifecycle::agent_web_port`) is only a fallback bind for an agent missing `HIVE_WEB_SOCKET` — the gateway never proxies through it.
|
nginx is the one external entry point. The per-agent web-port range (`8100`–`8999`) stays closed: agents serve their UI on a unix socket ([Per-agent unix-socket upstream](#per-agent-unix-socket-upstream)), and the hashed TCP port (`lifecycle::agent_web_port`) is only a fallback bind for an agent missing `HIVE_WEB_SOCKET` — the gateway never proxies through it.
|
||||||
|
|
||||||
The dashboard port (`services.hyperhive.c0re.dashboardPort`, default 7000) binds `127.0.0.1` only, so remote dashboard access goes through the gateway. The dashboard can approve, deny and destroy; don't expose it without a reverse proxy in front.
|
The dashboard port (`services.hyperhive.c0re.dashboardPort`, default 7000) binds `127.0.0.1` only, so remote dashboard access goes through the gateway. The dashboard can stop, rebuild and destroy agents; don't expose it without a reverse proxy in front.
|
||||||
|
|
||||||
## `HIVE_FORGE_URL`: agents reach the forge via the gateway by domain
|
## `HIVE_FORGE_URL`: agents reach the forge via the gateway by domain
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -163,27 +163,18 @@ the dashboard's "mark all read" (unbounded, per-agent).
|
||||||
### Loose-ends wire shape
|
### Loose-ends wire shape
|
||||||
|
|
||||||
`LooseEnd` is the per-row response shape for `GetLooseEnds` — one
|
`LooseEnd` is the per-row response shape for `GetLooseEnds` — one
|
||||||
request shape, no per-socket flavours. Tagged enum so
|
request shape, no per-socket flavours. Tagged enum so new thread kinds
|
||||||
new thread kinds (forge PRs, long-running approvals from a
|
(forge PRs, etc.) can land later without breaking existing handlers.
|
||||||
privileged bot, etc.) can land later without breaking existing
|
Each row carries enough context that the caller renders
|
||||||
handlers. Each row carries enough context that the caller renders
|
|
||||||
it directly as a bulleted list, no follow-up fetch needed.
|
it directly as a bulleted list, no follow-up fetch needed.
|
||||||
|
|
||||||
`GetLooseEnds` takes no target — it always returns the caller's own
|
`GetLooseEnds` takes no target — it always returns the caller's own
|
||||||
rows, on both the agent and manager sockets alike (the manager, `ruth`,
|
rows, on both the agent and manager sockets alike (the manager, `ruth`,
|
||||||
is a normal agent here, just deployed automatically with different
|
is a normal agent here, just deployed automatically with different
|
||||||
default capabilities). `Approval` rows only appear when the calling agent is
|
default capabilities). `Reminder` rows always carry `owner == self`.
|
||||||
the manager (sub-agents don't submit approvals). `Reminder` rows always
|
|
||||||
carry `owner == self`.
|
|
||||||
|
|
||||||
Per-variant fields:
|
Per-variant fields:
|
||||||
|
|
||||||
- `Approval { id, agent, commit_ref, description?, age_seconds }`
|
|
||||||
— `agent` is the affected agent (target of the spawn / config
|
|
||||||
commit), not the asker. `description` is the manager's free-text
|
|
||||||
blurb shown on the dashboard card. `commit_ref` is the
|
|
||||||
kind-specific payload (see `docs/agent-lifecycle/approvals.md::Approval kinds
|
|
||||||
(wire shapes)`).
|
|
||||||
- `Reminder { id, owner, message, due_at, age_seconds }` —
|
- `Reminder { id, owner, message, due_at, age_seconds }` —
|
||||||
`due_at` is the absolute time the scheduler is targeting (RFC
|
`due_at` is the absolute time the scheduler is targeting (RFC
|
||||||
3339 on the wire, see *Timestamps on the wire* below); clients
|
3339 on the wire, see *Timestamps on the wire* below); clients
|
||||||
|
|
@ -205,16 +196,9 @@ Per-variant fields:
|
||||||
unsynchronised wall clock, etc.) so the bulleted list never
|
unsynchronised wall clock, etc.) so the bulleted list never
|
||||||
shows nonsense ages.
|
shows nonsense ages.
|
||||||
|
|
||||||
`CancelLooseEnd { kind, id }` is the matching write surface. The
|
`cancel_loose_end` is the matching write surface, handled in the
|
||||||
`kind` enum (`Reminder` / `Approval`) selects which underlying store
|
harness without a hive-c0re round-trip: `reminder` deletes one of the
|
||||||
the dispatcher reaches into. `Reminder` cancels from either surface
|
caller's own pending reminders, `todo` clears one of its todos.
|
||||||
subject to an ownership check (the scheduling agent).
|
|
||||||
`Approval` is manager-only — sub-agents don't submit approvals
|
|
||||||
so they have nothing of their own to withdraw; their wire
|
|
||||||
surface returns a clear error if they try. Cancelling an approval
|
|
||||||
transitions the row to `ApprovalStatus::Cancelled` and fires
|
|
||||||
`ApprovalResolved { status: "cancelled" }` so the dashboard pulls
|
|
||||||
the card out of the pending pane.
|
|
||||||
|
|
||||||
### Agent metadata
|
### Agent metadata
|
||||||
|
|
||||||
|
|
@ -296,7 +280,6 @@ binary flavor.
|
||||||
| `inbox` | `get_loose_ends`, `cancel_loose_end`, `remind` |
|
| `inbox` | `get_loose_ends`, `cancel_loose_end`, `remind` |
|
||||||
| `execution` | vestigial — `mcp__bash__run` / `mcp__bash__status` are always available unconditionally via `extraMcpServers`; this group's entries expand to non-existent `mcp__hyperhive__run` / `mcp__hyperhive__status` and have no effect. See `docs/tools/bash.md`. |
|
| `execution` | vestigial — `mcp__bash__run` / `mcp__bash__status` are always available unconditionally via `extraMcpServers`; this group's entries expand to non-existent `mcp__hyperhive__run` / `mcp__hyperhive__status` and have no effect. See `docs/tools/bash.md`. |
|
||||||
| `lifecycle` | none |
|
| `lifecycle` | none |
|
||||||
| `approvals` | none — gates `cancel_loose_end`'s approval-cancel arm server-side. |
|
|
||||||
| `forge` | none |
|
| `forge` | none |
|
||||||
| `web_tools` | none (gates the Claude built-ins `WebFetch`/`WebSearch`, not an MCP tool) |
|
| `web_tools` | none (gates the Claude built-ins `WebFetch`/`WebSearch`, not an MCP tool) |
|
||||||
|
|
||||||
|
|
@ -316,7 +299,7 @@ clear them.
|
||||||
meta repo alongside `topology.json`). Format: `{ "alice": ["messaging", "meta",
|
meta repo alongside `topology.json`). Format: `{ "alice": ["messaging", "meta",
|
||||||
"inbox", "lifecycle"], "bob": ["messaging", "meta", "inbox"] }`. An absent entry
|
"inbox", "lifecycle"], "bob": ["messaging", "meta", "inbox"] }`. An absent entry
|
||||||
means "use role default." Tool permissions are intentionally NOT configurable
|
means "use role default." Tool permissions are intentionally NOT configurable
|
||||||
from `agent.nix` — that file goes through the manager's approval flow, so
|
from `agent.nix` — that file changes through config PRs the manager opens, so
|
||||||
letting it declare its own groups would let the manager grant itself any tool by
|
letting it declare its own groups would let the manager grant itself any tool by
|
||||||
submitting a config commit, bypassing the operator gate.
|
submitting a config commit, bypassing the operator gate.
|
||||||
|
|
||||||
|
|
@ -385,9 +368,7 @@ dashboard and `app.js` on the per-agent UI) intercepts, shows a spinner,
|
||||||
POSTs `application/x-www-form-urlencoded` (axum's `Form` extractor
|
POSTs `application/x-www-form-urlencoded` (axum's `Form` extractor
|
||||||
rejects multipart), calls `refreshState()` on success. New mutating
|
rejects multipart), calls `refreshState()` on success. New mutating
|
||||||
forms should add `data-async` and optionally `data-confirm` (for a
|
forms should add `data-async` and optionally `data-confirm` (for a
|
||||||
JS-side `confirm()` prompt) or `data-prompt="…"` (for a
|
JS-side `confirm()` prompt).
|
||||||
`window.prompt()` whose answer goes into a hidden input named by
|
|
||||||
`data-prompt-field`, default `note`).
|
|
||||||
|
|
||||||
`refreshState` defers automatically when `document.activeElement`
|
`refreshState` defers automatically when `document.activeElement`
|
||||||
sits inside a managed section so the operator's typing isn't lost;
|
sits inside a managed section so the operator's typing isn't lost;
|
||||||
|
|
@ -404,7 +385,7 @@ into the `Swap` node, then runs `nixos-container update` + stop +
|
||||||
start across the `StopForUpdate → Swap → RebuildBookkeeping`
|
start across the `StopForUpdate → Swap → RebuildBookkeeping`
|
||||||
brace and the tail `Reconcile` node. `flake.nix` itself lives in the agent's
|
brace and the tail `Reconcile` node. `flake.nix` itself lives in the agent's
|
||||||
proposed/applied repos and rides along on every fetch (see
|
proposed/applied repos and rides along on every fetch (see
|
||||||
`docs/agent-lifecycle/approvals.md::Two repos per agent`).
|
`docs/agent-lifecycle/config-changes.md::Two repos per agent`).
|
||||||
|
|
||||||
Anything that changes per-container state on the host should be
|
Anything that changes per-container state on the host should be
|
||||||
re-applied here so a manual `↻ R3BU1LD` from the dashboard is
|
re-applied here so a manual `↻ R3BU1LD` from the dashboard is
|
||||||
|
|
@ -412,7 +393,7 @@ sufficient to recover.
|
||||||
|
|
||||||
## Actions live in one place
|
## Actions live in one place
|
||||||
|
|
||||||
`approve` / `deny` / `destroy` (and the lifecycle helper) live in
|
`destroy` (and the lifecycle helper) live in
|
||||||
`actions.rs` / `hive-c0re/src/dashboard/`. The admin socket and the dashboard
|
`actions.rs` / `hive-c0re/src/dashboard/`. The admin socket and the dashboard
|
||||||
POST handlers both call into them so the two surfaces never drift.
|
POST handlers both call into them so the two surfaces never drift.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -218,15 +218,6 @@ overlap" case. REUSEADDR does **not** allow two simultaneous
|
||||||
`LISTEN` sockets on the same port (that would be `SO_REUSEPORT`,
|
`LISTEN` sockets on the same port (that would be `SO_REUSEPORT`,
|
||||||
not used here) — it preserves exclusivity.
|
not used here) — it preserves exclusivity.
|
||||||
|
|
||||||
## Approvals
|
|
||||||
|
|
||||||
### Orphan approvals
|
|
||||||
|
|
||||||
If test scripts or a manual `rm -rf` wipe state dirs out from under a
|
|
||||||
pending approval, the dashboard's next render marks them
|
|
||||||
`failed` with note `"agent state dir missing"` so they fall out of
|
|
||||||
`pending`. They stay in sqlite for audit.
|
|
||||||
|
|
||||||
## Gateway / SPA serving
|
## Gateway / SPA serving
|
||||||
|
|
||||||
### SPA fallback: use `Accept` header map, not `try_files ... /index.html`
|
### SPA fallback: use `Accept` header map, not `try_files ... /index.html`
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ from the same primitive nodes.
|
||||||
|
|
||||||
### Two levels: DAG and node
|
### Two levels: DAG and node
|
||||||
|
|
||||||
The **DAG** is the unit of cancel / approval-resolution and the
|
The **DAG** is the unit of cancel and the
|
||||||
dashboard group; the **node** is the unit of scheduling / execution /
|
dashboard group; the **node** is the unit of scheduling / execution /
|
||||||
build-log. Deps are intra-DAG edges only (`AfterOk` by default:
|
build-log. Deps are intra-DAG edges only (`AfterOk` by default:
|
||||||
the dep must succeed, a failed/cancelled dep cancels the dependent —
|
the dep must succeed, a failed/cancelled dep cancels the dependent —
|
||||||
|
|
@ -69,9 +69,8 @@ Cheap — no build slot:
|
||||||
| `PauseDrain` | await the harness reporting `PauseAcknowledged`, bounded timeout; best-effort like `Drain` |
|
| `PauseDrain` | await the harness reporting `PauseAcknowledged`, bounded timeout; best-effort like `Drain` |
|
||||||
| `DestroyContainer` | `nixos-container destroy` + un-registration (drop from the roster, clear the ephemeral runtime dir). Runs downstream of a `Stop`, so deliberately excluded from `takes_container_down` — the container is already down by the time it claims |
|
| `DestroyContainer` | `nixos-container destroy` + un-registration (drop from the roster, clear the ephemeral runtime dir). Runs downstream of a `Stop`, so deliberately excluded from `takes_container_down` — the container is already down by the time it claims |
|
||||||
| `PurgeState` | the `purge = true` half of a destroy: delete the agent's state subvolume (via hive-priv) plus its state/applied dirs. Own node because it's conditional and the irreversible step |
|
| `PurgeState` | the `purge = true` half of a destroy: delete the agent's state subvolume (via hive-priv) plus its state/applied dirs. Own node because it's conditional and the irreversible step |
|
||||||
| `DestroyBookkeeping` | the post-destroy tail — meta sync, fail pending approvals, drop the power intent, notify the manager, rescan, re-emit the tombstone. Same split rationale as `RebuildBookkeeping`/`Swap`. Its `purge` flag only selects the wording of the approval-failure reason and the manager notification — the destructive work is `PurgeState`'s |
|
| `DestroyBookkeeping` | the post-destroy tail — meta sync, drop the power intent, notify the manager, rescan, re-emit the tombstone. Same split rationale as `RebuildBookkeeping`/`Swap`. The destructive work is `PurgeState`'s |
|
||||||
| `SetWanted` | write the durable power intent (`wanted = Up`/`Offline`) as the head node of a power-op DAG. Takes the agent lease even though it's a store write, so the intent write and the tail `Reconcile` are atomic per-agent — two racing power ops can't clobber each other's intent before either reconciles |
|
| `SetWanted` | write the durable power intent (`wanted = Up`/`Offline`) as the head node of a power-op DAG. Takes the agent lease even though it's a store write, so the intent write and the tail `Reconcile` are atomic per-agent — two racing power ops can't clobber each other's intent before either reconciles |
|
||||||
| `ResolveApproval` | tail of an approval-carrying DAG — resolve the approval row from how the work ended (`AfterAny`, one node emitted per outcome). Agentless: the approval row already names its agent |
|
|
||||||
| `EmitRebuilt` | tail of a rebuild/perm-change — emit the agent's `Rebuilt` manager event (ok/fail per outcome, nothing on cancel). One node per agent _and_ per outcome |
|
| `EmitRebuilt` | tail of a rebuild/perm-change — emit the agent's `Rebuilt` manager event (ok/fail per outcome, nothing on cancel). One node per agent _and_ per outcome |
|
||||||
| `WriteDropin` | `set_nspawn_flags` + `set_resource_limits` + daemon-reload |
|
| `WriteDropin` | `set_nspawn_flags` + `set_resource_limits` + daemon-reload |
|
||||||
| `WritePermFile` | commit `tool-groups.json` / `capabilities.json` (single git commit under `META_LOCK`) + emit the P3RM1SS10NS snapshots |
|
| `WritePermFile` | commit `tool-groups.json` / `capabilities.json` (single git commit under `META_LOCK`) + emit the P3RM1SS10NS snapshots |
|
||||||
|
|
@ -139,7 +138,7 @@ stop(a..): online a: SetWanted(a,Off) → [Signal→Drain→ if graceful] Re
|
||||||
restart(a..): online a: [Signal→Drain→ if graceful] StopForUpdate(a) → Reconcile(a) (no SetWanted)
|
restart(a..): online a: [Signal→Drain→ if graceful] StopForUpdate(a) → Reconcile(a) (no SetWanted)
|
||||||
offline a: Reconcile(a) (nothing to stop; Reconcile converges to existing wanted)
|
offline a: Reconcile(a) (nothing to stop; Reconcile converges to existing wanted)
|
||||||
start(a..): a: SetWanted(a,Up) → Reconcile(a) (down+stale ⇒ SetWanted(a,Up) → «rebuild subgraph»)
|
start(a..): a: SetWanted(a,Up) → Reconcile(a) (down+stale ⇒ SetWanted(a,Up) → «rebuild subgraph»)
|
||||||
spawn(a): [wanted=Up at approve] Provision(a) → Create(a) → WriteDropin(a) → Reconcile(a)
|
spawn(a): [wanted=Up at deploy] Provision(a) → Create(a) → WriteDropin(a) → Reconcile(a)
|
||||||
perm-change(a): WritePermFile(a) → «rebuild subgraph»
|
perm-change(a): WritePermFile(a) → «rebuild subgraph»
|
||||||
meta-update(inp): MetaLock(inp) →(in-DAG) «rebuild subgraph» per affected agent
|
meta-update(inp): MetaLock(inp) →(in-DAG) «rebuild subgraph» per affected agent
|
||||||
boot: (if any rev marker stale) MetaLock(hyperhive) →(in-DAG) «rebuild subgraph» per stale agent;
|
boot: (if any rev marker stale) MetaLock(hyperhive) →(in-DAG) «rebuild subgraph» per stale agent;
|
||||||
|
|
@ -280,13 +279,6 @@ Roll-up state: `Failed` if any node failed, else `Running` / `Queued` /
|
||||||
DAGs — a flat cap over the whole sorted list, not per template, since
|
DAGs — a flat cap over the whole sorted list, not per template, since
|
||||||
the dashboard renders one recent-builds list and one number bounds it.
|
the dashboard renders one recent-builds list and one number bounds it.
|
||||||
|
|
||||||
### Approvals
|
|
||||||
|
|
||||||
`UpdateMetaInputs` approvals map onto the ordinary `meta-update` shapes.
|
|
||||||
The scheduler fires `actions::resolve_approval_dag` exactly once when
|
|
||||||
**any** approval-carrying DAG settles terminal (including
|
|
||||||
cancelled-while-queued, which fails the approval instead of dangling it).
|
|
||||||
|
|
||||||
### Wire shape
|
### Wire shape
|
||||||
|
|
||||||
`RebuildQueueChanged { seq }` (event name kept) — **a bare trigger, no
|
`RebuildQueueChanged { seq }` (event name kept) — **a bare trigger, no
|
||||||
|
|
@ -297,7 +289,7 @@ That endpoint serves the graph generically (`hive-jobq-wire`): every node
|
||||||
carries `id`, `parent`, `deps`, `state`, `label` (the node kind's own wire
|
carries `id`, `parent`, `deps`, `state`, `label` (the node kind's own wire
|
||||||
string — the kind _is_ the phase label, there is no separate sub-step
|
string — the kind _is_ the phase label, there is no separate sub-step
|
||||||
string) and free-form `data` for what only some kinds have (`agent`,
|
string) and free-form `data` for what only some kinds have (`agent`,
|
||||||
`approval_id`, `inputs`, `build_log_id`). Group roots ride as ordinary
|
`inputs`, `build_log_id`). Group roots ride as ordinary
|
||||||
nodes, so a group's state is just the root's own `state`.
|
nodes, so a group's state is just the root's own `state`.
|
||||||
|
|
||||||
No group-level `agent` exists — agent is per-node, so one group can
|
No group-level `agent` exists — agent is per-node, so one group can
|
||||||
|
|
@ -521,7 +513,7 @@ which is almost never desired. Leave off on non-x86 hosts.
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- `docs/agent-lifecycle/approvals.md` — approval flow
|
- `docs/agent-lifecycle/config-changes.md` — config PRs, meta flake, helper events
|
||||||
- `docs/agent-lifecycle/persistence.md` — SQLite schema, state-dir layout
|
- `docs/agent-lifecycle/persistence.md` — SQLite schema, state-dir layout
|
||||||
- `docs/process/conventions.md` — wire protocol, recipient sentinels
|
- `docs/process/conventions.md` — wire protocol, recipient sentinels
|
||||||
- `docs/agent-lifecycle/agent-roster.md` — the agent roster and privilege
|
- `docs/agent-lifecycle/agent-roster.md` — the agent roster and privilege
|
||||||
|
|
|
||||||
|
|
@ -292,7 +292,7 @@ decision, not an event to adjudicate.
|
||||||
A `config-pr` delivery reporting a PR merged into `main` queues a deploy
|
A `config-pr` delivery reporting a PR merged into `main` queues a deploy
|
||||||
of its `merge_commit_sha` on the one hive whose wanted state places the
|
of its `merge_commit_sha` on the one hive whose wanted state places the
|
||||||
agent; with no such hive, or several, the controller deploys nothing. See
|
agent; with no such hive, or several, the controller deploys nothing. See
|
||||||
[approvals.md § Config changes](../agent-lifecycle/approvals.md#config-changes).
|
[config-changes.md § Config changes](../agent-lifecycle/config-changes.md#config-changes).
|
||||||
A merge whose `closed` delivery never arrives deploys nothing either; the
|
A merge whose `closed` delivery never arrives deploys nothing either; the
|
||||||
operator recovers by redelivering that delivery from the forge hook page,
|
operator recovers by redelivering that delivery from the forge hook page,
|
||||||
which re-enters the same handler and re-queues the deploy.
|
which re-enters the same handler and re-queues the deploy.
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ always on), the gateway proxies all operator-facing traffic, and
|
||||||
|
|
||||||
- **Operator** — reaches every UI (the dashboard + every
|
- **Operator** — reaches every UI (the dashboard + every
|
||||||
per-agent page) through the gateway, on one origin.
|
per-agent page) through the gateway, on one origin.
|
||||||
The core daemon serves operator-authority actions (approve / deny,
|
The core daemon serves operator-authority actions
|
||||||
answer-as-operator, lifecycle POSTs), reachable only via the
|
(answer-as-operator, lifecycle POSTs), reachable only via the
|
||||||
gateway.
|
gateway.
|
||||||
- **Agent** — speaks only for itself, only over its per-agent
|
- **Agent** — speaks only for itself, only over its per-agent
|
||||||
unix socket. The socket's identity _is_ the agent (see
|
unix socket. The socket's identity _is_ the agent (see
|
||||||
|
|
|
||||||
|
|
@ -118,8 +118,9 @@ checkpoints**, not about sandboxing the agent from its own tools:
|
||||||
settings nor merge its own PR. It's **not** set up for external VCS (GitHub
|
settings nor merge its own PR. It's **not** set up for external VCS (GitHub
|
||||||
etc.), though — there, operator-merge is process + accepted risk, not a
|
etc.), though — there, operator-merge is process + accepted risk, not a
|
||||||
technical control.
|
technical control.
|
||||||
- **Approvals** — config changes are config PRs an operator merges on the
|
- **Operator sign-off** — config changes are config PRs an operator merges
|
||||||
forge (see [`approvals.md`](../agent-lifecycle/approvals.md)), and
|
on the forge (see
|
||||||
|
[`config-changes.md`](../agent-lifecycle/config-changes.md)), and
|
||||||
scheduled prompts are operator-only, set at swarm level
|
scheduled prompts are operator-only, set at swarm level
|
||||||
(see [`schedules.md`](../swarm/schedules.md)).
|
(see [`schedules.md`](../swarm/schedules.md)).
|
||||||
|
|
||||||
|
|
@ -314,6 +315,6 @@ writes to only the paths `hive-priv` legitimately needs.
|
||||||
|
|
||||||
| Component | Runs as | Privilege needed for |
|
| Component | Runs as | Privilege needed for |
|
||||||
| ---------------------------- | -------------- | ------------------------------------------------------------ |
|
| ---------------------------- | -------------- | ------------------------------------------------------------ |
|
||||||
| `hive-c0re` | `hive-core` | broker, HTTP dashboard, approvals |
|
| `hive-c0re` | `hive-core` | broker, HTTP dashboard |
|
||||||
| `hive-priv` | `root` | container lifecycle, journal reads, bind mounts, cred writes |
|
| `hive-priv` | `root` | container lifecycle, journal reads, bind mounts, cred writes |
|
||||||
| `hive-ag3nt` (per-container) | per-agent user | turn execution, MCP serving |
|
| `hive-ag3nt` (per-container) | per-agent user | turn execution, MCP serving |
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ for what reaches a subagent's own `--mcp-config` and what doesn't.
|
||||||
|
|
||||||
Tool groups (`HIVE_TOOL_GROUPS`) gate tool access. The default
|
Tool groups (`HIVE_TOOL_GROUPS`) gate tool access. The default
|
||||||
preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and
|
preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and
|
||||||
`execution`. Privileged groups (`lifecycle`, `approvals`)
|
`execution`. The privileged `lifecycle` group
|
||||||
are opt-in via the P3RM1SS10NS tab.
|
is opt-in via the P3RM1SS10NS tab.
|
||||||
|
|
||||||
## Core tools (always available)
|
## Core tools (always available)
|
||||||
|
|
||||||
|
|
@ -61,14 +61,10 @@ are opt-in via the P3RM1SS10NS tab.
|
||||||
Transient pings (sentinel id 0) have nothing to ack and show no marker.
|
Transient pings (sentinel id 0) have nothing to ack and show no marker.
|
||||||
|
|
||||||
**System messages** (from sender `system`): the broker delivers
|
**System messages** (from sender `system`): the broker delivers
|
||||||
`hive_sh4re::manager::HelperEvent` (three variants: `ApprovalResolved`,
|
`hive_sh4re::manager::HelperEvent` (two variants: `ContainerCrash`,
|
||||||
`ContainerCrash`, `NeedsUpdate` — declared, but no call site
|
`NeedsUpdate` — declared, but no call site constructs it) as regular
|
||||||
constructs it) as regular inbox messages (same `recv`
|
inbox messages (same `recv` path; body is a JSON object with an `event`
|
||||||
path; body is a JSON object with an `event` discriminant field).
|
discriminant field).
|
||||||
`ApprovalResolved` goes to whichever agent actually submitted the
|
|
||||||
approval (`Coordinator::notify_submitter`, looked up from the
|
|
||||||
authenticated socket caller at submit time; a row with no
|
|
||||||
recorded submitter falls back to the manager, `ruth`).
|
|
||||||
`ContainerCrash` always goes to `ruth` (`Coordinator::notify_manager`,
|
`ContainerCrash` always goes to `ruth` (`Coordinator::notify_manager`,
|
||||||
hardcoded — `hive-c0re/src/workers/crash_watch.rs`). Lifecycle
|
hardcoded — `hive-c0re/src/workers/crash_watch.rs`). Lifecycle
|
||||||
transitions the job-queue scheduler or crash watcher drive directly —
|
transitions the job-queue scheduler or crash watcher drive directly —
|
||||||
|
|
@ -84,15 +80,11 @@ at_unix_timestamp?)`.
|
||||||
- `get_loose_ends()` — list this agent's own open threads: pending
|
- `get_loose_ends()` — list this agent's own open threads: pending
|
||||||
reminders, open todos pushed by an in-container subsystem (matrix,
|
reminders, open todos pushed by an in-container subsystem (matrix,
|
||||||
bash, forge, or any user-configured MCP server), an undelivered-inbox
|
bash, forge, or any user-configured MCP server), an undelivered-inbox
|
||||||
count, and unread matrix notifications; `approval` rows only appear
|
count, and unread matrix notifications. Always the caller's own rows. Full per-variant wire shape in
|
||||||
when the caller is the manager (`ruth`) — sub-agents don't submit
|
|
||||||
approvals. Always the caller's own rows. Full per-variant wire shape in
|
|
||||||
[`docs/process/conventions.md` § Loose-ends wire
|
[`docs/process/conventions.md` § Loose-ends wire
|
||||||
shape](../process/conventions.md#loose-ends-wire-shape).
|
shape](../process/conventions.md#loose-ends-wire-shape).
|
||||||
- `cancel_loose_end` — hard-delete a `reminder`, cancel a pending
|
- `cancel_loose_end` — hard-delete a `reminder` or clear a `todo` row
|
||||||
`approval` row, or clear a `todo` row (loose-ends-v2). Agents may
|
(loose-ends-v2). Agents may only cancel rows they own.
|
||||||
only cancel rows they own; the `approval` kind is further restricted
|
|
||||||
to the root agent (`ruth`) server-side.
|
|
||||||
- `remind` — schedule a reminder in this agent's own inbox. Large
|
- `remind` — schedule a reminder in this agent's own inbox. Large
|
||||||
payloads spill to `/agents/<self>/state/reminders/`. Pending count
|
payloads spill to `/agents/<self>/state/reminders/`. Pending count
|
||||||
capped at 50 per agent (`HIVE_REMIND_MAX_PENDING_PER_AGENT`).
|
capped at 50 per agent (`HIVE_REMIND_MAX_PENDING_PER_AGENT`).
|
||||||
|
|
@ -144,11 +136,9 @@ hive_name?, swarm_name?, matrix_accounts? }`. `matrix_accounts` is a
|
||||||
(claude or ACP, via `hive-runtime`) as background tasks, shipped
|
(claude or ACP, via `hive-runtime`) as background tasks, shipped
|
||||||
default-on like bash execution; no tool group gates it. See
|
default-on like bash execution; no tool group gates it. See
|
||||||
[`docs/tools/subagent.md`](../tools/subagent.md).
|
[`docs/tools/subagent.md`](../tools/subagent.md).
|
||||||
- **Lifecycle + config** (`lifecycle`, `approvals`) — `approvals`
|
- **Lifecycle + config** (`lifecycle`) — gates nothing.
|
||||||
gates `cancel_loose_end`'s approval-cancel arm server-side;
|
|
||||||
`lifecycle` gates nothing.
|
|
||||||
Config changes go through a forge PR on `agent-configs/<name>` — see
|
Config changes go through a forge PR on `agent-configs/<name>` — see
|
||||||
[`docs/agent-lifecycle/approvals.md`](../agent-lifecycle/approvals.md).
|
[`docs/agent-lifecycle/config-changes.md`](../agent-lifecycle/config-changes.md).
|
||||||
- **Forge repos** (`forge`) — gates nothing.
|
- **Forge repos** (`forge`) — gates nothing.
|
||||||
- **Web egress** (`web_tools`) — enables Claude's built-in `WebFetch`
|
- **Web egress** (`web_tools`) — enables Claude's built-in `WebFetch`
|
||||||
and `WebSearch` tools (not MCP tools; added directly to the
|
and `WebSearch` tools (not MCP tools; added directly to the
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Dashboard & Web UI
|
# Dashboard & Web UI
|
||||||
|
|
||||||
The per-hive dashboard: approvals, container state and the rebuild
|
The per-hive dashboard: container state and the rebuild
|
||||||
queue for one hive. For swarm-wide day to day, see
|
queue for one hive. For swarm-wide day to day, see
|
||||||
[the swarm UI](../swarm/ui.md) instead. If you want implementation
|
[the swarm UI](../swarm/ui.md) instead. If you want implementation
|
||||||
detail — wire formats, DOM structure, event plumbing — see [Dashboard
|
detail — wire formats, DOM structure, event plumbing — see [Dashboard
|
||||||
|
|
@ -20,8 +20,8 @@ of your time. It's a single page with exactly four tabs:
|
||||||
|
|
||||||
- **SW4RM** — every agent, live. This is the default tab and the one
|
- **SW4RM** — every agent, live. This is the default tab and the one
|
||||||
you'll check most.
|
you'll check most.
|
||||||
- **Y3R C4LL** — anything waiting on _you_: pending approvals. If an
|
- **Y3R C4LL** — anything waiting on _you_: messages agents sent to
|
||||||
agent needs a decision from you, it's here.
|
the operator.
|
||||||
- **P3RM1SS10NS** — what tools and system-level access each agent has.
|
- **P3RM1SS10NS** — what tools and system-level access each agent has.
|
||||||
- **SCH3DUL3S** — a read-only list of the scheduled prompts left in the
|
- **SCH3DUL3S** — a read-only list of the scheduled prompts left in the
|
||||||
hive, due for removal; operators set schedules at swarm level now.
|
hive, due for removal; operators set schedules at swarm level now.
|
||||||
|
|
@ -48,14 +48,14 @@ whether it's running, what it's currently doing (a live status pill —
|
||||||
and quick links (stats, screen, forge profile). Select the name to open
|
and quick links (stats, screen, forge profile). Select the name to open
|
||||||
its terminal and watch it work in real time.
|
its terminal and watch it work in real time.
|
||||||
|
|
||||||
**Approve something an agent is waiting on.** Y3R C4LL is the one tab
|
**Read what agents sent you.** Y3R C4LL is the one tab worth checking
|
||||||
worth checking regularly — it's everything that needs _you_. The tab's
|
regularly — it's everything that needs _you_. The tab's count pill
|
||||||
count pill tells you at a glance if anything's pending.
|
tells you at a glance if anything's unread.
|
||||||
|
|
||||||
**Review a config change.** Agent config changes (new packages, env
|
**Review a config change.** Agent config changes (new packages, env
|
||||||
vars, MCP servers) are pull requests on the agent's `agent-configs`
|
vars, MCP servers) are pull requests on the agent's `agent-configs`
|
||||||
repo. Review and merge them on the forge; the merge deploys the change.
|
repo. Review and merge them on the forge; the merge deploys the change.
|
||||||
See [`approvals.md`](../agent-lifecycle/approvals.md#config-changes).
|
See [`config-changes.md`](../agent-lifecycle/config-changes.md#config-changes).
|
||||||
|
|
||||||
**Start, stop, restart, or rebuild an agent.** Select one or more
|
**Start, stop, restart, or rebuild an agent.** Select one or more
|
||||||
agents on SW4RM (select the icon) and use the selection bar, or use the
|
agents on SW4RM (select the icon) and use the selection bar, or use the
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ from the dashboard tab strip.
|
||||||
standalone **C0R3** page (`/core.html`), and FL0W / L0GS / ST4TS
|
standalone **C0R3** page (`/core.html`), and FL0W / L0GS / ST4TS
|
||||||
live on their own pages too, all reachable from the
|
live on their own pages too, all reachable from the
|
||||||
H0M3 hub (not the tab strip). Count pills on SW4RM
|
H0M3 hub (not the tab strip). Count pills on SW4RM
|
||||||
(container count), Y3R C4LL (pending approvals + unread
|
(container count), Y3R C4LL (unread
|
||||||
operator messages), and SCH3DUL3S (active schedules left to copy).
|
operator messages), and SCH3DUL3S (active schedules left to copy).
|
||||||
- **Banner-thin** (`░▒▓█▓▒░ HYPERHIVE / HIVE-C0RE / WE ARE THE WIRED ░▒▓█▓▒░`)
|
- **Banner-thin** (`░▒▓█▓▒░ HYPERHIVE / HIVE-C0RE / WE ARE THE WIRED ░▒▓█▓▒░`)
|
||||||
— sits below the tab strip.
|
— sits below the tab strip.
|
||||||
|
|
@ -46,13 +46,12 @@ from the dashboard tab strip.
|
||||||
snapshot. Hidden when there are no warnings.
|
snapshot. Hidden when there are no warnings.
|
||||||
- **Browser tab title** — `hyperhive // h1ve-c0re` by default; updated
|
- **Browser tab title** — `hyperhive // h1ve-c0re` by default; updated
|
||||||
to `<swarm> / <hive> // h1ve-c0re` once `hive_name` / `swarm_name`
|
to `<swarm> / <hive> // h1ve-c0re` once `hive_name` / `swarm_name`
|
||||||
arrive in the state snapshot. When there are pending approvals, the
|
arrive in the state snapshot. When there are unread operator messages,
|
||||||
page prepends a `(N)` prefix — `(3) pr1ma // h1ve-c0re` — so the
|
the page prepends a `(N)` prefix — `(3) pr1ma // h1ve-c0re` — so the
|
||||||
operator can see the call count in an unfocused browser tab without
|
operator can see the call count in an unfocused browser tab without
|
||||||
opening the dashboard. The page sets the prefix on the initial
|
opening the dashboard. The page sets the prefix on the operator-inbox
|
||||||
`/api/state` cold-load and updates it live via
|
cold-load and updates it live as broker `sent` events arrive; it's
|
||||||
`approval_resolved` SSE events; it's preserved when `hive_name` /
|
preserved when `hive_name` / `swarm_name` later replace the raw title.
|
||||||
`swarm_name` later replace the raw title.
|
|
||||||
|
|
||||||
The FL0W and L0GS pages use a slim header (a `← home` back-link + the
|
The FL0W and L0GS pages use a slim header (a `← home` back-link + the
|
||||||
page title) rather than the dashboard tab strip — they're standalone
|
page title) rather than the dashboard tab strip — they're standalone
|
||||||
|
|
@ -74,12 +73,7 @@ agent is stale.
|
||||||
|
|
||||||
## Y3R C4LL tab
|
## Y3R C4LL tab
|
||||||
|
|
||||||
Things blocked on operator decision — the approval queue.
|
Things blocked on operator decision — the operator inbox.
|
||||||
|
|
||||||
**P3NDING APPR0VALS** — the queue (see "Approval card" below). A
|
|
||||||
`pending · N` / `history · N` tab pair switches between the live
|
|
||||||
queue and the last 30 resolved approvals (see "Approval card" for
|
|
||||||
the history row shape).
|
|
||||||
|
|
||||||
**0PER4T0R 1NB0X** — messages agents have sent to `to="operator"` but
|
**0PER4T0R 1NB0X** — messages agents have sent to `to="operator"` but
|
||||||
the operator hasn't read yet. Cold-loaded from `/api/operator-inbox` on
|
the operator hasn't read yet. Cold-loaded from `/api/operator-inbox` on
|
||||||
|
|
@ -332,7 +326,7 @@ named buckets of MCP tools; each agent starts with a role default
|
||||||
(sub-agents: `messaging`, `meta`, `inbox`, `execution` —
|
(sub-agents: `messaging`, `meta`, `inbox`, `execution` —
|
||||||
`ToolGroup::AGENT_DEFAULT`; hive-c0re seeds the root agent to
|
`ToolGroup::AGENT_DEFAULT`; hive-c0re seeds the root agent to
|
||||||
`ToolGroup::MANAGER_DEFAULT` — `messaging`, `meta`, `inbox`,
|
`ToolGroup::MANAGER_DEFAULT` — `messaging`, `meta`, `inbox`,
|
||||||
`lifecycle`, `approvals`, `execution`,
|
`lifecycle`, `execution`,
|
||||||
that is, every group except `forge` and `web_tools`). Checking /
|
that is, every group except `forge` and `web_tools`). Checking /
|
||||||
unchecking stages which groups are active for the agent; the
|
unchecking stages which groups are active for the agent; the
|
||||||
page-level **save all** button (below) commits it. Columns come from
|
page-level **save all** button (below) commits it. Columns come from
|
||||||
|
|
@ -341,8 +335,7 @@ takes effect.
|
||||||
<!-- vale write-good.Passive = YES -->
|
<!-- vale write-good.Passive = YES -->
|
||||||
|
|
||||||
The current tool groups are: `messaging`, `meta`, `inbox`, `lifecycle`,
|
The current tool groups are: `messaging`, `meta`, `inbox`, `lifecycle`,
|
||||||
`approvals`, `forge`, `execution`,
|
`forge`, `execution`, `web_tools`. All listed in `ToolGroup::ALL` in `hive-sh4re`. The
|
||||||
`web_tools`. All listed in `ToolGroup::ALL` in `hive-sh4re`. The
|
|
||||||
`web_tools` group is special: it carries no MCP tools; instead it adds
|
`web_tools` group is special: it carries no MCP tools; instead it adds
|
||||||
Claude's built-in `WebFetch` and `WebSearch` to `--tools` /
|
Claude's built-in `WebFetch` and `WebSearch` to `--tools` /
|
||||||
`--allowedTools` for that agent session.
|
`--allowedTools` for that agent session.
|
||||||
|
|
@ -739,19 +732,17 @@ that wants step-level counts instead, unused here.
|
||||||
|
|
||||||
### Themed dialogs
|
### Themed dialogs
|
||||||
|
|
||||||
All confirmations, prompts, and transient error notices use an
|
All confirmations and transient error notices use an in-app themed
|
||||||
in-app themed dialog system (`@hive/shared/modal.js`) rather than the
|
dialog system (`@hive/shared/modal.js`) rather than the browser's
|
||||||
browser's native `confirm()` / `prompt()` / `alert()` chrome, so
|
native `confirm()` / `alert()` chrome, so they match the Catppuccin
|
||||||
they match the Catppuccin palette and the OS can't style them away.
|
palette and the OS can't style them away. Two primitives, both built
|
||||||
Three primitives, all built on the `openDialog` core:
|
on the `openDialog` core:
|
||||||
|
|
||||||
- `themedConfirm({ message, danger, confirmLabel, checkboxes })`
|
- `themedConfirm({ message, danger, confirmLabel, checkboxes })`
|
||||||
— a modal confirm that resolves to `null` on cancel or an object
|
— a modal confirm that resolves to `null` on cancel or an object
|
||||||
of checkbox states on confirm. Destructive actions pass
|
of checkbox states on confirm. Destructive actions pass
|
||||||
`danger: true` (the confirm button turns red and the cancel
|
`danger: true` (the confirm button turns red and the cancel
|
||||||
button takes focus). Selecting the backdrop and `Esc` both cancel.
|
button takes focus). Selecting the backdrop and `Esc` both cancel.
|
||||||
- `themedPrompt(...)` — a modal with a text input, resolving to the
|
|
||||||
entered string or `null`.
|
|
||||||
- `themedToast(message, { type, ... })` — a non-blocking toast (top-right,
|
- `themedToast(message, { type, ... })` — a non-blocking toast (top-right,
|
||||||
`info` / `error` / `ok`) for transient validation + action
|
`info` / `error` / `ok`) for transient validation + action
|
||||||
failures, so an error doesn't trap the operator behind a modal.
|
failures, so an error doesn't trap the operator behind a modal.
|
||||||
|
|
@ -816,51 +807,18 @@ frosted-mauve bar slides up from the bottom of the viewport
|
||||||
Every render prunes stale selections (agents destroyed while
|
Every render prunes stale selections (agents destroyed while
|
||||||
selected) before the bar appears.
|
selected) before the bar appears.
|
||||||
|
|
||||||
## Approval card
|
|
||||||
|
|
||||||
Each pending approval renders as a card (`assets/call.js::
|
|
||||||
renderApprovals`) with three stacked sections:
|
|
||||||
|
|
||||||
- **identity header** — glyph, `#id`, agent, kind chip, and a
|
|
||||||
right-aligned `requested <N> ago` relative time from
|
|
||||||
`ApprovalView.requested_at`. Glyph and chip vary by kind:
|
|
||||||
|
|
||||||
| kind | glyph | chip |
|
|
||||||
|---|---|---|
|
|
||||||
| `update_meta_inputs` | `↻` | `meta-update` |
|
|
||||||
|
|
||||||
<!-- vale write-good.Passive = NO -->
|
|
||||||
The chip ticks live every second via a `data-requested-at`
|
|
||||||
attribute + client-side interval (no re-render). Turns amber once
|
|
||||||
the request has been pending ≥ 1h so a stale approval stands out;
|
|
||||||
the `.stale` class flips precisely at the 3600s boundary rather
|
|
||||||
than at the next `renderApprovals` call.
|
|
||||||
- **what-changed body** — the submitting agent's description, then the
|
|
||||||
kind's payload: the inputs to bump (`update_meta_inputs`).
|
|
||||||
- **decision actions** — `◆ APPR0VE` and `DENY`. Deny pops a
|
|
||||||
`prompt()` for an optional reason carried to the submitting agent as
|
|
||||||
`HelperEvent::ApprovalResolved.note`.
|
|
||||||
<!-- vale write-good.Passive = YES -->
|
|
||||||
|
|
||||||
A `pending · N` / `history · N` tab pair switches the section
|
|
||||||
between the live queue and the last 30 resolved approvals.
|
|
||||||
|
|
||||||
## Browser notifications
|
## Browser notifications
|
||||||
|
|
||||||
Pure frontend (`Notification` API). Two signals trigger them:
|
Pure frontend (`Notification` API), triggered by a new broker message
|
||||||
|
sent `to: "operator"` (live via SSE).
|
||||||
- new pending approval (per id, delta on `/api/state`)
|
|
||||||
- new broker message sent `to: "operator"` (live via SSE)
|
|
||||||
|
|
||||||
The toggle controls live in the Y3R C4LL tab's ◆ PR3F3R3NC3S ◆
|
The toggle controls live in the Y3R C4LL tab's ◆ PR3F3R3NC3S ◆
|
||||||
section; see that section above for the user-facing shape. Dispatch
|
section; see that section above for the user-facing shape. Dispatch
|
||||||
logic lives in `common.js::NOTIF`.
|
logic lives in `common.js::NOTIF`.
|
||||||
|
|
||||||
<!-- vale write-good.Passive = NO -->
|
<!-- vale write-good.Passive = NO -->
|
||||||
First `/api/state` after page load seeds "seen" sets without
|
Only messages that arrive while the page is open count. Per-event tags
|
||||||
firing — only items that arrive while the page is open count.
|
(`hyperhive:msg:<at>:<rand>`) so distinct events stack in the OS
|
||||||
Per-event tags (`hyperhive:approval:<id>`,
|
|
||||||
`hyperhive:msg:<at>:<rand>`) so distinct events stack in the OS
|
|
||||||
notification center instead of overwriting each other.
|
notification center instead of overwriting each other.
|
||||||
`console.debug` logs at every block point (unsupported,
|
`console.debug` logs at every block point (unsupported,
|
||||||
permission ungranted, muted) for in-browser debugging. Selecting it
|
permission ungranted, muted) for in-browser debugging. Selecting it
|
||||||
|
|
@ -880,17 +838,6 @@ the H0M3 hub's **API** tile), with the raw spec at `/api/openapi.json`.
|
||||||
It's a growing supplement, not yet a full replacement for the list
|
It's a growing supplement, not yet a full replacement for the list
|
||||||
below — some endpoints aren't in it yet.
|
below — some endpoints aren't in it yet.
|
||||||
|
|
||||||
- `POST /api/approve/{id}` — approve a pending approval. Fires
|
|
||||||
`ApprovalResolved` on the dashboard event channel; client
|
|
||||||
updates derived approvals state from the event.
|
|
||||||
- `POST /api/deny/{id}` (`note=<reason>`, optional) — deny a pending
|
|
||||||
approval with an optional operator-supplied reason. The reason
|
|
||||||
travels to the submitting agent as `HelperEvent::ApprovalResolved.note`
|
|
||||||
and also rides on the dashboard's `ApprovalResolved` event.
|
|
||||||
Dashboard prompts via the themed `themedPrompt()` dialog on
|
|
||||||
selection — a resizable `<textarea>` where Enter submits and
|
|
||||||
Shift+Enter inserts a newline (so multi-sentence rejection
|
|
||||||
notes are possible).
|
|
||||||
- `POST /api/{rebuild,kill,restart,start,destroy}/{name}` — lifecycle.
|
- `POST /api/{rebuild,kill,restart,start,destroy}/{name}` — lifecycle.
|
||||||
`destroy` accepts `purge=on` to also wipe state dirs.
|
`destroy` accepts `purge=on` to also wipe state dirs.
|
||||||
- `POST /api/purge-tombstone/{name}` — wipe a tombstone's state dirs.
|
- `POST /api/purge-tombstone/{name}` — wipe a tombstone's state dirs.
|
||||||
|
|
@ -1046,10 +993,6 @@ payload):
|
||||||
rows with a `↳ reply` tag that scroll-highlights the parent
|
rows with a `↳ reply` tag that scroll-highlights the parent
|
||||||
row on selection. Used by the message-flow terminal renderer and
|
row on selection. Used by the message-flow terminal renderer and
|
||||||
the operator-inbox derived state.
|
the operator-inbox derived state.
|
||||||
- `approval_resolved` (id, agent, approval_kind, status,
|
|
||||||
resolved_at, note, description) — moves a row from the pending
|
|
||||||
queue to history. Client mutates a derived store and
|
|
||||||
re-renders only the approvals section.
|
|
||||||
- `transient_set` (name, transient_kind, since_unix) /
|
- `transient_set` (name, transient_kind, since_unix) /
|
||||||
`transient_cleared` (name) — lifecycle action spinners. The
|
`transient_cleared` (name) — lifecycle action spinners. The
|
||||||
client ticks the elapsed-seconds badge off `since_unix`
|
client ticks the elapsed-seconds badge off `since_unix`
|
||||||
|
|
@ -1104,15 +1047,13 @@ payload):
|
||||||
`/api/state` is **only fetched on cold-load and on the few
|
`/api/state` is **only fetched on cold-load and on the few
|
||||||
forms that mutate non-event-derived state** (PURG3 +
|
forms that mutate non-event-derived state** (PURG3 +
|
||||||
meta-update, since tombstones + meta_inputs aren't event-
|
meta-update, since tombstones + meta_inputs aren't event-
|
||||||
shaped yet). Every other section — approvals,
|
shaped yet). Every other section — transients,
|
||||||
transients, containers, operator inbox, message flow —
|
containers, operator inbox, message flow —
|
||||||
derives from `/api/dashboard/stream` after the initial snapshot,
|
derives from `/api/dashboard/stream` after the initial snapshot,
|
||||||
maintaining its own client-side store and applying events on
|
maintaining its own client-side store and applying events on
|
||||||
top.
|
top.
|
||||||
<!-- vale write-good.Passive = YES -->
|
<!-- vale write-good.Passive = YES -->
|
||||||
|
|
||||||
Generalised form helpers: `form[data-confirm="…"]` pops
|
Generalised form helper: `form[data-confirm="…"]` pops
|
||||||
`confirm()` before submit; `form[data-prompt="…"]` pops
|
`confirm()` before submit.
|
||||||
`prompt()` and stashes the answer in a hidden input named by
|
|
||||||
`data-prompt-field` (default `note`).
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,7 @@
|
||||||
(per-agent) → `text/event-stream` SSE for live updates. The
|
(per-agent) → `text/event-stream` SSE for live updates. The
|
||||||
dashboard stream carries broker `Sent` / `Delivered` (mirrored
|
dashboard stream carries broker `Sent` / `Delivered` (mirrored
|
||||||
by a forwarder task from the broker's intra-process channel)
|
by a forwarder task from the broker's intra-process channel)
|
||||||
plus mutation events (`approval_resolved`,
|
plus mutation events (`transient_set` / `transient_cleared`). Each frame carries a `seq`. The
|
||||||
`transient_set` / `transient_cleared`). Each frame carries a `seq`. The
|
|
||||||
matching backfill endpoint is `GET /api/dashboard/history` (last
|
matching backfill endpoint is `GET /api/dashboard/history` (last
|
||||||
~200 broker messages wrapped in `{ seq, events }`) on the
|
~200 broker messages wrapped in `{ seq, events }`) on the
|
||||||
dashboard and `GET /events/history` (last 2000 `LiveEvent`s
|
dashboard and `GET /events/history` (last 2000 `LiveEvent`s
|
||||||
|
|
@ -262,8 +261,8 @@ that needs it rather than ripped out along with their one caller.
|
||||||
swipes in from the right — a singleton `#side-panel` with a
|
swipes in from the right — a singleton `#side-panel` with a
|
||||||
titled header, a close button, and a scrollable body. Closes on
|
titled header, a close button, and a scrollable body. Closes on
|
||||||
the button, selecting the backdrop, or `Escape`. `Panel.open(title,
|
the button, selecting the backdrop, or `Escape`. `Panel.open(title,
|
||||||
node)` swaps the body; the JS builders for file previews,
|
node)` swaps the body; the JS builders for file previews and
|
||||||
approval diffs, and journald logs all render into it. **The
|
journald logs all render into it. **The
|
||||||
drawer width is drag-to-resize**: a thin 6px hit-strip on
|
drawer width is drag-to-resize**: a thin 6px hit-strip on
|
||||||
the left edge captures pointer events, resizes the drawer in
|
the left edge captures pointer events, resizes the drawer in
|
||||||
real-time (pointer capture keeps dragging even if the cursor
|
real-time (pointer capture keeps dragging even if the cursor
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue