Commit graph

169 commits

Author SHA1 Message Date
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
müde
6ab3810e18 docs: refresh for the dashboard rework + recent harness commits
- web-ui.md: side panel, approval card + 3-way diff base, stats
  page, forge config links, removed agent.nix viewer, per-agent
  loose-ends inline answer.
- approvals.md: forge mirror section + diff base toggle.
- turn-loop.md: recv(max), get_logs, remind, loose-ends, whoami.
- agent.md / manager.md prompts: recv(max), remind, get_logs.
- CLAUDE.md: forge.rs / stats.rs / hive-forge.nix in the file
  map, scratchpad refresh.

also: forgejo migrations.ALLOW_LOCALNETWORKS = true so an in-hive
mirror of the hyperhive repo can import from a localhost source.
2026-05-20 11:34:43 +02:00
müde
f13c3dff8f stats: per-bucket turns-by-model chart
each turn_stats row already records the model; roll it up per bucket
so the /stats page can show which model ran when. model choice
greatly affects token cost, so the new stacked-bar chart sits right
under the cost chart for eyeball correlation across the window.

Snapshot gains a sorted `models` series list; each Bucket carries a
`model_counts` map.
2026-05-20 10:58:14 +02:00
damocles
0a79912b67 get_logs: resolve machine name via container_name like every other verb 2026-05-20 10:48:24 +02:00
damocles
1529c2d777 lifecycle: bind each sub-agent's config repo read-only at /agents/<name>/config 2026-05-20 10:05:02 +02:00
müde
56e7eb6e73 agent ui: answer questions inline from the per-agent page
loose-ends question rows get a textarea + send button; the operator
answers as operator by POSTing to the core dashboard's
/answer-question route, not the per-agent socket — keeps the
operator-authority path off the agent's own socket. cross-origin POST
needs a CORS shim on that route for now; drops out once the gateway
makes the page same-origin.

also splits deployment/ops/boundaries/gateway work into TODO-ops.md.
2026-05-20 10:01:12 +02:00
damocles
f8795dc029 fix: request_apply_commit resolves sha locally + rejects non-sha refs 2026-05-20 09:48:05 +02:00
damocles
5d27ae3048 recv: fold batch drain into recv(max) — one tool, uniform list response 2026-05-19 01:07:30 +02:00
damocles
77b89bf2c6 broker: recv_batch(max) — drain a bursty inbox in one round-trip 2026-05-19 00:47:21 +02:00
müde
96ffb0e39a stats: uniform chip size in summary row
fixed min-width + height so every headline chip lines up regardless
of value length; stacked label-over-value layout with smaller uppercase
label so chips read as a row of identically-sized tiles.
2026-05-19 00:28:01 +02:00
müde
d3f90f4cc0 stats: per-agent /stats page with chart.js trends + breakdowns
new hive-ag3nt::stats module reads turn_stats.sqlite read-only and
aggregates over 24h/7d/30d windows (hourly/daily buckets) — turn
rate, p50/p95/avg duration, ctx tokens (avg/max), cost token
components, top tools, wake mix, result mix. served by the agent
itself so per-MCP extensions can register more providers without
the host knowing their schemas.

/stats route + /api/stats?window=... on the per-agent web ui.
chart.js v4.4.4 pulled from jsdelivr (SRI hash deferred). nav
links: 📊 chip on the dashboard container row + 📊 stats → on
the per-agent header.

todo housekeeping: softened damocles-area note at the top,
new reverse-proxy + deferred reminder-rollup items, removed
the two telemetry-ui items absorbed by this page.
2026-05-19 00:27:01 +02:00
damocles
f9f1346eae clippy: zero pedantic warnings across the tree 2026-05-18 22:09:34 +02:00
damocles
690cb5ab5b broker: lease-style delivery — ack_turn + requeue_inflight close the no-drop loop 2026-05-18 22:01:48 +02:00
damocles
2b38805c00 cancel_loose_end: canonicalize kind in success ack (alias 'q' → 'question') 2026-05-18 18:49:54 +02:00
damocles
59412452af format_loose_ends: align output text with rename (loose end(s) / no loose ends) 2026-05-18 18:49:54 +02:00
müde
5c6c607e25 agent badges: split into ctx (last-inference) + cost (cumulative)
the existing ctx badge was misnamed: it summed `result.usage`, which is
the cumulative tokens billed across every inference in the turn. for
tool-heavy turns that easily exceeds the model's context window (a 600k
cached prefix × 15 sub-calls = 9M cache_read), making it useless as a
"should i compact?" signal.

now two separate badges:

  ctx · N    last inference's prompt size = actual context window in
             use right now. parsed from each `assistant` event's
             `.message.usage`; the harness tracks the most recent one
             across the stream and snapshots it when the `result`
             event lands.

  cost · M   cumulative tokens billed across the whole turn (the
             previous behaviour, now correctly labelled).

both update via a single `TokenUsageChanged { ctx, cost }` SSE event at
turn-end. turn_stats grows four columns (`last_input_tokens`,
`last_output_tokens`, `last_cache_read_input_tokens`,
`last_cache_creation_input_tokens`) so the cold-load seed can paint both
badges on page load. migrations run try-and-ignore ALTERs so existing
agent dbs catch up; pre-migration rows have last-inference zeros and
yield no `ctx` seed (badge stays empty until next turn) rather than a
misleading 0.
2026-05-18 18:48:35 +02:00
müde
14549dd8a9 agent ctx-badge: use dot decimal for M (1.3M not 1,3M) 2026-05-18 18:39:22 +02:00
müde
93fe1a60dc agent ctx-badge: format >=1M as X,YM 2026-05-18 18:38:37 +02:00
müde
118fbe6a71 agent ctx-badge: drop decimal — 1304.2k misread as 1304.2 2026-05-18 18:37:46 +02:00
damocles
6e23d087d2 rename: open_threads → loose_ends + cancel_thread → cancel_loose_end across wire / tools / web ui 2026-05-18 18:24:09 +02:00
damocles
b1d0a62cb9 cancel_thread: new mcp tool — unify reminder + question cancel on both surfaces 2026-05-18 18:24:09 +02:00
müde
f8f2ccff52 agent terminal: coherence pass
layout
- unified prefix-column for every row kind: padding-left + negative
  text-indent so the glyph (→ ← · ◆ ✓ ✗ ⌁ !) sits in the same column
  whether the row is flat or a <details>. wraps hang under the body,
  not under the glyph.
- expandable rows drop the directional glyph from their summary text;
  the ▸/▾ disclosure marker from CSS sits in the prefix column instead,
  and the row's colour still carries cyan = outbound, muted = inbound.
- turn-start / turn-end de-weighted: bold/margin/tint dropped, the
  coloured left rule alone marks the boundary.

note classification
- stderr lines render orange with a `!` glyph (was muted `·`)
- operator-initiated notes (cancel/compact/model/new-session) render
  mauve italic (was muted `·` indistinguishable from harness chatter)
- catch-all .sys row escalates to orange `!` so unrecognised stream-json
  shapes surface for follow-up instead of hiding in muted noise

message-bearing rows
- send / ask / answer tool_use rich renderers default-open with the
  body inline; new ask + answer renderers (previously fell through to
  the generic JSON dump). recv tool_result also default-open, keyed by
  tracking tool_use_id → name across the stream so we know which
  result came from which tool.
- assistant text rows render markdown.
- bodies use vendored marked v4.0.2 (hive-fr0nt::MARKED_JS); falls
  back to plain text when the asset doesn't load.

extra-mcp tool pretty-print
- generic args formatter replaces the raw JSON dump for unknown tools
  (single-string field → `name k: "v"`; single dict / multi-field →
  trimmed `k: v · k: v …` summary)

dashboard .live .msgrow gets a text-indent: 0 reset so the new
hanging-indent metrics from TERMINAL_CSS don't leak into the flex-grid
broker rows.
2026-05-18 18:13:14 +02:00
müde
f827187341 agent ctx-badge: seed Bus::last_usage from latest turn_stats row on startup 2026-05-18 18:00:48 +02:00
müde
fd7712f5c1 agent terminal: pretty-render task_started / task_notification
claude's Task tool spawns subagents whose progress lands as
stream-json events with subtype=task_started or
task_notification — previously fell through to the .sys
catch-all and rendered as a raw json dump that wrapped per
char in the live pane.

now matched by subtype before the catch-all:
- task_started → cyan tool-use row, ⌁ glyph, first 8 chars of
  task_id, description, and optional [task_type]
- task_notification → row styled by status: completed →
  turn-end-ok (green ✓), failed → turn-end-fail (red ✗),
  other → tool-result (muted ◌). output_file rendered inline
  if present so the operator can trace where the body landed.

matching on `v.subtype` rather than a particular `v.type` so
the renderer survives claude wrapping these under different
top-level type fields across versions.
2026-05-18 11:25:54 +02:00
damocles
d395bdc945 whoami: drop operator_pronouns (redundant — already in system prompts at boot) 2026-05-18 00:04:58 +02:00
damocles
3c66cb6707 whoami: new mcp tool returning name/role/pronouns/hyperhive_rev on both surfaces 2026-05-18 00:04:58 +02:00
müde
378e8bf9df agent ui: open-threads section (questions + approvals pending)
new /api/open-threads endpoint on hive-ag3nt proxies the agent's
own GetOpenThreads RPC (manager flavour proxies the hive-wide
ManagerRequest::GetOpenThreads). same data the
mcp__hyperhive__get_open_threads tool sees from inside claude.

frontend renders a collapsible <details> section above the
terminal, listing each pending row (approval / question) with
asker → target, age, and free-form body. auto-expands on the
first appearance of any open thread; sticky after that.
refreshed on cold load + after every turn_end (turns are when
threads land or resolve).
2026-05-17 23:53:40 +02:00
müde
8f5752980f turn_stats: per-turn analytics sink
new sqlite table at /state/hyperhive-turn-stats.sqlite on each
agent's state dir. one row per claude turn captures identity
(model, wake_from, result_kind), timing (started/ended_at,
duration_ms), cost (input/output/cache_read/cache_creation token
counts), behaviour (tool_call_count + per-tool breakdown JSON),
and post-turn snapshot metrics (open_threads_count,
open_reminders_count).

wire additions:
- AgentRequest/ManagerRequest::CountPendingReminders +
  Broker::count_pending_reminders_for(agent)
- Bus::observe_stream + take_tool_calls — pumps the existing
  stdout stream-json, picks out tool_use blocks, accumulates per
  turn. bin loops fold the breakdown into each row.
- TurnStats::open_default + TurnStatRow + record() — best-effort
  inserts; failures log + don't block the harness.

both ag3nt and m1nd bins capture started_at + duration via
Instant::elapsed, fetch open-thread + reminder counts from
hive-c0re via the existing socket (post-turn, best-effort), and
record one row at turn_end. record_kind splits ok / failed /
prompt_too_long; failures carry the error message in note.

todo entries for host-side vacuum sweep + reading the table back
into agent/dashboard badges.
2026-05-17 23:00:41 +02:00
damocles
dc1ce1f236 open_threads: new get_open_threads MCP tool on agent + manager surfaces 2026-05-17 22:52:08 +02:00
müde
39d8359c10 agent ui: event-driven status / model / token_usage / turn_state
new LiveEvent variants on the per-agent bus —
status_changed / model_changed / token_usage_changed /
turn_state_changed — replace the per-agent web UI's
/api/state polling for the badge row.

emit sites:
- Bus::set_model → model_changed
- Bus::record_usage → token_usage_changed
- Bus::set_state → turn_state_changed
- turn::wait_for_login → status_changed("online") on creds detect
- post_login_start / post_login_cancel → status_changed("needs_login_*")

per-agent endpoints (post_set_model / post_compact / post_new_session
/ post_cancel_turn / post_login_*) now all return 200; client
drops the post-submit refetch except on login transitions, which
still need /api/state to render the OAuth form + session stream.

client adds dispatch on the four new event kinds, threads
`currentLabel` through so the composer re-enables on a live
status flip, and no longer fires refreshState() from turn_end or
postModel — the events carry the same signal faster.

closes the per-agent half of the dashboard event-channel
refactor; TODO entry dropped.
2026-05-17 22:49:55 +02:00
müde
b444dac6e8 agent ui: consolidate status into state-row badges
drop the "● harness alive — turn loop running" paragraph; the
new #alive-badge chip in the state row carries the same signal
across all statuses (loading / online / needs-login / offline)
with colour coding. token-usage chip renamed + restyled as
#ctx-badge — primary number is total context-window tokens
used, mirroring claude code's "N tokens" indicator.

every state-row badge now has hover detail: state-badge gets
per-state tooltips + age suffix, model-chip explains the
/model command, last-turn shows the raw ms duration, ctx-badge
breaks out input / cache_read / cache_write / output.

new todo entry for the per-turn stats sink (start/end/model/
tokens/tool-call-count) the harness should be writing.
2026-05-17 22:36:02 +02:00
damocles
15f141801b limits: raise message body cap 1k → 4k (catches ~95% of conversational overflow) 2026-05-17 22:15:25 +02:00
damocles
82b0877c47 ask: rename ask_operator → ask + optional 'to' for agent-to-agent Q&A 2026-05-17 13:20:32 +02:00
müde
b60774a66c events: LiveEvent::Note becomes struct variant so serde can actually serialize it 2026-05-17 13:14:09 +02:00
müde
aa24080f7b agent: /send returns 200 (terminal + turn-end refresh already cover the visual update) 2026-05-17 12:41:37 +02:00
müde
1340a654e7 sse: seq plumbing + subscribe-first dedupe dance 2026-05-17 12:26:00 +02:00
müde
f27108aecf agent: route terminal scroll+backfill+SSE through hive-fr0nt::TERMINAL_JS 2026-05-17 11:53:50 +02:00
müde
0b9e7cbcf6 css: extract terminal pane styles to hive-fr0nt::TERMINAL_CSS 2026-05-17 11:50:39 +02:00
müde
e283e39949 css: route palette + body typography through hive-fr0nt::BASE_CSS 2026-05-17 11:47:45 +02:00
damocles
1770b51845 manager mcp: expose 'remind' tool sharing storage helper with agent surface 2026-05-17 11:43:14 +02:00
damocles
0e6bac8388 limits: unified 1 KiB cap on send/ask + reminder auto-file on overflow 2026-05-17 11:36:12 +02:00
damocles
07b7988915 agent mcp: add 'remind' to --allowedTools so claude doesn't have to ask 2026-05-17 11:20:01 +02:00
damocles
f2484b5e78 agent mcp: expose 'remind' tool for self-scheduled wakes 2026-05-17 10:54:36 +02:00
damocles
4f56954422 extract TokenUsage::from_stream_event helper to keep run_claude under clippy line limit 2026-05-17 02:59:51 +02:00
damocles
ce740483c6 show token usage on per-agent web ui after each turn 2026-05-17 02:59:51 +02:00
damocles
ca86bcf4bd add claudePluginsAutoUpdate NixOS option, default false 2026-05-17 02:59:51 +02:00
müde
411cf86632 nix fmt + rustfmt sweep 2026-05-17 01:40:28 +02:00
müde
597351ca4e harness: declarative claude plugin marketplaces
new `hyperhive.claudeMarketplaces` option (list of strings — URL,
path, or github:owner/repo). harness boot adds each via
`claude plugin marketplace add` before updating + installing the
configured plugins, so specs like `foo@some-marketplace` resolve
on a fresh container. idempotent: 'already exists' stderr is
treated as success.
2026-05-17 01:36:18 +02:00
müde
4a06615c5c fix /state paths: sub-agents use /agents/<name>/state, not /state
sub-agent containers post-refactor bind their state at
/agents/<name>/state (manager keeps the legacy /state — see
lifecycle.rs:751). agent.md still said /state/forge-token; corrected
to /agents/{label}/state/forge-token (template-substituted at
boot). tea-login systemd unit now walks both candidates so the same
harness module works for the manager and sub-agents.
2026-05-16 23:37:49 +02:00