From b4a2aeb5ba234f4fcd0217fd450af50de2a5c037 Mon Sep 17 00:00:00 2001 From: atlas Date: Sun, 4 Oct 2026 21:23:18 +0200 Subject: [PATCH] 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. --- CLAUDE.md | 4 +- Cargo.lock | 2 - README.md | 6 +- docs/README.md | 2 +- docs/agent-lifecycle/agent-roster.md | 8 +- docs/agent-lifecycle/approvals.md | 85 ++------------ docs/agent-lifecycle/persistence.md | 28 ++--- docs/process/conventions.md | 8 +- docs/scheduler/coordinator.md | 2 +- docs/swarm/schedules.md | 10 +- docs/tools/README.md | 2 - docs/tools/scheduling.md | 74 ------------ docs/trust-boundary/security.md | 10 +- docs/turn-loop/mcp.md | 4 +- docs/web-ui/README.md | 15 +-- docs/web-ui/dashboard.md | 164 ++++++--------------------- docs/web-ui/shape.md | 2 +- hive-c0re/README.md | 4 +- 18 files changed, 87 insertions(+), 343 deletions(-) delete mode 100644 docs/tools/scheduling.md diff --git a/CLAUDE.md b/CLAUDE.md index b990bfb8..2a227390 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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. diff --git a/Cargo.lock b/Cargo.lock index 1f16b1e2..35974ebc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/README.md b/README.md index ede2a5e0..f4d1f9e5 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/docs/README.md b/docs/README.md index b7a25bc1..04fc5445 100644 --- a/docs/README.md +++ b/docs/README.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 diff --git a/docs/agent-lifecycle/agent-roster.md b/docs/agent-lifecycle/agent-roster.md index a015eac4..e337402e 100644 --- a/docs/agent-lifecycle/agent-roster.md +++ b/docs/agent-lifecycle/agent-roster.md @@ -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: ` message straight into the caller's own inbox) is on this socket too, unprivileged on diff --git a/docs/agent-lifecycle/approvals.md b/docs/agent-lifecycle/approvals.md index 6ec0f062..4fb25291 100644 --- a/docs/agent-lifecycle/approvals.md +++ b/docs/agent-lifecycle/approvals.md @@ -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/`. 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 ` 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. - `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:`; the worker fans the body out as - inbox messages to each target at the scheduled time, recurring - when `interval_seconds` is set. -### 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:"`): 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:` 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:"` 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: "` 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 = ":"` for dedup, +they go out via `swarm_notices::notify` (see above) instead +of a `HelperEvent`: `subsystem = "core"`, `key = ":"`, 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 diff --git a/docs/agent-lifecycle/persistence.md b/docs/agent-lifecycle/persistence.md index 48181a57..d5544161 100644 --- a/docs/agent-lifecycle/persistence.md +++ b/docs/agent-lifecycle/persistence.md @@ -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:") / 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:") / 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). diff --git a/docs/process/conventions.md b/docs/process/conventions.md index 872d12a6..87c2bec6 100644 --- a/docs/process/conventions.md +++ b/docs/process/conventions.md @@ -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 diff --git a/docs/scheduler/coordinator.md b/docs/scheduler/coordinator.md index e77713a2..1636d72f 100644 --- a/docs/scheduler/coordinator.md +++ b/docs/scheduler/coordinator.md @@ -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 diff --git a/docs/swarm/schedules.md b/docs/swarm/schedules.md index fe95a472..45c7de1d 100644 --- a/docs/swarm/schedules.md +++ b/docs/swarm/schedules.md @@ -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, diff --git a/docs/tools/README.md b/docs/tools/README.md index 865681b6..db74e7e2 100644 --- a/docs/tools/README.md +++ b/docs/tools/README.md @@ -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). diff --git a/docs/tools/scheduling.md b/docs/tools/scheduling.md deleted file mode 100644 index 457b0f43..00000000 --- a/docs/tools/scheduling.md +++ /dev/null @@ -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?)` - - - -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. - - - -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?)` - - - -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). - - - -### `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`. diff --git a/docs/trust-boundary/security.md b/docs/trust-boundary/security.md index 3b8dd5e7..37feaa15 100644 --- a/docs/trust-boundary/security.md +++ b/docs/trust-boundary/security.md @@ -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 | diff --git a/docs/turn-loop/mcp.md b/docs/turn-loop/mcp.md index 3bf22965..045086a9 100644 --- a/docs/turn-loop/mcp.md +++ b/docs/turn-loop/mcp.md @@ -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/` — 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 diff --git a/docs/web-ui/README.md b/docs/web-ui/README.md index 07d02307..39cbb25c 100644 --- a/docs/web-ui/README.md +++ b/docs/web-ui/README.md @@ -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 diff --git a/docs/web-ui/dashboard.md b/docs/web-ui/dashboard.md index 87dfb675..bbffe8fb 100644 --- a/docs/web-ui/dashboard.md +++ b/docs/web-ui/dashboard.md @@ -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. 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 ``; 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 ``; 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** → `` 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` | 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`) — - 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`) — full snapshot of all pending reminders. Emitted after every reminder mutation: agent `remind` calls (`agent_server`), diff --git a/docs/web-ui/shape.md b/docs/web-ui/shape.md index 0f294f31..12fe9525 100644 --- a/docs/web-ui/shape.md +++ b/docs/web-ui/shape.md @@ -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 diff --git a/hive-c0re/README.md b/hive-c0re/README.md index bfc1c876..269bf672 100644 --- a/hive-c0re/README.md +++ b/hive-c0re/README.md @@ -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).