Files
gott/wwwroot/js/pages/shared-page.js
T
Meleki 4bec740765 feat: Finalizer-Agent – dritter Agent der nach Worker + Review einschreitet
Backend:
- SessionRecord + DB-Migration: finalizer_agent_id auf sessions
- ChatContext.FinalizerContext (Model, SystemPrompt, Tools, Workspace)
- ChatService: FinalizerContext aufbauen (analog zu ReviewContext)
- AnthropicService: RunFinalizer() nach Review-Phase (oder direkt nach Worker)
- RunReviewAgentChat() refactored zu generischem RunAgentChat()
  mit agentType-Parameter ('review' | 'finalizer') für SSE-Events und DB-Tags
- SSE-Events: finalizer_start, finalizer_text, finalizer_tool_use, finalizer_done

Frontend:
- Session-Page: Finalizer-Agent-Picker (im 'Weitere Einstellungen' Bereich)
- Chat: SSE-Events für Finalizer-Phase verarbeiten
- UI: addReviewBubble/finalizeReviewBubble unterstützen agentType 'finalizer'
  mit eigener Farbe (success-green) und Flag-Checkered-Icon
- renderMarkdown: [Finalizer: AgentName] Tags parsen
- History: Finalizer-Messages als .finalizer-bubble rendern
- Shared-Page: Finalizer-Messages korrekt erkennen und stylen
- Dashboard: Finalizer-Badge (flag-checkered) auf Session-Cards
2026-03-06 23:35:30 +01:00

82 lines
2.9 KiB
JavaScript

// ── Shared Message Page (öffentlich, kein Login nötig) ──
registerPage('shared', {
init: async (params) => {
const token = params.id;
const container = document.getElementById('sharedContent');
if (!token || !container) {
container.innerHTML = '<div class="shared-error"><i class="fa fa-times-circle"></i> Ungültiger Link</div>';
return;
}
try {
const data = await apiGetShared(token);
container.innerHTML = '';
let messages = [];
if (data.messages && data.messages.length > 0) {
messages = data.messages;
} else if (data.message) {
messages = [data.message];
}
// Aufeinanderfolgende Assistant-Text-Messages mergen (wie im Chat)
messages = _mergeConsecutiveTexts(messages);
for (const msg of messages) {
const c = msg.content || '';
const isReview = c.startsWith('[Review:') || c.startsWith('[Review-Feedback') || c.startsWith('[Finalizer:');
const div = document.createElement('div');
if (isReview) {
div.className = 'msg review-bubble markdown' + (c.startsWith('[Finalizer:') ? ' finalizer-bubble' : '');
} else {
div.className = 'msg ' + (msg.role === 'user' ? 'user' : 'assistant') + ' markdown';
}
if (!isReview && msg.role === 'user') {
renderUserContent(div, msg.content);
} else {
div.innerHTML = renderMarkdown(c);
parseFluentEmojis(div);
wrapKreaGalleries(div);
}
container.appendChild(div);
}
// Timestamp anzeigen
if (data.createdAt) {
const meta = document.createElement('div');
meta.className = 'shared-meta';
const d = new Date(data.createdAt);
meta.innerHTML = '<i class="fa fa-clock-o"></i> Geteilt am ' + d.toLocaleDateString('de-DE') + ' um ' + d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
container.appendChild(meta);
}
} catch (err) {
container.innerHTML = '<div class="shared-error"><i class="fa fa-times-circle"></i> ' + esc(err.message) + '</div>';
}
},
destroy: () => {}
});
function _mergeConsecutiveTexts(msgs) {
const result = [];
for (const msg of msgs) {
const c = msg.content || '';
const isReview = c.startsWith('[Review:') || c.startsWith('[Review-Feedback') || c.startsWith('[Finalizer:');
if (msg.role === 'assistant' && !isReview) {
const prev = result[result.length - 1];
if (prev && prev.role === 'assistant'
&& !(prev.content || '').startsWith('[Review:')
&& !(prev.content || '').startsWith('[Finalizer:')
&& prev.iteration === msg.iteration) {
prev.content = (prev.content || '') + ' ' + c;
if (msg.createdAt) prev.createdAt = msg.createdAt;
continue;
}
}
result.push(Object.assign({}, msg));
}
return result;
}