docs: scheduled prompts are swarm-level; the hive page is a read-only list
Drops docs/tools/scheduling.md and every description of hive-level schedule creation, the worker, the removed MCP tools, the scheduling tool group and the removed endpoints/events, pointing at docs/swarm/schedules.md.
This commit is contained in:
parent
a4a3f81d5c
commit
b4a2aeb5ba
18 changed files with 90 additions and 346 deletions
|
|
@ -31,8 +31,8 @@ hand-maintained per-file tree drifts out of sync with the code.
|
|||
user). `src/main.rs` is the `hive-c0re` binary — **daemon-only**
|
||||
(`serve` + the periodic vacuum/sweep loops); the operator CLI lives in
|
||||
the separate `hivectl` crate, which talks to the daemon over the host
|
||||
admin socket. Owns the sqlite broker, approval + reminder +
|
||||
schedule queues, the meta flake, lifecycle (`nixos-container`
|
||||
admin socket. Owns the sqlite broker, approval + reminder
|
||||
queues, the meta flake, lifecycle (`nixos-container`
|
||||
shellouts), gateway / forge / matrix wiring (agent forge and matrix
|
||||
accounts are `swarm-controller`'s), per-container stats, and the axum
|
||||
operator dashboard (`dashboard/`). Largest crate.
|
||||
|
|
|
|||
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1769,7 +1769,6 @@ dependencies = [
|
|||
"clap_complete",
|
||||
"forgejo-api",
|
||||
"futures-util",
|
||||
"hive-agent-sock",
|
||||
"hive-core-agent-sock",
|
||||
"hive-host-sock",
|
||||
"hive-jobq",
|
||||
|
|
@ -1777,7 +1776,6 @@ dependencies = [
|
|||
"hive-log",
|
||||
"hive-priv-sock",
|
||||
"hive-sh4re",
|
||||
"hive-sock-client",
|
||||
"hive-types",
|
||||
"hmac 0.13.0",
|
||||
"indicatif",
|
||||
|
|
|
|||
|
|
@ -79,9 +79,9 @@ sudo inside the cage) and a durable, self-compacting session. Out of the
|
|||
box it gets:
|
||||
|
||||
- **messaging** — `send` / `recv` to other agents and the operator,
|
||||
`remind` and recurring schedules to wake itself, and a todo list that
|
||||
forge notifications and other producers feed. →
|
||||
[scheduling](docs/tools/scheduling.md)
|
||||
`remind` to wake itself, scheduled prompts the operator sets for it at
|
||||
swarm level, and a todo list that forge notifications and other
|
||||
producers feed. → [schedules](docs/swarm/schedules.md)
|
||||
- **background bash** — long-running commands as tracked tasks
|
||||
(`run` / `status` / `kill`), so a build doesn't block the turn. →
|
||||
[bash](docs/tools/bash.md)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ declarations.
|
|||
go deeper into implementation.
|
||||
- **What tools does an agent (or the operator) have available?** →
|
||||
[`tools/`](tools/README.md) — `hivectl` (yours) plus every agent's MCP
|
||||
tool surface (bash, forge, lifecycle, matrix, scheduling).
|
||||
tool surface (bash, forge, lifecycle, matrix).
|
||||
|
||||
## Agent lifecycle
|
||||
|
||||
|
|
|
|||
|
|
@ -94,12 +94,8 @@ apply.
|
|||
approval step — every other agent is created at swarm level
|
||||
(`swarmctl agent create`).
|
||||
Roster-wise, `ruth` is just another entry.
|
||||
- **Wire-protocol** — the `*(privileged)*` `Request` variants in
|
||||
`hive-core-agent-sock`'s unified enum are the scheduling ops
|
||||
(`RequestSchedulePrompt`, `CancelSchedule`, `FireScheduleNow`,
|
||||
`EditSchedule`), reachable only from the manager's socket flavour,
|
||||
matching the `scheduling` tool group
|
||||
([`docs/turn-loop/mcp.md`](../turn-loop/mcp.md)). Container lifecycle
|
||||
- **Wire-protocol** — `hive-core-agent-sock`'s unified `Request` enum
|
||||
is the same on both socket flavours. Container lifecycle
|
||||
ops (kill/start/restart/rebuild) go through the separate host-admin
|
||||
socket `hivectl` speaks. `Wake` (inject a `from: <X>` message straight
|
||||
into the caller's own inbox) is on this socket too, unprivileged on
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# Approvals + helper events
|
||||
|
||||
The approval queue is where an agent asks the operator for something it
|
||||
can't do itself: a scheduled prompt, or a meta-input bump. Config
|
||||
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
|
||||
|
|
@ -16,13 +17,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).
|
||||
- **Scheduled prompt** (`SchedulePrompt`) — an agent asked to schedule
|
||||
a message to one or more inboxes at a future time. It lands on your
|
||||
dashboard's Y3R C4LL tab (or `hivectl approvals pending` /
|
||||
`approve <id>` from the CLI). You can also add schedules yourself
|
||||
directly from the SCH3DUL3S tab, which skips this approval step
|
||||
entirely — the gate here is specifically for an *agent* asking to
|
||||
schedule something, not for you doing it.
|
||||
- **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.
|
||||
|
|
@ -113,9 +107,8 @@ merged by an operator on the forge (see [Config changes](#config-changes)).
|
|||
|
||||
### Approval kinds (wire shapes)
|
||||
|
||||
`ApprovalKind` carries two variants; each maps to a different
|
||||
`commit_ref` encoding because `ApprovalKind` overloads that field as
|
||||
the kind-specific payload carrier.
|
||||
`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
|
||||
|
|
@ -123,64 +116,8 @@ the kind-specific payload carrier.
|
|||
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.
|
||||
- `SchedulePrompt` — `commit_ref` stores the JSON-encoded
|
||||
`SchedulePromptPayload` (target list, body, schedule) so the
|
||||
approval row carries the full submission verbatim. On approve
|
||||
hive-c0re inserts a row into `scheduled_prompts` with
|
||||
`source = approval:<id>`; the worker fans the body out as
|
||||
inbox messages to each target at the scheduled time, recurring
|
||||
when `interval_seconds` is set.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
### Scheduled prompts (submit paths)
|
||||
|
||||
Two ways a row lands in `scheduled_prompts`:
|
||||
|
||||
- **Operator-direct** (`source = "operator"`): the operator adds a schedule through the dashboard form. Lands in the table immediately, no approval gate — operator action is already the trust boundary.
|
||||
- **Agent-requested** (`source = "approval:<id>"`): an agent submits a `RequestSchedulePrompt` through its MCP socket (the `request_schedule_prompt` tool, `scheduling` group). hive-c0re queues an `ApprovalKind::SchedulePrompt` row; on approve, it inserts the schedule row with `source = approval:<id>` so the audit trail points back at the operator decision (above).
|
||||
|
||||
No self-target shortcut: even agent-self schedules need approval. The existing `remind` MCP tool stays the quick self-wake path (no approval, lands directly in the agent's own inbox); this module is the bigger, multi-recipient, operator-visible thing.
|
||||
|
||||
### Scheduled prompt worker (catch-up clamp)
|
||||
|
||||
When hive-c0re comes back from being down, the worker sees rows whose `next_fire_at_unix` is well in the past. For recurring rows that would mean firing N delayed pulses in a row — spammy and useless. Instead the worker fires **once** per row and bumps `next_fire_at_unix` to the next interval slot ≥ `now`, recording how many cycles it skipped in `last_result` (per-target). Operators see "fired late, caught up from 17 skipped" instead of 17 wake-up storms.
|
||||
|
||||
The worker fires one-shot rows once (if past due, on the next worker pass) and deletes them; recurring rows survive until cancelled.
|
||||
|
||||
`targets` is its own table (`scheduled_prompt_targets`) so partial cancellation flips a single row and the dashboard can show last-fired / last-result per recipient. Cancelling every target reaps the parent row on the next worker pass.
|
||||
|
||||
### Scheduled prompt delivery: todo, not a broker message
|
||||
|
||||
An agent target's delivery is `push_todo` (`Coordinator::push_todo`,
|
||||
`docs/scheduler/coordinator.md` covers the mechanism generally), not a broker
|
||||
`Message` — a scheduled prompt wakes its target with a todo instead of
|
||||
driving an immediate turn, by design. `key = "schedule:<id>"` per
|
||||
target drives `push_todo`'s own upsert-by-key dedup: a re-fire of the
|
||||
*same schedule* against a target that hasn't reviewed the last one
|
||||
collapses into that one todo instead of stacking up.
|
||||
|
||||
### Missing-target failure
|
||||
|
||||
When a target name doesn't resolve to a known agent (container
|
||||
destroyed, typo, etc.) the worker:
|
||||
|
||||
1. Records `last_result = "no such agent: <name>"` on the
|
||||
per-target row.
|
||||
2. Sends a single advisory `Message` from `system` to `operator`
|
||||
naming the schedule, target, and reason. This one stays a `Message`
|
||||
regardless of target type — it's a to-operator advisory about a
|
||||
broken schedule, not the schedule's own delivery.
|
||||
3. Continues fanning out to the other live targets.
|
||||
|
||||
Transient broker errors (sqlite lock contention, etc.) get the same
|
||||
`last_result` annotation plus a `tracing::warn`, and then:
|
||||
|
||||
- **Recurring rows** re-arm to the next interval slot — the retry
|
||||
self-heals on the next worker pass.
|
||||
- **One-shot rows**: the worker deletes them unconditionally after their single
|
||||
fan-out pass; a broker error on a one-shot isn't retried (the
|
||||
operator advisory and `last_result` are the only audit trail).
|
||||
|
||||
### Reminder delivery: file-path semantics
|
||||
|
||||
A reminder may carry a `file_path` (the agent-visible path inside its
|
||||
|
|
@ -324,7 +261,6 @@ by the approval handler rather than run inline:
|
|||
| `ApprovalKind` | DAG submitted | source |
|
||||
|---|---|---|
|
||||
| `UpdateMetaInputs` | `meta_update` (`MetaLock` + rebuild fan-out) | `approval` |
|
||||
| `SchedulePrompt` | — runs inline (single sqlite insert) | — |
|
||||
|
||||
The DAG carries the originating `approval_id`. **Every** queued kind
|
||||
resolves through `actions::resolve_approval_dag` when its DAG settles
|
||||
|
|
@ -508,10 +444,8 @@ 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 — those go
|
||||
through `Coordinator::push_todo` instead, a direct live dial of the target
|
||||
agent's in-container todo socket (same `UpsertTodo` request in-container
|
||||
producers use). Legacy
|
||||
"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`):
|
||||
|
||||
|
|
@ -533,9 +467,8 @@ root agent. Variants (`hive_sh4re::manager::HelperEvent`):
|
|||
The remaining lower-urgency lifecycle notices — `Rebuilt`, `Killed`,
|
||||
`Destroyed`, `NeedsLogin`, `LoggedIn` — are "FYI, check
|
||||
when convenient" events with no reason to drive an immediate turn, so
|
||||
they deliver via `push_todo` (see above) instead
|
||||
of `HelperEvent`: an `agent_todo_socket` push instead of a broker
|
||||
message, `subsystem = "core"`, `key = "<event>:<agent>"` for dedup,
|
||||
they go out via `swarm_notices::notify` (see above) instead
|
||||
of a `HelperEvent`: `subsystem = "core"`, `key = "<event>:<agent>"`,
|
||||
and a single free-text `summary` (`rebuilt_todo_summary` renders
|
||||
`Rebuilt`'s `ok`/`note`/`sha`/`tag` fields into that string).
|
||||
|
||||
|
|
@ -543,7 +476,7 @@ To add a new lifecycle notice: if it needs to drive an immediate turn
|
|||
(something genuinely urgent, like `ContainerCrash`), add a
|
||||
`HelperEvent` variant + call sites + update `prompts/system.md`'s
|
||||
message-event list. If it's "FYI, check when convenient," call
|
||||
`push_todo` directly instead — no new wire type
|
||||
`swarm_notices::notify` instead — no new wire type
|
||||
needed.
|
||||
|
||||
## Autoupdate on startup
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ schemas, file layouts, and internal migration mechanics.
|
|||
|
||||
### `/var/lib/hyperhive/db/broker.sqlite` (host)
|
||||
|
||||
Seven tables, all in one file — three queues, a small key/value
|
||||
table, the schedule header/targets split, and the per-agent
|
||||
Six tables, all in one file — two queues, 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,21 +56,19 @@ 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 |
|
||||
schedule_prompt) /
|
||||
- `approvals` — the queue. `agent / kind (update_meta_inputs) /
|
||||
commit_ref / requested_at / status / resolved_at / note`.
|
||||
- `scheduled_prompts` — recurring + one-shot prompt queue.
|
||||
- `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) /
|
||||
next_fire_at_unix / created_at_unix / source ("operator" or
|
||||
"approval:<id>") / cancelled_at_unix / description`. `owner`
|
||||
drives cancel-permission checks (operator vs the submitting
|
||||
agent). The worker tombstones and reaps cancelled rows
|
||||
on its next pass.
|
||||
"approval:<id>") / cancelled_at_unix / description / paused_at_unix`.
|
||||
Read-only: the dashboard's SCH3DUL3S tab lists them so the operator
|
||||
can recreate them with `swarmctl schedule`; nothing writes or fires
|
||||
them.
|
||||
- `scheduled_prompt_targets` — per-target state for each schedule.
|
||||
`schedule_id / target / cancelled_at_unix /
|
||||
last_fired_at_unix / last_result`. `ON DELETE CASCADE` from
|
||||
`scheduled_prompts(id)` — requires `PRAGMA foreign_keys = ON`
|
||||
per connection (set at open).
|
||||
last_fired_at_unix / last_result`.
|
||||
- `agent_power` — one row per agent: `agent PK / wanted (up |
|
||||
offline) / updated_at`, owned by `hive-c0re/src/stores/power.rs`.
|
||||
This is the durable power *intent* the job queue reconciles the
|
||||
|
|
@ -90,11 +88,7 @@ Retention:
|
|||
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: the worker deletes one-shot rows on fire;
|
||||
recurring rows live until the operator cancels them
|
||||
(`cancel_schedule` MCP / dashboard ✗) which tombstones via
|
||||
`cancelled_at_unix`, then `reap_cancelled` drops the row on
|
||||
the next worker pass.
|
||||
- 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).
|
||||
|
||||
|
|
|
|||
|
|
@ -254,8 +254,8 @@ the wire structs in hive-sh4re) serialize as **RFC 3339 UTC strings**
|
|||
fields as `i64` unix seconds internally, only the JSON representation
|
||||
changes, and deserialization accepts both the string form and a bare
|
||||
integer (rolling-deploy skew, persisted blobs).
|
||||
**Input-direction** fields agents compute as epoch (`first_fire_at_unix`,
|
||||
schedule-edit `next_fire_at_unix`, `Wakeup::At`) stay integers. The
|
||||
**Input-direction** fields agents compute as epoch (`remind`'s
|
||||
`at_unix_timestamp`) stay integers. The
|
||||
`*_unix` field *names* stay for now — renaming is the wire-types
|
||||
refactor's concern. The dashboard frontend parses via
|
||||
`util.js::epochSec` wherever it needs arithmetic and feeds the string
|
||||
|
|
@ -297,7 +297,6 @@ binary flavor.
|
|||
| `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. |
|
||||
| `scheduling` | `request_schedule_prompt`, `fire_schedule_now`, `cancel_schedule`, `edit_schedule`, `list_schedules` *(privileged)* |
|
||||
| `forge` | none |
|
||||
| `web_tools` | none (gates the Claude built-ins `WebFetch`/`WebSearch`, not an MCP tool) |
|
||||
|
||||
|
|
@ -375,8 +374,7 @@ bypass the operator approval gate.
|
|||
`hive-sh4re/src/permissions.rs` + an arm to `as_str`. Add it to `Capability::ALL`
|
||||
(the source of truth for the permissions UI columns). Implement the access
|
||||
check in the relevant handler (`hive-c0re/src/socket_server/mod.rs`,
|
||||
`hive-c0re/src/socket_server/schedules.rs`, `coordinator.rs`, or a
|
||||
handler under `hive-c0re/src/dashboard/`).
|
||||
`coordinator.rs`, or a handler under `hive-c0re/src/dashboard/`).
|
||||
|
||||
## Async forms
|
||||
|
||||
|
|
|
|||
|
|
@ -521,7 +521,7 @@ which is almost never desired. Leave off on non-x86 hosts.
|
|||
|
||||
## See also
|
||||
|
||||
- `docs/agent-lifecycle/approvals.md` — approval flow + scheduled prompts
|
||||
- `docs/agent-lifecycle/approvals.md` — approval flow
|
||||
- `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
|
||||
|
|
|
|||
|
|
@ -64,11 +64,11 @@ works them out from the prompt itself, so:
|
|||
watches the paused prompt; if the agent was down for the whole pause, it
|
||||
fires the latest time it missed once, as above.
|
||||
|
||||
A fire reaches the agent as a `schedule` loose end with the prompt's text,
|
||||
the same as a hive schedule's. The agent's turn loop wakes for it like any
|
||||
other loose end. If the agent hasn't dealt with the previous fire yet, the
|
||||
new fire replaces it rather than adding a second one; once the agent
|
||||
dismissed the previous one, the next fire opens a new one.
|
||||
A fire reaches the agent as a `schedule` loose end with the prompt's text.
|
||||
The agent's turn loop wakes for it like any other loose end. If the agent
|
||||
hasn't dealt with the previous fire yet, the new fire replaces it rather
|
||||
than adding a second one; once the agent dismissed the previous one, the
|
||||
next fire opens a new one.
|
||||
|
||||
Each agent reads its prompts every 10 seconds, so a fire can land up to 10
|
||||
seconds after its time. A change reaches each agent within the same window,
|
||||
|
|
|
|||
|
|
@ -43,5 +43,3 @@ debug agent behavior.
|
|||
- **[matrix](matrix.md)** — the matrix MCP tool surface
|
||||
(`mcp__matrix__*`) for agents with a matrix account, multiple
|
||||
accounts per agent, and declaring extra MCP servers generally.
|
||||
- **[scheduling](scheduling.md)** — scheduled prompts (operator
|
||||
approval required).
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
# Scheduling and diagnostics tools
|
||||
|
||||
## `scheduling` tool group
|
||||
|
||||
Scheduled prompts fan a message body out to one or more agent inboxes
|
||||
at a future time, optionally recurring.
|
||||
|
||||
**Creating** one goes through the operator approval queue, even when it
|
||||
targets only yourself — use `remind` for an unapproved self-wake. The
|
||||
other four verbs need no approval: holding the `scheduling` tool group
|
||||
is the whole gate.
|
||||
|
||||
Authorization is one rule for all four verbs: you reach every schedule in
|
||||
the hive, whoever owns it. `list_schedules` shows the same set, so the
|
||||
snapshot only ever shows schedules you could also cancel.
|
||||
|
||||
### `request_schedule_prompt(targets, body, first_fire_at_unix, interval_seconds?, description?)`
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
|
||||
Queue an operator-approval for a scheduled prompt. On approve,
|
||||
hive-c0re fans `body` out to each agent in `targets` at
|
||||
`first_fire_at_unix` (Unix timestamp). Recurring when `interval_seconds`
|
||||
is set, one-shot otherwise.
|
||||
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
Catch-up clamp: if hive-c0re is down across multiple intervals, only
|
||||
ONE delayed fire happens on resume (per recurring schedule). The
|
||||
skipped-cycle count surfaces in the per-target `last_result` for
|
||||
audit.
|
||||
|
||||
### `edit_schedule(id, body?, description?, interval_seconds?, next_fire_at_unix?, targets_add?, targets_remove?)`
|
||||
|
||||
Partial-update a schedule. Pass only the fields to change; absent
|
||||
fields stay untouched. `targets_add` / `targets_remove` mutate the
|
||||
recipient list in the same transaction — re-adding a previously
|
||||
cancelled target drops its tombstone and starts fresh.
|
||||
`interval_seconds` accepts positive values only via this tool (omit
|
||||
to keep the existing cadence; pass a new positive value to change
|
||||
it). Toggling recurring → one-shot (clearing the interval) is
|
||||
operator-only via the dashboard PATCH endpoint. Refuses cancelled
|
||||
rows (terminal state).
|
||||
|
||||
### `cancel_schedule(id, targets?)`
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
|
||||
Cancel a schedule. Omit `targets` / pass empty to cancel the whole
|
||||
schedule; pass a list to cancel just those recipients (the schedule
|
||||
autocancels once every target is gone).
|
||||
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
### `fire_schedule_now(id)`
|
||||
|
||||
Fire a scheduled prompt out of band immediately. Recurring schedules
|
||||
keep their cadence — the manual fire is additive. The manual fire
|
||||
consumes one-shot schedules and cancels them afterwards.
|
||||
|
||||
### `list_schedules()`
|
||||
|
||||
Snapshot every schedule in the hive (active, and cancelled but not yet
|
||||
reaped) — same read scope as the rest of this group. Returns id, owner,
|
||||
body, per-target `last_fired_at` and `last_result`,
|
||||
`next_fire_at_unix`, `interval_seconds`. Use to look up an id before
|
||||
cancelling, or to audit upcoming wake-ups across the hive.
|
||||
|
||||
## See also
|
||||
|
||||
- `remind` (no-approval self-wake path) — documented in
|
||||
[`docs/turn-loop/`](../turn-loop/README.md).
|
||||
- [`docs/agent-lifecycle/approvals.md`](../agent-lifecycle/approvals.md) — approval flow for
|
||||
`request_schedule_prompt`.
|
||||
|
|
@ -131,10 +131,10 @@ 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** — schedule additions and other blast-radius-y operations
|
||||
route through the operator approval queue; config changes are config PRs
|
||||
an operator merges on the forge
|
||||
(see [`approvals.md`](../agent-lifecycle/approvals.md)).
|
||||
- **Approvals** — config changes are config PRs an operator merges on the
|
||||
forge (see [`approvals.md`](../agent-lifecycle/approvals.md)), and
|
||||
scheduled prompts are operator-only, set at swarm level
|
||||
(see [`schedules.md`](../swarm/schedules.md)).
|
||||
|
||||
### Capability = accepted risk
|
||||
|
||||
|
|
@ -327,6 +327,6 @@ writes to only the paths `hive-priv` legitimately needs.
|
|||
|
||||
| Component | Runs as | Privilege needed for |
|
||||
| ---------------------------- | -------------- | ------------------------------------------------------------ |
|
||||
| `hive-c0re` | `hive-core` | broker, HTTP dashboard, scheduling, approvals |
|
||||
| `hive-c0re` | `hive-core` | broker, HTTP dashboard, approvals |
|
||||
| `hive-priv` | `root` | container lifecycle, journal reads, bind mounts, cred writes |
|
||||
| `hive-ag3nt` (per-container) | per-agent user | turn execution, MCP serving |
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ 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`, `scheduling`)
|
||||
`execution`. Privileged groups (`lifecycle`, `approvals`)
|
||||
are opt-in via the P3RM1SS10NS tab.
|
||||
|
||||
## Core tools (always available)
|
||||
|
|
@ -149,8 +149,6 @@ hive_name?, swarm_name?, matrix_accounts? }`. `matrix_accounts` is a
|
|||
`lifecycle` gates nothing.
|
||||
Config changes go through a forge PR on `agent-configs/<name>` — see
|
||||
[`docs/agent-lifecycle/approvals.md`](../agent-lifecycle/approvals.md).
|
||||
- **Scheduling** (`scheduling`) — scheduled prompts. See
|
||||
[`docs/tools/scheduling.md`](../tools/scheduling.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
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ of your time. It's a single page with exactly four tabs:
|
|||
- **Y3R C4LL** — anything waiting on _you_: pending approvals. If an
|
||||
agent needs a decision from you, it's here.
|
||||
- **P3RM1SS10NS** — what tools and system-level access each agent has.
|
||||
- **SCH3DUL3S** — scheduled prompts and agent self-reminders.
|
||||
- **SCH3DUL3S** — a read-only list of the scheduled prompts left in the
|
||||
hive, due for removal; operators set schedules at swarm level now.
|
||||
|
||||
Everything else lives on its own page instead, all reachable from the
|
||||
H0M3 hub: **Flow** (`/flow.html`, the raw live message stream across
|
||||
|
|
@ -48,9 +49,8 @@ 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_: an agent's
|
||||
request to schedule a prompt. The tab's count pill tells you at a glance if
|
||||
anything's pending.
|
||||
worth checking regularly — it's everything that needs _you_. The tab's
|
||||
count pill tells you at a glance if anything's pending.
|
||||
|
||||
**Review a config change.** Agent config changes (new packages, env
|
||||
vars, MCP servers) are pull requests on the agent's `agent-configs`
|
||||
|
|
@ -76,9 +76,10 @@ save queues a rebuild for whichever agents actually changed.
|
|||
journald view for any agent + service; the per-agent `⋮` menu's
|
||||
**journal logs →** link jumps straight there, pre-filtered.
|
||||
|
||||
**Set up a schedule or check on a reminder.** SCH3DUL3S covers both —
|
||||
recurring or one-shot prompts you schedule for one or more agents, and
|
||||
reminders agents have set for themselves.
|
||||
**Set up a schedule.** Scheduled prompts are swarm-level: set them with
|
||||
`swarmctl schedule` ([`swarm/schedules.md`](../swarm/schedules.md)).
|
||||
SCH3DUL3S only lists the ones still in the hive, so you can copy them
|
||||
over.
|
||||
|
||||
## More depth
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ from the dashboard tab strip.
|
|||
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
|
||||
operator messages), and SCH3DUL3S (active schedules).
|
||||
operator messages), and SCH3DUL3S (active schedules left to copy).
|
||||
- **Banner-thin** (`░▒▓█▓▒░ HYPERHIVE / HIVE-C0RE / WE ARE THE WIRED ░▒▓█▓▒░`)
|
||||
— sits below the tab strip.
|
||||
- **Server-warnings banner** — a generic, sticky top-of-page strip shown
|
||||
|
|
@ -50,7 +50,7 @@ from the dashboard tab strip.
|
|||
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_added` /
|
||||
`/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.
|
||||
|
||||
|
|
@ -332,7 +332,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`, `scheduling`, `execution`,
|
||||
`lifecycle`, `approvals`, `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,7 +341,7 @@ takes effect.
|
|||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
The current tool groups are: `messaging`, `meta`, `inbox`, `lifecycle`,
|
||||
`approvals`, `scheduling`, `forge`, `execution`,
|
||||
`approvals`, `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` /
|
||||
|
|
@ -374,83 +374,31 @@ isn't clobbered.
|
|||
|
||||
## SCH3DUL3S tab
|
||||
|
||||
Anything that fires at a future time. The operator creates schedules
|
||||
inline in the table (last row); agent self-paced
|
||||
reminders surface at the bottom as a sibling list — they
|
||||
share enough conceptual ground to live together.
|
||||
A read-only list of the scheduled prompts still in the hive's store.
|
||||
The hive no longer fires them: scheduled prompts live at swarm level
|
||||
([`swarm/schedules.md`](../swarm/schedules.md)), and a warning banner at
|
||||
the top of the tab says the page is due for removal and points at
|
||||
`swarmctl schedule`. The list is there so the operator can recreate each
|
||||
row with `swarmctl schedule add`.
|
||||
|
||||
**N3W SCH3DUL3 / QU3U3D SCH3DUL3S** — operator-managed
|
||||
scheduled prompts. **Single-table layout**: each schedule is
|
||||
one `<tr>`; columns are
|
||||
`# | src | next | every | owner | body | …agents… | actions`.
|
||||
Agent columns are dynamic — `operator` + `root` + every
|
||||
live container + any extra name that appears as a target on
|
||||
some schedule but isn't a current container (same
|
||||
`buildTargetChips` membership rule the new/edit forms use,
|
||||
so table and forms agree on what's addressable). Column
|
||||
headers tilt –45° via CSS so each column reads as a narrow
|
||||
~28px strip; per-agent cells render as:
|
||||
**Single-table layout**: each schedule is one `<tr>`; columns are
|
||||
`# | src | next | every | owner | body | …agents…`. Agent columns are
|
||||
dynamic — every live container plus any extra name that appears as a
|
||||
target on some schedule but isn't a current container (struck through
|
||||
as gone). Column headers render vertically so each column reads as a
|
||||
narrow ~28px strip; per-agent cells show `✓` for an active target, a
|
||||
muted `✕` for a cancelled one, and nothing when the agent isn't a
|
||||
target. Hovering a target cell shows its last fire and result.
|
||||
|
||||
- **active target** → `<button>✓</button>` that cancels just
|
||||
that one target on selection
|
||||
- **cancelled target** → muted `✕` glyph (no button —
|
||||
re-adding goes through the edit form's targets multi-select)
|
||||
- **not a target** → empty cell
|
||||
The `next` column shows the stored next fire time as an absolute UTC
|
||||
timestamp, the value to pass as `--at`; paused and cancelled rows say
|
||||
so instead, with the time in the cell's tooltip. The body cell
|
||||
truncates; hovering shows the full body and description.
|
||||
|
||||
Per-schedule action column:
|
||||
|
||||
- **`↯ fire now`** — sends an out-of-band manual pulse to every
|
||||
active target (recurring schedules keep their cadence; the manual
|
||||
fire consumes one-shots). Disabled for paused schedules.
|
||||
- **`⏸` / `▶` pause/resume toggle** — only on recurring schedules
|
||||
(the operator may also pause one-shots to delay a one-time fire, so
|
||||
the button always appears). Paused rows show a `⏸ paused` badge in
|
||||
the `next` column; the table sorts them to the middle bucket (after
|
||||
active, before cancelled). A paused schedule doesn't fire regardless of
|
||||
`next_fire_at`; resuming (`▶`) clears the pause without adjusting
|
||||
the next-fire time. POSTs to `/api/schedules/{id}/pause` or
|
||||
`/api/schedules/{id}/resume`.
|
||||
- **`✎ edit`** — expands an inline edit form as a colspan'd row
|
||||
directly under the schedule's row (body / description / interval /
|
||||
next-fire / targets all editable; targets are a multi-select diff'd
|
||||
against the original active set so unchecked-was-active =
|
||||
`targets_remove`, checked-not-originally-active = `targets_add`;
|
||||
submit PATCHes `/api/schedules/{id}`).
|
||||
- **`✕`** — cancels the whole schedule
|
||||
(`POST /api/schedules/{id}/cancel`).
|
||||
|
||||
The `next` column cell (`.sched-due`) carries a `data-due-at`
|
||||
Unix timestamp attribute; a shared 1s ticker rewrites it
|
||||
in-place showing `fmtDuration` while in the future and
|
||||
`overdue X ago` once the fire time has passed — same
|
||||
zero-re-render pattern as the reminder due-at labels.
|
||||
|
||||
The table's last row is a permanent inline creation row:
|
||||
inputs live directly in table cells (targets as checkboxes,
|
||||
body textarea that expands on focus, datetime-local pre-filled
|
||||
to 5 minutes from now, mini d/h/m/s number inputs (blank or
|
||||
all-zero = one-shot), description). Select `+` to POST to
|
||||
`/api/schedules` as JSON (or `⌫` to clear the half-filled row);
|
||||
carry-state preserves partially typed inputs across re-renders. The tab pill shows the count of active
|
||||
schedules (at least one live target not yet cancelled).
|
||||
Refreshed on tab activation and after each submit/cancel. Backed by
|
||||
`GET /api/schedules`. No hive-c0re changes for the table layout
|
||||
— it renders entirely from existing `schedulesState` +
|
||||
`containersState`.
|
||||
|
||||
**QU3U3D R3M1ND3RS** — reminders agents have scheduled for
|
||||
themselves (via the `remind` tool) but not yet delivered.
|
||||
Each row shows the owner, due time, and message; a `CANC3L`
|
||||
button hard-deletes (`POST /api/cancel-reminder/{id}`) and a
|
||||
`R3TRY` button re-arms one whose delivery failed
|
||||
(`POST /api/retry-reminder/{id}`). Backed by `GET /api/reminders`.
|
||||
Lives in the SCH3DUL3S tab alongside operator schedules so the
|
||||
operator has one place for everything time-fired. The due-time
|
||||
label (`.reminder-due`) carries a `data-due-at` Unix timestamp
|
||||
attribute; a shared 1s ticker rewrites it in-place — showing
|
||||
`in Xm Ys` while the reminder is in the future and
|
||||
`overdue X ago` once the deadline passes — without triggering a
|
||||
full re-render of the list.
|
||||
The tab has no controls: nothing creates, edits, pauses, fires or
|
||||
cancels a row. The tab pill shows the count of active schedules (at least one
|
||||
live target not yet cancelled). Fetched from `GET /api/schedules` on
|
||||
tab activation and on each dashboard state load.
|
||||
|
||||
## ST4TS page (`/stats.html`)
|
||||
|
||||
|
|
@ -880,7 +828,6 @@ renderApprovals`) with three stacked sections:
|
|||
| kind | glyph | chip |
|
||||
|---|---|---|
|
||||
| `update_meta_inputs` | `↻` | `meta-update` |
|
||||
| `schedule_prompt` | `⏱` | `schedule` |
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
The chip ticks live every second via a `data-requested-at`
|
||||
|
|
@ -889,8 +836,7 @@ renderApprovals`) with three stacked sections:
|
|||
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`) or the prompt to
|
||||
schedule (`schedule_prompt`).
|
||||
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`.
|
||||
|
|
@ -1075,42 +1021,9 @@ below — some endpoints aren't in it yet.
|
|||
rows with both fields omitted rather than erroring on them.
|
||||
Returns `200 "ok"` on
|
||||
success.
|
||||
- `GET /api/schedules` — list all schedules (active and
|
||||
recently cancelled) for the SCH3DUL3S scheduled-prompts panel.
|
||||
- `POST /api/schedules` — operator-direct schedule create:
|
||||
`{ targets, body, first_fire_at_unix, interval_seconds?, description? }`.
|
||||
Agent-initiated schedules go through the approval queue instead
|
||||
(via the `request_schedule_prompt` MCP tool).
|
||||
- `PATCH /api/schedules/{id}` — partial edit. JSON body
|
||||
`{ body?, description?, interval_seconds?, next_fire_at_unix?,
|
||||
targets_add?, targets_remove? }`.
|
||||
Missing key = "leave alone"; explicit `null` on
|
||||
`description` / `interval_seconds` clears the field (so a
|
||||
recurring schedule flips to one-shot when the caller sends
|
||||
`interval_seconds` as `null`). `targets_add` is replace-on-conflict:
|
||||
re-adding a previously cancelled target drops the tombstone
|
||||
and the target starts fresh (operator intent on re-add =
|
||||
"this target is active again"). `targets_remove` delegates
|
||||
to the same path as `cancel_targets` — tombstones preserve
|
||||
audit, parent schedule autocancels when no active targets
|
||||
remain. Refuses cancelled rows; returns the updated
|
||||
`WireSchedule` on success.
|
||||
- `POST /api/schedules/{id}/cancel` — cancel a schedule. Body
|
||||
`{ targets?: ["name", …] }` cancels just those recipients;
|
||||
absent or empty body cancels the whole schedule.
|
||||
- `POST /api/schedules/{id}/fire-now` — out-of-band manual
|
||||
pulse. Fires the schedule body once immediately to
|
||||
every active target. Recurring schedules: `next_fire_at_unix`
|
||||
is untouched; the regular cadence continues. One-shots: the
|
||||
manual fan-out consumes the schedule (cancels it).
|
||||
The endpoint annotates per-target `last_result` as a manual fire so
|
||||
the audit trail distinguishes scheduled fires from operator-
|
||||
triggered ones.
|
||||
- `POST /api/schedules/{id}/pause` — pause a recurring or
|
||||
one-shot schedule. A paused schedule doesn't fire regardless
|
||||
of `next_fire_at`; the row gains a `⏸ paused` badge.
|
||||
- `POST /api/schedules/{id}/resume` — resume a paused schedule.
|
||||
Clears the pause flag without adjusting `next_fire_at`.
|
||||
- `GET /api/schedules` — list every schedule row left in the hive's
|
||||
store (cancelled ones included) for the read-only SCH3DUL3S tab.
|
||||
Targets naming an agent that no longer exists are left out.
|
||||
- `POST /api/meta-update` — `nix flake update` the selected
|
||||
`meta/flake.lock` inputs, then rebuild the affected agents.
|
||||
- `GET /api/dashboard/stream` — unified live event channel:
|
||||
|
|
@ -1133,10 +1046,9 @@ 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_added` (id, agent, approval_kind, sha_short, diff,
|
||||
description) / `approval_resolved` (id, agent, approval_kind,
|
||||
sha_short, status, resolved_at, note, description) — pending
|
||||
queue + history mutations. Client mutates a derived store and
|
||||
- `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
|
||||
|
|
@ -1158,16 +1070,6 @@ payload):
|
|||
terminal-history trim). Unlike the snapshot events below it ships
|
||||
no state at all: the client re-fetches `GET /api/jobq/graph`, which
|
||||
is where it reads the queue from cold too.
|
||||
- `schedules_changed` (seq, schedules: `Vec<WireSchedule>`) —
|
||||
full snapshot of all scheduled prompts. Emitted after every
|
||||
operator mutation via the `/api/schedules` surface (new /
|
||||
edit / cancel / fire-now) and after the worker fires or
|
||||
rearms a row. Same snapshot-shape rationale as
|
||||
`tombstones_changed` / `meta_inputs_changed` (small list, no
|
||||
add/remove races). The SCH3DUL3S tab subscribes and
|
||||
re-renders `schedulesState` on receipt; tab activation still
|
||||
re-fetches as a safety net for approval-path inserts and
|
||||
disconnect windows.
|
||||
- `reminders_changed` (seq, reminders: `Vec<PendingReminder>`) —
|
||||
full snapshot of all pending reminders. Emitted after every
|
||||
reminder mutation: agent `remind` calls (`agent_server`),
|
||||
|
|
|
|||
|
|
@ -50,7 +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_added` / `approval_resolved`,
|
||||
plus mutation events (`approval_resolved`,
|
||||
`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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# hive-c0re
|
||||
|
||||
The unprivileged host daemon (runs as `hive-core`). Owns the sqlite
|
||||
broker, the approval/reminder/schedule queues, the generic job-DAG
|
||||
broker, the approval/reminder queues, the generic job-DAG
|
||||
queue, container lifecycle, gateway vhost wiring, forge/matrix config
|
||||
reconciliation, per-container stats, and the axum operator dashboard.
|
||||
Largest crate in the workspace — bin-only, no separate lib.
|
||||
|
|
@ -32,7 +32,7 @@ to track it:
|
|||
- **`lifecycle/`** — `nixos-container` lifecycle + per-agent config
|
||||
flake generation.
|
||||
- **`stores/`** — sqlite-backed stores (broker, queues, audit, power).
|
||||
- **`workers/`** — background sweeps (crash watch, scheduled prompts,
|
||||
- **`workers/`** — background sweeps (crash watch,
|
||||
autoupdate, knowledge sync).
|
||||
- **`agent_config/`** — per-agent registries (tool groups,
|
||||
capabilities, resource limits, topology).
|
||||
|
|
|
|||
Loading…
Reference in a new issue