Commit graph

586 commits

Author SHA1 Message Date
damocles
e28b0a1dab refactor: extract format_wake_prompt/now_unix/build_row into serve_common (closes #169) 2026-05-21 18:53:41 +02:00
iris
dfee0574a5 fix: screen fit-to-window actually scales the canvas (#133)
The fit toggle relied on canvas.fit { max-width/max-height: 100% }.
The canvas is a flex item, and a flex item's automatic minimum size
(min-width/min-height: auto → the replaced element's intrinsic size)
overrides max-*, so an oversized desktop never shrank — it just got
centred and clipped, looking like the toggle did nothing.

Replace the CSS approach with explicit JS sizing: relayoutCanvas()
computes a scale factor (min of width/height ratios, capped at 1 so
it never upscales) and sets canvas.style.width/height in px,
preserving aspect ratio. Recomputed on fit toggle, window resize,
and framebuffer-size (ServerInit). Pointer mapping is unaffected —
sendPointer already derives scale from getBoundingClientRect().
2026-05-21 18:45:13 +02:00
damocles
00281730bb harness: sync agent icon to Forgejo user avatar on rebuild (closes #141) 2026-05-21 18:29:33 +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
iris
4539091f3c terminal: show recv wait/max params + bash [bg] flag
recv tool-use rows rendered as a bare recv() regardless of args,
hiding whether a turn is parked on a long-poll (wait_seconds) or
draining a burst (max). fmtToolUse now surfaces both. Bash rows
gain a [bg] flag when run_in_background is set.

closes #158
2026-05-21 17:57:24 +02:00
damocles
468d682085 forge: use branding/hyperhive.svg for logo and favicon 2026-05-21 17:50:46 +02:00
damocles
615928453d forge: replace forgejo logo with hyperhive mark (closes #143) 2026-05-21 17:50:46 +02:00
damocles
ca2f98017b fix: add tempfile to Cargo.lock (dev-dep for hive-c0re tests) 2026-05-21 17:46:20 +02:00
damocles
72522be8c0 harness: add hyperhive.autoCompact option (default true, false=disable proactive compaction) 2026-05-21 17:41: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
39bd46b244 agent icon: add hyperhive.icon option + GET /icon endpoint
Foundation for the per-agent icon feature (#137).

- harness-base.nix: new hyperhive.icon option (nullable path to an
  SVG). An agent commits an SVG into its config repo and references
  it as ./icon.svg; when set it lands at /etc/hyperhive/icon.svg.
- web_ui.rs: GET /icon serves the configured SVG, falling back to the
  bundled hyperhive logo when none is set — so it always returns an
  image and consumers can hit it unconditionally.

Closes #139
2026-05-21 01:06:34 +02:00
iris
a4cb66bffe branding: inline the logo in the README heading
The centered logo above a left-aligned heading looked disjointed.
Place it as a small inline mark next to the title instead.
2026-05-21 00:40:53 +02:00
iris
729306a357 branding: add hyperhive logo + show it in the README
Adds the hyperhive logo (amber-on-dark hexagonal hive) as repo assets
under branding/ — hyperhive.svg (primary, scalable, 5 KB) and
hyperhive.png (raster fallback). Displays the SVG centered at the top
of the README.

Closes #136
2026-05-21 00:37:45 +02:00
iris
305a32220b screen: add fit-to-window toggle
Adds a 'fit' toolbar toggle on the /screen VNC viewer. When on (the
default), the canvas is CSS-scaled down to fit the browser viewport
preserving aspect ratio — no more scrolling a desktop larger than the
window. When off, the canvas renders at native resolution with scroll.
The choice persists in localStorage.

The canvas's intrinsic resolution is never touched — only its display
size. sendPointer now rescales client coordinates by the canvas
display-vs-intrinsic ratio so clicks stay accurate in fit mode (this
also fixes a latent off-by-scale bug). Toolbar buttons share a .tbtn
class for consistent styling.

Issue #133 (fit toggle now; dynamic desktop resize tracked separately).
2026-05-20 22:56:19 +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
01cbd5e7cc manager prompt: update get_loose_ends docs for agent param
PR #119 changed get_loose_ends to default to the manager's own items
and accept an optional agent arg ("*" = hive-wide, "<name>" = one
agent), but the manager system prompt still described it as
"hive-wide ... no args". docs/turn-loop.md was already updated;
only manager.md was missed.
2026-05-20 22:13:25 +02:00
iris
a5ef10c10c screen: fix framebuffer height parse and stray byte consume
Two RFB protocol bugs in the /screen client:

1. ServerInit parsed framebuffer-height from byte offset 1 instead of
   2. RFB 3.8 ServerInit is width@0-1, height@2-3; u16be(b,1) mixes
   the low byte of width with the high byte of height. The wrong
   height went into the initial non-incremental FramebufferUpdate-
   Request; neatvnc feeds the client's width/height straight into
   pixman_region_union_rect un-clipped (src/server.c), so an
   out-of-range height corrupts the server damage region and pixman
   reports 'Invalid rectangle passed'.

2. FramebufferUpdate handler had a stray drainTo(1) after the 3-byte
   padding+nRects header, consuming the first byte of the first
   rectangle and desyncing every update.

Closes #128
2026-05-20 22:11:28 +02:00
damocles
08913484cc add nixosModules.default alias for single-import deploy 2026-05-20 22:07:27 +02:00
damocles
6a643f2fd2 docs: update turn-loop get_loose_ends, add hive-forge and cp gotchas 2026-05-20 22:00:05 +02:00
iris
c10b4e26ef screen: fix MD5 rotation schedule and send root username
Two bugs found via the weston journal (issue #92):

1. MD5 rotation index used (j>>2 & 3) for the round number, which
   cycles every 4 steps instead of every 16. Verified against RFC 1321
   test vectors: md5("") was 7a1dce5b... instead of d41d8cd9... — the
   derived AES key was wrong, so the server decrypted the credentials
   to garbage. Fixed to j>>4.

2. weston's vnc_handle_auth calls getpwnam(username) and requires
   pw_uid == weston's own uid before PAM is consulted. We sent an empty
   username, which fails outright ("VNC: wrong user"). weston runs as
   root, so send username "root"; the empty password still passes via
   pam_permit.so on the weston-remote-access service.

Fixes #92
2026-05-20 21:47:27 +02:00
damocles
a95ca22b49 hive-forge: single command with subverbs instead of per-verb scripts 2026-05-20 21:45:01 +02:00
damocles
0a4cde88b0 add hive-forge-tools: shell wrappers for common forge API operations 2026-05-20 21:45:01 +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
a13f63f30c forge_notify: check mark-read HTTP status, warn on non-2xx 2026-05-20 21:22:53 +02:00
iris
ddd0248619 harness-base: fix python3 -c quoting inside nix string
Two consecutive single quotes ('') inside a Nix indented string (''...'')
are treated as the string-end delimiter, breaking nix evaluation.
Switch the shell -c argument from double-quotes to single-quotes so
the Python string literals use double-quotes instead, avoiding any ''
sequences in the Nix source.
2026-05-20 21:01:26 +02:00
iris
e7d7aef1aa screen: fix Apple-DH response byte order
rfb_apple_dh_client_msg has encrypted_credentials at offset 0 and
public_key as a flexible array at offset 128. We were sending them
in the wrong order (pub_key first), so neatvnc decrypted the wrong
bytes as credentials and sent the wrong bytes as the DH public key,
causing a mismatched shared secret and SecurityResult=1.

Fixes #92
2026-05-20 20:53:30 +02:00
damocles
3c6c257506 fix tea-login: write config.yml directly, always refresh token 2026-05-20 20:51:02 +02:00
damocles
d8e64742f4 fix question answer sender and self-cancel feedback loop 2026-05-20 20:31:25 +02:00
iris
e50173f3e1 weston-vnc: fix PAM service name (weston-remote-access, not weston)
weston calls pam_start("weston-remote-access", ...) in libweston/auth.c.
The previous security.pam.services.weston entry created /etc/pam.d/weston
which was never consulted, so PAM fell back to the system default and
rejected all credentials. Renaming to weston-remote-access makes
pam_permit.so actually take effect.

Fixes #92
2026-05-20 20:28:32 +02:00
iris
f9d1e69a50 stats: add 1h, 4h, 3d time range windows
Adds Hour (5-min buckets), FourHour (15-min buckets), and ThreeDay
(hourly buckets) to the Window enum, plus the matching tab buttons
in stats.html. Simplifies web_ui.rs to use Window::span_secs()
instead of a duplicate match.

Closes #25
2026-05-20 20:23:09 +02:00
damocles
4bb3877460 add jq and curl to agent base packages 2026-05-20 20:14:26 +02:00
damocles
cc7d349139 fix: use --no-preserve=mode when copying forgejo static root 2026-05-20 20:09:00 +02:00
damocles
30c7274cc7 fix forge theme: bake css into static root via STATIC_ROOT_PATH 2026-05-20 20:02:06 +02:00
damocles
0873159b9e fix: use combined_rev for needs_update dashboard badge 2026-05-20 20:00:38 +02:00
damocles
5951758b35 track compacted turns separately in stats 2026-05-20 19:59:59 +02:00
iris
fceab658f4 weston-vnc: add pam_permit.so for weston service to accept any Apple-DH credentials 2026-05-20 19:59:19 +02:00
damocles
de2179540a fix: enrich forge notifications with subject body and comment content 2026-05-20 19:30:28 +02:00
iris
1b7d058d3c weston-vnc: restore auth-method=none ini so weston accepts empty Apple-DH credentials 2026-05-20 19:27:41 +02:00
iris
3224178d2d screen: implement Apple-DH (type 30) auth for neatvnc 0.9 compatibility 2026-05-20 19:12:40 +02:00
iris
fd433d2406 weston-vnc: revert neatvnc overlay, use full TLS build with --disable-transport-layer-security 2026-05-20 19:12:40 +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
iris
3e5d430873 weston-vnc: fix neatvnc overlay to use -Dtls=disabled instead of removing gnutls 2026-05-20 18:02:36 +02:00
damocles
cddaacd12f feat: poll forge notifications in agent harness
Closes #27
2026-05-20 17:59:56 +02:00
iris
a4706d793e weston-vnc: build neatvnc without gnutls to disable RSA/DH auth types 2026-05-20 17:49:26 +02:00
iris
76061eb3ae screen: reject unsupported security types with clear error instead of mishandling 2026-05-20 17:49:26 +02:00