dashboard: clickable file-path previews

agents constantly emit pointer strings to /agents/<n>/state/foo.md
since broker bodies cap at 1 KiB. now those tokens linkify in the
message flow, question bodies, answer text, and operator inbox;
clicking expands an inline <details> that lazy-fetches via the
new /api/state-file?path=... endpoint.

endpoint allow-list: per-agent state dirs + shared docs, both
in their container-mount form (/agents/<n>/state, /shared) and
host form (/var/lib/hyperhive/...). 1 MiB read cap; canonicalises
before the prefix check so `..` / symlinks can't escape.

legacy bare `/state/...` is deliberately not matched — ambiguous
from the host's perspective (we'd need to know which agent the
message references to translate). agents should use the qualified
form going forward.
This commit is contained in:
müde 2026-05-17 22:08:15 +02:00
parent a15fafb5de
commit cb71a07300
4 changed files with 249 additions and 18 deletions

View file

@ -450,6 +450,41 @@ summary:hover { color: var(--purple); }
0%, 100% { box-shadow: 0 0 12px -4px rgba(250, 179, 135, 0.55); }
50% { box-shadow: 0 0 22px -2px rgba(250, 179, 135, 0.95); }
}
/* Path linkification agents drop pointer strings into messages
constantly; clicking the anchor expands a sibling <details> that
lazy-loads from /api/state-file. */
.path-link {
color: var(--blue, #89b4fa);
text-decoration: underline dotted;
cursor: pointer;
}
.path-link:hover { color: var(--amber); }
.path-preview {
margin: 0.2em 0 0.4em 1.5em;
border-left: 2px solid var(--border);
padding-left: 0.6em;
}
.path-preview > summary {
cursor: pointer;
color: var(--muted);
font-size: 0.85em;
list-style: none;
user-select: none;
}
.path-preview > summary::marker { content: ''; }
.path-preview-body {
background: var(--bg);
border: 1px solid var(--border);
padding: 0.5em 0.7em;
margin: 0.3em 0 0;
max-height: 30em;
overflow: auto;
white-space: pre-wrap;
word-break: break-word;
font-size: 0.85em;
color: var(--fg);
}
/* Filter chip row above the questions list. The active chip lights
up amber to match the rest of the dashboard's selection accents. */
.questions-filters {