"use strict"; const $ = (sel) => document.querySelector(sel); const state = { runs: [], tree: { categories: {} }, status: { current: null, queue: [], tau: 0.6 }, dream: {}, selectedRunId: null, selectedDecisionId: null, phaseFilter: "", scrub: 0, flowSteps: [], }; async function getJSON(url, opts) { const res = await fetch(url, opts); if (!res.ok) throw new Error(`${url}: ${res.status}`); return res.json(); } async function refreshAll() { const [trace, tree, status, dream] = await Promise.all([ getJSON("/api/trace"), getJSON("/api/tree"), getJSON("/api/status"), getJSON("/api/dream"), ]); state.runs = trace.runs; state.tree = tree; state.status = status; state.dream = dream; if (state.selectedRunId && !state.runs.some((r) => r.run_id === state.selectedRunId)) { state.selectedRunId = null; state.selectedDecisionId = null; } render(); } function flash(msg) { $("#mode-badge").textContent = msg; } /* ---------------- phase helpers ---------------- */ function phaseClass(phase) { if (phase.startsWith("navigate:")) return "navigate"; return phase; } function shortPhase(phase) { return phase.replace("navigate:", "nav:"); } const ALL_PHASES = ["gate", "choice", "score", "navigate:category", "navigate:leaf", "predict"]; function optionProbs(row) { return (row.options || []).map((o) => ({ id: o.id, description: o.description, p: (row.predicted_probs || {})[o.id] || 0, })); } /* ---------------- timeline ---------------- */ function renderTimeline() { const el = $("#timeline"); el.innerHTML = ""; for (const run of state.runs) { const decisions = run.decisions.filter( (d) => !state.phaseFilter || d.phase === state.phaseFilter ); if (state.phaseFilter && decisions.length === 0) continue; const group = document.createElement("div"); group.className = "run-group"; const head = document.createElement("div"); head.className = "run-head"; head.textContent = run.decisions[0] ? run.decisions[0].state.slice(0, 60) : run.run_id; head.title = run.run_id; head.addEventListener("click", () => { state.selectedRunId = run.run_id; state.selectedDecisionId = null; state.scrub = 0; render(); }); group.appendChild(head); if (state.selectedRunId === run.run_id) { for (const evt of run.events) { group.appendChild(eventRow(evt)); } for (const d of decisions) { group.appendChild(decisionRow(d)); } } else { group.appendChild(dimRow(`${decisions.length} decisions`)); } el.appendChild(group); } } function dimRow(text) { const div = document.createElement("div"); div.className = "trow event"; div.textContent = text; return div; } function eventRow(evt) { const div = document.createElement("div"); div.className = "trow event"; div.textContent = evt.kind; if (evt.kind === "assessed") { div.textContent = `assessed → ${evt.success ? "ok" : "fail"}`; div.title = evt.summary || ""; } else if (evt.kind === "queued") { div.textContent = `queued (${evt.label})`; } else if (evt.kind === "preempted") { div.textContent = `preempted ${evt.preempted}`; } else if (evt.kind === "skill_writing") { div.textContent = `writing skill ${evt.skill} — ${evt.description} (${evt.model || "codegen"})`; } else if (evt.kind === "skill_created") { div.textContent = `created ${evt.skill}${evt.written ? " (body written)" : " (stub)"}`; div.title = evt.body || evt.description || ""; } return div; } function decisionRow(d) { const div = document.createElement("div"); div.className = "trow" + (d.id === state.selectedDecisionId ? " selected" : ""); div.appendChild(pill(d.phase)); if (d.label_source === "human") div.appendChild(star()); const label = document.createElement("span"); label.textContent = `${d.selected} ${(d.predicted_probs[d.selected] || 0).toFixed(2)}`; div.appendChild(label); if (d.cost && !d.cost.correct) div.appendChild(mark("x")); div.title = d.question; div.addEventListener("click", () => { state.selectedRunId = (d.extra || {}).run_id; state.selectedDecisionId = d.id; render(); }); return div; } function pill(text) { const span = document.createElement("span"); span.className = `pill ${phaseClass(text)}`; span.textContent = shortPhase(text); return span; } function star() { const span = document.createElement("span"); span.className = "human-star"; span.textContent = "★"; return span; } function mark(kind) { const span = document.createElement("span"); span.className = kind === "x" ? "x" : "chk"; span.textContent = kind === "x" ? "✗" : "✓"; return span; } /* ---------------- flow graph ---------------- */ function buildFlowSteps(run) { const items = []; for (const evt of run.events) items.push({ kind: "event", ts: evt.ts, data: evt }); for (const d of run.decisions) items.push({ kind: "decision", ts: d.ts, data: d }); items.sort((a, b) => a.ts - b.ts); return items; } function renderFlow() { const el = $("#flow"); const run = state.runs.find((r) => r.run_id === state.selectedRunId); $("#run-label").textContent = run ? run.run_id : ""; $("#scrubber").max = 0; $("#scrubber").value = 0; state.flowSteps = []; el.innerHTML = ""; if (!run) { const empty = document.createElement("div"); empty.className = "muted"; empty.textContent = "select a run from the timeline"; el.appendChild(empty); return; } const steps = buildFlowSteps(run); state.flowSteps = steps; $("#scrubber").max = Math.max(0, steps.length - 1); $("#scrubber").value = 0; for (let i = 0; i < steps.length; i++) { if (i > 0) el.appendChild(edge(steps[i - 1].data)); const node = steps[i].kind === "event" ? eventNode(steps[i].data) : decisionNode(steps[i].data); node.dataset.step = i; if (i > state.scrub) node.classList.add("dim"); el.appendChild(node); } } function edge(prev) { const div = document.createElement("div"); div.className = "edge"; if (prev.kind === "decision") { const p = (prev.predicted_probs || {})[prev.selected] || 0; if (p >= 0.6) div.classList.add("hot"); } return div; } function decisionNode(d) { const node = document.createElement("div"); const ok = d.cost ? d.cost.correct : null; node.className = "node" + (d.id === state.selectedDecisionId ? " selected" : ""); if (ok === true) node.classList.add("ok"); if (ok === false) node.classList.add("fail"); const head = document.createElement("div"); head.className = "node-head"; head.appendChild(pill(d.phase)); const q = document.createElement("div"); q.className = "node-question"; q.textContent = d.question; head.appendChild(q); if (d.label_source === "human") head.appendChild(star()); const id = document.createElement("div"); id.className = "node-id"; id.textContent = d.id; head.appendChild(id); node.appendChild(head); for (const opt of optionProbs(d)) { node.appendChild(optionRow(opt, d)); } const meta = document.createElement("div"); meta.className = "node-meta"; const sel = document.createElement("span"); sel.textContent = `selected: ${d.selected}`; meta.appendChild(sel); if (d.label_source === "human") { const obs = document.createElement("span"); obs.textContent = `human override → ${d.observed_outcome}`; obs.style.color = "var(--human)"; meta.appendChild(obs); } if (d.cost) { const nll = document.createElement("span"); nll.className = "cost-nll"; nll.textContent = `nll ${d.cost.nll.toFixed(3)} ×${d.cost.weight}`; meta.appendChild(nll); } const ex = d.extra || {}; const timing = document.createElement("span"); timing.textContent = ex.total_seconds ? `${ex.total_seconds.toFixed(2)}s` : ""; meta.appendChild(timing); node.appendChild(meta); node.addEventListener("click", () => { state.selectedDecisionId = d.id; state.selectedRunId = (d.extra || {}).run_id; render(); }); return node; } function optionRow(opt, d) { const row = document.createElement("div"); const cls = ["opt-row"]; if (opt.id === d.selected) cls.push("selected"); row.className = cls.join(" "); const label = document.createElement("span"); label.className = "opt-label"; label.textContent = opt.id; label.title = opt.description; row.appendChild(label); const barTrack = document.createElement("span"); barTrack.className = "opt-bar-track"; const bar = document.createElement("span"); bar.className = "opt-bar"; bar.style.width = `${Math.max(opt.p * 100, 1)}%`; barTrack.appendChild(bar); row.appendChild(barTrack); const marks = document.createElement("span"); marks.className = "opt-marks"; if (opt.id === d.observed_outcome && opt.id !== d.selected) marks.appendChild(mark("chk")); row.appendChild(marks); const pct = document.createElement("span"); pct.className = "opt-pct"; pct.textContent = `${(opt.p * 100).toFixed(0)}%`; row.appendChild(pct); return row; } function eventNode(evt) { const node = document.createElement("div"); node.className = "node event-node"; if (evt.kind === "assessed") node.classList.add(evt.success ? "ok" : "fail"); const kind = document.createElement("div"); kind.className = "evt-kind"; kind.textContent = evt.kind; node.appendChild(kind); const body = document.createElement("div"); body.className = "node-question"; if (evt.kind === "assessed") { body.textContent = evt.summary || ""; if (evt.updated_request) { const req = document.createElement("div"); req.className = "muted"; req.textContent = `→ requeued: ${evt.updated_request}`; node.appendChild(req); } } else if (evt.kind === "queued") { body.textContent = `urgency ${evt.label} (weight ${Number(evt.weight || 0).toFixed(2)})`; } else if (evt.kind === "preempted") { body.textContent = `interrupted ${evt.preempted}, requeued with state`; } else if (evt.kind === "dropped") { body.textContent = evt.reason || ""; } else if (evt.kind === "skill_writing") { node.classList.add("writing"); const title = document.createElement("div"); title.className = "skill-title"; title.textContent = evt.skill; body.appendChild(title); const desc = document.createElement("div"); desc.className = "muted"; desc.textContent = evt.description || ""; body.appendChild(desc); const badge = document.createElement("span"); badge.className = "writing-badge"; badge.textContent = "writing skill body…"; node.appendChild(badge); } else if (evt.kind === "skill_created") { const title = document.createElement("div"); title.className = "skill-title"; title.textContent = evt.skill; body.appendChild(title); const desc = document.createElement("div"); desc.className = "muted"; desc.textContent = evt.description || ""; body.appendChild(desc); if (evt.body) { const p = document.createElement("div"); p.className = "muted"; p.textContent = `body: ${evt.body}`; node.appendChild(p); } node.classList.add(evt.written ? "ok" : "stub"); } else { body.textContent = evt.summary || evt.text || ""; } node.appendChild(body); return node; } /* ---------------- inspector ---------------- */ function selectedDecision() { if (!state.selectedDecisionId) return null; for (const run of state.runs) { for (const d of run.decisions) { if (d.id === state.selectedDecisionId) return d; } } return null; } function renderInspector() { const el = $("#inspector"); const d = selectedDecision(); $("#relabel-btn").disabled = !d; if (!d) { el.innerHTML = '