Commit graph

2,053 commits

Author SHA1 Message Date
atlas
2b4e928afe feat(#1867): make the internal forge mandatory (remove forge.enable)
The internal forge is the canonical store for the meta flake, every
agent's config repo, and the internal/* repos, so it can no longer be
optional. Remove the services.hyperhive.forge.enable toggle:

- hive-forge.nix: drop the `enable` option; the forge config now
  deploys gated on `services.hyperhive.enable` (it ships with hyperhive).
- hive-c0re.nix: HIVE_FORGE_URL env unconditional; forge-public-URL gate
  drops the enable check (keeps behindGateway).
- hive-gateway.nix: local /etc/hosts forge entry keyed on behindGateway.
- hive-ci.nix: drop the now-moot `forge.ci.enable requires forge.enable`
  assertion (forge is always present); reword the option doc.
- nix/docs/default.nix: drop the `forge.enable = mkForce false` stub
  (option gone); the options-doc eval stays light via hyperhive.enable.
- hive-c0re forge.rs / hivectl.rs: reword 'forge.enable = true' error
  text to 'wait for hive-c0re to start the container' (the runtime
  token-absent path is unchanged — it's a bootstrap-timing check, not
  the opt-out).
- docs/approvals.md, docs/ci.md: drop stale forge.enable references.

Migration: configs that set `services.hyperhive.forge.enable = false`
must drop the line — the forge is now mandatory.

Prereq/companion to #1838 (PR-based config flow, which assumes the forge
is always present).
2026-06-22 19:26:34 +02:00
iris
c091d8a4b5 docs(web-ui): align dashboard endpoint list with the /api FE routes
The 'Dashboard endpoints' list was half-migrated: newer routes were
documented under /api/* but the older ones (approve, deny, the
rebuild/kill/restart/start/destroy lifecycle group, purge-tombstone,
answer-question, cancel-question, request-spawn, update-all, op-send)
were still listed bare. The dashboard FE calls all of them under /api/
since the route migration, so the doc was stale. Prefix the bare entries
(plus the two inline references) to match what the FE actually requests.
Documentation-only; no behaviour change.
2026-06-22 19:09:53 +02:00
atlas
dc3d6bc753 fix(#1897): treat empty HYPERHIVE_HIVE_DOMAIN as unset in HiveDomain
Per argus review: std::env::var(..).ok() yields Some("") for an empty
env value, so require_hive_domain would 'succeed' with an empty domain
and emit invalid nix (swarm.peers."" = …). Filter empty so it resolves
to None → the CLI's clear 'domain unset' error fires instead.
2026-06-22 19:07:56 +02:00
atlas
8ea19b3b12 feat(#1897): always auto-fill the hive domain — drop the --domain override
Per operator review (#1899): the hive's own domain should never be
typed by hand. Remove the --domain flag from peer-config and wg init
entirely; both now resolve it from the daemon (HostRequest::HiveDomain).
peer-config errors with a clear hint if the daemon can't answer; wg init
prints the peer block best-effort (skips it, still enables the mesh, if
unresolved). Regenerated docs/tools/hivectl-cli.md.
2026-06-22 19:07:56 +02:00
atlas
72d9422a7a feat(#1897): auto-fill the hive domain in peer-config (drop required --domain)
Per operator review: hivectl shouldn't make the operator retype this
hive's own domain. Add a HostRequest::HiveDomain admin-socket query
(c0re answers from HYPERHIVE_HIVE_DOMAIN, already in its service env) and
a domain field on HostResponse.

hivectl peer-config now resolves the domain as: --domain override (kept
for offline/scripted use), else the daemon query; errors with a clear
hint when neither resolves. wg init resolves it best-effort too, so it
prints the hand-over peer-config block without --domain (skipped, not
failed, when the daemon is unreachable).

Regenerated docs/tools/hivectl-cli.md.
2026-06-22 19:07:56 +02:00
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