Commit graph

425 commits

Author SHA1 Message Date
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
müde
69a3ca7469 docs: prune landed todos + refresh scratchpad + as-built terminal-rendering
todo: drop landed entries (terminal coherence pass, get_state_file
defense-in-depth, self-management of loose ends, persist+cold-load
ctx-badge).

claude.md:
- scratchpad: new just-landed entries for ctx+cost badge split,
  terminal coherence pass, loose_ends rename + cancel_loose_end,
  whoami, reminder failure persistence, path linkify, tombstones+
  meta_inputs events, agent open-threads section + container pending-
  reminder chip + task event rendering. drops the meta-flake
  "just landed" — structural facts live in the file map +
  approvals.md, the narrative was no longer load-bearing.
- file map: hive-fr0nt now lists MARKED_JS + marked.min.js + the
  unified prefix-column terminal.css update.
- reading paths: terminal-rendering.md description matches as-built.

docs/terminal-rendering.md: rewritten as as-built reference. layout
contract documents the padding-left + negative text-indent prefix
column + how details inherits it. row taxonomy reflects current
state (notes split into .note / .note.stderr / .note.op; .sys is
amber; recv tool_results default-open with markdown body via
tool_use_id correlation; rich send/ask/answer renderers). new
sections for renderer dispatch flow, markdown integration,
fmtArgsGeneric extra-MCP fallback, dashboard msgrow text-indent
reset.
2026-05-18 19:25:50 +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
fcd407da11 todo: mark terminal coherence pass as landed 2026-05-18 18:13:25 +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
8a3e8bfb7f todo + terminal-rendering: ctx-badge cold-load, auto session-reset, more coherence-pass gripes 2026-05-18 18:00:46 +02:00
damocles
9995bbc891 get_state_file: refuse symlinks below root + require world-readable mode 2026-05-18 17:35:24 +02:00
müde
f84011abc3 todo: bug — token budget exhaustion crashes harness, leaves container up 2026-05-18 11:29:49 +02:00
müde
487be2e1fd todo: per-agent terminal coherence pass → docs/terminal-rendering.md 2026-05-18 11:28:06 +02:00
müde
5389875079 docs: terminal-rendering.md — row taxonomy + inconsistencies + coherence proposal 2026-05-18 11:27:35 +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
müde
63f5f9a2ef todo: drop landed entries — get_open_threads, whoami, oversize-msg, tombstones+meta_inputs 2026-05-18 00:11:48 +02:00
müde
0a75e62ffe todo: drop landed entries (reminder errors + tombstones/meta_inputs events) 2026-05-18 00:08:25 +02:00
müde
978a3cf391 reminders: persist + surface delivery failures
Broker schema gains attempt_count INTEGER + last_error TEXT
columns via idempotent ALTER TABLE migration (pragma-probed so
fresh + existing dbs converge). reminder_scheduler::tick calls
record_reminder_failure on every deliver_reminder error,
bumping the counter + stashing the message. get_due_reminders
filters out rows where attempt_count >= MAX_REMINDER_ATTEMPTS
(5) so the scheduler stops retrying a stuck row until the
operator intervenes.

new POST /retry-reminder/{id} → reset_reminder_failure clears
the counters; next 5s tick re-attempts. cancel-reminder
unchanged (hard-delete).

dashboard renders failed rows with a red left rule, the error
text inline, and a ⚠ N failed badge. ↻ R3TRY button appears
when attempt_count > 0 — sits next to ✗ C4NC3L in a small
actions row below the body.
2026-05-18 00:08:09 +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
4ec401a6c7 question/answer text: server-side file_refs
DashboardEvent::QuestionAdded gains question_refs and
QuestionResolved gains answer_refs — both populated via
scan_validated_paths at emit time, same helper the broker
forwarder uses for Sent/Delivered. cold-load snapshot wraps
each OpQuestion in QuestionView with the same fields computed
once per /api/state.

client threads refs through questionsState rows (pending +
history) and passes them to appendLinkified at every render
site (live pane, history details). path tokens in question and
answer bodies now linkify with the same server-vouched
guarantee broker messages already enjoyed.
2026-05-17 23:54:35 +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
087a5366fb container row: pending-reminder count chip ( N)
ContainerView gains pending_reminders: u64; computed during
build_all via Broker::count_pending_reminders_for, mapping
manager → MANAGER_AGENT recipient + sub-agents → logical name.
Updates on every rescan (mutation sites + crash_watch's 10s
poll); accept 10s staleness on background remind / scheduler
delivery — live updates on operator cancel via /api/state path.

client renders a small cyan chip on the row when the count > 0;
tooltip points the operator at the reminders section to view
or cancel.
2026-05-17 23:52:56 +02:00
müde
aed43ce4df dashboard: tombstones + meta_inputs events — last /api/state refetches drop
new DashboardEvent::TombstonesChanged + MetaInputsChanged carry
full snapshots (lists are tiny; snapshot beats diff for race
avoidance). Coordinator-side helpers
emit_tombstones_snapshot + emit_meta_inputs_snapshot fire from
every mutation site: actions::destroy + post_purge_tombstone +
actions::approve (spawn finalise consumes tombstone) +
run_meta_update + auto_update::rebuild_agent (lock bumps).

client adds derived stores + apply* handlers + drops the
post-submit refetch on PURG3 (container row + tombstone row)
and meta-update.

after this commit /api/state is fetched exactly once per page
session (cold load); every other change rides the SSE channel.
2026-05-17 23:52:12 +02:00
müde
76e4034e01 path linkify: server attaches file_refs at message ingest
drop the /api/state-file/check probe endpoint (which let any
dashboard visitor enumerate filesystem layout by feeding paths)
and the client's optimistic-then-downgrade dance. instead, the
broker forwarder calls scan_validated_paths(body) — same
allow-list helper as the read endpoint — and attaches the
verified file tokens to DashboardEvent::Sent/Delivered as
file_refs: Vec<String>. /dashboard/history backfill does the
same per-row.

client appendLinkified takes a (text, refs) pair, walks
left-to-right linkifying every occurrence of any ref token,
longest-first tie-break. no regex, no probe, no cache, no
queue. when refs is empty/absent the body emits as plain text
(question/answer/reminder rendering — refs for those are a
follow-up).

operator inbox stores file_refs from the sent event so its
renderer gets the same anchors as the message-flow terminal.
2026-05-17 23:44:50 +02:00
müde
6e098fad29 path linkify: server-side validation via /api/state-file/check
regex back to permissive ("looks like a path") — the server is
authoritative on whether each match is a file. anchors render
optimistically, paths queue for batch validation (50ms coalesce),
non-files downgrade to plain text + the sibling <details>
preview is dropped. session-scoped cache (pathValidity Map) so
repeated paths skip the roundtrip.

new endpoint POST /api/state-file/check accepts { paths } and
returns { results: {<path>: bool} }. shares resolve_state_path
helper with the read endpoint so security rules can't drift —
both refuse anything outside the allow-list, anything resolved
outside via symlink, or anything in a per-agent subdir other
than state/. capped at 64 paths/request.

drops the brittle client-side filename heuristic (the .ext-
required rule that missed README/Makefile and still matched bare
dirs without trailing slash). single source of truth.
2026-05-17 23:36:44 +02:00
müde
0e2d26304e path linkify: require last segment to look like name.ext (skips dir refs) 2026-05-17 23:32:37 +02:00
müde
9585edef9b todo: rewrite harness-state split — /agents/<n>/{state,harness}, kill legacy /state 2026-05-17 23:30:28 +02:00
müde
bcb3f580ff todo: split harness-internal state from agent-visible /state 2026-05-17 23:28:59 +02:00
müde
d890509be3 docs: turn_stats sink + event-driven agent badges + dashboard event vocabulary 2026-05-17 23:28:34 +02:00
müde
e772182724 path linkify: skip trailing-slash (directory) matches 2026-05-17 23:25:51 +02:00
müde
2ad5a94897 dashboard: msgrow flex layout — fix per-char wrap when paths linkify 2026-05-17 23:20:29 +02:00
müde
c2c475bd65 events_vacuum: drop row cap, age-only retention (7d min) 2026-05-17 23:19:08 +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
damocles
9ec0d60308 flake: update nixpkgs + nixpkgs-unstable 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