hive-ag3nt: identity module with hive-qualified label (#589 phase A, first PR)

First chunk of #589 v0 phase A: plumbing the hive-qualified
'name@hive' form through the per-agent surfaces that the harness
itself owns. Broker from/to + dashboard rendering + container_view
follow in subsequent PRs once damocles ships the HYPERHIVE_HIVE_DOMAIN
env var in harness-base.nix.

- new hive_ag3nt::identity module: label() / hive_domain() /
  qualified_label() / qualify(label). Reads HYPERHIVE_HIVE_DOMAIN
  (set by hive-c0re.nix module from hyperhive.domain) — when unset
  or empty, qualified_label degrades to just the short label so
  existing single-hive deployments are unchanged. Six unit tests
  cover the set / unset / empty / arbitrary-label paths.
- prompt::render gains {qualified_label} substitution alongside
  the existing {label}. system.md template uses both: the agent
  intro now reads 'You are hyperhive agent iris (qualified:
  iris@darkest.space) in a multi-agent system. ... When you're
  talking to or about a peer on a different hive, use the
  qualified form (name@hive) so the operator + the manager can
  disambiguate'. Manager flavor gets the same treatment.
- /api/state gains qualified_label: String. Always present, equals
  label when no domain is configured.
- frontend setHeader takes the qualified_label, drives the browser
  tab title (so two  tabs from different hives are
  distinguishable in the tab bar) while the glyphic #title stays
  short for the cinematic header.

Gated on env var presence — no behaviour change for single-hive
deployments. Pairs with damocles's upcoming harness-base.nix
HYPERHIVE_HIVE_DOMAIN ship; safe to land in either order.
This commit is contained in:
iris 2026-05-29 19:09:44 +02:00
commit f44bf19707
6 changed files with 178 additions and 5 deletions

View file

@ -139,13 +139,22 @@ window.marked = marked;
})();
// ─── state rendering ────────────────────────────────────────────────────
function setHeader(label, dashboardPort) {
function setHeader(label, qualifiedLabel, dashboardPort) {
const title = $('title');
// Title is now just the glowing identity glyph — DASHB04RD,
// R3BU1LD, NEW SESSION all live in the overflow `⋯` menu now
// (#394). Glow + uppercase styling from h2 / .agent-header-title-row.
// The glyphic title stays short (no @hive suffix) — the hive
// qualifier lives on the second row's `qualified` chip + the
// browser tab title so the cinematic header reads cleanly at a
// glance (#589).
title.textContent = `${label}`;
document.title = `${label} // hyperhive`;
// Document title carries the qualified name so the browser's tab
// bar disambiguates between same-named agents on different hives
// in a federated swarm. Falls back to `${label} // hyperhive` when
// the qualified form is just the short label (single-hive deploys).
const tab = qualifiedLabel && qualifiedLabel !== label ? qualifiedLabel : label;
document.title = `${tab} // hyperhive`;
const dashUrl = `${location.protocol}//${location.hostname}:${dashboardPort}/`;
dashboardBase = dashUrl;
populateOverflowMenu(label, dashUrl);
@ -996,7 +1005,7 @@ window.marked = marked;
const resp = await fetch('/api/state');
if (!resp.ok) throw new Error('http ' + resp.status);
const s = await resp.json();
if (!headerSet) { setHeader(s.label, s.dashboard_port); headerSet = true; }
if (!headerSet) { setHeader(s.label, s.qualified_label, s.dashboard_port); headerSet = true; }
currentLabel = s.label;
// Render server-supplied navigation links — stats, screen, the
// forge profile, the agent-configs mirror, plus any