/** * Event Log — real-time SSE events with filtering */ const EVENT_CATEGORIES = { 'session.created': 'lifecycle', 'execution.started': 'lifecycle', 'execution.completed': 'lifecycle', 'agent.delta': 'content', 'tool.started': 'tool', 'tool.completed': 'tool', 'subagent.assigned': 'orchestration', 'error': 'error', 'keepalive': 'keepalive', }; const CATEGORY_LABELS = ['lifecycle', 'content', 'tool', 'orchestration', 'error']; let entriesEl; let activeFilters = new Set(CATEGORY_LABELS); let stickToBottom = true; let events = []; export function initEventLog() { const panel = document.getElementById('event-log-panel'); panel.innerHTML = `
Event Log 0 events
`; entriesEl = document.getElementById('event-entries'); // Toggle panel document.getElementById('event-log-toggle').addEventListener('click', () => { panel.classList.toggle('open'); }); // Filters const filtersEl = document.getElementById('event-filters'); for (const cat of CATEGORY_LABELS) { const btn = document.createElement('button'); btn.className = `event-filter-btn active`; btn.textContent = cat; btn.dataset.category = cat; btn.addEventListener('click', () => { if (activeFilters.has(cat)) { activeFilters.delete(cat); btn.classList.remove('active'); } else { activeFilters.add(cat); btn.classList.add('active'); } renderEvents(); }); filtersEl.appendChild(btn); } // Auto-scroll detection entriesEl.addEventListener('scroll', () => { const { scrollTop, scrollHeight, clientHeight } = entriesEl; stickToBottom = scrollHeight - scrollTop - clientHeight < 20; }); // Clear document.getElementById('btn-clear-events').addEventListener('click', (e) => { e.stopPropagation(); clearEvents(); }); // Export document.getElementById('btn-export-events').addEventListener('click', (e) => { e.stopPropagation(); const blob = new Blob([JSON.stringify(events, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `events-${Date.now()}.json`; a.click(); URL.revokeObjectURL(url); }); } export function addEvent(event) { events.push(event); document.getElementById('event-count').textContent = `${events.length} events`; const type = event.type || ''; const category = EVENT_CATEGORIES[type] || 'lifecycle'; if (!activeFilters.has(category)) return; if (category === 'keepalive') return; // Hide keepalives by default const entry = createEventEntry(event, type, category); entriesEl.appendChild(entry); if (stickToBottom) { entriesEl.scrollTop = entriesEl.scrollHeight; } } function createEventEntry(event, type, category) { const el = document.createElement('div'); el.className = 'event-entry'; const time = event.timestamp ? new Date(event.timestamp).toLocaleTimeString('en-US', { hour12: false, fractionalSecondDigits: 3 }) : '--:--:--'; const data = event.data || {}; const summary = summarizeEventData(type, data); el.innerHTML = ` ${time} ${type.split('.').pop()} ${summary} `; // Toggle expand const dataEl = el.querySelector('.event-data'); dataEl.addEventListener('click', () => { if (dataEl.classList.contains('expanded')) { dataEl.classList.remove('expanded'); dataEl.textContent = summary; } else { dataEl.classList.add('expanded'); dataEl.textContent = JSON.stringify(data, null, 2); } }); return el; } function summarizeEventData(type, data) { switch (type) { case 'agent.delta': return `[${data.agent || '?'}] "${(data.delta || '').substring(0, 60)}..."`; case 'tool.started': return `Tool: ${data.tool}`; case 'tool.completed': return `Tool: ${data.tool} → ${data.status}`; case 'subagent.assigned': return `Step ${data.step}/${data.total_steps}: ${data.agent} — ${(data.description || '').substring(0, 50)}`; case 'execution.started': return `Session: ${(data.session_id || '').substring(0, 12)}`; case 'execution.completed': return `Steps: ${data.steps_completed} — ${data.status}`; case 'error': return JSON.stringify(data); default: return JSON.stringify(data).substring(0, 80); } } function renderEvents() { entriesEl.innerHTML = ''; for (const event of events) { const type = event.type || ''; const category = EVENT_CATEGORIES[type] || 'lifecycle'; if (!activeFilters.has(category)) continue; if (category === 'keepalive') continue; entriesEl.appendChild(createEventEntry(event, type, category)); } } export function clearEvents() { events = []; if (entriesEl) entriesEl.innerHTML = ''; const countEl = document.getElementById('event-count'); if (countEl) countEl.textContent = '0 events'; }