Commit graph

279 commits

Author SHA1 Message Date
damocles
597e4ba03a manager: add update_meta_inputs tool to update flake.lock on demand (closes #235) 2026-05-22 10:49:20 +02:00
iris
15e44955a8 web: clickable links in terminal rows and dashboard messages (issue #233) 2026-05-22 01:17:38 +02:00
damocles
66c481a07a harness+dashboard: declarative dashboardLinks option (closes #191) 2026-05-21 23:20:13 +02:00
iris
ab1f8d6e33 dashboard: icon fallback on real img load failure, not container-state guess 2026-05-21 22:01:38 +02:00
iris
16f614f45d dashboard: dimmed default icon for unreachable containers
A stopped or mid-transient (restarting / rebuilding) container's
web server isn't answering, so its <url>/icon background-image
just failed to an empty box on the card.

When the container isn't reachable (not running, or a transient
is in flight) the icon now falls back to the dimmed hyperhive
mark — /favicon.svg, served by the dashboard itself so it's
always loadable — greyscaled + lowered opacity via the
.icon-unreachable class.

closes #195
2026-05-21 21:56:39 +02:00
iris
f42ba9b561 dashboard file preview: markdown tabs + raster image rendering
Follow-up to #188. Two additions to the side-panel file preview:

- Markdown files get a rendered/plain tabbed view (was: always
  rendered, no way to see source) — same tab pattern as SVG.
- Raster images (png/jpg/gif/webp/bmp/ico/avif) render as an
  <img>. /api/state-file previously from_utf8_lossy-stringified
  every file and served text/plain, which corrupts binary; it
  now serves image files as raw bytes with their real
  content-type (over-cap images are rejected, not truncated —
  a clipped binary is corrupt).

buildSvgPanel generalised to buildTabbedPreview, shared by SVG +
markdown. .svg-host/.svg-render renamed .preview-host/.img-preview
since they now back images + md too.

closes #192
2026-05-21 21:49:15 +02:00
iris
a8ab91ecd8 dashboard: render SVG file previews
SVG files in the side-panel file preview showed only raw source.
Add a rendered/source tabbed view: 'rendered' (default) shows the
image, 'source' shows the markup.

The image loads via an <img> data: URI — <img>-loaded SVG runs in
the browser's secure static mode (scripts + external fetches
disabled), so an untrusted SVG from an agent's state dir can't
execute code in the dashboard origin. Tabs reuse the existing
diff-base-tab styling; a checkerboard backs the image so
transparent regions read clearly.

closes #188
2026-05-21 20:40:30 +02:00
damocles
433bc85b91 auto_update: rebuild all on startup, needs_update = applied HEAD vs deployed sha 2026-05-21 19:47:01 +02:00
iris
9abcda280a dashboard: full-height square agent icon, icon-left card layout
The agent icon was a 26px <img> inline in the card head, hidden via
onerror when a stopped container's web server didn't answer — which
collapsed the slot and shifted the row.

Restructure the live container card as icon-left / body-right:
- the icon is a background-image div with aspect-ratio 1 and
  align-self stretch — full card height, square, and (being a
  background) it has no intrinsic size, so loading or failing it
  can never reflow the row;
- a failed load (stopped container) falls through to a placeholder
  fill instead of collapsing;
- the three content lines move into a .card-body column.

Tombstone rows keep the plain stacked layout (:not(.tombstone)).

closes #177
2026-05-21 19:19:47 +02:00
damocles
3b44410427 manager: receive forge notifications (ManagerRequest::Wake) 2026-05-21 19:05:53 +02:00
iris
d5009cd175 dashboard: add favicon (hyperhive mark)
The main dashboard had no favicon — PR #145 added them to the
per-agent pages but missed hive-c0re's index. Serve branding/
hyperhive.svg at /favicon.svg and declare it in the index head.
The dashboard represents the whole hive, so it uses the project
mark (per-agent pages keep their own configurable /icon).

closes #173
2026-05-21 18:58:05 +02:00
iris
32f4796a7f dashboard: re-sync /api/state on SSE (re)connect
The dashboard cold-loaded its derived stores (approvals, questions,
containers, …) from /api/state once, then relied solely on live SSE
events. Events that fired during a disconnect window (reconnect,
hive-c0re restart) are never replayed, so the dashboard drifted stale
until a manual reload.

- terminal.js: add onStreamOpen, fired on every EventSource open
  (initial + reconnect); the dashboard wires it to refreshState() so
  every connection epoch re-syncs the authoritative snapshot.
- terminal.js: seq-dedupe only event kinds that actually appeared in
  the history replay. Mutation events are never in /dashboard/history,
  so deduping them against the broker-history seq wrongly dropped ones
  that fired between the /api/state snapshot and the history fetch.
- app.js: make applyApprovalResolved / applyQuestionResolved
  idempotent (guard the history unshift by id) so a re-sync
  overlapping a live event can't double a history row.

closes #163
2026-05-21 18:25:42 +02:00
iris
fefa91a39e test: cover init_config approval deser + lenient row collection 2026-05-21 18:20:15 +02:00
iris
189fc587a4 fix: handle init_config approval kind in row deserializer
row_to_approval matched only apply_commit + spawn, so any approvals
row with kind=init_config (added by 80dd5bb's two-step spawn) failed
to deserialize. pending() / recent_resolved() collect all-or-nothing
via collect::<Result<Vec>>(), so one bad row errored the whole query;
api_state's log_default then swallowed the error and returned an empty
list — every pending approval vanished from the dashboard (issue #160).

- add the missing init_config arm to row_to_approval
- collect_lenient(): skip + log unparseable rows so a single bad row
  can never blank the whole approvals list again
- dashboard: label init_config approvals 'init' (was mislabeled
  'spawn' by the apply-vs-other fallthrough)

closes #160
2026-05-21 18:14:53 +02:00
damocles
3214328fd5 test: setup_proposed seeds both agent.nix and flake.nix (regression #146) 2026-05-21 17:39:30 +02:00
iris
62aa3bb3ec agent icon: render on dashboard + per-agent web UI
Consumes the GET /icon endpoint from #139:

- Dashboard: each container card shows the agent's icon next to its
  name (26px). Loaded from <agent-url>/icon; onerror hides it for a
  stopped container whose web server isn't answering.
- Per-agent web UI: the agent's icon next to the page title (40px),
  and /icon as the favicon on the index, stats, and screen pages.

/icon always returns an image (configured SVG or the default
hyperhive logo), so no presence check is needed.

Closes #140
2026-05-21 15:36:58 +02:00
iris
1f52746bd9 manager: add optional agent param to reminder RPCs
CountPendingReminders and ReminderRollup were hardcoded to
MANAGER_AGENT. Both now take agent: Option<String> — None keeps the
current behavior (manager's own), Some(name) returns that agent's
reminder stats. The broker functions already take an agent name, so
this is a thin wire-protocol change. Callers (web UI stats page,
post-turn counts) pass None.

Closes #122
2026-05-20 22:14:09 +02:00
iris
d348ce885f manager: add optional agent param to GetLooseEnds
GetLooseEnds now takes agent: Option<String>:
- None   = manager's own loose ends (default; the bug fix)
- Some("*")    = hive-wide view (every approval/question/reminder)
- Some("name") = that agent's loose ends

The get_loose_ends MCP tool exposes this as an optional agent arg, so
the manager can still scan the whole swarm on demand. The web UI and
post-turn counts pass None (manager's own).
2026-05-20 21:44:17 +02:00
iris
873d5a083d manager: scope GetLooseEnds to manager's own items
hive_wide returns ALL agents' reminders and questions, causing other
agents' reminders (e.g. triage) to appear on the manager's web page
and in the get_loose_ends MCP tool. The MCP tool spec says it shows
your own pending items — switch to for_agent(MANAGER_AGENT) which
includes all approvals (manager is sole submitter), questions where
the manager is asker/target, and only the manager's own reminders.

Fixes #118
2026-05-20 21:44:17 +02:00
damocles
d8e64742f4 fix question answer sender and self-cancel feedback loop 2026-05-20 20:31:25 +02:00
damocles
0873159b9e fix: use combined_rev for needs_update dashboard badge 2026-05-20 20:00:38 +02:00
damocles
49caf6e539 fix: also add write:notification scope to mark notifications as read 2026-05-20 19:08:21 +02:00
damocles
72aef13eaf fix: add read:notification scope, always remint token on ensure_user_for 2026-05-20 19:06:32 +02:00
damocles
cddaacd12f feat: poll forge notifications in agent harness
Closes #27
2026-05-20 17:59:56 +02:00
damocles
1e325c84f2 fix: rebuild containers when meta flake changes, not only on hyperhive rev
Closes #78
2026-05-20 17:07:42 +02:00
damocles
a024ca65c0 fix: align forge user email to git user.email so commits link to profiles
agent users were created with {name}@hive.local but git commits use
{name}@hyperhive (set by meta::render_flake). forgejo matches by email,
so no profile link appeared on any commit.

- extract agent_email() helper returning {name}@hyperhive
- use it in ensure_user_exists (new users)
- add ensure_user_email() that runs gitea admin user edit to patch
  existing users; called from ensure_all on every startup sweep

Closes #64
2026-05-20 16:36:32 +02:00
damocles
4bcc8bf0b1 fix: import anyhow::Context for .context() on serde_json result 2026-05-20 16:01:47 +02:00
damocles
d3d52349c3 model/context: move context window config to host-level hive-c0re.nix 2026-05-20 15:49:03 +02:00
iris
b1f10b1d1b render message reply threads in dashboard and per-agent inbox
- MessageEvent and DashboardEvent Sent/Delivered now carry id and in_reply_to
- broker.send() includes last_insert_rowid in the emitted event
- recent_all() and recv_batch() include id and in_reply_to from the DB
- deliver_reminders_batch() tracks per-row rowids within the transaction
- dashboard message flow: reply rows are indented with a border-left and a
  clickable '↳ reply' tag that scroll-jumps + briefly highlights the parent
- per-agent inbox: reply messages get a '↳ reply ·' prefix and indent

Closes #26
2026-05-20 15:29:47 +02:00
iris
804875d670 surface rate_limited status as red badge on per-agent page and dashboard
- add rate_limited: Arc<AtomicBool> to Bus; set/cleared by emit_status
- write/remove sentinel file hyperhive-rate-limited in state dir so host-side
  dashboard can detect it without a live socket call
- api_state returns status=rate_limited when flag is set (cold-load accurate)
- ALIVE_LABELS gains rate_limited entry (⊘ red chip) on per-agent page
- ContainerView gains rate_limited: bool read from sentinel file
- dashboard container row shows ⊘ rate limited badge (red) ahead of needs_login

Closes #24
2026-05-20 15:16:00 +02:00
iris
808b9cbe1a extract ctx token thresholds into named constants 2026-05-20 15:05:42 +02:00
iris
270ef19920 dashboard: add per-agent ctx-window usage chip to container rows
Reads the most recent turn's context-window token count directly from
each agent's hyperhive-turn-stats.sqlite (same path the host-side
stats_vacuum uses). Adds ctx_tokens: Option<u64> to ContainerView;
populated in build_all via a single best-effort SQL query.

Dashboard app.js renders a 'ctx·Nk' badge colour-coded by harness
watermarks: green <100k (safe), yellow 100-150k (near auto-reset),
red ≥150k (compact territory). Badge only shown when ctx_tokens
is present (agent has run at least one turn).

Closes #17
2026-05-20 15:01:28 +02:00
damocles
80dd5bb69e two-step agent spawn: request_init_config + request_spawn 2026-05-20 14:40:15 +02:00
damocles
0b237d7d8c broker: skip broadcast self-delivery 2026-05-20 13:55:46 +02:00
damocles
931d4b26e7 broker: batch reminder delivery in single db transaction 2026-05-20 13:35:28 +02:00
damocles
c05a750409 enforce per-agent pending reminder cap (default 50, HIVE_REMIND_MAX_PENDING_PER_AGENT) 2026-05-20 13:35:28 +02:00
damocles
e27984b74c add graceful shutdown signal to coordinator and all background tasks 2026-05-20 13:35:28 +02:00
damocles
67b47872e0 add optional in_reply_to field on send for conversation threading 2026-05-20 13:35:27 +02:00
damocles
6f7cc6e77d add host-side turn-stats vacuum (90d retention, hourly sweep) 2026-05-20 13:35:27 +02:00
damocles
d0b65b1f47 fix: manager uses /agents/hm1nd/state, not /state, for HYPERHIVE_STATE_DIR 2026-05-20 13:35:12 +02:00
damocles
f21ae1ff98 inject HYPERHIVE_STATE_DIR into container env + extra MCP server envs 2026-05-20 13:35:12 +02:00
iris
91bfa269fd add reminder rollup RPC and broker query
Surface reminder activity statistics (scheduled, delivered, pending counts)
for each agent over configurable time windows. Needed by the per-agent
stats page to display reminder metrics.

Adds:
- ReminderStats struct and ReminderRollup request/response variants
- Broker::reminder_rollup_for(agent, since_secs) method
- Agent and manager socket handlers for the new RPC
- SocketReply mapping for response conversion
2026-05-20 13:24:17 +02:00
damocles
4715e88fff docs: move backlog to forge issue tracker, extract boundary doc 2026-05-20 12:19:16 +02:00
damocles
44c86b9278 docs: correct stale recv_blocking + vacuum references 2026-05-20 12:10:34 +02:00
damocles
94781ccd08 lifecycle: append container journal tail to failed nixos-container update 2026-05-20 11:26:12 +02:00
müde
49f4e9cc89 dashboard: forge-linked config + approval card + 3-way diff base
- forge nix option moves to hyperhive.forge.enable, defaults true;
  hive-c0re imports the forge module so it's on by default.
- drop the agent.nix container-row viewer + /api/agent-config; link
  to the agent-configs forge repo instead.
- restructure pending approvals into a card (identity header /
  what-changed body / decision actions) with a link to the proposal
  commit on the forge.
- diff opens in the side panel with a 3-way base toggle: vs applied
  (running) / vs last-approved / vs previous proposal, served by the
  new /api/approval-diff/{id}?base= endpoint.
2026-05-20 11:22:28 +02:00
müde
0c62bbf1cd dashboard: render markdown file previews in the side panel
clicking a .md / .markdown path reference now opens a marked-rendered
view in the slide-in panel instead of raw text; other files stay raw
in a <pre>. serves the vendored marked bundle at /static/marked.js and
scopes a .md stylesheet to the panel body.
2026-05-20 11:01:16 +02:00
damocles
24b10becc9 get_logs: resolve the broker-logical 'manager' alias to the hm1nd machine 2026-05-20 10:48:24 +02:00
damocles
0a79912b67 get_logs: resolve machine name via container_name like every other verb 2026-05-20 10:48:24 +02:00
müde
7ce3da1e21 dashboard: open long content in a slide-in side panel
file previews, approval diffs, journald logs and applied config no
longer expand inline — they open in a drawer that swipes in from the
right, with a title naming what's open and a close button (esc /
backdrop also close). path references in messages become plain inline
links that open the file in the panel; the sibling-<details> dance in
appendLinkified is gone.

also: the question-answer free-text field is now a textarea — enter
submits, shift+enter inserts a newline.
2026-05-20 10:43:23 +02:00