agent page: inbox view of last 30 messages addressed to this agent
new wire request AgentRequest::Recent { limit } / ManagerRequest::Recent
(plus matching responses with Vec<InboxRow>). InboxRow moved to
hive-sh4re so it lives on both surfaces without an internal-to-wire
conversion. host-side dispatch in agent_server / manager_server
calls broker.recent_for(name, limit).
per-agent web_ui /api/state grew an inbox: Vec<InboxRow> populated
via the same per-agent socket (best-effort; transport failure
returns empty). frontend renders as a collapsible <details> section
between the state row and the terminal — fmt timestamp / from /
body in a tight grid, capped at 16em scrollable. only visible when
there are rows.
This commit is contained in:
parent
bd7d2d4860
commit
538e0446d7
13 changed files with 151 additions and 20 deletions
|
|
@ -130,6 +130,42 @@ pre.diff {
|
|||
align-items: center;
|
||||
gap: 0.6em;
|
||||
}
|
||||
/* Per-agent inbox section — collapsible, dim, lives between the
|
||||
state row and the terminal so the operator can peek at what
|
||||
landed without scrolling through the live tail. */
|
||||
.agent-inbox {
|
||||
margin: 0.4em 0;
|
||||
font-size: 0.85em;
|
||||
color: var(--muted);
|
||||
}
|
||||
.agent-inbox > summary {
|
||||
cursor: pointer;
|
||||
letter-spacing: 0.05em;
|
||||
list-style: none;
|
||||
}
|
||||
.agent-inbox > summary::marker { content: ''; }
|
||||
.agent-inbox[open] > summary > span::before { content: ''; }
|
||||
.agent-inbox ul {
|
||||
list-style: none;
|
||||
padding: 0.4em 0.8em;
|
||||
margin: 0.3em 0 0;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border-left: 2px solid var(--purple-dim);
|
||||
max-height: 16em;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.agent-inbox li {
|
||||
padding: 0.15em 0;
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto 1fr;
|
||||
gap: 0.5em;
|
||||
align-items: baseline;
|
||||
}
|
||||
.agent-inbox .inbox-ts { color: var(--muted); font-size: 0.9em; }
|
||||
.agent-inbox .inbox-from { color: var(--amber); }
|
||||
.agent-inbox .inbox-sep { color: var(--muted); }
|
||||
.agent-inbox .inbox-body { color: var(--fg); white-space: pre-wrap; word-break: break-word; }
|
||||
|
||||
.last-turn {
|
||||
color: var(--muted);
|
||||
font-size: 0.8em;
|
||||
|
|
|
|||
|
|
@ -337,6 +337,30 @@
|
|||
}
|
||||
renderStateBadge();
|
||||
}
|
||||
function renderInbox(rows) {
|
||||
const root = $('inbox-section');
|
||||
const list = $('inbox-list');
|
||||
const summary = $('inbox-summary');
|
||||
if (!root || !list || !summary) return;
|
||||
if (!rows.length) {
|
||||
root.hidden = true;
|
||||
return;
|
||||
}
|
||||
root.hidden = false;
|
||||
summary.textContent = 'inbox · ' + rows.length;
|
||||
list.innerHTML = '';
|
||||
const fmt = (n) => new Date(n * 1000).toISOString().replace('T', ' ').slice(5, 19);
|
||||
for (const m of rows) {
|
||||
const li = el('li');
|
||||
li.append(
|
||||
el('span', { class: 'inbox-ts' }, fmt(m.at)), ' ',
|
||||
el('span', { class: 'inbox-from' }, m.from), ' ',
|
||||
el('span', { class: 'inbox-sep' }, '→'), ' ',
|
||||
el('span', { class: 'inbox-body' }, m.body),
|
||||
);
|
||||
list.append(li);
|
||||
}
|
||||
}
|
||||
function renderLastTurn(ms) {
|
||||
const el_ = $('last-turn');
|
||||
if (!el_) return;
|
||||
|
|
@ -386,6 +410,7 @@
|
|||
const s = await resp.json();
|
||||
if (!headerSet) { setHeader(s.label, s.dashboard_port); headerSet = true; }
|
||||
renderTermInput(s.label, s.status === 'online');
|
||||
renderInbox(s.inbox || []);
|
||||
// Drive the state badge from the harness status. Live SSE events
|
||||
// override to 'thinking' / 'idle' as turns start/end; this only
|
||||
// kicks in for the not-online (offline) case and the initial seed.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@
|
|||
<button type="button" id="cancel-btn" class="btn-cancel-turn" hidden>■ cancel turn</button>
|
||||
</div>
|
||||
|
||||
<details id="inbox-section" class="agent-inbox" hidden>
|
||||
<summary>▸ <span id="inbox-summary">inbox</span></summary>
|
||||
<ul id="inbox-list"></ul>
|
||||
</details>
|
||||
|
||||
<div class="terminal-wrap">
|
||||
<div id="live" class="live terminal"><div class="meta">connecting…</div></div>
|
||||
<div id="term-input" class="term-input"></div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue