feat: Belege — jede ARIA-Antwort zeigt, was sie wirklich ausgefuehrt hat

Was ARIA selbst im Proxy ausfuehrt (Bash/ssh, Read, Edit …), war nur live
sichtbar — ob sie fuer "auf Prod nachgesehen" wirklich nachgesehen hat (und
wo), liess sich hinterher nicht pruefen. Das Befehlslog gab es nur fuer die
Host-Agenten.

Proxy (proxy-patches/routes.js): start/tool_use/tool_result/end zusaetzlich
dauerhaft nach /shared/logs/aria-tools/<tag>.jsonl (30 Tage), Passwoerter,
Tokens, API-Keys, private Schluessel und Zugangsdaten in URLs geschwaerzt.

Diagnostic: 🔍 an jeder ARIA-Bubble → alle Werkzeug-Aufrufe zwischen Stefans
Frage und dieser Antwort (Befehl + Ausgabe). Hat sie nichts ausgefuehrt,
sagt das Fenster es deutlich (Stand kommt dann aus Verlauf/Gedaechtnis).
GET /api/aria-proof?ts=&project=.

Deploy: proxy --force-recreate (Patch wirkt nur beim Start), diagnostic --build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-28 15:42:49 +01:00
co-authored by Claude Opus 5.5
parent f479a0781d
commit 26ad872c6c
3 changed files with 168 additions and 3 deletions
+66 -2
View File
@@ -74,6 +74,12 @@
transition:opacity 0.15s; }
.chat-msg:hover .bubble-trash { opacity: 1; }
.chat-msg .bubble-trash:hover { background:#FF3B30; color:#fff; }
.chat-msg .bubble-proof { position:absolute; top:4px; right:32px; background:rgba(0,150,255,0.15);
color:#4DB8FF; border:none; border-radius:50%; width:22px; height:22px;
font-size:11px; line-height:18px; padding:0; cursor:pointer; opacity:0;
transition:opacity 0.15s; }
.chat-msg:hover .bubble-proof { opacity: 1; }
.chat-msg .bubble-proof:hover { background:#0096FF; color:#fff; }
.chat-msg.sent { background: #0096FF; color: #fff; align-self: flex-end;
border-bottom-right-radius: 4px; }
.chat-msg.received { background: #1E1E2E; color: #E8E8F0; align-self: flex-start;
@@ -197,6 +203,17 @@
</div>
</div>
<!-- Belege: Werkzeug-Aufrufe zu einer ARIA-Antwort -->
<div id="proof-modal" style="display:none;position:fixed;inset:0;z-index:1003;background:rgba(0,0,0,0.8);align-items:center;justify-content:center;" onclick="if(event.target===this)this.style.display='none'">
<div style="background:#0D0D1A;border:1px solid #2A2A3E;border-radius:10px;padding:16px 20px;max-width:1000px;width:94%;max-height:92vh;overflow:auto;color:#C8C8D8;font-size:13px;">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;">
<h3 style="margin:0;color:#fff;">🔍 Belege zu dieser Antwort</h3>
<button onclick="document.getElementById('proof-modal').style.display='none'" style="background:none;border:none;color:#8888AA;font-size:22px;cursor:pointer;">&times;</button>
</div>
<div id="proof-body">…</div>
</div>
</div>
<!-- Bestenlisten: Ranglisten-Links + HF-Trends (direkt in den Katalog) -->
<div id="boards-modal" style="display:none;position:fixed;inset:0;z-index:1002;background:rgba(0,0,0,0.8);align-items:center;justify-content:center;" onclick="if(event.target===this)this.style.display='none'">
<div style="background:#0D0D1A;border:1px solid #2A2A3E;border-radius:10px;padding:16px 20px;max-width:1000px;width:94%;max-height:92vh;overflow:auto;color:#C8C8D8;font-size:13px;">
@@ -3005,7 +3022,10 @@
? `<button class="bubble-trash" title="Diese Bubble loeschen" onclick="deleteDiagBubble(${m.ts})">🗑</button>`
: '';
const histBadge = srcBadgeHtml(m.type, m.answeredBy || '', m.model || '', m.modelReason || '');
const innerHtml = `${trashBtn}${linked}<div class="meta">${escapeHtml(m.meta)}${histBadge} — ${time}</div>`;
const proofBtn = (m.ts && m.type === 'received')
? `<button class="bubble-proof" title="Belege: was hat ARIA für diese Antwort wirklich ausgeführt?" onclick="showAriaProof(${m.ts}, this)">🔍</button>`
: '';
const innerHtml = `${trashBtn}${proofBtn}${linked}<div class="meta">${escapeHtml(m.meta)}${histBadge} — ${time}</div>`;
for (const b of boxes) {
const el = document.createElement('div');
el.className = `chat-msg ${m.type}`;
@@ -3661,7 +3681,10 @@
// Quell-Badge: welcher Backend die Antwort erzeugt hat (nur ARIA-Bubbles)
const srcBadge = srcBadgeHtml(type, (options && options.answeredBy) || '',
(options && options.model) || '', (options && options.modelReason) || '');
const html = `${trashBtn}${linked}${ttsBlock}${gpsBlock}<div class="meta">${escapeHtml(meta)}${srcBadge} — ${new Date().toLocaleTimeString('de-DE')}</div>`;
const proofBtn = (backupTs && type === 'received')
? `<button class="bubble-proof" title="Belege: was hat ARIA für diese Antwort wirklich ausgeführt?" onclick="showAriaProof(${backupTs}, this)">🔍</button>`
: '';
const html = `${trashBtn}${proofBtn}${linked}${ttsBlock}${gpsBlock}<div class="meta">${escapeHtml(meta)}${srcBadge} — ${new Date().toLocaleTimeString('de-DE')}</div>`;
// Thinking-Indikator ausblenden bei neuer Nachricht
updateThinkingIndicator({ activity: 'idle' });
@@ -9535,6 +9558,47 @@
} catch (e) { btn.disabled = false; btn.textContent = 'In Katalog'; alert(e.message); }
}
// ── Belege: was hat ARIA fuer eine Antwort wirklich ausgefuehrt? ──
async function showAriaProof(ts, btn) {
const project = (btn && btn.closest('.chat-msg') && btn.closest('.chat-msg').dataset.projectId) || '';
const body = document.getElementById('proof-body');
document.getElementById('proof-modal').style.display = 'flex';
body.innerHTML = '<span style="color:#8888AA;">lade …</span>';
let j;
try { j = await (await fetch('/api/aria-proof?ts=' + ts + '&project=' + encodeURIComponent(project))).json(); }
catch (e) { body.innerHTML = '<span style="color:#FF6B6B;">✗ ' + escapeHtml(e.message) + '</span>'; return; }
if (!j.ok) { body.innerHTML = '<span style="color:#FF6B6B;">✗ ' + escapeHtml(j.error || 'Fehler') + '</span>'; return; }
const t = x => new Date(x).toLocaleTimeString('de-DE');
const head = '<div style="font-size:12px;color:#8888AA;margin-bottom:10px;">Zeitraum ' + t(j.from) + ' – ' + t(j.to) +
(project ? ' · Projekt <code>' + escapeHtml(project) + '</code>' : ' · Hauptchat') + '</div>';
if (!j.toolCount) {
const before = j.logSince && new Date(j.to).toISOString().slice(0, 10) < j.logSince;
body.innerHTML = head + '<div style="border:1px solid #FFB020;border-radius:8px;padding:12px;background:#2A2410;color:#FFD580;">' +
(before ? 'Für diese Antwort gibt es noch kein Protokoll (aufgezeichnet wird seit ' + escapeHtml(j.logSince) + ').'
: '<b>Keine Befehle ausgeführt.</b> ARIA hat für diese Antwort nichts nachgesehen (kein Bash/ssh, kein Lesen). ' +
'Sagt sie darin „nachgesehen" oder nennt sie einen aktuellen Stand, stammt das aus dem Verlauf oder ihrem Gedächtnis.') +
'</div><div style="font-size:11px;color:#555570;margin-top:8px;">Nicht enthalten: ARIAs eigene Werkzeuge wie Gedächtnis-Suche oder Timer — die stehen im Brain-Log.</div>';
return;
}
const prettyInput = (name, input) => {
try { const o = JSON.parse(input); if (name === 'Bash' && o.command) return o.command;
if (o.file_path) return o.file_path + (o.pattern ? ' · ' + o.pattern : '');
if (o.pattern) return o.pattern + (o.path ? ' in ' + o.path : '');
return JSON.stringify(o, null, 1); } catch { return input || ''; }
};
body.innerHTML = head + j.requests.map(r => r.tools.map(x =>
'<div style="border:1px solid #1E1E2E;border-radius:8px;padding:8px 10px;margin-bottom:8px;background:#12122A;">' +
'<div style="display:flex;justify-content:space-between;gap:8px;"><b style="color:' + (x.isError ? '#FF6B6B' : '#4DB8FF') + ';">' + escapeHtml(x.name || '?') + (x.isError ? ' · Fehler' : '') + '</b>' +
'<span style="color:#555570;font-size:11px;">' + t(x.ts) + '</span></div>' +
'<pre style="margin:6px 0 0;white-space:pre-wrap;word-break:break-all;background:#000;border-radius:6px;padding:6px 8px;color:#E0E0F0;font-size:12px;">' + escapeHtml(prettyInput(x.name, x.input)) + '</pre>' +
(x.result != null ? '<details style="margin-top:6px;"><summary style="cursor:pointer;color:#8888AA;font-size:12px;">Ausgabe' +
(x.resultTruncatedBytes ? ' (gekürzt, ' + x.resultTruncatedBytes + ' Zeichen mehr)' : '') + '</summary>' +
'<pre style="margin:4px 0 0;white-space:pre-wrap;word-break:break-all;background:#000;border-radius:6px;padding:6px 8px;color:#3FFF9F;font-size:11px;max-height:320px;overflow:auto;">' + escapeHtml(x.result) + '</pre></details>'
: '<div style="color:#8888AA;font-size:11px;margin-top:4px;">(keine Ausgabe protokolliert)</div>') +
'</div>').join('')).join('') +
'<div style="font-size:11px;color:#555570;">Passwörter/Tokens sind im Protokoll geschwärzt. Nicht enthalten: Gedächtnis-Suche/Timer (Brain-Werkzeuge).</div>';
}
function openCtxInfo() { document.getElementById('ctx-info-modal').style.display = 'flex'; }
// Kontextfenster einer Karte aendern = dasselbe Modell mit anderem -c neu