Commit graph

194 commits

Author SHA1 Message Date
iris
229c4292e9 frontend: cut over Rust binaries to ServeDir; delete legacy assets
Phase 4 of #273 — the actual switch. Both axum routers now serve
their static surface via `tower_http::services::ServeDir` mounted
as a fallback service, reading the dist path from `HIVE_STATIC_DIR`
(set by Phase 3's NixOS module wiring).

Deletes:
- `hive-c0re/assets/{index.html, app.js, dashboard.css}`
- `hive-ag3nt/assets/{index.html, app.js, agent.css, stats.html,
   stats.js, screen.html}`
- The whole `hive-fr0nt/` crate (workspace member dropped, both
  hive-c0re and hive-ag3nt drop their `hive-fr0nt.workspace = true`
  dep). Its contents now live as `@hive/shared` under
  `frontend/packages/shared/`.

Rust changes:
- `hive-c0re/src/dashboard.rs`: remove `serve_index`, `serve_css`,
  `serve_app_js`, `serve_shared_js`, `serve_marked_js`,
  `serve_favicon` (all six `include_str!` handlers); replace their
  routes with a single `.fallback_service(ServeDir::new(static_dir))`
  on the router. Fail closed (anyhow::bail) if `HIVE_STATIC_DIR` is
  unset or not a directory at startup.
- `hive-ag3nt/src/web_ui.rs`: remove `serve_index`, `serve_css`,
  `serve_app_js`, `serve_shared_js`, `serve_marked_js`,
  `serve_stats`, `serve_stats_js`, `serve_screen`; same
  `fallback_service` pattern. `serve_icon` stays (consumes
  `/etc/hyperhive/icon.svg` + `branding/hyperhive.svg` fallback,
  neither of which lives under the frontend dist).
- `AgentLink` URLs for stats/screen switched from `/stats` / `/screen`
  to `/stats.html` / `/screen.html` since ServeDir doesn't auto-
  append the extension and the on-disk filename is the natural URL
  post-cutover.
- `Cargo.toml` (workspace): drop `hive-fr0nt` member + workspace
  dep, add `tower-http = { version = "0.6", features = ["fs"] }`.
- `hive-c0re/Cargo.toml` + `hive-ag3nt/Cargo.toml`: drop the
  `hive-fr0nt.workspace = true` dep, add `tower-http.workspace =
  true`.

Docs updated:
- `CLAUDE.md`: file map reflects `frontend/` (was `hive-fr0nt/` +
  `assets/`) and the ServeDir/HIVE_STATIC_DIR shape.
- `docs/web-ui.md` 'Shape (shared by both)' section: describes the
  ServeDir fallback + bundled-by-esbuild surface, no more
  `include_str!` references.
- `docs/terminal-rendering.md`: src paths point at
  `frontend/packages/{agent,shared}/src/`; marked is the npm dep,
  not vendored UMD.

Validation:
- `cargo check --workspace` — clean (5 warnings, all pre-existing
  in `rebuild_queue.rs`, none on changed files).
- `cargo clippy --workspace --all-targets` — clean (11 warnings,
  same pre-existing source).
- `cd frontend && npm run build` from the prior commit's lockfile
  produces the dist directories the new routers consume:
    dashboard: `dist/{index.html, static/{app.js, dashboard.css}}`
    agent:     `dist/{index.html, stats.html, screen.html,
                       static/{app.js, stats.js, agent.css}}`
  (favicon.svg lands in dashboard/ during the nix build —
  `nix/frontend.nix` install phase copies `branding/hyperhive.svg`
  there, since it's outside the npm tree.)

Refs #273.
2026-05-23 14:51:01 +02:00
damocles
73871f18c3 set_status: consolidate whoami into get_agent_meta with optional name 2026-05-23 11:32:33 +02:00
damocles
77fdaf0d1e set_status: add get_agent_meta tool for inter-agent status queries 2026-05-23 11:32:33 +02:00
damocles
8e8e8a771f set_status: add status_set_at timestamp (mtime of status file) 2026-05-23 11:32:33 +02:00
damocles
fe2933b213 feat: add set_status MCP tool and status field to whoami/dashboard (closes #325) 2026-05-23 11:32:33 +02:00
iris
d73175a23e harness: keep retrying web-UI bind on AddrInUse
The retry was capped at 12 attempts (~20s of exponential backoff
capped at 2s). Two back-to-back nspawn restarts in #324 left the
previous socket holding the port longer than that budget; once the
cap fired, the web-UI task returned an error and silently died for
the rest of the process lifetime — the agent kept running fine
otherwise (MCP, turn loop), but the operator's dashboard click
hit nothing.

Genuine port collisions are preflighted host-side
(lifecycle::{spawn,rebuild}) and surfaced as a port-conflict banner,
so at this layer a persistent AddrInUse always reflects a
recoverable stale socket. Drop the cap, keep retrying forever with
the same 2s-capped backoff. WARN for the first dozen attempts so a
normal restart-race is visible; INFO after that to avoid spamming
the journal during a long stale-socket hold. Logs a one-line INFO
on success when we did have to retry, so post-mortems can find the
attempt count.

Closes #324.
2026-05-23 02:19:14 +02:00
iris
222a5b4dc6 dashboard+agent: agent backend owns its nav links; dashboard proxies
The previous take put a shared NavLink wire type in hive-sh4re and
duplicated the link-building logic across crates. Per @mara on #326:
that doesn't fit the eventual frontend/backend split goal (#273).
The agent backend is the natural source of truth for what links its
own page exposes; hive-c0re just passes the list through to the
dashboard.

* hive-ag3nt/src/web_ui.rs: agent_links now also serves the
  config-repo link + reads agent-declared dashboardLinks extras
  from {state_dir}/hyperhive-dashboard-links.json. AgentLink gains a
  kind enum (Container | Forge | External) so the frontend can build
  the right href no matter which surface is rendering. The host
  header is no longer used — URLs are paths for Container/Forge,
  absolute for External.

* hive-c0re/src/dashboard.rs: new GET /api/agent/{name}/links route,
  a same-origin proxy that fetches the agent's /api/state and
  forwards just the links field. No shared wire type — hive-c0re
  treats the payload as opaque JSON (serde_json::Value). All failure
  modes degrade to an empty list so the dashboard still renders.

* hive-c0re/assets/app.js: container card head row gets an async-
  populated icon-only nav strip from the proxy. The hardcoded stats
  link, the standalone config-repo trigger, and the extras block are
  gone. The deployed:<sha> chip stays — the agent harness can't know
  its own deployed sha, so this chip is how the operator sees what's
  live alongside the agent's (root-only) config link.

* hive-ag3nt/assets/app.js: agent page meta-links rendered via
  el() / textContent (DOM build) so agent-declared icon / label / url
  strings never reach innerHTML. kind-based href resolution mirrors
  the dashboard side.

* docs/web-ui.md: dashboard + per-agent sections updated for the new
  architecture.

Closes #262.
2026-05-23 02:11:40 +02:00
damocles
77b249076f events: HIVE_DEFAULT_MODEL takes priority over persisted model (closes #319) 2026-05-23 00:38:17 +02:00
damocles
edf7f1602d forge_notify: include reason field in all notification messages (closes #110) 2026-05-22 23:59:47 +02:00
damocles
a94b504883 forge_notify: skip-reasons drop-list filter, configurable via agent.nix 2026-05-22 22:29:32 +02:00
damocles
b0f6bd8ece fix: self-calibrate context window from API result event
the stream-json result event carries modelUsage.<model>.contextWindow
which is the actual per-inference active window the model enforces.
for claude-sonnet-4-6 this is 200k even though the full prompt cache
can hold millions of tokens via accumulated cache reads.

with the nix-configured sonnet = 1000000 the proactive compact watermark
sat at 750k and was never reached. agents grew context until prompt_too_long
at ~170k — reactive compact, no checkpoint turn.

changes:
- bus gains api_context_window field seeded from modelUsage.*.contextWindow
  in each turn's result event. authoritative; falls back to env var, then 200k.
- new effective_context_window(bus) helper used by both watermark functions
- compact_watermark (75%) and auto_reset_watermark (50%) call effective_context_window
- context_tokens() docstring clarified: all three token fields (input +
  cache_read + cache_creation) count against the per-inference contextWindow
  limit. the large cache_read values seen in the result event are cumulative
  across all inferences in a turn, not per-inference.
- /api/state context_window_tokens now reflects the calibrated window

closes #129
2026-05-22 22:20:07 +02:00
damocles
908cadb151 docs: add missing #[must_use], # Errors, # Panics across public api 2026-05-22 19:41:27 +02:00
damocles
748536203b refactor: split long functions per review feedback; remove all #[allow] attributes 2026-05-22 19:24:44 +02:00
damocles
bbe2112dc9 fix: use try_from for i64/u64 casts; split format_notification into helpers 2026-05-22 19:14:22 +02:00
damocles
484cea62c7 fix #265: resolve all remaining clippy warnings (cast, too_many_lines, doc nits) 2026-05-22 19:11:40 +02:00
damocles
30d82148e0 clippy: apply auto-fixable warnings across workspace (closes #265 partial) 2026-05-22 18:55:57 +02:00
damocles
4a0d08154e forge_notify: detect review requests via requested_reviewers (closes #253) 2026-05-22 16:54:29 +02:00
damocles
845fafdf1b forge_notify: show assignees and reviewers unconditionally (closes #256) 2026-05-22 16:45:41 +02:00
damocles
5c360e8293 forge_notify: filter self-notifications, detect PR reviews (closes #230, closes #231) 2026-05-22 15:29:38 +02:00
damocles
3e098c56ff update_meta_inputs: require operator approval, rename to request_update_meta_inputs 2026-05-22 10:49:20 +02:00
damocles
597e4ba03a manager: add update_meta_inputs tool to update flake.lock on demand (closes #235) 2026-05-22 10:49:20 +02:00
damocles
4b51c198d5 forge_notify: add repo slug to notification header for multi-repo disambiguation 2026-05-22 00:50:19 +02:00
damocles
20bb343f27 forge_notify: add issue/PR number to header, drop repo: line, show assignee on new items (closes #225) 2026-05-22 00:50:19 +02:00
damocles
c99261b042 harness: add request_next_turn MCP tool — immediate self-continuation (closes #216) 2026-05-22 00:35:13 +02:00
damocles
717086b02d forge_notify: HIVE_FORGE_KEEP_SUBSCRIPTIONS=1 disables auto-unsubscribe 2026-05-21 22:33:35 +02:00
damocles
eb63c7ebb1 forge_notify: auto-unsubscribe from repo watches on subscribed notifications 2026-05-21 22:33:35 +02:00
damocles
da8a711a60 forge_notify: explicit open/empty state, unknown states show raw value 2026-05-21 22:27:22 +02:00
damocles
8cc6306728 forge_notify: generic type label helper, compose kind from label+state 2026-05-21 22:27:22 +02:00
damocles
13b7a94257 forge_notify: fix notification type strings and PR state detection (fixes #201) 2026-05-21 22:27:22 +02:00
damocles
0884a54960 agent page: backend-supplied links list replaces hardcoded stats/screen/forge (closes #189) 2026-05-21 20:49:34 +02:00
iris
fc3490086b agent page: assemble forge URL backend-side
Per review: build the full forge profile URL in the harness instead
of the client. /api/state now returns forge_url: Option<String>
(assembled from the request Host header — resolves against whatever
host the operator reached the page on), replacing the forge_present
bool. The JS just links forge_url when present — no client-side URL
construction.
2026-05-21 20:27:43 +02:00
iris
6ab667901d agent page: link to the agent's forge profile
Add a '⬡ forge ↗' link to the per-agent page's meta row, next to
the stats + screen links. It opens the agent's Forgejo profile
(http://<host>:3000/<label> — the per-agent forge user is named
after the agent) in a new tab.

- web_ui.rs: StateSnapshot gains forge_present, true when the
  agent's forge-token file exists in the state dir (same signal
  that tells the agent it has a forge account).
- index.html / app.js: hidden link, shown + href-filled when
  forge_present, mirroring the existing gui_enabled/screen-link
  pattern. Host comes from window.location so it works off
  whatever host the page is served from.

closes #185
2026-05-21 20:27:43 +02:00
damocles
3b44410427 manager: receive forge notifications (ManagerRequest::Wake) 2026-05-21 19:05:53 +02:00
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
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
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
damocles
a13f63f30c forge_notify: check mark-read HTTP status, warn on non-2xx 2026-05-20 21:22:53 +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
5951758b35 track compacted turns separately in stats 2026-05-20 19:59:59 +02:00
damocles
de2179540a fix: enrich forge notifications with subject body and comment content 2026-05-20 19:30:28 +02:00
damocles
cddaacd12f feat: poll forge notifications in agent harness
Closes #27
2026-05-20 17:59:56 +02:00
damocles
5c27ab9d13 fix: scope rate-limit detection on stdout to JSON error events only
raw line.contains() on stdout false-positives when the agent's own
text mentions rate_limit_error or similar strings. stderr keeps the
raw match (it is always claude CLI output, not conversation content);
stdout now only fires on parsed {"type":"error"} events.

Closes #69
2026-05-20 16:22:57 +02:00
damocles
7e2f13cad8 model/context: defaults in nix module, no heuristic in rust 2026-05-20 15:49:03 +02:00
damocles
770cbaccf9 model/context: per-model ctx window overrides + expose window size in /api/state 2026-05-20 15:49:03 +02:00
damocles
9064cd3c57 model/context: configurable default model + model-derived context window 2026-05-20 15:49:03 +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
damocles
25659ee9f3 auto session-reset: drop checkpoint turn, reset cold without warming cache 2026-05-20 14:49:26 +02:00
damocles
fb951c1004 auto session-reset: fix default cache TTL to 1h, clarify state-preservation purpose 2026-05-20 14:49:26 +02:00
damocles
44c903f265 auto session-reset when context large and cache is cold 2026-05-20 14:49:26 +02:00