Compare commits

...
4 changed files with 59 additions and 19 deletions

View file

@ -81,7 +81,7 @@ hive-c0re/ host daemon + sibling operator CLI (lib + 2 bins)
fans out one Message per active target, fans out one Message per active target,
re-arms recurring rows, deletes fired one-shots re-arms recurring rows, deletes fired one-shots
src/events_vacuum.rs host-side hourly sweep of every agent's src/events_vacuum.rs host-side hourly sweep of every agent's
/state/hyperhive-events.sqlite /harness/hyperhive-events.sqlite
src/crash_watch.rs poll every 10s; fire HelperEvent::ContainerCrash src/crash_watch.rs poll every 10s; fire HelperEvent::ContainerCrash
when a previously-running container disappears when a previously-running container disappears
without an operator-initiated transient (or a without an operator-initiated transient (or a
@ -130,6 +130,17 @@ hive-c0re/ host daemon + sibling operator CLI (lib + 2 bins)
read). Reconciled by `meta::sync_agents`; read). Reconciled by `meta::sync_agents`;
operator/manager edits land via the operator/manager edits land via the
eventual write API. eventual write API.
src/tool_groups.rs per-agent tool-group config — reads/writes
`/var/lib/hyperhive/meta/tool-groups.json`;
`set_groups` + `groups_for` + `remove_agent`.
Wired into `render_flake` for `HIVE_TOOL_GROUPS`
injection; operator write via dashboard action.
src/limits.rs wire-protocol size limits (inline body cap,
reminder spill threshold) shared across agent
+ manager sockets. Single source of truth.
src/flake_check.rs pre-apply `flake.lock` validation — rejects
proposals missing `inputs.<X>.follows` wiring
(duplicate `original` node check).
src/forge.rs optional Forgejo wiring: per-agent users + src/forge.rs optional Forgejo wiring: per-agent users +
tokens, the `agent-configs` org (`push_config`), tokens, the `agent-configs` org (`push_config`),
and meta read access; mirrors each applied repo and meta read access; mirrors each applied repo
@ -177,14 +188,14 @@ hive-ag3nt/ in-container harness crate; produces ONE `hive`
/api/compact, /api/model, /events/history, /api/compact, /api/model, /events/history,
/screen, /screen/ws) /screen, /screen/ws)
src/turn_stats.rs per-turn analytics sink (one sqlite row per src/turn_stats.rs per-turn analytics sink (one sqlite row per
turn at /state/hyperhive-turn-stats.sqlite); turn at /harness/hyperhive-turn-stats.sqlite);
schema + best-effort writer schema + best-effort writer
src/stats.rs read-side aggregations over turn-stats.sqlite src/stats.rs read-side aggregations over turn-stats.sqlite
backing the /stats page (bucketed Snapshot: backing the /stats page (bucketed Snapshot:
turns / duration / tokens / model mix) turns / duration / tokens / model mix)
src/events.rs LiveEvent + broadcast Bus + sqlite-backed history src/events.rs LiveEvent + broadcast Bus + sqlite-backed history
(/state/hyperhive-events.sqlite) + TurnState + (/harness/hyperhive-events.sqlite) + TurnState +
model selection (persisted at /state/hyperhive-model) model selection (persisted at /harness/hyperhive-model)
src/turn.rs claude --print + stream-json pump; --compact retry; src/turn.rs claude --print + stream-json pump; --compact retry;
proactive compaction + auto session-reset proactive compaction + auto session-reset
src/mcp.rs embedded MCP server (rmcp): AgentServer + ManagerServer src/mcp.rs embedded MCP server (rmcp): AgentServer + ManagerServer
@ -383,3 +394,5 @@ The docs below own the details — this section just points at them.
window:** → [`docs/turn-loop.md`](docs/turn-loop.md). window:** → [`docs/turn-loop.md`](docs/turn-loop.md).
- **Two-step spawn, approval flow, flake.lock validation:** - **Two-step spawn, approval flow, flake.lock validation:**
[`docs/approvals.md`](docs/approvals.md). [`docs/approvals.md`](docs/approvals.md).

View file

@ -285,6 +285,7 @@ binary flavor.
| `messaging` | `send`, `recv`, `ask`, `answer` | | `messaging` | `send`, `recv`, `ask`, `answer` |
| `meta` | `set_status`, `get_agent_meta` | | `meta` | `set_status`, `get_agent_meta` |
| `inbox` | `get_loose_ends`, `cancel_loose_end`, `remind`, `request_next_turn` | | `inbox` | `get_loose_ends`, `cancel_loose_end`, `remind`, `request_next_turn` |
| `execution` | `bash_run`, `bash_status` |
| `lifecycle` | `kill`, `start`, `restart`, `update` *(privileged)* | | `lifecycle` | `kill`, `start`, `restart`, `update` *(privileged)* |
| `approvals` | `request_init_config`, `request_apply_commit`, `request_update_meta_inputs` *(privileged)* | | `approvals` | `request_init_config`, `request_apply_commit`, `request_update_meta_inputs` *(privileged)* |
| `scheduling` | `request_schedule_prompt`, `fire_schedule_now`, `cancel_schedule`, `edit_schedule`, `list_schedules` *(privileged)* | | `scheduling` | `request_schedule_prompt`, `fire_schedule_now`, `cancel_schedule`, `edit_schedule`, `list_schedules` *(privileged)* |
@ -307,7 +308,7 @@ the new `HIVE_TOOL_GROUPS` env var. Agents with no entry get no var.
**Runtime resolution** — at session start the harness reads `HIVE_TOOL_GROUPS` **Runtime resolution** — at session start the harness reads `HIVE_TOOL_GROUPS`
(a comma-separated list of snake_case group names injected by the meta renderer (a comma-separated list of snake_case group names injected by the meta renderer
from `tool-groups.json`). Unrecognised tokens are logged and skipped. Falls back from `tool-groups.json`). Unrecognised tokens are logged and skipped. Falls back
to `ToolGroup::AGENT_DEFAULT` (`messaging`, `meta`, `inbox`) or to `ToolGroup::AGENT_DEFAULT` (`messaging`, `meta`, `inbox`, `execution`) or
`ToolGroup::MANAGER_DEFAULT` (all groups) when the var is absent or empty. `ToolGroup::MANAGER_DEFAULT` (all groups) when the var is absent or empty.
**Updating the surface** — when a new `#[tool]` fn is added to `AgentServer` **Updating the surface** — when a new `#[tool]` fn is added to `AgentServer`

View file

@ -64,17 +64,17 @@ Retention:
`cancelled_at_unix`, then `reap_cancelled` drops the row on `cancelled_at_unix`, then `reap_cancelled` drops the row on
the next worker pass. the next worker pass.
### `/state/hyperhive-events.sqlite` (per agent) ### `/harness/hyperhive-events.sqlite` (per agent)
Lives inside each container's bind-mounted `/state/` dir (host Lives inside each container's bind-mounted `/harness/` dir (host
path: `/var/lib/hyperhive/agents/<name>/state/hyperhive-events.sqlite`). path: `/var/lib/hyperhive/agents/<name>/harness/hyperhive-events.sqlite`).
One table: One table:
- `events(id, ts, kind, payload_json)` — every `LiveEvent` the - `events(id, ts, kind, payload_json)` — every `LiveEvent` the
harness emits during turn loop execution. harness emits during turn loop execution.
The harness writes; the host vacuums. `hive-c0re::events_vacuum` The harness writes; the host vacuums. `hive-c0re::events_vacuum`
runs hourly and sweeps every existing agent state dir, deleting runs hourly and sweeps every existing agent harness dir, deleting
rows older than 7 days. Age-only — no row cap — so a chatty turn rows older than 7 days. Age-only — no row cap — so a chatty turn
doesn't lose history sooner than a quiet one; disk pressure on a doesn't lose history sooner than a quiet one; disk pressure on a
sustained burst is the cheaper problem to have. Centralising sustained burst is the cheaper problem to have. Centralising
@ -82,12 +82,12 @@ retention on the host means a misbehaving harness can't disable
its own vacuum and agents don't need any cleanup wiring of their its own vacuum and agents don't need any cleanup wiring of their
own. own.
Path overridable via `HYPERHIVE_EVENTS_DB` (for dev / no-`/state` Path overridable via `HYPERHIVE_EVENTS_DB` (for dev / no-`/harness`
setups). On open failure the `Bus` falls back to no-store mode setups). On open failure the `Bus` falls back to no-store mode
rather than crashing the harness — events still broadcast over SSE, rather than crashing the harness — events still broadcast over SSE,
just nothing persisted. just nothing persisted.
### `/state/hyperhive-turn-stats.sqlite` (per agent) ### `/harness/hyperhive-turn-stats.sqlite` (per agent)
Per-turn analytics sink. One row per claude turn captures Per-turn analytics sink. One row per claude turn captures
identity (`model`, `wake_from`, `result_kind`), timing identity (`model`, `wake_from`, `result_kind`), timing
@ -149,7 +149,7 @@ Writes are best-effort: `append_stdout` / `append_stderr` / `finish`
log a warning on sqlite error and let the build continue. A failed log a warning on sqlite error and let the build continue. A failed
log row never blocks a rebuild. log row never blocks a rebuild.
### `/state/hyperhive-model` (per agent) ### `/harness/hyperhive-model` (per agent)
Single-line text file holding the claude model name currently Single-line text file holding the claude model name currently
selected for this agent (default `haiku` when absent). Written by selected for this agent (default `haiku` when absent). Written by
@ -169,11 +169,21 @@ Under `/var/lib/hyperhive/agents/<name>/`:
`/agents` tree bind. `/agents` tree bind.
- `claude/` — claude OAuth credentials, bind-mounted RW to - `claude/` — claude OAuth credentials, bind-mounted RW to
`/home/<name>/.claude` inside the container. `/home/<name>/.claude` inside the container.
- `state/` — durable notes, the events.sqlite db, and the - `state/` — durable notes and `hyperhive-harness.json`. Bind-mounted
turn-stats sqlite db. Bind-mounted to `/agents/<name>/state` to `/agents/<name>/state` inside the container (uniform for
inside the container (uniform for sub-agents + manager). sub-agents + manager). The `$HYPERHIVE_STATE_DIR` env var exposes
The `$HYPERHIVE_STATE_DIR` env var exposes the same path to the same path to in-container scripts.
in-container scripts. - `harness/` — harness-internal ephemeral state; not intended for
agent consumption. Bind-mounted to `/agents/<name>/harness`
inside the container (`$HYPERHIVE_HARNESS_DIR`). Contents:
- `bash-tasks/` — task JSON + stdout/stderr files for
background `bash_run` jobs. JSON files are
`<id>.json` (status + tails), `<id>.out` / `<id>.err`
(full captured output). Task files persist until container
purge.
- `hyperhive-events.sqlite` — turn-loop event log.
- `hyperhive-turn-stats.sqlite` — per-turn timing stats.
- `hyperhive-model` — single-line model name override file.
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
@ -207,7 +217,7 @@ step would re-fire).
- `PURG3` (opt-in via the dashboard button or - `PURG3` (opt-in via the dashboard button or
`hive-c0re destroy --purge <name>`) — DESTR0Y plus wipes `hive-c0re destroy --purge <name>`) — DESTR0Y plus wipes
`/var/lib/hyperhive/{agents,applied}/<name>/`. Config history, `/var/lib/hyperhive/{agents,applied}/<name>/`. Config history,
claude creds, /state/ notes, and the events db are all gone. claude creds, /state/ notes, and the harness dir are all gone.
No undo. No undo.
The manager is non-destroyable from both paths (declarative The manager is non-destroyable from both paths (declarative
@ -314,3 +324,4 @@ trigger from the `.path` watcher becomes a no-op. Setting it to
`false` lets re-fires actually re-execute. The trade-off is the `false` lets re-fires actually re-execute. The trade-off is the
service unit shows `inactive (dead)` between fires — visible in service unit shows `inactive (dead)` between fires — visible in
`journalctl` but harmless; the `.path` unit drives the lifecycle. `journalctl` but harmless; the `.path` unit drives the lifecycle.

View file

@ -148,7 +148,7 @@ default is set by `hyperhive.model` in the agent's `agent.nix`
(NixOS option; propagates via `HIVE_DEFAULT_MODEL` env var; falls (NixOS option; propagates via `HIVE_DEFAULT_MODEL` env var; falls
back to `"haiku"` if unset). The operator can flip it at runtime back to `"haiku"` if unset). The operator can flip it at runtime
with `/model <name>` in the web terminal — the next turn picks it with `/model <name>` in the web terminal — the next turn picks it
up. The choice is persisted to `/state/hyperhive-model` so it up. The choice is persisted to `/harness/hyperhive-model` so it
survives restart; override path: `HYPERHIVE_MODEL_FILE` env var survives restart; override path: `HYPERHIVE_MODEL_FILE` env var
for tests. for tests.
@ -369,6 +369,20 @@ it as a stdio child via `--mcp-config`. The hyperhive socket name is
continue without waiting for an external message. The next turn starts continue without waiting for an external message. The next turn starts
with `from: "self"` and `body: "continue"`. No-op if new inbox with `from: "self"` and `body: "continue"`. No-op if new inbox
messages arrive before this turn ends. No args. messages arrive before this turn ends. No args.
- `bash_run(cmd, timeout_secs?)` — submit a shell command for
background execution (`sh -c <cmd>`). Returns a task ID immediately;
the command runs asynchronously in a harness-managed tokio task. Stdout
and stderr stream to `harness/bash-tasks/<id>.{out,err}`. When the
task completes (or times out, or the process errors), the harness wakes
the agent with a summary body — handle on a future turn. Default
timeout 180s; pass `timeout_secs` to override. Requires the
`execution` tool group.
- `bash_status(id)` — poll the status of a task submitted with
`bash_run`. Returns status (`pending`/`running`/`done`/`timed_out`/
`interrupted`), exit code, run duration, and the last 4 KiB of stdout
and stderr (full output in the `.out`/`.err` files). Tasks marked
`interrupted` had their process killed by a harness restart; a best-
effort wake was still sent so the agent is not silently blocked.
### Waking the agent from inside the container ### Waking the agent from inside the container
@ -573,3 +587,4 @@ The harness reads `/etc/hyperhive/bash-allow.json` and replaces
Commands outside the pattern list require confirmation — which in Commands outside the pattern list require confirmation — which in
`--print` mode means they will not run. An empty list (default) keeps `--print` mode means they will not run. An empty list (default) keeps
the current wholesale `Bash` entry. the current wholesale `Bash` entry.