Commit graph

1,918 commits

Author SHA1 Message Date
damocles
17e6afef8d ci: re-trigger (prior run hit a runner timeout, not a code error) 2026-06-17 13:49:45 +02:00
damocles
f723806eae list: reject --page 0 and --limit 0 (1-based; clap range parser, per review) 2026-06-17 13:49:45 +02:00
damocles
2005b205e9 list: paging via --page instead of --all (token-bounded per call, per review) 2026-06-17 13:49:45 +02:00
damocles
0aef643797 hive-forge: add --all to list for full-pagination repo-wide sweeps 2026-06-17 13:49:45 +02:00
damocles
21e5233c1f ci-log: graceful message when a run's logs are gc'd (404/500) instead of raw http error 2026-06-17 10:05:21 +02:00
damocles
e8d39ecb8b hive-sh4re: make harness_dir the single resolver; hive-ag3nt delegates (#1450) 2026-06-16 12:36:48 +02:00
damocles
a69844d463 hive-forge: fix ci-log clippy cast_possible_truncation (use try_from) 2026-06-16 11:51:01 +02:00
damocles
2223af1b21 hive-forge: add ci-log verb for actions job step logs (closes #1674) 2026-06-16 11:34:31 +02:00
damocles
1d259e84f2 dashboard: tighten is_plain_ident to match hive-priv validate_name_chars (argus review) 2026-06-16 11:18:18 +02:00
damocles
9c480daf0a dashboard: POST /matrix-account-login + account-aware hive-priv matrix-token write (BE-2) 2026-06-16 11:18:18 +02:00
damocles
8799a9aa11 hive-matrix-mcp: document redaction power-level + concurrency semantics 2026-06-16 11:06:39 +02:00
damocles
55ba30aa18 hive-matrix-mcp: cover send_redact reason=None in round-trip test (argus note) 2026-06-16 11:03:53 +02:00
damocles
1c0f7a72f7 hive-matrix-mcp: add send_redact tool to delete matrix events 2026-06-16 10:58:34 +02:00
damocles
79af902d47 dashboard: drop tracker tag from matrix_accounts doc comment (lint) 2026-06-16 10:29:17 +02:00
damocles
1ebc384e66 dashboard: GET /api/matrix-accounts — list an agent's provisioned matrix accounts (#1698) 2026-06-16 10:29:17 +02:00
iris
06d57a61f8 dashboard: fix matrix-accounts GET path to /api/matrix-accounts
BE-1 (the list endpoint) lives at GET /api/matrix-accounts (reads go under
/api/, matching /api/state and /api/operator-inbox); the page was calling
the un-prefixed /matrix-accounts. Correct the fetch + the contract comment +
the docs reference. POST /matrix-account-login (mutation, root path like
/approve) is unchanged.
2026-06-16 10:22:53 +02:00
iris
8bb3bd82a1 dashboard: drop matrix-accounts experimental notice; relabel list as provisioned
Per-account token-failure isolation is now live on the matrix daemon, so a
bad credential entered via this form can no longer take down the agent's
whole matrix session — remove the experimental notice (+ its now-unused CSS).

Also relabel the account list 'configured accounts' -> 'provisioned accounts'
to match the v1 read path (lists accounts with a stored token; a
config-declared-but-unprovisioned account appears once provisioned through
the form). Docs + module header updated to match.
2026-06-16 10:22:53 +02:00
iris
9293fe3ac9 dashboard: matrix-accounts page — provision/login per-agent external matrix accounts
New standalone H0M3 page (/matrix-accounts.html) and tile. An agent
picker drives a list of that agent's configured matrix accounts (name,
homeserver, token-stored status) and a provision form that logs in by
password or stores an existing token.

Frontend half of the per-agent external matrix-account provisioning
work. Built against the v1 backend contract:
  GET  /matrix-accounts?agent=<name>
       -> { accounts: [ { name, homeserver, token_present } ] }
  POST /matrix-account-login  (x-www-form-urlencoded, operator-auth)
       fields: agent, account, homeserver, mode=password|token,
               user_id?, password?, token?
       -> 2xx { ok, user_id } | 4xx { error }

The token is never echoed back; secret inputs are cleared on submit.
Token-status dot reflects token-stored, not live session (a true
up/down indicator needs the daemon account registry, a follow-up). The
form carries an experimental notice pending per-account failure
isolation on the matrix daemon.

Blocked from merge on the backend endpoints and the daemon
failure-isolation fix; opening for review + to pin the UI/backend wire
contract.
2026-06-16 10:22:53 +02:00
iris
c187366961 dashboard: dedent call.js moved blocks to module top level
Cosmetic follow-up to the Y3R C4LL extraction (argus nit on #1705): the
approvals + questions sections kept their original 2-space IIFE indentation
from tabs.js, which read as if they were inside a block and was inconsistent
with the unindented operator-inbox section + the other domain modules.
Uniform dedent — no logic change, build unaffected.
2026-06-16 10:22:53 +02:00
iris
a0fc7e1a9f dashboard: extract questions domain into call.js — Y3R C4LL split complete
Final domain: move the operator question queue (questionRowCache, the
sync/apply/filter/fingerprint/buildQuestionLi/renderQuestions fns, formatTtl,
and the .q-ttl countdown ticker) from tabs.js into call.js. tabs.js imports
syncQuestionsFromSnapshot, applyQuestionAdded, applyQuestionResolved,
renderQuestions, and the activeQuestionCount getter; refreshTabCounts now
sums activeApprovalCount()+activeQuestionCount()+operatorInboxCount().

The .status-age (SW4RM) and .approval-ts/.reminder-due/.sched-due
(approvals+schedules) tickers stay in tabs.js — only the questions-specific
.q-ttl ticker moved. questionsState stays in state.js (shared with the SW4RM
badges). Behaviour-preserving; no visual change.

Net: tabs.js 2323 to 1603 lines; the Y3R C4LL pane now lives in call.js,
alongside permissions.js / schedules.js as a per-domain module.
2026-06-16 10:22:53 +02:00
iris
d0ec0d2896 dashboard: extract operator-inbox + approvals into call.js (#1451)
Begin splitting the tabs.js monolith: lift the Y3R C4LL domain into a new
call.js module (mirrors permissions.js / schedules.js).

- questionsState + QUESTION_HISTORY_LIMIT move to state.js: they're read by
  both the SW4RM container rows (per-agent question-count badges) and the
  Y3R C4LL questions domain, so they're cross-domain shared state.
- operator-inbox domain (state + refresh/render/mark/append) → call.js.
- approvals domain (state + sync/apply/render/diff-panel/history) → call.js.
- call.js exposes count getters (activeApprovalCount, operatorInboxCount);
  the entry's refreshTabCounts pulls them. Live-mutation paths call an
  injected onCountsChanged callback (registered via initCall at boot)
  instead of reaching into the coordinator (avoids a circular import).
- renderPeerHives, physically interleaved in the moved range but part of
  the SW4RM/peers domain, stays in tabs.js.

Behaviour-preserving; no visual change. Questions domain follows next.

(Recreated after a harness-restart git-object corruption; identical content.)
2026-06-16 10:22:53 +02:00
atlas
e6f9679ec9 hive-forge: add issues/issue-list discovery aliases for the list verb
The verb that lists issues/PRs with filters is `list`, but `issues` and
`issue-list` are the names people reach for first — and clap's
"did you mean" tip points at `issue` / `issue-create`, never at `list`.
Two agents independently failed to find it that way.

Add `issues` and `issue-list` as visible clap aliases so the verb is
discoverable under the names people actually type (and they show in
`--help`). No behaviour change; `list` stays canonical.
2026-06-16 00:18:32 +02:00
damocles
e5cd4e58f1 matrix multi-account: isolate per-account stale-token failure so a bad secondary token doesn't kill the daemon 2026-06-15 23:13:45 +02:00
atlas
c27f915e47 fix(meta): forward HIVE_FORGE_URL etc. into globalEnvironment
The forwarded host env vars (HIVE_FORGE_URL, HIVE_FORGE_PUBLIC_URL,
HYPERHIVE_HIVE_DOMAIN, ...) were emitted only into the harness
service's own unit environment. Under network isolation that left the
bash-task runner, the matrix daemon, tea-login and interactive shells
without HIVE_FORGE_URL, so `hive-forge` and friends fell back to the
loopback default (localhost:3000) — unreachable from a private netns,
where the in-cluster gateway URL (forge.<domain>) is the only path.

Emit the forwarded set into systemd.globalEnvironment too, which every
unit + shell in the container inherits, so all in-container surfaces
see the same forge/matrix endpoints. The existing harness-service
forward is kept (harmless redundancy). Shared netns is unaffected (the
localhost default still works there).

Adds a regression test asserting the forwarded vars land inside the
globalEnvironment block, and the generated flake parses.
2026-06-15 22:04:43 +02:00
damocles
f7199f183c drop issue number from source comment (tracker-tag lint) 2026-06-15 21:55:55 +02:00
damocles
354b270d2a matrixAccounts url: single-source-of-truth default binding (addresses review) 2026-06-15 21:48:51 +02:00
damocles
d15ecff6d2 isolation: forward HIVE_MATRIX_URL so matrix daemon reaches the gateway 2026-06-15 21:38:34 +02:00
lexis
314fb8298f docs: add agent.nix hyperhive.matrixAccounts multi-account matrix configuration (follow-up to #1692) 2026-06-15 21:28:36 +02:00
damocles
8ba0abcf27 matrixAccounts: assert no extra named 'main' (reserved for hive account) 2026-06-15 21:24:16 +02:00
damocles
36fb041c55 matrixAccounts: hive 'main' is implicit primary, option declares extras only 2026-06-15 21:24:16 +02:00
damocles
63db108516 rework matrixAccounts to attrset keyed by name + matrixPrimaryAccount 2026-06-15 21:24:16 +02:00
damocles
dcc059b4b8 feat: hyperhive.matrixAccounts nix option (matrix multi-account layer 2) 2026-06-15 21:24:16 +02:00
atlas
26d81cd36e fix(harness): stop resolvconf/dhcpcd clobbering the bridge DNS resolver
Under network isolation, agents lost DNS resolution of the hive's own
zones (forge.<domain>, matrix.<domain>): the hyperhive-isolated-dns
oneshot points resolv.conf at the bridge resolver, but resolvconf
regenerated it from host-tracking right after (dhcpcd re-triggering that
when the veth comes up), reverting to the non-authoritative host
resolver. Agents then couldn't reach the in-hive forge or matrix through
the gateway, which broke an isolate-by-default rollout.

Take resolvconf and dhcpcd out of the resolv.conf loop: disable
resolvconf and tell dhcpcd not to touch resolv.conf (without disabling
dhcpcd itself, so the veth still gets its address). Then the last writer
owns it -- the nixos-container host-copy in shared netns, or the oneshot
in isolated mode. Mirrors the approach the matrix container already
takes. Eval-checked; flake check passes.
2026-06-15 20:45:20 +02:00
damocles
3b26f58a00 docs: note ping arm in dispatch_op is unreachable 2026-06-15 20:28:31 +02:00
damocles
8e79eb4f26 feat: multi-account matrix daemon (account-routed mcp surface) 2026-06-15 20:23:42 +02:00
damocles
73c53c7d4a feat(#1685): surface attachments in read_room + add download_file 2026-06-15 18:17:35 +02:00
atlas
6490fc422e feat(hive-forge): add artifact-get verb to download CI run artifacts
Forgejo 15 has no REST endpoint to download an Actions artifact — the
only path is the web UI download route, which is keyed by the run's
global id rather than the per-repo run number shown in run-page URLs.
The REST artifacts list route keys off the run number instead, so the
two can't be chained directly.

artifact-get takes the run number (what pr-status surfaces as a CI
context target_url), translates it to the global run id via the REST
runs list by matching each run's html_url tail, then GETs the web
download route with the agent's forge token. Saves the artifact zip to
a path (default /tmp/forge-artifact-<name>.zip) or streams to stdout
with -o -.

The artifact name is percent-encoded into the path. The encoder that
list already used for query-string filters is promoted to a shared
verbs::pct_encode helper so both call sites stay in sync.

Lets an agent pull a CI-built artifact (e.g. a paper PDF) into /shared
without host access.
2026-06-15 17:59:38 +02:00
damocles
801b886a4c drop stale send_file_dm doc refs (per review) 2026-06-15 17:59:02 +02:00
damocles
07941421c1 replace send_file_dm with open_dm resolver (per review) 2026-06-15 17:59:02 +02:00
damocles
19f1fa4f28 rustfmt send_file handlers 2026-06-15 17:59:02 +02:00
damocles
cbbd6f3d6e feat(#1681): add matrix send_file + send_file_dm tools 2026-06-15 17:59:02 +02:00
damocles
1b0f035a05 matrix: add #[must_use] on room_label per review 2026-06-15 15:50:53 +02:00
damocles
1b1c6e03b9 refactor(matrix): extract room_label helper, dedup 4 call sites 2026-06-15 15:50:53 +02:00
atlas
9902d9d9a5 fix(ci): resolve the forge domain to loopback in the hive-ci runner
Forgejo's Actions artifact API hands the runner an upload URL built from
forgejo's ROOT_URL — the public forge domain (forge.<domain>), not the
runner's 127.0.0.1:<httpPort> registration URL. The hive-ci container shares
host netns but has no resolver entry for the hive's own domains (the
gateway's localHostsEntry only touches the host), so actions/upload-artifact
fails with 'getaddrinfo ENOTFOUND forge.<domain>' while normal CI (checkout
via the localhost registration URL) is unaffected. Add a networking.extraHosts
entry mapping the forge domain to loopback so the upload reaches the local
forge via the gateway.
2026-06-15 13:32:01 +02:00
atlas
f633abbdc4 refactor(hive-forge): share latest-per-reviewer logic between pr-status and pr-merge
pr-status and pr-merge both computed 'latest non-comment review verdict per
reviewer' independently (identical oldest-first, COMMENT/PENDING-skipping,
supersede-by-later loop). Extract it to a shared verbs::latest_reviews helper
so the verdict semantics live in one place and can't drift between the
health view and the pre-merge changes-requested gate. Pure dedup, no
behaviour change.
2026-06-15 11:44:19 +02:00
damocles
f6e1651dff feat(#1671): reject matrix send when room has unread messages 2026-06-15 11:38:25 +02:00
damocles
d89666baa9 docs(ci): document jobTimeout, fix forge.ci option path 2026-06-15 11:37:50 +02:00
atlas
bfeb23ce06 fix(forge): pre-create Actions artifact storage dir so uploads persist
Forgejo's local Actions-artifact storage defaults to
/var/lib/forgejo/data/actions_artifacts but Forgejo does not pre-create it.
The artifact endpoint ingests the chunked upload, then the merge-chunks step
fails with 'lstat .../actions_artifacts/tmpNNN: no such file or directory',
so every upload-artifact step dies after a successful build. Pre-create the
dir (forgejo-owned) via tmpfiles. actions.ENABLED already registers the
endpoints; this gives them somewhere to write.
2026-06-15 11:37:33 +02:00
atlas
9d8367bb24 feat(hive-forge): add pr-merge verb (#1670)
Adds `hive-forge pr-merge <n> [--method merge|rebase] [--keep-branch]
[--force]` wrapping POST /repos/{owner}/{repo}/pulls/{n}/merge, so agents on
the peer-review-and-merge workflow have a CLI path instead of the raw API.

- Methods: merge (default) | rebase. Squash is intentionally not offered.
- Deletes the head branch after merge unless --keep-branch.
- Safe by default: refuses unless the PR is mergeable, CI is not red, and no
  review's current verdict requests changes (latest-per-reviewer wins, so a
  later approval clears an earlier request-changes). --force overrides and
  also sets Forgejo's force_merge.
- New client helper post_no_content for the 200-empty-body merge response.
2026-06-15 11:36:17 +02:00
iris
06c5d68071 refactor(dashboard): move SYST3M section CSS into shared system-sections.css
The C0R3 page (/core.html) pulled the four SYST3M panel styles (rebuild
queue, meta inputs, container load) by @importing the whole ~39kb
dashboard.css, dragging the entire dashboard stylesheet into the C0R3
CSS bundle.

Extract those section-internal rules into a new system-sections.css and
@import it from both core.css and dashboard.css. core.html now loads only
the small shared file (its CSS bundle drops from ~40kb to 5.3kb) while
the dashboard keeps the rules — a couple are still referenced from
dashboard JS (the schedules view uses .rqe-source*). The container-load
table's .hive-stats-table already lives in common.css, linked directly
by core.html, so it is unaffected.

Behaviour-preserving: identical rules, same custom properties; the moved
selectors are uniquely named to these panels and not redefined
elsewhere, so the cascade is unchanged. Verified the built bundles:
core.css carries the section rules and no dashboard chrome; dashboard.css
still carries them via the import.
2026-06-15 09:54:55 +02:00