const state = { manifest: null, graph: null, trace: null, report: null, filter: "all", }; const ids = { manifestUrl: document.getElementById("manifest-url"), manifestUrlLoad: document.getElementById("manifest-url-load"), manifestFile: document.getElementById("manifest-file"), graphFile: document.getElementById("graph-file"), traceFile: document.getElementById("trace-file"), reportFile: document.getElementById("report-file"), summaryStatus: document.getElementById("summary-status"), graphStatus: document.getElementById("graph-status"), traceStatus: document.getElementById("trace-status"), stats: document.getElementById("stats"), analysisMetrics: document.getElementById("analysis-metrics"), nodeChips: document.getElementById("node-chips"), edgeList: document.getElementById("edge-list"), traceList: document.getElementById("trace-list"), traceTemplate: document.getElementById("trace-card-template"), filterButtons: Array.from(document.querySelectorAll(".filter")), }; ids.manifestUrlLoad.addEventListener("click", () => loadManifestFromUrl()); ids.manifestUrl.addEventListener("keydown", (event) => { if (event.key === "Enter") { event.preventDefault(); loadManifestFromUrl(); } }); ids.manifestFile.addEventListener("change", (event) => loadJsonFile(event, "manifest")); ids.graphFile.addEventListener("change", (event) => loadJsonFile(event, "graph")); ids.traceFile.addEventListener("change", (event) => loadJsonFile(event, "trace")); ids.reportFile.addEventListener("change", (event) => loadJsonFile(event, "report")); for (const button of ids.filterButtons) { button.addEventListener("click", () => { state.filter = button.dataset.filter ?? "all"; syncFilterButtons(); renderTrace(); }); } syncFilterButtons(); renderSummary(); renderGraph(); renderTrace(); async function loadJsonFile(event, kind) { const input = event.currentTarget; const file = input.files?.[0]; if (!file) { return; } try { const text = await file.text(); const parsed = JSON.parse(text); if (kind === "manifest") { validateManifest(parsed); state.manifest = parsed; ids.summaryStatus.textContent = `Manifest loaded: schema ${parsed.schema_version}. ` + "For full auto-load, use the manifest URL field while serving the artifacts over HTTP."; return; } const envelope = validateEnvelope(parsed, expectedArtifactType(kind)); state[kind] = envelope.payload; if (kind === "graph") { renderGraph(); } else if (kind === "trace") { renderTrace(); } else if (kind === "report") { renderSummary(); } } catch (error) { const message = error instanceof Error ? error.message : String(error); window.alert(`Failed to load ${kind} JSON: ${message}`); } finally { input.value = ""; } } async function loadManifestFromUrl() { const manifestUrl = ids.manifestUrl.value.trim(); if (!manifestUrl) { window.alert("Enter a manifest URL first."); return; } try { const manifestResponse = await fetch(manifestUrl); if (!manifestResponse.ok) { throw new Error(`Manifest request failed with status ${manifestResponse.status}`); } const manifest = await manifestResponse.json(); validateManifest(manifest); state.manifest = manifest; const baseUrl = new URL(manifestUrl, window.location.href); const parent = new URL("./", baseUrl); const artifactMap = Object.fromEntries( manifest.artifacts.map((artifact) => [artifact.artifact_type, artifact.file_name]) ); const graph = await fetchArtifactJson(parent, artifactMap.graph_schema, "graph_schema"); const trace = await fetchArtifactJson( parent, artifactMap.execution_trace, "execution_trace" ); const report = await fetchArtifactJson(parent, artifactMap.run_report, "run_report"); state.graph = graph.payload; state.trace = trace.payload; state.report = report.payload; ids.summaryStatus.textContent = `Loaded manifest and artifacts from ${baseUrl.origin}.`; renderGraph(); renderTrace(); renderSummary(); } catch (error) { const message = error instanceof Error ? error.message : String(error); window.alert(`Failed to load manifest URL: ${message}`); } } async function fetchArtifactJson(baseUrl, fileName, expectedType) { if (!fileName) { throw new Error(`Manifest does not include ${expectedType}`); } const artifactUrl = new URL(fileName, baseUrl); const response = await fetch(artifactUrl); if (!response.ok) { throw new Error( `${expectedType} request failed with status ${response.status}` ); } const json = await response.json(); return validateEnvelope(json, expectedType); } function expectedArtifactType(kind) { if (kind === "graph") return "graph_schema"; if (kind === "trace") return "execution_trace"; if (kind === "report") return "run_report"; return kind; } function validateEnvelope(value, expectedType) { if ( !value || typeof value !== "object" || typeof value.artifact_type !== "string" || typeof value.schema_version !== "string" || !("payload" in value) || typeof value.metadata !== "object" ) { throw new Error("Invalid artifact envelope"); } if (value.artifact_type !== expectedType) { throw new Error( `Unexpected artifact type: expected ${expectedType}, got ${value.artifact_type}` ); } return value; } function validateManifest(value) { if ( !value || typeof value !== "object" || typeof value.schema_version !== "string" || !Array.isArray(value.artifacts) ) { throw new Error("Invalid artifact manifest"); } } function syncFilterButtons() { for (const button of ids.filterButtons) { const active = button.dataset.filter === state.filter; button.classList.toggle("is-active", active); } } function renderSummary() { ids.stats.replaceChildren(); ids.analysisMetrics.replaceChildren(); if (!state.report) { ids.summaryStatus.textContent = "Load a report to populate metrics."; return; } ids.summaryStatus.textContent = "Report loaded."; appendStat("Accepted", state.report.accepted_count, ids.stats); appendStat("Attempts", state.report.attempt_count, ids.stats); appendStat( "Avg / Accept", formatNumber(state.report.average_attempts_per_accept), ids.stats ); appendStat("Seconds", formatNumber(state.report.total_seconds), ids.stats); const analysis = state.report.sequence_analysis ?? {}; if (Object.keys(analysis).length === 0) { const empty = document.createElement("div"); empty.className = "empty-note"; empty.textContent = "No sequence analysis in this report."; ids.analysisMetrics.append(empty); return; } for (const [key, value] of Object.entries(analysis)) { appendMetric(key, value, ids.analysisMetrics); } } function renderGraph() { ids.nodeChips.replaceChildren(); ids.edgeList.replaceChildren(); if (!state.graph) { ids.graphStatus.textContent = "Load a graph schema to inspect node wiring."; return; } ids.graphStatus.textContent = `${state.graph.nodes.length} nodes, ${state.graph.edges.length} edges.`; for (const node of state.graph.nodes) { const chip = document.createElement("article"); chip.className = "chip"; chip.innerHTML = `
${escapeHtml(node.node_type)}
${escapeHtml(node.input_names.join(", ") || "no inputs")} -> ${escapeHtml( node.output_names.join(", ") || "no outputs" )}
`; ids.nodeChips.append(chip); } for (const edge of state.graph.edges) { const item = document.createElement("li"); item.innerHTML = `${escapeHtml(edge.source_node_id)}.${escapeHtml(
edge.source_output
)} -> ${escapeHtml(edge.target_node_id)}.${escapeHtml(
edge.target_input
)}`;
ids.edgeList.append(item);
}
}
function renderTrace() {
ids.traceList.replaceChildren();
if (!state.trace) {
ids.traceStatus.textContent =
"Load a trace to inspect candidate-by-candidate behavior.";
return;
}
const attempts = state.trace.attempts ?? [];
const filtered = attempts.filter((attempt) => {
if (state.filter === "accepted") {
return attempt.accepted;
}
if (state.filter === "rejected") {
return !attempt.accepted;
}
return true;
});
ids.traceStatus.textContent = `${filtered.length} shown of ${attempts.length} attempts.`;
filtered.forEach((trace, index) => {
const fragment = ids.traceTemplate.content.cloneNode(true);
const card = fragment.querySelector(".trace-card");
const traceLabel = fragment.querySelector(".trace-label");
const traceTitle = fragment.querySelector(".trace-title");
const badge = fragment.querySelector(".badge");
const metrics = fragment.querySelector(".metric-list");
const metadata = fragment.querySelector(".json-block");
const transition = fragment.querySelector(".transition-block");
card.dataset.accepted = String(trace.accepted);
traceLabel.textContent = `Attempt ${index + 1}`;
traceTitle.textContent = `Candidate ${JSON.stringify(trace.candidate)}`;
badge.textContent = trace.accepted ? "Accepted" : "Rejected";
badge.classList.toggle("accepted", trace.accepted);
badge.classList.toggle("rejected", !trace.accepted);
appendMetric("elapsed_seconds", trace.elapsed_seconds, metrics);
const score =
trace.metadata?.critique?.outputs?.[0] ?? trace.metadata?.decision?.accepted;
if (score !== undefined) {
appendMetric("signal", score, metrics);
}
metadata.textContent = JSON.stringify(trace.metadata, null, 2);
transition.textContent = JSON.stringify(
{
previous_state: trace.previous_state,
next_state: trace.next_state,
},
null,
2
);
ids.traceList.append(fragment);
});
}
function appendStat(label, value, container) {
const article = document.createElement("article");
article.className = "stat-card";
article.innerHTML = `${escapeHtml(label)}