| import { parseTrace, compareBranch, parseExchange, toExchange, attachFindings, downloadJson } from "./replay-core.js"; |
| const $ = selector => document.querySelector(selector); |
| let trace = null; let importedFindings = []; |
| function esc(value) { return String(value ?? "").replace(/[&<>'"]/g, c => ({"&":"&","<":"<",">":">","'":"'",'"':"""}[c])); } |
| function renderEvents(events, target) { target.innerHTML = events.map(event => `<article class="event"><span class="seq">${event.sequence ?? "路"}</span><strong>${esc(event.event_type)}</strong><span>${esc(event.actor)}</span><p>${esc(event.output_summary || event.input_summary || "")}</p></article>`).join("") || "<p class=muted>No events.</p>"; } |
| function renderTrace() { |
| $("#meta").textContent = `${trace.trace_id} 路 ${trace.source_type || trace.source?.format || "trace"} 路 ${(trace.original_trajectory || trace.events).length} events`; |
| if (trace.original_trajectory) { renderEvents(trace.original_trajectory, $("#original-events")); const select = $("#branch"); select.innerHTML = trace.branches.map(branch => `<option value="${esc(branch.branch_id)}">${esc(branch.branch_id)}</option>`).join(""); } |
| renderComparison(); |
| } |
| function renderComparison() { if (!trace || !trace.branches?.length) return; const comparison = compareBranch(trace, $("#branch").value); renderEvents(comparison.alternate, $("#alternate-events")); $("#classification").textContent = comparison.classification; $("#outcomes").textContent = `Original: ${JSON.stringify(comparison.originalOutcome)} 路 Alternate: ${JSON.stringify(comparison.alternateOutcome)}`; $("#findings").innerHTML = importedFindings.map(f => `<div class="finding"><strong>${esc(f.category || f.rule_id || "finding")}</strong> ${esc(f.severity || "")} 路 evidence: ${esc((f.evidence_refs || []).join(", "))}</div>`).join(""); } |
| async function loadText(text) { try { const parsed = JSON.parse(text.trim().split("\n").find(Boolean)); trace = parsed.schema_version === "solstice-agent-trace-exchange/v1" ? parsed : parseTrace(parsed); renderTrace(); $("#error").textContent = ""; } catch (error) { $("#error").textContent = error.message; } } |
| $("#file").addEventListener("change", event => event.target.files[0]?.text().then(loadText)); |
| $("#findings-file").addEventListener("change", event => event.target.files[0]?.text().then(text => { try { importedFindings = JSON.parse(text).findings || JSON.parse(text); if (trace?.schema_version === "solstice-agent-trace-exchange/v1") trace = attachFindings(trace, importedFindings); renderTrace(); } catch (error) { $("#error").textContent = error.message; } })); |
| $("#branch").addEventListener("change", renderComparison); |
| $("#download").addEventListener("click", () => { if (trace) downloadJson(trace.schema_version === "solstice-agent-trace-exchange/v1" ? trace : toExchange(trace), `${trace.trace_id || "trace"}-exchange.json`); }); |
| fetch("data/traces.jsonl").then(response => response.text()).then(text => loadText(text)).catch(() => { $("#error").textContent = "Load a local trace JSON file to begin."; }); |
|
|