Initial commit

This commit is contained in:
Jordan
2026-04-01 23:16:45 +01:00
commit bc4199aed2
201 changed files with 25612 additions and 0 deletions

View File

@@ -0,0 +1,171 @@
/**
* 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 = `
<div class="event-log-header" id="event-log-toggle">
<span class="chevron">&#9660;</span>
<span>Event Log</span>
<span class="toolbar-spacer"></span>
<span class="text-muted text-sm" id="event-count">0 events</span>
<button class="btn btn-sm" id="btn-clear-events" style="margin-left:8px">Clear</button>
<button class="btn btn-sm" id="btn-export-events">Export</button>
</div>
<div class="event-log-filters" id="event-filters"></div>
<div class="event-log-entries" id="event-entries"></div>
`;
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 = `
<span class="event-time">${time}</span>
<span class="event-type-badge ${category}">${type.split('.').pop()}</span>
<span class="event-data" title="Click to expand">${summary}</span>
`;
// 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';
}