145 lines
5.1 KiB
JavaScript
145 lines
5.1 KiB
JavaScript
/**
|
|
* Session Inspector — right panel with full state rendering
|
|
*/
|
|
|
|
let inspectorEl;
|
|
|
|
export function initInspector() {
|
|
inspectorEl = document.getElementById('inspector');
|
|
clearInspector();
|
|
}
|
|
|
|
export function clearInspector() {
|
|
if (!inspectorEl) return;
|
|
inspectorEl.innerHTML = '<div class="inspector-empty">No session selected</div>';
|
|
}
|
|
|
|
export function updateInspector(session) {
|
|
if (!inspectorEl || !session) return;
|
|
|
|
inspectorEl.innerHTML = '';
|
|
|
|
// Header
|
|
inspectorEl.appendChild(buildSection('Session', `
|
|
<div class="inspector-field">
|
|
<span class="label">ID</span>
|
|
<span class="value truncate" style="max-width:180px" title="${session.session_id}">${session.session_id.substring(0, 16)}...</span>
|
|
</div>
|
|
<div class="inspector-field">
|
|
<span class="label">Status</span>
|
|
<span class="badge ${session.status}">${session.status}</span>
|
|
</div>
|
|
<div class="inspector-field">
|
|
<span class="label">Turns</span>
|
|
<span class="value">${session.turn_count}</span>
|
|
</div>
|
|
<div class="inspector-field">
|
|
<span class="label">Created</span>
|
|
<span class="value">${formatTime(session.created_at)}</span>
|
|
</div>
|
|
<div class="inspector-field">
|
|
<span class="label">Updated</span>
|
|
<span class="value">${formatTime(session.updated_at)}</span>
|
|
</div>
|
|
`));
|
|
|
|
// Current Task
|
|
if (session.current_task) {
|
|
const task = session.current_task;
|
|
let taskHtml = `
|
|
<div class="inspector-field">
|
|
<span class="label">Objective</span>
|
|
</div>
|
|
<div class="text-sm" style="margin-bottom:6px;color:var(--text-primary)">${escapeHtml(task.objective)}</div>
|
|
<div class="inspector-field">
|
|
<span class="label">Status</span>
|
|
<span class="badge ${task.status}">${task.status}</span>
|
|
</div>
|
|
<div class="inspector-field">
|
|
<span class="label">Step</span>
|
|
<span class="value">${task.current_step_index + 1} / ${(task.plan || []).length}</span>
|
|
</div>
|
|
`;
|
|
|
|
// Facts
|
|
if (task.facts_extracted && task.facts_extracted.length > 0) {
|
|
taskHtml += '<div class="mt-2"><span class="label text-sm">Facts:</span><ul style="padding-left:16px;margin-top:4px">';
|
|
for (const f of task.facts_extracted.slice(-8)) {
|
|
taskHtml += `<li class="text-sm">${escapeHtml(f)}</li>`;
|
|
}
|
|
taskHtml += '</ul></div>';
|
|
}
|
|
|
|
// Constraints
|
|
if (task.constraints && task.constraints.length > 0) {
|
|
taskHtml += '<div class="mt-2"><span class="label text-sm">Constraints:</span><ul style="padding-left:16px;margin-top:4px">';
|
|
for (const c of task.constraints) {
|
|
taskHtml += `<li class="text-sm">${escapeHtml(c)}</li>`;
|
|
}
|
|
taskHtml += '</ul></div>';
|
|
}
|
|
|
|
inspectorEl.appendChild(buildSection('Current Task', taskHtml));
|
|
|
|
// Plan
|
|
if (task.plan && task.plan.length > 0) {
|
|
let planHtml = '<div class="timeline">';
|
|
for (let i = 0; i < task.plan.length; i++) {
|
|
const step = task.plan[i];
|
|
const stepStatus = step.status || 'pending';
|
|
const isActive = i === task.current_step_index && task.status === 'executing';
|
|
const stepClass = isActive ? 'active' : (stepStatus === 'completed' ? 'completed' : stepStatus === 'failed' ? 'failed' : '');
|
|
|
|
planHtml += `
|
|
<div class="timeline-step ${stepClass}">
|
|
<div class="timeline-step-header">
|
|
<span class="role-badge ${step.agent_role || 'coder'}">${step.agent_role || 'coder'}</span>
|
|
<span class="badge ${stepStatus}">${stepStatus}</span>
|
|
</div>
|
|
<div class="timeline-step-desc">${escapeHtml(step.description)}</div>
|
|
${step.tools_used && step.tools_used.length > 0 ? `
|
|
<div class="timeline-tools">
|
|
${step.tools_used.map(t => `<span class="tool-chip">${escapeHtml(t)}</span>`).join('')}
|
|
</div>
|
|
` : ''}
|
|
${step.result_summary ? `<div class="text-sm text-muted mt-2">${escapeHtml(step.result_summary.substring(0, 200))}</div>` : ''}
|
|
</div>
|
|
`;
|
|
}
|
|
planHtml += '</div>';
|
|
inspectorEl.appendChild(buildSection('Execution Plan', planHtml));
|
|
}
|
|
}
|
|
|
|
// Completed tasks
|
|
if (session.completed_tasks && session.completed_tasks.length > 0) {
|
|
let html = '<ul style="padding-left:16px">';
|
|
for (const t of session.completed_tasks) {
|
|
html += `<li class="text-sm mono">${t}</li>`;
|
|
}
|
|
html += '</ul>';
|
|
inspectorEl.appendChild(buildSection(`Completed (${session.completed_tasks.length})`, html));
|
|
}
|
|
}
|
|
|
|
function buildSection(title, innerHtml) {
|
|
const section = document.createElement('div');
|
|
section.className = 'inspector-section';
|
|
section.innerHTML = `<div class="inspector-section-title">${title}</div>${innerHtml}`;
|
|
return section;
|
|
}
|
|
|
|
function formatTime(isoStr) {
|
|
if (!isoStr) return '—';
|
|
try {
|
|
return new Date(isoStr).toLocaleTimeString('en-US', { hour12: false });
|
|
} catch {
|
|
return isoStr;
|
|
}
|
|
}
|
|
|
|
function escapeHtml(str) {
|
|
if (!str) return '';
|
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
}
|