Watch
0
0
Fork
You've already forked hyperhive
0

Compare commits

..
5 changed files with 52 additions and 3 deletions

View file

@ -82,7 +82,8 @@ agent is stale.
Things blocked on operator decision — the approval queue. Things blocked on operator decision — the approval queue.
**P3NDING APPR0VALS** — the queue (see "Approval card" below). A **P3NDING APPR0VALS** — the queue (see "Approval card" below).
The R3QU3ST SP4WN form lives at the top of this section. A
`pending · N` / `history · N` tab pair switches between the live `pending · N` / `history · N` tab pair switches between the live
queue and the last 30 resolved approvals (see "Approval card" for queue and the last 30 resolved approvals (see "Approval card" for
the history row shape). the history row shape).
@ -135,7 +136,8 @@ page — see below.)
**K3PT ST4T3** — two sub-sections on one pane: **K3PT ST4T3** — two sub-sections on one pane:
*Tombstones*: agents with kept state and **no container** (size + age + *Tombstones*: agents with kept state and **no container** (size + age +
claude-creds badge). One action: `PURG3` (wipes state + applied claude-creds badge). Two actions: `⊕ R3V1V3` (queues a Spawn
approval; existing state is reused), `PURG3` (wipes state + applied
dirs; `POST /api/purge-tombstone/{name}`). dirs; `POST /api/purge-tombstone/{name}`).
⚠️ **Not only *destroyed* agents.** Nothing records a destroy: every ⚠️ **Not only *destroyed* agents.** Nothing records a destroy: every

View file

@ -159,8 +159,33 @@ export function renderApprovals() {
// no-op elsewhere — `approval_added` / `approval_resolved` SSE // no-op elsewhere — `approval_added` / `approval_resolved` SSE
// events route through here on every page that loads the bundle. // events route through here on every page that loads the bundle.
if (!root) return; if (!root) return;
// Save spawn form input + focus state before the DOM wipe so a live
// approval_added/resolved event doesn't erase a partially-typed name
// or steal focus from the operator.
const savedSpawnName = root.querySelector('.spawnform input[name="name"]')?.value ?? '';
const spawnHadFocus = document.activeElement === root.querySelector('.spawnform input[name="name"]');
root.replaceChildren(); root.replaceChildren();
// Spawn request form: submitting it queues a Spawn approval that
// lands in this same list, so the form belongs here rather than on
// the containers list (the agent doesn't exist yet).
const spawnNameInput = el('input', {
name: 'name',
placeholder: 'new agent name (≤9 chars)',
maxlength: '9', required: '', autocomplete: 'off',
});
if (savedSpawnName) spawnNameInput.value = savedSpawnName;
if (spawnHadFocus) spawnNameInput.focus();
const spawn = el('form', {
method: 'POST', action: '/api/request-spawn',
class: 'spawnform', 'data-async': '', 'data-no-refresh': '',
});
spawn.append(
spawnNameInput,
el('button', { type: 'submit', class: 'btn btn-spawn' }, '◆ R3QU3ST SP4WN'),
);
root.append(spawn);
const pending = approvalsState.pending; const pending = approvalsState.pending;
const history = approvalsState.history; const history = approvalsState.history;
const active = localStorage.getItem(APPROVAL_TAB_KEY) || 'pending'; const active = localStorage.getItem(APPROVAL_TAB_KEY) || 'pending';

View file

@ -33,7 +33,7 @@
<main class="core-main"> <main class="core-main">
<!-- K3PT ST4T3: tombstoned-agent kept state + stale permission entries. <!-- K3PT ST4T3: tombstoned-agent kept state + stale permission entries.
tombstones-section shows destroyed agents (purge button). tombstones-section shows destroyed agents (revive / purge buttons).
tombstones-stale-perms is lazy-loaded on tab activation and shows tombstones-stale-perms is lazy-loaded on tab activation and shows
agents with explicit capability/tool-group entries but no live agents with explicit capability/tool-group entries but no live
container (typically renamed/deleted agents whose JSON entries container (typically renamed/deleted agents whose JSON entries

View file

@ -74,6 +74,16 @@ function renderTombstones(s) {
li.append(head); li.append(head);
const actions = el('div', { class: 'actions' }); const actions = el('div', { class: 'actions' });
const respawn = el('form', {
method: 'POST', action: '/api/request-spawn',
class: 'inline', 'data-async': '',
'data-confirm': 'queue spawn approval for ' + t.name + '? state will be reused.',
});
respawn.append(
el('input', { type: 'hidden', name: 'name', value: t.name }),
el('button', { type: 'submit', class: 'btn btn-start' }, '⊕ R3V1V3'),
);
actions.append(respawn);
actions.append(form( actions.append(form(
'/api/purge-tombstone/' + t.name, 'btn-destroy', 'PURG3', '/api/purge-tombstone/' + t.name, 'btn-destroy', 'PURG3',
'PURGE ' + t.name + '? config history, claude creds, ' 'PURGE ' + t.name + '? config history, claude creds, '

View file

@ -610,6 +610,18 @@ hive-agent-menu {
} }
ul form.inline { display: inline-block; } ul form.inline { display: inline-block; }
.spawnform { display: flex; gap: 0.6em; align-items: stretch; margin: 0.5em 0; }
.spawnform input {
font-family: inherit;
font-size: 1em;
background: var(--bg-elev);
color: var(--fg);
border: 1px solid var(--border);
padding: 0.4em 0.6em;
flex: 1;
}
.spawnform input::placeholder { color: var(--muted); }
.spawnform input:focus { outline: 1px solid var(--purple); }
.role-pending { color: var(--amber); border-color: var(--amber); } .role-pending { color: var(--amber); border-color: var(--amber); }
.btn-inline { .btn-inline {
font-family: inherit; font-family: inherit;