Commit graph

1,560 commits

Author SHA1 Message Date
atlas
93d28ce0b1 fix: strip code-span backticks from extracted matrix password
mara observed the tuwunel reply renders the new password as a code span,
so the plain message body carries literal backticks:

  Successfully reset password for @user:server to: `<password>`

The previous extraction stopped at the first whitespace, capturing the
surrounding backticks ("`<password>`") and producing a login string that
doesn't match the password the bot actually set — recovery would still
fail after parsing.

Strip a leading backtick after the marker and stop the token at the first
whitespace OR closing backtick. Generated passwords contain neither, so a
real password is never truncated mid-token. Two regression tests cover the
backtick-wrapped form, including trailing prose after the closing backtick.
2026-06-05 00:47:37 +02:00
atlas
defface0a5 fix: parse tuwunel 'reset password for X to: <pw>' admin reply
extract_new_password had markers for 'is:', 'changed to:', 'reset to:',
'set to:' etc. but none match tuwunel's actual reset-password reply:

  Successfully reset password for @user:server to: <password>

Here the verb ('reset') is not adjacent to 'to:', so every marker missed
and the function returned None for every polled event. The admin-room poll
then ran its full 15s loop without a match and the auto-recovery timed out
on every agent — even though the bot replied correctly and the backward
poll found the message. This is why matrix password auto-recovery kept
looping despite the poll-strategy fix.

Add a generic ' to: ' marker (with surrounding spaces) placed after the
specific verb markers and before the bare 'password:' last resort. Matrix
user ids and server names can't contain ' to: ', so it only ever anchors
on the prose delimiter. Two regression tests cover the exact production
wording.
2026-06-05 00:47:37 +02:00
damocles
7f7ee7dbef fix: correct doc comment — path goes to stdout not stderr 2026-06-05 00:46:49 +02:00
damocles
98f96e5435 feat(#1315): hive-forge attachment-get verb - download attachments by uuid or url 2026-06-05 00:46:49 +02:00
iris
86165f07c8 docs: update hivectl verb list in CLAUDE.md (add gateway, agents, choom) 2026-06-05 00:46:17 +02:00
iris
33be854c50 docs: fix stale /api/agent/{name}/links reference in gateway.md 2026-06-05 00:46:17 +02:00
iris
30c2d94f13 docs: fix remaining stale AgentLink comment (per damocles review) 2026-06-05 00:46:17 +02:00
iris
4769d7ce5a docs: remove stale /api/agent/{name}/links references
The c0re-proxy endpoint was removed in the dashboard-state refactor.
Links are now served by agent_links() via two agent-local endpoints:
StateSnapshot.links (GET /api/state, per-agent page) and
DashboardState.links (GET /api/dashboard-state, dashboard card).

Update docs/web-ui/agent.md and the StateSnapshot.links doc comment
to reflect the current source of truth.
2026-06-05 00:46:17 +02:00
damocles
bc1c4d1cc5 docs: update get_host_journal container param to mention hive-ci and other siblings 2026-06-05 00:40:45 +02:00
damocles
12eac03324 fix(#1310): add hive-ci to SIBLING_CONTAINERS so agents can read its journal 2026-06-05 00:40:45 +02:00
atlas
4f446269ae fix: re-run hive-ci-prefetch on every container restart via partOf
The hive-ci-prefetch oneshot has RemainAfterExit=true and is wired to the
container only via wantedBy + before. Once it runs successfully it stays
'active (exited)' indefinitely, so systemd skips it on subsequent container
restarts. The runner-token file it wrote is never refreshed.

This breaks the runner after its first registration: the token written on
the first successful boot is either a placeholder (forge-core-token wasn't
ready yet) or a registration token that has since been consumed/rotated.
On the next container restart prefetch does not re-run, the stale token
persists, and the in-container register service fails with
'invalid_argument: runner registration token not found' — exactly the
symptom in the field (worked briefly, two orphan runners registered, then
permanently offline).

Add partOf = [ nixos-container@hive-ci.service ] so a container stop/restart
propagates to the prefetch unit, forcing it to re-run and fetch a fresh
registration token before the container comes back up. before= still orders
it ahead of the container start within the same transaction.
2026-06-05 00:39:13 +02:00
damocles
84e468dadb fix(#1309): retry forge token read at startup instead of giving up immediately 2026-06-04 21:15:03 +02:00
damocles
cf6479b3a9 fix(#1307) add appearance:none to .btn to strip browser UA button chrome 2026-06-04 21:04:45 +02:00
damocles
6c16d25039 fix(#1304) scope state/harness chowns to own agent dir in hive-agent-user-migrate 2026-06-04 21:04:12 +02:00
atlas
60adb5aac9 fix: chmod .claude dir 0755 in hive-agent-user-migrate activation script
ensure_claude_dir creates the dir as 0755 but cannot re-chmod after
hive-agent-user-migrate chowns it to the agent user (EPERM — non-owner).
The activation script runs as root and can always chmod it. Add an explicit
'chmod 755 $homeDir/.claude' after the existing chown so existing 0700
dirs from pre-fix containers are corrected on the next container boot.

Without this, all agents with pre-existing .claude dirs show 'needs login'
in the dashboard even with working sessions, because hive-core cannot list
the 0700 dir owned by a different user.
2026-06-04 20:48:39 +02:00
iris
f0271f95e4 docs(web-ui): update dashboard.md for dashboard-state endpoint
Replace the stale GET /api/agent/{name}/links reference with
GET /api/dashboard-state (merged in #1292). The new endpoint is
served by the agent backend and returns DashboardState carrying
links, status_text, rate_limited, ctx_tokens, and
context_window_tokens — all agent-owned fields that were previously
(mis)read by hive-c0re from disk.

Also update the ctx_tokens description: it's now sourced from
DashboardState rather than ContainerView (which no longer carries
ctx_tokens/context_window_tokens).
2026-06-04 20:15:09 +02:00
atlas
54f7c0facc docs: comment on empty event_id fallback in admin-room poll 2026-06-04 20:13:47 +02:00
atlas
e20a311786 tidy: is_whitespace() already covers newline, remove redundant arm 2026-06-04 20:13:47 +02:00
atlas
254fd1f9f1 fix: admin-room poll strategy — backward fetch anchored on sent event_id
The forward-pagination approach (dir=b anchor → dir=f poll) fails in
production: commands sent as @hive time out consistently even though
tuwunel responds. Root cause is likely a pagination-token direction
incompatibility in some tuwunel builds where 'end' from dir=b cannot
be used as 'from' for dir=f.

New strategy: send the command, capture the event_id from the PUT
response, then poll dir=b&limit=20 each tick. Events come back
newest-first; walk until we hit our own event_id, then stop — anything
before that marker arrived after our command. Simpler, avoids stored
tokens entirely.

Also:
- check formatted_body in addition to body (some admin bots put content
  only in HTML formatted_body)
- add more extract_new_password patterns: 'changed to:', 'reset to:',
  'set to:', 'new password:', 'password:' to handle different tuwunel
  version response formats
- add unit tests for new patterns

Fixes #1283.
2026-06-04 20:13:47 +02:00
damocles
9f8694e3cf fix(#1249): break sticky-bottom lock when user scrolls up during snap animation 2026-06-04 20:10:37 +02:00
iris
0723737d89 fix(#1029): use getBoundingClientRect for tab overflow cutoff
The old measurement used tabbar.clientWidth which includes horizontal
padding (~2em / ~30px total), causing an over-allocation of the same
amount. Flex gap between tabs (0.2em) was also not counted in the
accumulated offsetWidth, adding another ~20px of error. Together these
caused the algorithm to leave too many tabs visible, overflowing the
bar and pushing the ⋮ button off the right edge of the screen.

Replace the clientWidth + cumulative-offsetWidth approach with
getBoundingClientRect: the cutoff is the tabbar's actual right edge
minus its right padding, minus a reserve for the overflow wrapper.
Individual tabs are overflowed once their right edge exceeds the
cutoff; subsequent tabs are overflowed unconditionally to keep the
visible set contiguous and left-anchored.
2026-06-04 20:10:19 +02:00
iris
0f77fc3598 fix(#1297): prevent scroll jump on loadMore prepend
Two cooperating fixes:

1. overflow-anchor: none on .live — disables browser scroll anchoring
   so the manual scrollTop compensation in loadMore() is the sole
   mechanism. Without this the browser auto-adjusts scrollTop when
   rows are prepended above the viewport, then our += delta doubles
   the compensation, causing the erratic jump.

2. updateLoadMoreBtn() moved before the beforeH baseline capture —
   if histHasMore becomes false the load-more button is removed here
   rather than after the delta is applied, so the button height is
   already baked into beforeH and doesn't shift the viewport post-
   compensation.
2026-06-04 20:08:29 +02:00
damocles
ab65245619 feat(#1298): add hivectl choom verb — interactive claude session in agent container 2026-06-04 20:08:04 +02:00
iris
70832fcbf1 fix(#1293): override global li padding on agent menu separator
common.css sets `li { padding: 0.5em 0 }` globally. The .agent-menu-sep
li inherits this, bloating the 1px separator into a ~16px thick bar.

Add `padding: 0` to .agent-menu-sep to reset it.
2026-06-04 20:06:18 +02:00
iris
e0ea22f3ee refactor(#1292): remove agent-owned fields from ContainerView
Now that the dashboard fetches agent-owned state directly from
GET /api/dashboard-state (via gateway), hive-c0re no longer needs
to read those fields from disk on the agent's behalf.

Removed from ContainerView:
  ctx_tokens, context_window_tokens, rate_limited,
  extra_links, status_text, status_set_at

Removed from container_view.rs:
  DashboardLink struct, build_nav_links, read_dashboard_links,
  read_status, is_rate_limited, read_last_turn, resolve_ctx_window
  (and the resolve_ctx_window unit tests)

Removed from dashboard.rs:
  GET /api/agent/{name}/links route + get_agent_links handler

dashboard JS (tabs.js):
  Merged rate_limited, ctx-window badge, and status-text rendering
  into the async dashboard-state fetch block. c0re still provides
  needs_login (auth sentinel on host), needs_update, pending_reminders,
  running, deployed_sha, parent — all genuinely host-side fields.
2026-06-04 19:39:06 +02:00
iris
40845c21cd feat(#1292): add GET /api/dashboard-state to hive-ag3nt
hive-ag3nt now exposes a lean DashboardState endpoint that returns the
agent-owned fields the dashboard card needs:

  - status_text / status_set_at  (from hyperhive-status on disk)
  - ctx_tokens / context_window_tokens  (from Bus)
  - rate_limited  (from Bus)
  - links  (from agent_links — includes screen link for GUI agents,
            which c0re's disk-based fallback cannot determine)

The dashboard fetches `${containerBase}/api/dashboard-state` instead of
the previous c0re proxy `/api/agent/{name}/links`. For gateway deployments
this is a same-origin call to the agent via the gateway's unix-socket
upstream; for direct TCP it hits the agent port directly. Both paths fail
gracefully (empty strip) when the agent is starting up.

The main behaviour fix: GUI agents now show the screen link in the dashboard
nav strip. c0re's build_nav_links reads /etc/hyperhive/gui.json from outside
the container (not possible), so it always omitted the screen link.
2026-06-04 19:16:40 +02:00
iris
750f64b7f0 feat(#1290): show per-agent loose-end question count in dashboard
Add a ' N' badge to each container row showing the count of pending
questions where the agent is the asker (awaiting an answer) or the
target (owes a reply). Derived live from questionsState — no backend
field needed.

Details:
- New .badge-loose-ends CSS class (purple, matching Q33R1ES section colour)
- Badge tooltip breaks down the count: 'N asked, N to answer'
- renderContainersFromState() called on QuestionAdded/QuestionResolved
  so the badge updates instantly when question state changes
- Existing  reminders badge kept separate (different signal)
2026-06-04 19:06:56 +02:00
iris
bddeb5478e fix(#1286): pretty-print system events in the agent terminal
The catch-all renderer was dumping raw JSON (in loud orange) for
claude's system events like api_retry — producing noise like:
  ! {"attempt":1,"error":"unknown","subtype":"api_retry",...}

Replace the single 'init' filter with a full system-event block:
- api_retry → muted note: '⚠ api retry 1/10 · unknown · 502ms'
- api_error → amber note:  '✗ api error · <message>'
- other subtypes (context_window_exceeded, etc.) → '⚙ <subtype>'

The loud orange sys catch-all is now reserved for truly unrecognised
top-level event types, not routine operational noise.
2026-06-04 18:54:33 +02:00
iris
0894030754 fix(#1285): handle restart queue kind in dashboard container row
The pending-label derivation had no case for op.kind === 'restart', so
it fell through to the rebuild/rebuild-queued else branch — showing a
misleading 'rebuild pending' badge when the operator issued a restart.

Add explicit 'restarting' / 'restart queued' cases alongside the
existing meta_update and destroy cases.

Also add restart / startup_sweep / perm_change to QUEUE_KIND_GLYPH so
they render with a meaningful glyph (↺ /  / 🔑) instead of '?' in
the rebuild-queue panel.
2026-06-04 18:54:28 +02:00
lexis
13fe5ef9d3 docs(turn-loop.md): document get_loose_ends agent parameter scoping
Add clarification for the agent parameter: omit to list own threads,
pass agent name for direct children (always accessible), or query_agent_state
capability for non-children. Note that hive-wide '*' query unavailable
on agent socket.
2026-06-04 18:38:19 +02:00
damocles
b5503cde2c fix(logs): add ?id= deep-link to logs viewer, fix queue entry link target 2026-06-04 17:39:31 +02:00
damocles
5f05caee31 fix(queue): rename queued_ids to active_ids, document circular-dep caveat 2026-06-04 17:39:31 +02:00
damocles
7118c5efdd feat(queue): link rebuild queue entries to build log rows for live streaming 2026-06-04 17:39:31 +02:00
damocles
d31a723daf feat(#550): add depends_on to queue entries for explicit dep tracking 2026-06-04 17:39:31 +02:00
atlas
1fa398e99e fix: use correct tuwunel admin room command prefix for reset-password 2026-06-04 16:43:53 +02:00
atlas
85de83bd49 fix: use correct tuwunel admin room command prefix for make-user-admin
tuwunel requires '!admin users <cmd>' — bare 'make-user-admin @user:server'
is not recognised. Update command string and doc comments.
2026-06-04 16:35:59 +02:00
atlas
0f505d5c95 fix: increase admin-room poll timeout from 5s to 15s
tuwunel can take longer than 5 seconds to process admin-room commands
during startup when the homeserver is under load. Bump both the poll
count (5→15) and the timeout message strings to match.
2026-06-04 16:28:47 +02:00
atlas
e06d3e3a8b refactor: extract admin_room_send_and_poll helper — deduplicate poll loop 2026-06-04 16:28:47 +02:00
atlas
2252eac650 fix: replace Synapse admin API in promote_user_to_admin with admin-room command 2026-06-04 16:26:59 +02:00
atlas
74253a9f71 fix: increase admin-room poll timeout from 5s to 15s
tuwunel can take longer than 5 seconds to process admin-room commands
during startup when the homeserver is under load. Bump poll count 5→15
and update timeout message strings to match.
2026-06-04 16:26:04 +02:00
atlas
0a3a956d28 fix: add /var/lib/hyperhive to hive-priv ReadWritePaths
WriteAgentForgeToken and WriteAgentMatrixToken write agent state files
under /var/lib/hyperhive/agents/<name>/state/. The hive-priv service
has ProtectSystem=strict which makes all of /var read-only by default,
but /var/lib/hyperhive was missing from ReadWritePaths, causing EROFS
on every token write.

Closes #1274.
2026-06-04 15:35:46 +02:00
damocles
c2d023ba86 fix(#1269): add requires+after hive-c0re.socket to service unit 2026-06-04 15:16:39 +02:00
atlas
a56badd004 fix: remove Synapse admin API path — use admin-room reset exclusively (tuwunel only) 2026-06-04 15:16:22 +02:00
atlas
97a78cc5d6 fix: address argus review on matrix admin-room reset — ascii lowercase, tighter markers, unit tests 2026-06-04 15:16:22 +02:00
atlas
b2913bc656 fix: admin-room fallback for matrix password reset when Synapse API absent
tuwunel 1.6.x does not implement the Synapse admin REST API.
reset_user_password() now falls back to the Matrix admin room
(#admins:<server>) when PUT /_synapse/admin/v2/users returns 404:

1. Discover admin room ID via #admins:<server> alias
2. Get current messages end-token (pagination anchor)
3. Send 'reset-password @<localpart>:<server>' as @hive admin user
4. Poll for bot response up to 5 x 1s; extract password from message
5. Persist the new password and return it

The function signature changes from Result<()> to Result<String> so the
caller can use the effective password (which may be server-generated on
the admin-room path) for subsequent login calls.

Closes #1267.
2026-06-04 15:16:22 +02:00
atlas
7e229889a6 fix: address argus review on ensure_claude_dir — narrow to EPERM, fix doc comment 2026-06-04 15:03:12 +02:00
atlas
e553e40577 fix: soft-fail chmod in ensure_claude_dir when dir is agent-owned
hive-agent-user-migrate chowns the bind-mounted claude dir to the agent
user on every container boot. After that, hive-core (a different user)
cannot chmod it — set_permissions fails with EPERM, which was propagated
as an error and caused the rebuild to fail entirely.

Fix: make the chmod best-effort. Newly created dirs (owned by hive-core)
get the 0755 mode set immediately; after the agent-migration chown the
mode is preserved so claude_has_session works correctly. Subsequent calls
that hit the EPERM path just log at DEBUG and continue.
2026-06-04 14:48:08 +02:00
damocles
911dc5227b fix(#1263): repeat security headers in locations with their own add_header 2026-06-04 14:35:18 +02:00
damocles
681a86bce9 fix: address argus review on #1260 — drop stale HIVE_ROLE and mcp::Flavor refs from CLAUDE.md 2026-06-04 14:31:44 +02:00
damocles
41eb3f806c refactor: remove hyperhive.role option — there is only one role: agent 2026-06-04 14:31:44 +02:00