Commit graph

2,048 commits

Author SHA1 Message Date
atlas
8464cb95cb feat(#1897): hivectl peer-config verb to generate a federation peer block
Add `hivectl peer-config --domain <this-hive-domain>`: prints the
`services.hyperhive.swarm.peers."<domain>"` nix block a peer operator
pastes to federate with this hive. Emits `caCert = ./<hive>-ca.pem` plus
a `cp /var/lib/hive-tls/ca.pem ./<hive>-ca.pem` line when this hive
serves a self-signed CA (the cert file exists); omits caCert for
ACME/public-CA hives (trusted by the default bundle). Includes the
WireGuard public key when the mesh key exists, and the
wireguardAddress/Endpoint passed via flags.

`wg init` gains an optional --domain; when set it calls peer-config at
the end, so a fresh mesh setup prints the hand-over block in one command.

Pure output — reads local state (TLS CA cert presence, wg key), never
mutates. Regenerated docs/tools/hivectl-cli.md.

Closes #1897.
2026-06-22 19:07:56 +02:00
atlas
156ca70b1e feat(#1888): read-before-comment guard via forge notification read-state
Rebuild on forge's own notification read-state instead of the local
seen-cursor mirror (operator-nacked: mirrors state forge owns + drifts
on restart).

- comment refuses to post when forge has an unread notification for the
  thread (someone commented since you last read it); --force overrides.
  Degrades open if the notification check itself fails.
- comments / view mark the thread's notification read (the "I've seen
  it" signal), clearing the guard for a subsequent reply.
- new crate::notify (no local file): unread_thread_id pages repo-scoped
  unread notifications (newest-first, capped) + subject-url number match;
  mark_thread_read via the new client.patch_no_content.

Pairs with the forge_notify harness change (#1895, merged) that leaves
delivered notifications unread until the agent actually reads. Covers
pr/issue comment too (they delegate to comment::run).
2026-06-22 18:54:40 +02:00
atlas
edad6f863c feat(#1886): trust a peer hive's root CA hive-wide for self-signed federation
Add swarm.peers.<domain>.caCert (path to a peer hive's root CA PEM),
trusted everywhere the hive's own internal CA is — so a self-signed
peer hive can federate (matrix) and any in-hive consumer validates its
certs.

Mechanism (reuses the existing hive-CA embedding): the meta-flake
renderer embeds a LIST of CA files next to each agent's flake —
hive-ca.pem (the hive's own self-signed CA, when active) plus each peer
caCert as peer-ca-<N>.pem — and emits them all in
security.pki.certificateFiles, so every agent trusts them at build
time. The matrix container trusts the same peer CAs for federation TLS.
Nothing is installed in the host trust store; the certs live in the nix
store (no mutable host file).

- meta.rs: embedded_ca_files() = hive CA + peer CAs (from new
  HIVE_PEER_CA_PATHS env); ca_embed_state() tracks the list (content +
  add/remove); sync_agents materialises + stages the list; render emits
  the multi-entry certificateFiles. Tests cover hive-only / hive+peers
  / peers-only / none.
- hive-c0re.nix: HIVE_PEER_CA_PATHS service env (colon-joined caCerts);
  caCert / certFingerprint option docs updated to the hive-wide scope.
- hive-matrix.nix + docs/swarm.md: scope + comment updates.

certFingerprint stays the c0re-only leaf-pin path.
2026-06-22 16:54:44 +02:00
damocles
eb09ec4e28 forge_notify: record dedupe cursor inside the ok arm for clarity 2026-06-22 16:45:24 +02:00
damocles
f5ac6d79e3 forge_notify: leave delivered threads unread, dedupe wakes in-memory 2026-06-22 16:23:04 +02:00
iris
4342a50895 docs(web-ui): refresh matrix-accounts page for heartbeat + age-dimming + problem+json
The M4TR1X ACC0UNTS section described the pre-heartbeat state: a
snapshot 'written at startup / rewritten each daemon (re)start' with an
'ambiguous' as_of, and the live-status dot as a 'dashboard-side
follow-up'. All three shipped since:

- the daemon now force-rewrites the snapshot every ~30s (heartbeat), so
  as_of advances while alive and a stalled value is an honest dead-daemon
  signal — documented, with the full dot state table (green / dim-green
  'no heartbeat' age case / amber container-down + offline / grey);
- the login endpoint moved to /api/matrix-account-login and its failure
  body is RFC 9457 application/problem+json (message in detail), not the
  old 4xx { error } — corrected, with the 400/500 status scheme.
2026-06-22 16:07:54 +02:00
damocles
65ad994c85 dashboard: return problem details directly from client-error handlers 2026-06-22 16:04:22 +02:00
damocles
2b0c51badf inline problem_details builder at call sites; drop the one-caller wrapper 2026-06-22 16:04:22 +02:00
damocles
cdf1bfe7db use the problem_details crate for problem+json responses 2026-06-22 16:04:22 +02:00
damocles
5dc1b3933a return accurate http status codes for dashboard client errors 2026-06-22 16:04:22 +02:00
atlas
dc4c5460d5 feat(#1877): restructure hive-forge into pr/issue sub-verbs
Group issue/PR operations under `pr` and `issue` parent commands
(`hive-forge pr close 42`, `issue create …`, `pr status --pr 42`) per the
operator decision — kind-namespaced verbs replace the flat surface.

- new `verbs::pr_cmd` / `verbs::issue_cmd` parent commands wrap the existing
  per-verb modules (reuse their Args + run fns) under `#[command(subcommand)]`.
- kind-validation (the win over the old generic verbs): the generics that work
  on both (view/comment/comments/close/labels/assign/timeline) call
  `assert_kind` first, so `pr close <issue>` / `issue close <pr>` are rejected
  with a 'use the other command' message. PR-only / issue-only verbs are
  kind-correct by construction. `number` exposed `pub(crate)` on the shared
  verbs so the wrappers can probe it.
- every flat kind verb (`close`, `pr-create`, `pr-status`, `issue-edit`, …)
  kept as a `#[command(hide = true)]` back-compat alias — still parses, dropped
  from --help; removed in a later sweep once usage migrates. (`pr`/`issue`
  bare-show become `pr show` / `issue show` — the names are now parents.)
- docs/tools/forge.md documents the new surface + the deprecated aliases.

cargo build/clippy/fmt clean, 54 tests pass; --help surface + alias parsing
smoke-tested.
2026-06-22 16:01:13 +02:00
iris
fe974c580d dashboard(matrix-accounts): dim live dot when daemon heartbeat stalls
The daemon now force-rewrites its matrix-accounts.json snapshot every
~30s, so as_of_unix advances while the daemon is alive and a stalled
value is an honest 'stopped publishing' signal rather than just an old
snapshot. Use it: when an account's snapshot still says live but as_of
hasn't advanced in more than ~90s (3 missed heartbeats) and the
container is NOT explicitly down, dim + desaturate the green dot and
label it 'online · no heartbeat', with a tooltip explaining the daemon
is likely dead or wedged. The container cross-ref still takes
precedence — a stopped container stays the solid amber 'stale'. Keeps
the existing 3 states intact; adds a distinct degraded-green sub-state.
2026-06-22 14:15:19 +02:00
atlas
fd2eac99ce refactor(#1865): drop stray issue-ref from start_manager doc (tracker-tag lint) 2026-06-22 13:58:52 +02:00
atlas
53b4e752ef refactor(#1865): replace the privileged flag with topology + capability gating
Per operator direction (no privileged mode; everything is perms /
capabilities), remove the socket-derived `privileged: bool` from the
unified dispatch and gate every verb on the caller's identity instead:

- serve/dispatch/dispatch_shared/dispatch_orchestration + all lifecycle
  handlers drop the `privileged` param.
- lifecycle (start/kill/restart/update/init_config/apply_commit) + get_logs
  gate on `topology::is_descendant_of` (a parent owns its whole subtree; the
  root covers every agent as a consequence, no positional privilege). The
  restart infra-branch stays InfraAdmin-gated (orthogonal).
- agent-state queries (loose-ends / reminder count + rollup): own subtree is
  free, other agents + the hive-wide `"*"` sweep require QueryAgentState.
  require_new_child + resolve_agent_state_target widened direct-child -> subtree.
- hive-wide orchestration verbs gate on the grantable tool-group via
  tool_groups::groups_for: schedules -> `scheduling`, meta-inputs +
  cancel-approval -> `approvals`. update_meta_inputs now attributes the
  approval to the caller, not a hardcoded MANAGER_AGENT.
- #1834 cancel-guard unwind: handle_cancel_loose_end drops `privileged`
  (agent path is never privileged); question/reminder cancels are
  ownership-only, approval cancel checks the `approvals` tool-group.

The manager socket stays as pure transport (serves agent=ruth, no authority
of its own); collapsing it into ruth's per-agent socket is the #1825
follow-up. No is_root here — root-identity primitives are #1825's.
2026-06-22 13:58:52 +02:00
atlas
674505fbe7 refactor(#1865): drop stray issue-ref from doc comment (tracker-tag lint) 2026-06-22 13:58:52 +02:00
atlas
f5f06a5f14 refactor(#1865): consolidate agent + manager socket servers into one
The per-agent and manager sockets ran two parallel dispatchers with
duplicated lifecycle handlers (agent-side topology-gated, manager-side
ungated) plus a manager-only handler set. Collapse to one parameterized
server in socket_server.rs:

- one serve() + dispatch(req, agent, privileged, coord); start() binds
  the per-agent sockets (privileged=false), start_manager() binds the
  manager socket (privileged=true).
- each lifecycle/config handler (start/restart/kill/update/init_config/
  apply_commit) merges its dual: the topology guard (require_child /
  require_new_child) runs only on the !privileged path; init_config
  records the requester as parent only when !privileged. restart keeps
  the orthogonal, capability-gated + audited infra-container branch.
- the agent-state queries (loose-ends / reminder count + rollup) branch
  on privileged: privileged keeps any-target + the "*" hive-wide sweep
  (query_agent_state-gated), non-privileged keeps the topology/cap gate.
- the privileged-only verbs (schedules / meta-inputs / get_logs) plus
  the submit/schedule/watchdog helpers move into socket_server; they are
  reached via dispatch_privileged_only(), which rejects the whole group
  on a non-privileged socket.
- delete manager_server.rs; repoint refs; merge the test modules.

No behavior change: the topology guard still applies on every
non-privileged lifecycle call, the privileged socket still acts on any
agent, and privileged-only verbs are still rejected on agent sockets.
2026-06-22 13:58:52 +02:00
atlas
a053d33184 refactor(#1865): rename agent_server module to socket_server
Pure rename ahead of the agent+manager server consolidation: the
per-agent socket dispatcher already hosts the shared dispatch and all
lifecycle handlers, and will absorb the manager-only handlers next, so
`agent_server` becomes a misnomer. No logic change — git mv plus a
mechanical `agent_server` -> `socket_server` rename across refs.
2026-06-22 13:58:52 +02:00
damocles
b11360503a emit dashboard errors as rfc 9457 problem+json 2026-06-22 13:50:27 +02:00
iris
0ec9e8b8a0 dashboard(matrix-accounts): shape-agnostic login error bodies
The matrix-account-login endpoint returns failures as a bare plain-text
body today (hive-c0re error_response). The RFC 9457 rework moves it to
application/problem+json. The submit handler previously called
resp.json() on the error path, which threw on the plain-text body and
collapsed every real failure to a generic 'login failed (HTTP 500)',
hiding the actual reason.

Read the error body shape-agnostically: parse JSON only on 2xx for the
success envelope; on failure read the body once as text and, if it
parses as JSON, surface problem+json 'detail' (then 'error'/'title'
fallbacks), else use the raw text. This handles both the current
plain-text and the future problem+json shapes with no BE/FE merge-order
coupling. Header contract doc updated to match.
2026-06-22 13:43:22 +02:00
damocles
55229fdf5b annotate mcp connect-refused as expected hive-c0re restart 2026-06-22 13:42:34 +02:00
iris
6a54866ec7 agent ui: drop dead .stats-nav + #toolbar css now superseded by page-header 2026-06-22 13:34:58 +02:00
iris
98d015edea agent ui: shared page-header nav on the stats + screen sub-pages
Per mara: the agent sub-pages should use the same back-link nav as the
dashboard's standalone pages, while the live terminal page stays as-is (no
tabs of each other, not an SPA).

Pull the shared `@hive/shared/chrome.css` into the agent bundle (via agent.css's
existing @import line; esbuild inlines it into the one dist/static/agent.css all
agent pages link), then:

- stats.html: replace the ASCII banner + bespoke `.stats-nav` with the shared
  `page-header` (← live back-link + dashboard link + title). Keeps the #back-link
  / #dashboard-link / #title ids stats.js drives, and the time-window picker +
  charts below are unchanged.
- screen.html: replace the bespoke `#toolbar` nav with the same `page-header`
  (← agent back-link + title), keeping the fit / match / debug controls + the
  status text in the bar.

The live terminal (index.html) is untouched. The now-unused `.banner` /
`.stats-nav` / `#toolbar` rules in agent.css are left for a follow-up prune to
keep this diff focused on the markup. Agent build clean.

Closes #1874.
2026-06-22 13:34:58 +02:00
damocles
d5202ebf60 heartbeat the matrix-accounts snapshot so as_of tracks daemon liveness 2026-06-22 13:30:07 +02:00
damocles
f38bc13293 matrix-accounts dashboard: live status + homeserver from daemon snapshot (be-4) 2026-06-22 13:02:43 +02:00
iris
06b2bd8b72 matrix-accounts: 3-state live status dot + user_id + snapshot freshness
Frontend for the BE-4 snapshot (#1702): GET /api/matrix-accounts now returns
per-account `live` + `user_id` and a top-level `as_of_unix`. Render a 3-state
dot instead of the v1 token-present-only one:

- green  (live + container running)  — online
- amber  (live + container DOWN)      — stale: the page already loads
  /api/state containers, so cross-reference running state; a container that's
  down means the daemon is down, so a "live" snapshot there is stale
- amber  (token_present + !live)      — provisioned but offline
- grey   (no token)                   — not provisioned

The daemon rewrites its snapshot only on (re)start, so `as_of_unix` is "live as
of", not a heartbeat — surfaced as a tooltip. We deliberately do NOT dim a
green purely on snapshot age (an old as_of is ambiguous: stable uptime vs dead
daemon); the container cross-ref catches the definitive down case, and true
daemon-up-but-client-dead detection is the daemon-heartbeat follow-up. user_id
is shown next to the account name. When `live` is absent (v1 backend not yet
deployed) the dot falls back to the token-present rendering, so this is safe to
ship independent of the backend deploy.

FE half of #1702 BE-4; pairs with the backend PR.
2026-06-22 12:53:52 +02:00
damocles
ad6b39b425 remove vestigial agent-ports.json tcp web-port map 2026-06-22 11:59:14 +02:00
atlas
53f49615fa refactor(#1834): derive cancel-loose-end privilege from the socket, not the MANAGER_AGENT name
The cancel-loose-end guards (cancel approval / question / reminder)
inferred manager-level privilege by string-matching the canceller
against the hardcoded `hive_sh4re::MANAGER_AGENT` ("ruth"). That laundered
privilege through a name: a request arrives on the privileged manager
socket, gets stamped with the bootstrap agent's name, and the guards
re-derive privilege from that name. Renaming or replacing the bootstrap
agent would then silently move privilege.

Privilege is a property of the SOCKET the request arrived on (the manager
socket is the trust boundary), so thread an explicit `privileged: bool`
through `dispatch_shared` → `handle_cancel_loose_end` → the three guards:

- `Broker::cancel_reminder_as` and `OperatorQuestions::cancel`: the
  `== MANAGER_AGENT` leg becomes `privileged` (owner/asker + operator name
  legs unchanged).
- `check_approval_canceller_is_manager(canceller)` →
  `check_can_cancel_approval(privileged)` (manager-socket-only); unit tests
  updated to assert on the flag.

The manager socket passes `privileged = true`; the agent socket passes
`false`. `MANAGER_AGENT` is still passed as the actor NAME for legitimate
attribution/routing (notifications, schedule ownership, bootstrap
destroy-protection) — those are not privilege checks and are left intact.
Scope is the privilege guards only.
2026-06-22 02:07:14 +02:00
iris
e797b75ca9 dashboard: stream the running rebuild's build log inline in the queue
The C0R3 rebuild queue only linked out to the logs page (logs →). Add an
inline live-log panel under the queue that streams the currently-running
rebuild's build output, so the operator watches progress without leaving the
page.

Extract the build-log SSE streaming logic (append stdout/stderr, sticky-bottom
scroll, stderr separator, reconnect-replay reset, done/error handling) into a
shared `openBuildLogStream(id, pre, {onDone, onError})` in common.js, and use it
from BOTH the L0GS page BUILD tab (logs.js, previously inline) and the new C0R3
panel (core.js) — one implementation, no duplication.

The panel is one persistent instance keyed to the running entry's build_log_id
(the queue runs one build at a time), in its own container (#rebuild-live-log)
outside rebuild-queue-section so the queue's per-row re-render — rows rebuild as
the build step advances — never tears down the open stream; it reconnects only
when the running build_log_id changes and won't reopen a stream that already
sent done. Collapsible, live/ok/fail badge, raw download. Hidden when nothing
is building; each row keeps its logs → link for full history.

Frontend-only — no backend change (endpoint + build_log_id already existed).
Closes #1860.
2026-06-22 01:55:38 +02:00
iris
93fa264bbf dashboard: stream the running rebuild's build log inline in the queue
The C0R3 rebuild queue only linked out to the logs page (logs →). Add an
inline live-log panel under the queue that streams the currently-running
rebuild's build output, so the operator watches progress without leaving the
page.

One panel keyed to the running entry's build_log_id (the queue runs one build
at a time), reusing the build-log SSE the logs page already uses
(GET /api/build-logs/id/{id}/stream; frames stdout_append/stderr_append/done).
It lives in its own container (#rebuild-live-log) outside rebuild-queue-section
so the queue's per-row re-render — rows rebuild as the build step advances —
never tears down the open stream; it reconnects only when the running
build_log_id changes and won't reopen a stream that already sent done. Sticky-
bottom scroll, collapsible, live/ok/fail badge, raw download. Hidden when
nothing is building; each row keeps its logs → link for full history.

Frontend-only — no backend change (endpoint + build_log_id already existed).
Closes #1860.
2026-06-22 01:48:32 +02:00
damocles
95c4854b4c hive-c0re: drop the bare dashboard routes, /api only (phase 3 of route consolidation) 2026-06-22 01:35:40 +02:00
atlas
4db8a8cd3d feat(#1843): static-serve the dashboard via the gateway, hive-c0re API-only
nginx proxied `<hive>/` straight to hive-c0re:7000, and hive-c0re served the
dashboard dist itself via `tower_http::ServeDir` (from `HIVE_STATIC_DIR` baked
into its service env). So a frontend-only change rebuilt the hive-c0re unit and
restarted the core daemon — every operator session dropped its SSE stream for a
pure CSS/JS change.

The gateway nginx now static-serves the dashboard dist directly; hive-c0re's
dashboard router is API-only. The split uses the Accept-header SPA fallback (the
same `map $http_accept` pattern the matrix/agent vhosts already use), so no
backend prefix has to be enumerated: a browser navigation (Accept: text/html)
whose path is not an on-disk asset gets the SPA index.html; everything else
(every /api route, the bare action/mutation routes, the two SSE streams, the
knowledge webhook — all Accept != text/html) falls through `try_files` to the
`@c0re` named location and is reverse-proxied to hive-c0re. A new c0re route
needs no gateway change.

- hive-c0re.nix: expose the themed dist as a new internal read-only option
  `services.hyperhive.c0re.servedFrontend`; drop `HIVE_STATIC_DIR` from the
  service env (the router no longer serves files).
- hive-gateway.nix: read that option in host-module scope (dashboardDist),
  static-serve `dashboard/` with the Accept-header `try_files ... @c0re` split;
  `@c0re` carries `proxy_buffering off` + a 1d read timeout for the SSE streams
  and a duplicated auth_basic block (named locations do not inherit it). The
  dashboard map is unconditional; the matrix map stays gated on the matrix GUI.
- dashboard.rs: drop the ServeDir fallback + the HIVE_STATIC_DIR resolution; the
  router 404s unmatched paths (the gateway only proxies non-static requests).
- hive-c0re/Cargo.toml: drop the now-unused tower-http dependency.
- docs/gateway.md: document the dashboard static split + the `@c0re` fall-through.

The store path is reachable inside the gateway nspawn container (shared
/nix/store), mirroring how HIVE_AGENT_FRONTEND_DIR already exposes the per-agent
UIs. The gateway and c0re changes must land together (atomic cutover) or the
dashboard 404s — this needs a watched gateway + c0re rebuild.
2026-06-22 01:18:01 +02:00
damocles
1caf978004 hive-c0re: create the agent subvolume before seeding dirs in init_config 2026-06-22 01:10:58 +02:00
iris
4b318a809a dashboard: reset-timer checkbox on recurring fire-now
Manually firing a recurring schedule now offers a "reset timer" checkbox
(default on) in the confirm dialog: when checked, the fire-now POST sends
{reset_timer:true} and the backend re-arms next_fire_at to now + interval.
Unchecking keeps today's behaviour (extra out-of-band pulse, cadence
intact). One-shot schedules omit the checkbox — they're consumed regardless.
The result flash shows "— timer reset" when the backend reports timer_reset.

Pairs with the backend reset_timer/timer_reset work. Closes #1848.
2026-06-22 01:06:24 +02:00
iris
905b38b6c7 dashboard: point frontend at the /api/* backend routes
Phase 2 of the dashboard route consolidation. The backend now double-registers
every bare top-level route (approve/deny/kill/restart/start/rebuild/destroy/
update-all/answer-question/cancel-question/purge-tombstone/matrix-account-login/
cancel-reminder/retry-reminder/request-spawn/op-send/meta-update/dashboard-stream/
dashboard-history) at an additional /api/<same> path. Switch every dashboard-pkg
fetch / EventSource / form action to the /api/ form so "backend = /api/*" holds
on the frontend side too.

Bare paths still answer, so this is independently deployable; once it ships the
backend can drop the bare registrations (phase 3). webhook/knowledge stays a
distinct webhook prefix (forge-driven, no SPA caller). app.js's /rebuild,/start
are the agent harness UI (different server) and are untouched.

Part of #1846 (phase 2).
2026-06-22 00:45:26 +02:00
damocles
d3e3c4a41e docs: agent-hierarchy default-parent is root, not the manager 2026-06-22 00:41:42 +02:00
iris
1c47bd4333 agent-ui: real fixed-width icon column for terminal rows
Replaces the first-character-glyph + negative-text-indent trick (which let a
wide emoji or a leading disclosure caret knock the icon out of column) with a
genuine icon cell.

terminal.js: row() / details() / detailsDiff() take an optional `icon` that
goes in a fixed-width `.row-glyph` element (inline-block, 1.4em). Details
summaries wrap their text in a `.summary-text` span; the disclosure caret
moves to `.summary-text::before` so it leads the text, not the icon — keeping
the icon in the shared column. terminal.css carries the cell + caret rules.

app.js passes the per-tool emoji as `icon` for the flat tool-use row and every
expandable tool summary (Write/Edit/send/ask/answer/bash) plus the 💭 thinking
row, instead of string-prefixing it. A details `🖥️` now lines up under a flat
row's `🧠` regardless of emoji width. Doc: terminal-rendering.md layout
contract updated. Closes #1844.
2026-06-22 00:41:24 +02:00
damocles
0c48caff1e hive-c0re: add /api aliases for bare dashboard routes (phase 1 of route consolidation) 2026-06-22 00:36:26 +02:00
iris
954bf8fe38 docs(web-ui): deny dialog uses themedPrompt textarea, not window.prompt
The deny-reason dialog moved from window.prompt() to a themed resizable
textarea (Enter submits, Shift+Enter newline). Update the dashboard route
reference that still described the old single-line window.prompt() flow.

Closes #1849.
2026-06-22 00:33:59 +02:00
damocles
b6aeaf0b57 hive-c0re: optional reset-timer on manual fire-now for recurring schedules 2026-06-22 00:32:49 +02:00
damocles
513c828ab2 hive-c0re: default new agents to root instead of under the bootstrap container 2026-06-22 00:19:35 +02:00
iris
08afa49c96 dashboard: themedPrompt is a textarea — Enter submits, Shift+Enter newline
The approval deny-reason prompt (and every themedPrompt dialog) used a
single-line input. Make themedPrompt always a resizable <textarea> with
chat-box keys: Enter submits (clicks the confirm button), Shift+Enter inserts
a newline. Short answers stay one keystroke; multi-line reasons (e.g. a deny
note) are now possible. No single-line variant — themedPrompt is only used by
the data-async data-prompt path, so all those dialogs get the textarea. Closes #1840.
2026-06-21 23:55:15 +02:00
damocles
0df52806f9 hive-c0re: drop the dead manager exclusion from the agent web-port map 2026-06-21 23:24:09 +02:00
iris
fb58f0e5b9 agent-ui: per-tool emoji icons + thinking/turn-end glyphs in terminal
Following the 🧠 thinking-tokens indicator (operator liked it), give terminal
rows evocative glyphs:
- 💭 thinking prose (pairs with the 🧠 token counter)
- / turn-end ok/fail (was ✓/✗)
- per-tool icons via toolIcon(name): 📤 send · 📥 recv ·  ask · ✍️ answer
  ·  remind · 🏷️ set_status · 🪢 loose-ends · ↻/⏹️/▶️/🔄 lifecycle · 📦
  request_* · ⏱️ schedule · 🖥️ bash · 💬 matrix · 📖 Read · 💾 Write · ✏️
  Edit · 🔍 Glob/Grep · 🔧 default — on both flat rows and the rich
  send/ask/answer/Write/Edit summaries.

Dropped the original 🐝 task-row glyphs: claude's nested-agent Task tool is
intentionally omitted from agents' allow-list (mcp.rs), so those rows never
render for agents. Doc table updated.

Closes the emojify-terminal request.
2026-06-21 23:15:25 +02:00
iris
eb1d913b47 fix(dashboard): call.js question handlers referenced tabs.js-only renderContainersFromState
applyQuestionAdded / applyQuestionResolved (call.js) called
renderContainersFromState() to refresh the SW4RM rows' per-agent
question-count badges, but that's a tabs.js closure-local not in call.js's
scope — so the question_added / question_resolved SSE handlers threw
'ReferenceError: renderContainersFromState is not defined' and aborted.
Inject it as an onContainersDirty callback via initCall (same pattern as
onCountsChanged), wired by tabs.js to renderContainersFromState. Closes #1826.
2026-06-21 22:42:34 +02:00
atlas
27ac0153c4 forge authz: scrub tracker tags from comments
Replace the #-number tracker references in code comments with prose
(tracker-tag lint; hive convention is prose in source). No behaviour
change. Branch-protection / collaborator / team / repo API field names
were verified against the live Forgejo swagger.
2026-06-21 22:36:45 +02:00
atlas
f1d54ce12c agent surface: create_repo through hive-c0re (#1787)
Closes the #1787 loop — the sanctioned create path now that agents
can't create repos directly. Adds:

- wire: Request::CreateRepo{repo} + Response::RepoCreated{full_name,
  clone_url} (hive-sh4re).
- agent_server: dispatch_shared arm + handle_create_repo — validates the
  repo name, then forge::create_agent_repo (org-owned repo, agent=write
  collaborator, operator-team branch protection). Returns the full name
  + clone url so the agent can git clone immediately.
- MCP: create_repo tool + CreateRepoArgs in the harness.
- a new opt-in ToolGroup::Forge (=[create_repo]) so the operator
  controls which agents can spin up repos (least privilege).

Workspace clippy -D warnings, cargo test, nix fmt all green.
2026-06-21 22:36:45 +02:00
atlas
867be7bb98 hive-c0re: block direct agent repo creation, add c0re-mediated create (#1787)
Agents must no longer create repos with their own forge token (a
write-scoped token otherwise creates + owns repos and can self-merge,
bypassing operator-only-merge). Instead:

- Set max_repo_creation=0 on every agent forge user (marker-guarded
  PATCH in sync_agent; covers agents provisioned before this). Blocks
  new direct creation; push/PR/clone and existing repos are untouched.
- Provision a c0re-owned 'agents' org (the namespace agent repos land
  in) plus an empty 'operators' team inside it. The org owns the repos
  so perms stay c0re-managed; the operator joins the team out-of-band.
- create_agent_repo() is the sanctioned path: creates the repo in the
  agents org, adds the requesting agent as a *write* collaborator (not
  owner), and applies branch protection that whitelists merge + required
  approval to the operators team — so the author can't merge its own PR.
- is_hive_managed_namespace() guards 'internal'/'agent-configs'/'core'
  against a future create surface passing an explicit owner.

No existing repos are modified. The agent/hivectl surface that invokes
create_agent_repo is a follow-up commit.
2026-06-21 22:36:45 +02:00
damocles
b3d002e4a7 hive-c0re: let request_init_config spawn a brand-new sub-agent under its requester 2026-06-21 22:36:35 +02:00
iris
63a79d76ba agent-ui: collapse streamed thinking_tokens ticks into one live row
claude streams a running 'estimated_tokens' counter as system/thinking_tokens
events — many per turn (thousands in a long turn). renderStream rendered each
as a '⚙ thinking_tokens' note, flooding the terminal scrollback. Coalesce
consecutive ticks into a single '🧠 thinking … ~N tokens' note row that
updates in place; reuse the row only while it's still the last one rendered
(nextElementSibling == null) so any other event starts a fresh one. Closes
#1818.
2026-06-21 22:09:31 +02:00
iris
6bb976ec72 dashboard: replace agent-tab transient list with build-queue summary banner
The SW4RM tab rendered a per-transient spinner list above the container
list, duplicating the running-step badge already shown on each agent card.
Replace it with a single compact amber banner that appears when the rebuild
queue has active (queued/running) entries — 'build queue — N running / M
queued — view queue →', linking to the full queue on /core.html. Per-agent
detail stays on the cards; the top of the tab just gives the at-a-glance
summary + a jump to the queue. Closes #1817.
2026-06-21 22:07:14 +02:00