feat(diagnostic): Agent-Historie pro Agent — Akkordeon je Nachricht
Jede Host-Aktion wird als Historie-Eintrag festgehalten und in der Diagnostic pro User-Nachricht gruppiert aufklappbar dargestellt (mit Screenshot-Thumbnails). - Brain: trace_id+msg pro chat(); _dispatch_host als Wrapper um _dispatch_host_inner emittiert je Aktion einen Eintrag an POST /internal/agent-history (Datei-Pfad aus Screenshot-Text erkannt). - Bridge: /internal/agent-history persistiert nach /shared/config/agent_history/ <hostId>.jsonl (Ringpuffer 200) + broadcastet agent_history; agent_history_query -> agent_history_list. - RVS: agent_history/_query/_list in ALLOWED_TYPES (sonst still verworfen). - Diagnostic-Server: relay browser<->RVS + /uploads/<file> Bild-Endpoint (nur /shared/uploads, kein Traversal). - index.html: 'Historie'-Button je Host, Modal mit Akkordeon (Gruppe=Nachricht, aufklappbar, Screenshot-Thumbs), Live-Refresh bei agent_history. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+106
-1
@@ -1594,6 +1594,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Agent-Historie-Modal (Befehle + Screenshots pro Nachricht) -->
|
||||
<div class="modal-overlay" id="agent-history-modal">
|
||||
<div class="modal-box" style="max-width:760px;">
|
||||
<div class="modal-header">
|
||||
<h3 id="agent-history-title">Historie</h3>
|
||||
<button class="modal-close" onclick="closeAgentHistory()">×</button>
|
||||
</div>
|
||||
<div style="padding:6px 16px;color:#8888AA;font-size:11px;">
|
||||
Jede Zeile = eine Anfrage an ARIA. Aufklappen zeigt, was auf diesem Agenten dafür gemacht wurde.
|
||||
</div>
|
||||
<div class="modal-body" id="agent-history-body" style="padding:10px 16px 16px;max-height:70vh;overflow-y:auto;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Memory-Modal (Neu + Editieren) -->
|
||||
<div class="modal-overlay" id="memory-modal">
|
||||
<div class="modal-box" style="max-width:640px;">
|
||||
@@ -2185,6 +2199,19 @@
|
||||
}
|
||||
if (msg.type === 'host_update') { hosts = msg.hosts || []; renderHosts(); return; }
|
||||
if (msg.type === 'rooms_info') { rooms = (msg.payload && msg.payload.rooms) || []; renderRooms(); return; }
|
||||
if (msg.type === 'agent_history_list') {
|
||||
if (agentHistoryHost && msg.payload && msg.payload.host === agentHistoryHost) {
|
||||
renderAgentHistory(msg.payload.events || []);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (msg.type === 'agent_history') {
|
||||
// Live: wenn das Modal fuer diesen Host offen ist, neu abfragen (einfach + robust).
|
||||
if (agentHistoryHost && msg.payload && msg.payload.host === agentHistoryHost) {
|
||||
send({ action: 'agent_history_query', host: agentHistoryHost });
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (msg.type === 'sat_creds_list_result') {
|
||||
const p = msg.payload || msg;
|
||||
const sat = p.satellite || '';
|
||||
@@ -4643,13 +4670,91 @@
|
||||
const caps = (h.caps || []).join(', ') || '—';
|
||||
const ctrl = h.control ? '<span style="color:#0096FF;">steuerbar</span>' : '<span style="color:#FF6E6E;">CONTROL_ENABLED=false</span>';
|
||||
return '<div style="border:1px solid #1E1E2E;border-radius:8px;padding:10px;margin-bottom:8px;">' +
|
||||
'<div><span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:' + dot + ';margin-right:6px;"></span>' +
|
||||
'<div style="display:flex;align-items:center;gap:8px;">' +
|
||||
'<div style="flex:1;"><span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:' + dot + ';margin-right:6px;"></span>' +
|
||||
'<strong style="color:#E0E0F0;">💻 ' + escapeHtml(h.name || h.hostId) + '</strong> ' +
|
||||
'<span style="color:#666;font-size:11px;">id=' + escapeHtml(h.hostId) + '</span></div>' +
|
||||
'<button onclick="openAgentHistory(\'' + escapeHtml(h.hostId) + '\')" style="background:#12121E;color:#FFD60A;border:1px solid #FFD60A44;border-radius:6px;padding:4px 10px;font-size:12px;cursor:pointer;">🕘 Historie</button>' +
|
||||
'</div>' +
|
||||
'<div style="color:#8888AA;font-size:11px;margin-top:4px;">' + escapeHtml(h.os || '') + ' · kann: ' + escapeHtml(caps) + ' · ' + ctrl + '</div>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
}
|
||||
// ── Agent-Historie (Befehle + Screenshots pro Nachricht) ──────────
|
||||
let agentHistoryHost = null;
|
||||
|
||||
function openAgentHistory(hostId) {
|
||||
const h = (hosts || []).find(x => x.hostId === hostId);
|
||||
agentHistoryHost = hostId;
|
||||
document.getElementById('agent-history-title').textContent =
|
||||
'Historie: ' + ((h && (h.name || h.hostId)) || hostId);
|
||||
document.getElementById('agent-history-body').innerHTML =
|
||||
'<span style="color:#8888AA;">lade …</span>';
|
||||
document.getElementById('agent-history-modal').classList.add('open');
|
||||
send({ action: 'agent_history_query', host: hostId });
|
||||
}
|
||||
function closeAgentHistory() {
|
||||
document.getElementById('agent-history-modal').classList.remove('open');
|
||||
agentHistoryHost = null;
|
||||
}
|
||||
function toggleHistGroup(gi) {
|
||||
const el = document.getElementById('hist-group-' + gi);
|
||||
const caret = document.getElementById('hist-caret-' + gi);
|
||||
if (!el) return;
|
||||
const open = el.style.display !== 'none';
|
||||
el.style.display = open ? 'none' : 'block';
|
||||
if (caret) caret.textContent = open ? '▶' : '▼';
|
||||
}
|
||||
function renderAgentHistory(events) {
|
||||
const body = document.getElementById('agent-history-body');
|
||||
if (!body) return;
|
||||
if (!events || !events.length) {
|
||||
body.innerHTML = '<span style="color:#8888AA;">Noch keine Aktionen für diesen Agenten aufgezeichnet. ' +
|
||||
'Sobald ARIA etwas auf ihm tut (Screenshot, tippen, …), erscheint es hier.</span>';
|
||||
return;
|
||||
}
|
||||
// Nach trace_id gruppieren (eine Gruppe = eine User-Nachricht), Reihenfolge erhalten.
|
||||
const groups = [], idx = {};
|
||||
events.forEach(ev => {
|
||||
const key = ev.trace_id || ('_' + ev.ts);
|
||||
if (!(key in idx)) { idx[key] = groups.length; groups.push({ msg: ev.msg || '', ts: ev.ts, items: [] }); }
|
||||
groups[idx[key]].items.push(ev);
|
||||
});
|
||||
groups.reverse(); // neueste Nachricht oben
|
||||
body.innerHTML = groups.map((g, gi) => {
|
||||
const t = new Date(g.ts).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
|
||||
const bad = g.items.some(e => e.ok === false);
|
||||
const dot = bad ? '#FF9500' : '#34C759';
|
||||
const head = '<div onclick="toggleHistGroup(' + gi + ')" style="cursor:pointer;padding:8px 10px;background:#12121E;border-radius:6px;display:flex;align-items:center;gap:8px;">' +
|
||||
'<span id="hist-caret-' + gi + '" style="color:#8888AA;">' + (gi === 0 ? '▼' : '▶') + '</span>' +
|
||||
'<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:' + dot + ';"></span>' +
|
||||
'<span style="color:#666;font-size:11px;">' + t + '</span>' +
|
||||
'<span style="color:#E0E0F0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">' + escapeHtml(g.msg || '(ohne Text)') + '</span>' +
|
||||
'<span style="color:#8888AA;font-size:11px;white-space:nowrap;">' + g.items.length + ' Aktion(en)</span></div>';
|
||||
const rows = g.items.map(e => {
|
||||
const good = e.ok !== false;
|
||||
const ic = good ? '✓' : '✗';
|
||||
const icColor = good ? '#34C759' : '#FF6E6E';
|
||||
const args = (e.args && Object.keys(e.args).length)
|
||||
? ' <span style="color:#666;font-size:11px;">' + escapeHtml(JSON.stringify(e.args)) + '</span>' : '';
|
||||
let thumb = '';
|
||||
if (e.file) {
|
||||
const base = String(e.file).split('/').pop();
|
||||
thumb = '<div style="margin-top:5px;"><img src="/uploads/' + encodeURIComponent(base) +
|
||||
'" loading="lazy" style="max-width:180px;max-height:320px;border:1px solid #1E1E2E;border-radius:6px;cursor:pointer;" ' +
|
||||
'onclick="window.open(this.src)" onerror="this.style.display=\'none\'"></div>';
|
||||
}
|
||||
return '<div style="padding:6px 10px 6px 26px;border-bottom:1px solid #15151F;">' +
|
||||
'<span style="color:' + icColor + ';">' + ic + '</span> ' +
|
||||
'<code style="color:#FFD60A;">' + escapeHtml(e.action || e.tool || '?') + '</code>' + args +
|
||||
(e.summary ? '<div style="color:#8888AA;font-size:11px;margin-top:2px;">' + escapeHtml(e.summary) + '</div>' : '') +
|
||||
thumb + '</div>';
|
||||
}).join('');
|
||||
return '<div style="margin-bottom:6px;">' + head +
|
||||
'<div id="hist-group-' + gi + '" style="display:' + (gi === 0 ? 'block' : 'none') + ';">' + rows + '</div></div>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function scanSatellite(id) {
|
||||
satScanning = id;
|
||||
renderSatellites();
|
||||
|
||||
Reference in New Issue
Block a user