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,101 @@
/**
* Agent Timeline — visual execution timeline built from SSE events
*/
let containerEl;
let steps = [];
export function initTimeline() {
// The timeline renders inside the inspector; this module manages the data
containerEl = null;
steps = [];
}
export function addTimelineStep({ step, totalSteps, agent, description, status }) {
steps.push({
step,
totalSteps,
agent,
description,
status: status || 'executing',
tools: [],
});
renderTimeline();
}
export function updateTimelineStep(agent, { tool, status }) {
// Find the last step for this agent
for (let i = steps.length - 1; i >= 0; i--) {
if (steps[i].agent === agent) {
if (tool) {
steps[i].tools.push({ name: tool, status: status || 'completed' });
}
break;
}
}
renderTimeline();
}
export function clearTimeline() {
steps = [];
renderTimeline();
}
function renderTimeline() {
// Find or create the timeline container in the inspector
const inspector = document.getElementById('inspector');
if (!inspector) return;
// Remove existing timeline section
let existing = document.getElementById('live-timeline-section');
if (existing) existing.remove();
if (steps.length === 0) return;
const section = document.createElement('div');
section.className = 'inspector-section';
section.id = 'live-timeline-section';
let html = '<div class="inspector-section-title">Live Timeline</div>';
html += '<div class="timeline">';
for (const s of steps) {
const stepClass = s.status === 'executing' ? 'active' : s.status === 'completed' ? 'completed' : s.status === 'failed' ? 'failed' : '';
html += `
<div class="timeline-step ${stepClass}">
<div class="timeline-step-header">
<span class="role-badge ${s.agent}">${s.agent}</span>
<span class="text-sm text-muted">Step ${s.step}/${s.totalSteps}</span>
</div>
<div class="timeline-step-desc">${escapeHtml(s.description)}</div>
`;
if (s.tools.length > 0) {
html += '<div class="timeline-tools">';
for (const t of s.tools) {
html += `<span class="tool-chip">${escapeHtml(t.name)}</span>`;
}
html += '</div>';
}
html += '</div>';
}
html += '</div>';
section.innerHTML = html;
// Insert at the top of inspector (after session info if present)
const firstSection = inspector.querySelector('.inspector-section');
if (firstSection && firstSection.nextSibling) {
inspector.insertBefore(section, firstSection.nextSibling);
} else {
inspector.appendChild(section);
}
}
function escapeHtml(str) {
if (!str) return '';
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}