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 => `
${event.sequence ?? "·"}${esc(event.event_type)}${esc(event.actor)}

${esc(event.output_summary || event.input_summary || "")}

`).join("") || "

No events.

"; } 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 => ``).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 => `
${esc(f.category || f.rule_id || "finding")} ${esc(f.severity || "")} · evidence: ${esc((f.evidence_refs || []).join(", "))}
`).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."; });