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
|
|
@ -26,8 +26,8 @@ declarations.
|
|||
## Agent lifecycle
|
||||
|
||||
- **How do config changes flow from manager to operator to container?** →
|
||||
[`agent-lifecycle/approvals.md`](agent-lifecycle/approvals.md) (approval kinds, approval
|
||||
state machine, `flake.lock` validation).
|
||||
[`agent-lifecycle/config-changes.md`](agent-lifecycle/config-changes.md)
|
||||
(config PRs, the meta flake, helper events).
|
||||
- **What state survives destroy / purge / restart?** →
|
||||
[`agent-lifecycle/persistence.md`](agent-lifecycle/persistence.md).
|
||||
- **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
|
||||
can't do itself. Nothing submits a new approval today: the one kind
|
||||
left, a meta-input bump, only reads back from existing rows. Config
|
||||
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.
|
||||
Config changes are pull requests on the agent's config repo, which an
|
||||
operator merges on the forge; that merge deploys them. Helper events are
|
||||
how agents stay informed about lifecycle changes that need a reaction.
|
||||
|
||||
## 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
|
||||
`agent-configs/<agent>`. Review it on the forge like any other PR, and
|
||||
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
|
||||
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.
|
||||
No MCP tool exists for config changes: opening the PR IS the request.
|
||||
|
||||
1. The submitting agent (the child's parent, holding the `approvals`
|
||||
tool group) **clones** `agent-configs/<name>`, edits it there (any
|
||||
1. The submitting agent **clones** `agent-configs/<name>`, edits it there (any
|
||||
tracked path, but `agent.nix` is the contract entry point), commits
|
||||
with its own git identity, and pushes a branch + opens a PR with
|
||||
`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
|
||||
deploys nothing.
|
||||
|
||||
## Approval queue
|
||||
|
||||
### Withdrawing a pending approval
|
||||
|
||||
The submitting agent can call `cancel_loose_end(kind: "approval", id)` to
|
||||
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.
|
||||
Creating a brand-new agent is 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
|
||||
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)).
|
||||
|
||||
### Approval kinds (wire shapes)
|
||||
|
||||
`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 -->
|
||||
## Agent lifecycle
|
||||
|
||||
### Reminder delivery: file-path semantics
|
||||
|
||||
|
|
@ -144,10 +96,10 @@ body strings before calling it.
|
|||
|
||||
### Destroy semantics
|
||||
|
||||
`HostRequest::Destroy { name, purge }` is the lifecycle tear-down,
|
||||
not an approval. Stops + removes the nspawn container, drops the
|
||||
systemd drop-in, fails any pending approvals. Persistent state
|
||||
(proposed/applied repos, claude credentials, `/state/` notes) is
|
||||
`HostRequest::Destroy { name, purge }` is the lifecycle tear-down.
|
||||
Stops + removes the nspawn container and drops the systemd drop-in.
|
||||
Persistent state (proposed/applied repos, claude credentials, `/state/`
|
||||
notes) is
|
||||
**kept by default** — recreating the agent with the same name
|
||||
reuses prior config + login. With `purge = true` the agent's
|
||||
`/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
|
||||
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
|
||||
|
||||
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
|
||||
capability sees no other agent's config at all.
|
||||
|
||||
An agent with the `approvals` tool group submits a change the same way
|
||||
it makes any other change: **clone the target agent's config repo from the
|
||||
An agent submits a change to another agent's config the same way it
|
||||
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
|
||||
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.
|
||||
|
||||
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/agents/ruth/config/`. The root agent can edit its own
|
||||
`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.
|
||||
|
||||
Differences from sub-agents:
|
||||
|
|
@ -415,8 +332,6 @@ Differences from sub-agents:
|
|||
and `/var/lib/hyperhive/applied` → `/applied` (RO) so the root agent
|
||||
can `git fetch` deployed/failed/denied tags from any agent's
|
||||
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,
|
||||
pure transport with no dedicated helpers — it uses the same
|
||||
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
|
||||
varies is which MCP tools it surfaces (gated by tool groups and
|
||||
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
|
||||
the system prompt template.
|
||||
|
||||
## Helper events to the submitting agent
|
||||
## Helper events
|
||||
|
||||
`Coordinator::notify_submitter(approval_id, &HelperEvent)` routes the
|
||||
event to the agent that originally submitted the approval (looked up from
|
||||
the `submitter` column on the `approvals` table). The harness delivers it
|
||||
as a regular `system` inbox message so it drives a normal claude turn.
|
||||
`finish_approval` fires an `ApprovalResolved` HelperEvent this way for
|
||||
**every** approval kind's terminal state. A
|
||||
"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`):
|
||||
`Coordinator::notify_agent(agent, &HelperEvent)` pushes the event into
|
||||
the agent's inbox, and the harness delivers it as a regular `system`
|
||||
inbox message so it drives a normal claude turn. A "FYI, check when
|
||||
convenient" event doesn't need a message — hive-c0re publishes those as
|
||||
swarm lifecycle notices instead (`swarm_notices::notify`). 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-
|
||||
running container went away with no operator-initiated transient
|
||||
state (Stopping / Restarting / Destroying / Rebuilding) AND nothing
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ keeps its state, purging it doesn't.**
|
|||
|
||||
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
|
||||
undelivered or delivered-but-not-yet-acked is always kept, however
|
||||
old.
|
||||
|
|
@ -40,8 +38,8 @@ schemas, file layouts, and internal migration mechanics.
|
|||
|
||||
### `/var/lib/hyperhive/db/broker.sqlite` (host)
|
||||
|
||||
Six tables, all in one file — two queues, a small key/value table,
|
||||
the read-only schedule header/targets split, and the per-agent
|
||||
Five tables, all in one file — the message queue, a small key/value
|
||||
table, the read-only schedule header/targets split, and the per-agent
|
||||
power-intent registry:
|
||||
|
||||
- `messages` — every inter-agent / operator-bound message.
|
||||
|
|
@ -56,8 +54,6 @@ power-intent registry:
|
|||
per-agent store — see [`/harness/` contents
|
||||
below](#state-dirs-per-agent) for where reminders (and todos)
|
||||
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
|
||||
moved to swarm level ([`swarm/schedules.md`](../swarm/schedules.md)).
|
||||
`owner / body / interval_seconds (NULL = one-shot) /
|
||||
|
|
@ -86,8 +82,6 @@ Retention:
|
|||
rows are always kept — the harness `ack_turn`s only after a
|
||||
successful turn, so `requeue_inflight` can still requeue
|
||||
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.
|
||||
- `agent_power` rows live until the operator destroys the agent (one row per
|
||||
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
|
||||
applied repo. Tracks `flake.nix` (module-only boilerplate; never
|
||||
edited after first spawn) + `agent.nix` (the actual config; the
|
||||
root agent's edits land here via the approval flow) + any other
|
||||
files committed via the approval flow. `.git/` carries the proposal /
|
||||
approved / building / deployed / failed / denied tag history.
|
||||
edited after first spawn) + `agent.nix` (the actual config; edits land
|
||||
here from merged config PRs) + any other files those PRs commit.
|
||||
`.git/` carries the proposal / approved / building / deployed / failed /
|
||||
denied tag history.
|
||||
|
||||
Under `/var/lib/hyperhive/meta/` — the swarm-wide deploy flake plus
|
||||
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
|
||||
an agent's state. The mechanics, for completeness:
|
||||
|
||||
- `DESTR0Y` also drops the systemd drop-in and fails any pending
|
||||
approvals; reviving the agent is `swarmctl agent create` with the same
|
||||
name and hive, which reuses the kept state.
|
||||
- `DESTR0Y` also drops the systemd drop-in; reviving the agent is
|
||||
`swarmctl agent create` with the same name and hive, which reuses the
|
||||
kept state.
|
||||
- `PURG3` wipes `/var/lib/hyperhive/{agents,applied}/<name>/` — the
|
||||
union of everything `DESTR0Y` left behind.
|
||||
|
||||
`actions::destroy` implements the root/bootstrap agent's specialness as a soft policy
|
||||
guard that refuses to destroy it, backstopped by
|
||||
`auto_update::ensure_root_agent`, which recreates it on the next
|
||||
hive-c0re startup if it's ever absent (bypassing the approval queue,
|
||||
as required infrastructure) — so even without the guard, destroying it
|
||||
would only be transient.
|
||||
hive-c0re startup if it's ever absent — so even without the guard,
|
||||
destroying it would only be transient.
|
||||
|
||||
### 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
|
||||
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
|
||||
|
||||
|
|
@ -150,7 +150,6 @@ hivectl list-agents # this hive's agents, status, rev
|
|||
hivectl agent <name> restart # stop + start, no rebuild
|
||||
hivectl agent <name> watch # follow its live event stream
|
||||
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)
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ Two things live in the `agent-configs` Forgejo organization:
|
|||
allowlists are the `operators` team, and the agent can neither push
|
||||
`main` directly nor self-merge. An operator's merge in the Forgejo UI
|
||||
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
|
||||
add-only status tags and `main` without force, and treats a refused
|
||||
`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`.)
|
||||
hive-c0re also references this repo as the agent's **persistent meta
|
||||
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 —
|
||||
so the config lives on the forge, not a hand-synced local checkout.
|
||||
- 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.
|
||||
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -163,27 +163,18 @@ the dashboard's "mark all read" (unbounded, per-agent).
|
|||
### Loose-ends wire shape
|
||||
|
||||
`LooseEnd` is the per-row response shape for `GetLooseEnds` — one
|
||||
request shape, no per-socket flavours. Tagged enum so
|
||||
new thread kinds (forge PRs, long-running approvals from a
|
||||
privileged bot, etc.) can land later without breaking existing
|
||||
handlers. Each row carries enough context that the caller renders
|
||||
request shape, no per-socket flavours. Tagged enum so new thread kinds
|
||||
(forge PRs, etc.) can land later without breaking existing handlers.
|
||||
Each row carries enough context that the caller renders
|
||||
it directly as a bulleted list, no follow-up fetch needed.
|
||||
|
||||
`GetLooseEnds` takes no target — it always returns the caller's own
|
||||
rows, on both the agent and manager sockets alike (the manager, `ruth`,
|
||||
is a normal agent here, just deployed automatically with different
|
||||
default capabilities). `Approval` rows only appear when the calling agent is
|
||||
the manager (sub-agents don't submit approvals). `Reminder` rows always
|
||||
carry `owner == self`.
|
||||
default capabilities). `Reminder` rows always carry `owner == self`.
|
||||
|
||||
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 }` —
|
||||
`due_at` is the absolute time the scheduler is targeting (RFC
|
||||
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
|
||||
shows nonsense ages.
|
||||
|
||||
`CancelLooseEnd { kind, id }` is the matching write surface. The
|
||||
`kind` enum (`Reminder` / `Approval`) selects which underlying store
|
||||
the dispatcher reaches into. `Reminder` cancels from either surface
|
||||
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.
|
||||
`cancel_loose_end` is the matching write surface, handled in the
|
||||
harness without a hive-c0re round-trip: `reminder` deletes one of the
|
||||
caller's own pending reminders, `todo` clears one of its todos.
|
||||
|
||||
### Agent metadata
|
||||
|
||||
|
|
@ -296,7 +280,6 @@ binary flavor.
|
|||
| `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`. |
|
||||
| `lifecycle` | none |
|
||||
| `approvals` | none — gates `cancel_loose_end`'s approval-cancel arm server-side. |
|
||||
| `forge` | none |
|
||||
| `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",
|
||||
"inbox", "lifecycle"], "bob": ["messaging", "meta", "inbox"] }`. An absent entry
|
||||
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
|
||||
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
|
||||
rejects multipart), calls `refreshState()` on success. New mutating
|
||||
forms should add `data-async` and optionally `data-confirm` (for a
|
||||
JS-side `confirm()` prompt) or `data-prompt="…"` (for a
|
||||
`window.prompt()` whose answer goes into a hidden input named by
|
||||
`data-prompt-field`, default `note`).
|
||||
JS-side `confirm()` prompt).
|
||||
|
||||
`refreshState` defers automatically when `document.activeElement`
|
||||
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`
|
||||
brace and the tail `Reconcile` node. `flake.nix` itself lives in the agent's
|
||||
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
|
||||
re-applied here so a manual `↻ R3BU1LD` from the dashboard is
|
||||
|
|
@ -412,7 +393,7 @@ sufficient to recover.
|
|||
|
||||
## 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
|
||||
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`,
|
||||
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
|
||||
|
||||
### 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
|
||||
|
||||
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 /
|
||||
build-log. Deps are intra-DAG edges only (`AfterOk` by default:
|
||||
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` |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|
|
@ -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)
|
||||
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»)
|
||||
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»
|
||||
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;
|
||||
|
|
@ -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
|
||||
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
|
||||
|
||||
`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
|
||||
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`,
|
||||
`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`.
|
||||
|
||||
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
|
||||
|
||||
- `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/process/conventions.md` — wire protocol, recipient sentinels
|
||||
- `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
|
||||
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
|
||||
[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
|
||||
operator recovers by redelivering that delivery from the forge hook page,
|
||||
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
|
||||
per-agent page) through the gateway, on one origin.
|
||||
The core daemon serves operator-authority actions (approve / deny,
|
||||
answer-as-operator, lifecycle POSTs), reachable only via the
|
||||
The core daemon serves operator-authority actions
|
||||
(answer-as-operator, lifecycle POSTs), reachable only via the
|
||||
gateway.
|
||||
- **Agent** — speaks only for itself, only over its per-agent
|
||||
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
|
||||
etc.), though — there, operator-merge is process + accepted risk, not a
|
||||
technical control.
|
||||
- **Approvals** — config changes are config PRs an operator merges on the
|
||||
forge (see [`approvals.md`](../agent-lifecycle/approvals.md)), and
|
||||
- **Operator sign-off** — config changes are config PRs an operator merges
|
||||
on the forge (see
|
||||
[`config-changes.md`](../agent-lifecycle/config-changes.md)), and
|
||||
scheduled prompts are operator-only, set at swarm level
|
||||
(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 |
|
||||
| ---------------------------- | -------------- | ------------------------------------------------------------ |
|
||||
| `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-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
|
||||
preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and
|
||||
`execution`. Privileged groups (`lifecycle`, `approvals`)
|
||||
are opt-in via the P3RM1SS10NS tab.
|
||||
`execution`. The privileged `lifecycle` group
|
||||
is opt-in via the P3RM1SS10NS tab.
|
||||
|
||||
## 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.
|
||||
|
||||
**System messages** (from sender `system`): the broker delivers
|
||||
`hive_sh4re::manager::HelperEvent` (three variants: `ApprovalResolved`,
|
||||
`ContainerCrash`, `NeedsUpdate` — declared, but no call site
|
||||
constructs it) as regular inbox messages (same `recv`
|
||||
path; body is a JSON object with an `event` 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`).
|
||||
`hive_sh4re::manager::HelperEvent` (two variants: `ContainerCrash`,
|
||||
`NeedsUpdate` — declared, but no call site constructs it) as regular
|
||||
inbox messages (same `recv` path; body is a JSON object with an `event`
|
||||
discriminant field).
|
||||
`ContainerCrash` always goes to `ruth` (`Coordinator::notify_manager`,
|
||||
hardcoded — `hive-c0re/src/workers/crash_watch.rs`). Lifecycle
|
||||
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
|
||||
reminders, open todos pushed by an in-container subsystem (matrix,
|
||||
bash, forge, or any user-configured MCP server), an undelivered-inbox
|
||||
count, and unread matrix notifications; `approval` rows only appear
|
||||
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
|
||||
count, and unread matrix notifications. Always the caller's own rows. Full per-variant wire shape in
|
||||
[`docs/process/conventions.md` § Loose-ends wire
|
||||
shape](../process/conventions.md#loose-ends-wire-shape).
|
||||
- `cancel_loose_end` — hard-delete a `reminder`, cancel a pending
|
||||
`approval` row, or clear a `todo` row (loose-ends-v2). Agents may
|
||||
only cancel rows they own; the `approval` kind is further restricted
|
||||
to the root agent (`ruth`) server-side.
|
||||
- `cancel_loose_end` — hard-delete a `reminder` or clear a `todo` row
|
||||
(loose-ends-v2). Agents may only cancel rows they own.
|
||||
- `remind` — schedule a reminder in this agent's own inbox. Large
|
||||
payloads spill to `/agents/<self>/state/reminders/`. Pending count
|
||||
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
|
||||
default-on like bash execution; no tool group gates it. See
|
||||
[`docs/tools/subagent.md`](../tools/subagent.md).
|
||||
- **Lifecycle + config** (`lifecycle`, `approvals`) — `approvals`
|
||||
gates `cancel_loose_end`'s approval-cancel arm server-side;
|
||||
`lifecycle` gates nothing.
|
||||
- **Lifecycle + config** (`lifecycle`) — gates nothing.
|
||||
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.
|
||||
- **Web egress** (`web_tools`) — enables Claude's built-in `WebFetch`
|
||||
and `WebSearch` tools (not MCP tools; added directly to the
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# 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
|
||||
[the swarm UI](../swarm/ui.md) instead. If you want implementation
|
||||
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
|
||||
you'll check most.
|
||||
- **Y3R C4LL** — anything waiting on _you_: pending approvals. If an
|
||||
agent needs a decision from you, it's here.
|
||||
- **Y3R C4LL** — anything waiting on _you_: messages agents sent to
|
||||
the operator.
|
||||
- **P3RM1SS10NS** — what tools and system-level access each agent has.
|
||||
- **SCH3DUL3S** — a read-only list of the scheduled prompts left in the
|
||||
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
|
||||
its terminal and watch it work in real time.
|
||||
|
||||
**Approve something an agent is waiting on.** Y3R C4LL is the one tab
|
||||
worth checking regularly — it's everything that needs _you_. The tab's
|
||||
count pill tells you at a glance if anything's pending.
|
||||
**Read what agents sent you.** Y3R C4LL is the one tab worth checking
|
||||
regularly — it's everything that needs _you_. The tab's count pill
|
||||
tells you at a glance if anything's unread.
|
||||
|
||||
**Review a config change.** Agent config changes (new packages, env
|
||||
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.
|
||||
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
|
||||
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
|
||||
live on their own pages too, all reachable from the
|
||||
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).
|
||||
- **Banner-thin** (`░▒▓█▓▒░ HYPERHIVE / HIVE-C0RE / WE ARE THE WIRED ░▒▓█▓▒░`)
|
||||
— sits below the tab strip.
|
||||
|
|
@ -46,13 +46,12 @@ from the dashboard tab strip.
|
|||
snapshot. Hidden when there are no warnings.
|
||||
- **Browser tab title** — `hyperhive // h1ve-c0re` by default; updated
|
||||
to `<swarm> / <hive> // h1ve-c0re` once `hive_name` / `swarm_name`
|
||||
arrive in the state snapshot. When there are pending approvals, the
|
||||
page prepends a `(N)` prefix — `(3) pr1ma // h1ve-c0re` — so the
|
||||
arrive in the state snapshot. When there are unread operator messages,
|
||||
the page prepends a `(N)` prefix — `(3) pr1ma // h1ve-c0re` — so the
|
||||
operator can see the call count in an unfocused browser tab without
|
||||
opening the dashboard. The page sets the prefix on the initial
|
||||
`/api/state` cold-load and updates it live via
|
||||
`approval_resolved` SSE events; it's preserved when `hive_name` /
|
||||
`swarm_name` later replace the raw title.
|
||||
opening the dashboard. The page sets the prefix on the operator-inbox
|
||||
cold-load and updates it live as broker `sent` events arrive; it's
|
||||
preserved when `hive_name` / `swarm_name` later replace the raw title.
|
||||
|
||||
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
|
||||
|
|
@ -74,12 +73,7 @@ agent is stale.
|
|||
|
||||
## Y3R C4LL tab
|
||||
|
||||
Things blocked on operator decision — the approval queue.
|
||||
|
||||
**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).
|
||||
Things blocked on operator decision — the operator inbox.
|
||||
|
||||
**0PER4T0R 1NB0X** — messages agents have sent to `to="operator"` but
|
||||
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` —
|
||||
`ToolGroup::AGENT_DEFAULT`; hive-c0re seeds the root agent to
|
||||
`ToolGroup::MANAGER_DEFAULT` — `messaging`, `meta`, `inbox`,
|
||||
`lifecycle`, `approvals`, `execution`,
|
||||
`lifecycle`, `execution`,
|
||||
that is, every group except `forge` and `web_tools`). Checking /
|
||||
unchecking stages which groups are active for the agent; the
|
||||
page-level **save all** button (below) commits it. Columns come from
|
||||
|
|
@ -341,8 +335,7 @@ takes effect.
|
|||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
The current tool groups are: `messaging`, `meta`, `inbox`, `lifecycle`,
|
||||
`approvals`, `forge`, `execution`,
|
||||
`web_tools`. All listed in `ToolGroup::ALL` in `hive-sh4re`. The
|
||||
`forge`, `execution`, `web_tools`. All listed in `ToolGroup::ALL` in `hive-sh4re`. The
|
||||
`web_tools` group is special: it carries no MCP tools; instead it adds
|
||||
Claude's built-in `WebFetch` and `WebSearch` to `--tools` /
|
||||
`--allowedTools` for that agent session.
|
||||
|
|
@ -739,19 +732,17 @@ that wants step-level counts instead, unused here.
|
|||
|
||||
### Themed dialogs
|
||||
|
||||
All confirmations, prompts, and transient error notices use an
|
||||
in-app themed dialog system (`@hive/shared/modal.js`) rather than the
|
||||
browser's native `confirm()` / `prompt()` / `alert()` chrome, so
|
||||
they match the Catppuccin palette and the OS can't style them away.
|
||||
Three primitives, all built on the `openDialog` core:
|
||||
All confirmations and transient error notices use an in-app themed
|
||||
dialog system (`@hive/shared/modal.js`) rather than the browser's
|
||||
native `confirm()` / `alert()` chrome, so they match the Catppuccin
|
||||
palette and the OS can't style them away. Two primitives, both built
|
||||
on the `openDialog` core:
|
||||
|
||||
- `themedConfirm({ message, danger, confirmLabel, checkboxes })`
|
||||
— a modal confirm that resolves to `null` on cancel or an object
|
||||
of checkbox states on confirm. Destructive actions pass
|
||||
`danger: true` (the confirm button turns red and the 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,
|
||||
`info` / `error` / `ok`) for transient validation + action
|
||||
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
|
||||
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
|
||||
|
||||
Pure frontend (`Notification` API). Two signals trigger them:
|
||||
|
||||
- new pending approval (per id, delta on `/api/state`)
|
||||
- new broker message sent `to: "operator"` (live via SSE)
|
||||
Pure frontend (`Notification` API), triggered by a new broker message
|
||||
sent `to: "operator"` (live via SSE).
|
||||
|
||||
The toggle controls live in the Y3R C4LL tab's ◆ PR3F3R3NC3S ◆
|
||||
section; see that section above for the user-facing shape. Dispatch
|
||||
logic lives in `common.js::NOTIF`.
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
First `/api/state` after page load seeds "seen" sets without
|
||||
firing — only items that arrive while the page is open count.
|
||||
Per-event tags (`hyperhive:approval:<id>`,
|
||||
`hyperhive:msg:<at>:<rand>`) so distinct events stack in the OS
|
||||
Only messages that arrive while the page is open count. Per-event tags
|
||||
(`hyperhive:msg:<at>:<rand>`) so distinct events stack in the OS
|
||||
notification center instead of overwriting each other.
|
||||
`console.debug` logs at every block point (unsupported,
|
||||
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
|
||||
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.
|
||||
`destroy` accepts `purge=on` to also wipe 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
|
||||
row on selection. Used by the message-flow terminal renderer and
|
||||
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_cleared` (name) — lifecycle action spinners. The
|
||||
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
|
||||
forms that mutate non-event-derived state** (PURG3 +
|
||||
meta-update, since tombstones + meta_inputs aren't event-
|
||||
shaped yet). Every other section — approvals,
|
||||
transients, containers, operator inbox, message flow —
|
||||
shaped yet). Every other section — transients,
|
||||
containers, operator inbox, message flow —
|
||||
derives from `/api/dashboard/stream` after the initial snapshot,
|
||||
maintaining its own client-side store and applying events on
|
||||
top.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
Generalised form helpers: `form[data-confirm="…"]` pops
|
||||
`confirm()` before submit; `form[data-prompt="…"]` pops
|
||||
`prompt()` and stashes the answer in a hidden input named by
|
||||
`data-prompt-field` (default `note`).
|
||||
Generalised form helper: `form[data-confirm="…"]` pops
|
||||
`confirm()` before submit.
|
||||
|
||||
|
|
|
|||
|
|
@ -50,8 +50,7 @@
|
|||
(per-agent) → `text/event-stream` SSE for live updates. The
|
||||
dashboard stream carries broker `Sent` / `Delivered` (mirrored
|
||||
by a forwarder task from the broker's intra-process channel)
|
||||
plus mutation events (`approval_resolved`,
|
||||
`transient_set` / `transient_cleared`). Each frame carries a `seq`. The
|
||||
plus mutation events (`transient_set` / `transient_cleared`). Each frame carries a `seq`. The
|
||||
matching backfill endpoint is `GET /api/dashboard/history` (last
|
||||
~200 broker messages wrapped in `{ seq, events }`) on the
|
||||
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
|
||||
titled header, a close button, and a scrollable body. Closes on
|
||||
the button, selecting the backdrop, or `Escape`. `Panel.open(title,
|
||||
node)` swaps the body; the JS builders for file previews,
|
||||
approval diffs, and journald logs all render into it. **The
|
||||
node)` swaps the body; the JS builders for file previews and
|
||||
journald logs all render into it. **The
|
||||
drawer width is drag-to-resize**: a thin 6px hit-strip on
|
||||
the left edge captures pointer events, resizes the drawer in
|
||||
real-time (pointer capture keeps dragging even if the cursor
|
||||
|
|
|
|||
Loading…
Reference in a new issue