hyperhive/TODO.md
müde a67aada7c9 todo: browser notifications for approvals / questions / operator msgs
pure frontend — Notification API + existing /api/state and
/messages/stream signals. Caveats: secure-context requirement
(HTTPS or localhost), per-browser permission grant. Includes a
sketch of the implementation: request-permission button, count
deltas on refreshState, SSE hook on operator-bound sends,
localStorage 'muted' toggle.
2026-05-15 21:07:21 +02:00

6.1 KiB

TODO

Pick anything from here when relevant. Cross-cutting design notes live in CLAUDE.md; high-level project intro in README.md.

Permissions / policy

  • Per-agent send allow-list. Today any agent can send to any other recipient (peer, manager, operator). Add a per-agent policy that constrains the to field — declared in agent.nix, e.g. hyperhive.allowedRecipients = [ "manager" "alice" ]. Broker rejects with an Err { message } when the policy denies. Default: unrestricted (back-compat). The manager can still always send anywhere. Useful for sandboxing untrusted sub-agents so they can only talk to the manager, not other sub-agents.

Security

  • Unprivileged containers (userns mapping). Today the nspawn container runs as a fully privileged root. Goal: PrivateUsersChown=yes (or the nixos-container equivalent) so uid 0 inside maps to an unprivileged uid on the host, and a container-root compromise lands the attacker on an ordinary user account, not the host's root. Requires per-agent state dirs to be chown'd to that uid on the host side.
  • Bash command allow-list. Replace the blanket Bash allow with a pattern allow-list (Bash(git *), Bash(nix build .*), etc.) per claude-code's --allowedTools extended grammar. Likely lives in agent.nix so each agent can scope its own shell surface.

Per-agent extension

  • Custom per-agent MCP tools. Today every sub-agent gets the same fixed MCP surface (send, recv). To move bitburner-agent (and anything else with rich domain tooling) into hyperhive, an agent needs a way to ship its own tools alongside hyperhive's. Sketch: agent.nix declares a list of extra MCP servers (command + args + env), each registered into the agent's --mcp-config blob at flake-render time. The harness MCP server remains the hyperhive surface; new servers slot in as additional entries under mcpServers.<name> so claude sees them as mcp__<name>__<tool>. Per-agent tool whitelist (allowedTools) derived from the same config so the operator stays in control of what's exposed.

UI / UX

  • Browser notifications for operator-bound events. Dashboard pings the OS notification center when (a) a new approval lands in the queue, (b) a new ask_operator question is queued, (c) a broker message is sent to: "operator". All three data sources are already in /api/state + /messages/stream so this is pure frontend. Sketch:

    1. Small "🔔 enable notifications" button somewhere (header or near the inbox section). Clicks call Notification.requestPermission(). Hide once granted.
    2. Track last-seen counts in the JS app (approvals.length, questions.length). On refreshState, if the count went up, fire new Notification(...) per new item.
    3. SSE handler for messages/stream fires a notification on kind === 'sent' && to === 'operator' (already triggers refreshState; just adds a notify call alongside).
    4. Notification body links back to the dashboard (onclick → window.focus() + section anchor). Caveats: Notification API requires a secure context (HTTPS or localhost). Most operators access via LAN / Tailscale — works fine for localhost forwards, otherwise needs a TLS cert in the module. Persist a per-browser "muted" toggle in localStorage so the operator can silence without revoking permission.
  • Terminal: /model slash command. Operator-typeable model override from the terminal. Depends on the model-override work above; once an override mechanism exists, wire a /model <name> command that POSTs to a new endpoint.

  • xterm.js terminal embedded per-agent, attached to a PTY exposed by the harness. Pairs well with the unprivileged-container work — would let the operator drop into the container without nixos-container root-login.

Telemetry

  • Harness stats per agent in sqlite, charted on the agent page. bitburner-agent samples 18 series; for hyperhive the generally-applicable ones are:
    • turns/min, tool calls/turn, turn duration p50/p95
    • claude exit code distribution (ok vs --compact-retry vs failure)
    • inbox depth (current + max-over-window)
    • messages sent/received per turn (split by recipient: peer / operator / manager / system)
    • approval queue length (across all agents — dashboard-level)
    • per-tool usage counts (Read/Edit/Bash/send/recv/…)
    • time-since-last-turn (helps spot stuck agents)
    • notes file size growth (cues compaction) Backend: a stats table with (agent, ts, key, value) written from the harness on TurnEnd; GET /api/stats?since=… returns the series; agent page renders with a small chart lib (uPlot is light).

Manager → operator question channel

Spawn flow

  • Two-step spawn. Today request_spawn(name) is one shot: manager asks → operator approves → container is created with a default agent.nix and empty /state/. Manager has no way to pre-stage per-agent prompt material, package additions, or initial notes before the agent first wakes. Split into:
    1. request_spawn_draft(name) — host creates the per-agent proposed/ repo (initial commit) and state/ dir with no container; manager now has /agents/<name>/{config,state}/ to edit + commit just like an existing agent.
    2. request_spawn_commit(name, commit_ref) — submits the queued approval; operator sees the diff in the dashboard like a normal apply_commit; on approve the container is created from that commit. Backwards-compat: keep the existing one-shot request_spawn for trivial agents (operator can still type a name in the dashboard). Surface "drafts" as a new section between K3PT ST4T3 and approvals.

Loop substance

  • Notes compaction. /state/ is bind-mounted persistently and agents are told (in the system prompt) to keep /state/notes.md for durable knowledge — but we don't currently nudge them to compact when notes grow. Bitburner-agent's pattern: a short-lived secondary claude session that takes the existing notes + a "compact this" prompt and rewrites them in place. Add when the notes start bloating.