const DEBOUNCE_MS = 420; const SILENCE_MS = 500; const SILENCE_TOKEN = "<|silence|>"; const BOUNDARY_PATTERN = /[\s.,!?;:)\]}"]$/; const MODE_BASELINE = "baseline"; const MODE_INTERACTIVE_BASE = "interactive-base"; const MODE_INTERACTIVE_RL = "interactive-rl"; const MODES = [MODE_BASELINE, MODE_INTERACTIVE_BASE, MODE_INTERACTIVE_RL]; const authView = document.querySelector("#authView"); const appView = document.querySelector("#appView"); const authForm = document.querySelector("#authForm"); const authError = document.querySelector("#authError"); const accessCode = document.querySelector("#accessCode"); const modeButtons = Array.from(document.querySelectorAll("[data-mode]")); const historyView = document.querySelector("#history"); const composer = document.querySelector("#composer"); const typedText = document.querySelector("#typedText"); const predictedText = document.querySelector("#predictedText"); const thinking = document.querySelector("#thinking"); const assistantResponse = document.querySelector("#assistantResponse"); const latencyAverage = document.querySelector("#latencyAverage"); const accuracyAverage = document.querySelector("#accuracyAverage"); const callList = document.querySelector("#callList"); const clearCalls = document.querySelector("#clearCalls"); const pageTabs = Array.from(document.querySelectorAll("[data-tab]")); const tabPanels = Array.from(document.querySelectorAll("[data-panel]")); const state = { mode: MODE_INTERACTIVE_RL, activeTab: "overview", history: [], typed: "", predictionAnchor: null, predictionModelUserText: "", predictedUserCompletion: "", remainingUserCompletion: "", assistantText: "", errorText: "", hasPrediction: false, predictionComplete: false, predictionTurnDone: false, inFlight: false, controller: null, requestSeq: 0, debounceTimer: null, silenceTimer: null, silenceAnchor: "", silenceTokenCount: 0, silenceStartedAt: null, activeCallNode: null, locked: false, lockedSeq: null, pendingCommitSeq: null, pendingCommitText: "", pendingCommitModelText: "", pendingLatency: null, latencyMeasurements: [], measuredLatencySeqs: new Set(), interactiveInferenceCount: 0, predictionAccuracies: [], }; async function boot() { const response = await fetch("/api/session", { credentials: "same-origin" }); const session = await response.json(); if (session.authenticated) { showApp(); } else { authView.classList.remove("hidden"); } } authForm.addEventListener("submit", async (event) => { event.preventDefault(); authError.textContent = ""; const response = await fetch("/api/auth", { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ code: accessCode.value }), }); const payload = await response.json(); if (!response.ok) { authError.textContent = payload.error || "Could not sign in."; return; } accessCode.value = ""; showApp(); }); composer.addEventListener("keydown", (event) => { if (event.key !== "Enter" || event.isComposing) { return; } event.preventDefault(); void commitCurrentTurn("enter"); }); for (const button of modeButtons) { button.addEventListener("click", () => { setMode(button.dataset.mode); }); } for (const tab of pageTabs) { tab.addEventListener("click", () => { setActiveTab(tab.dataset.tab); }); } composer.addEventListener("input", () => { if (state.locked) { composer.value = state.typed; return; } state.typed = composer.value; resizeComposer(); if (state.mode === MODE_BASELINE) { clearTimeout(state.debounceTimer); state.debounceTimer = null; stopSilenceLoop(); clearPredictionState(); render(); return; } const contradictedTurnDone = state.hasPrediction && state.predictionAnchor !== null && normalizeInline(state.predictedUserCompletion) === "" && state.typed.startsWith(state.predictionAnchor) && state.typed.length > state.predictionAnchor.length; if (reconcilePrediction()) { if (state.predictionTurnDone) { queueCommitFromPrediction("accepted", performance.now()); } render(); return; } invalidatePrediction(); if (!state.typed.trim()) { resetCurrentInteractiveMetrics(); stopSilenceLoop(); render(); return; } const immediate = contradictedTurnDone || BOUNDARY_PATTERN.test(state.typed); if (immediate) { startSilenceLoop(state.typed); } else { stopSilenceLoop(); } scheduleInference( contradictedTurnDone ? "continued" : immediate ? "boundary" : "pause", immediate ? 0 : DEBOUNCE_MS, ); render(); }); clearCalls.addEventListener("click", () => { callList.replaceChildren(); }); function showApp() { authView.classList.add("hidden"); appView.classList.remove("hidden"); setActiveTab(state.activeTab); render(); } function setActiveTab(tabName) { if (!tabPanels.some((panel) => panel.dataset.panel === tabName)) { return; } state.activeTab = tabName; for (const tab of pageTabs) { const active = tab.dataset.tab === tabName; tab.classList.toggle("active", active); tab.setAttribute("aria-selected", String(active)); } for (const panel of tabPanels) { panel.classList.toggle("hidden", panel.dataset.panel !== tabName); } if (tabName !== "demo") { stopSilenceLoop(); } if (tabName === "demo") { composer.focus(); } } function setMode(mode) { if ( state.locked || !MODES.includes(mode) || mode === state.mode ) { return; } invalidatePrediction(); resetCurrentInteractiveMetrics(); stopSilenceLoop(); state.mode = mode; state.typed = composer.value; state.assistantText = ""; state.errorText = ""; render(); composer.focus(); if (isInteractiveMode(state.mode) && state.typed.trim()) { if (BOUNDARY_PATTERN.test(state.typed)) { startSilenceLoop(state.typed); } scheduleInference("mode", 0); } } function reconcilePrediction() { if (state.predictionAnchor === null || !state.hasPrediction) { return false; } if (state.typed === state.predictionAnchor) { state.remainingUserCompletion = state.predictedUserCompletion; state.predictionTurnDone = normalizeInline(state.remainingUserCompletion) === ""; return true; } if (!state.typed.startsWith(state.predictionAnchor)) { return false; } const consumed = state.typed.slice(state.predictionAnchor.length); if (!state.predictedUserCompletion.startsWith(consumed)) { return false; } state.remainingUserCompletion = state.predictedUserCompletion.slice(consumed.length); state.predictionTurnDone = normalizeInline(state.remainingUserCompletion) === ""; return true; } function invalidatePrediction() { state.requestSeq += 1; if (state.controller) { state.controller.abort(); markCall(state.activeCallNode, "canceled", "canceled"); } clearTimeout(state.debounceTimer); state.debounceTimer = null; stopSilenceLoop(); state.inFlight = false; state.controller = null; state.activeCallNode = null; state.predictionAnchor = null; state.predictionModelUserText = ""; state.predictedUserCompletion = ""; state.remainingUserCompletion = ""; state.assistantText = ""; state.errorText = ""; state.hasPrediction = false; state.predictionComplete = false; state.predictionTurnDone = false; state.pendingCommitSeq = null; state.pendingCommitText = ""; state.pendingCommitModelText = ""; } function scheduleInference(reason, delayMs) { clearTimeout(state.debounceTimer); state.debounceTimer = window.setTimeout(() => { void runInference(reason); }, delayMs); } function startSilenceLoop(anchorText, delayMs = SILENCE_MS) { if (!isInteractiveMode(state.mode) || !anchorText.trim()) { stopSilenceLoop(); return; } if (!BOUNDARY_PATTERN.test(anchorText)) { stopSilenceLoop(); return; } if (state.silenceAnchor !== anchorText) { state.silenceAnchor = anchorText; state.silenceTokenCount = 0; state.silenceStartedAt = performance.now(); } clearTimeout(state.silenceTimer); state.silenceTimer = window.setTimeout(() => { if ( state.locked || !isInteractiveMode(state.mode) || composer.value !== state.silenceAnchor || !BOUNDARY_PATTERN.test(composer.value) ) { stopSilenceLoop(); return; } if (state.inFlight) { startSilenceLoop(state.silenceAnchor, 100); return; } const elapsedSilenceMs = performance.now() - (state.silenceStartedAt ?? performance.now()); state.silenceTokenCount = Math.max( state.silenceTokenCount + 1, Math.floor(elapsedSilenceMs / SILENCE_MS), ); void runInference(`silence x${state.silenceTokenCount}`, { userText: state.silenceAnchor, requestText: withSilenceTokens( state.silenceAnchor, state.silenceTokenCount, ), }); startSilenceLoop(state.silenceAnchor); }, delayMs); } function stopSilenceLoop() { clearTimeout(state.silenceTimer); state.silenceTimer = null; state.silenceAnchor = ""; state.silenceTokenCount = 0; state.silenceStartedAt = null; } function withSilenceTokens(text, count) { if (count <= 0) { return text; } return `${text}${" "}${Array(count).fill(SILENCE_TOKEN).join(" ")}`; } async function runInference(reason, options = {}) { const mode = options.mode ?? state.mode; const userText = options.userText ?? composer.value; const requestText = options.requestText ?? userText; const commitAfterResult = Boolean(options.commitAfterResult); const lockDuringRequest = Boolean(options.lockDuringRequest); const turnComplete = Boolean(options.turnComplete); const latencyStartedAt = options.latencyStartedAt; const latencySource = options.latencySource ?? reason; if (!requestText.trim()) { return null; } state.requestSeq += 1; const seq = state.requestSeq; if (state.controller) { state.controller.abort(); markCall(state.activeCallNode, "canceled", "canceled"); } const controller = new AbortController(); state.controller = controller; state.inFlight = true; if (isInteractiveMode(mode)) { state.interactiveInferenceCount += 1; } state.activeCallNode = addCall({ seq, reason: interactiveCallReason(mode, reason), text: userText, }); if (typeof latencyStartedAt === "number") { beginLatencyMeasurement(seq, latencySource, latencyStartedAt); } if (lockDuringRequest) { lockComposer(seq); } render(); let finalPrediction = null; let committed = false; try { const response = await fetch("/api/predict", { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ typed: requestText, history: state.history, turn_complete: turnComplete, model_variant: predictionVariantForMode(mode), }), signal: controller.signal, }); if (response.status === 401) { authView.classList.remove("hidden"); appView.classList.add("hidden"); return null; } if (!response.ok || !response.body) { throw new Error(`Prediction failed with HTTP ${response.status}`); } await readEventStream(response.body, (event, data) => { if (seq !== state.requestSeq) { return; } if (event === "meta") { updateCallMeta(state.activeCallNode, data); } if (event === "parsed" || event === "result") { const eventTime = performance.now(); const applied = applyPrediction(userText, data, requestText); if (event === "result") { finalPrediction = data; } if ( applied && !commitAfterResult && data.user_completion_closed && state.predictionTurnDone ) { queuePendingCommit( seq, composer.value, "predicted", eventTime, requestText, ); } observeAssistantFirstToken(seq, eventTime); } if (event === "error") { throw new Error(data.error || "Prediction failed."); } }); if (seq === state.requestSeq) { markCall(state.activeCallNode, "done", "done"); } if (seq === state.requestSeq && finalPrediction) { const pendingText = state.pendingCommitSeq === seq ? state.pendingCommitText : ""; const pendingModelText = state.pendingCommitSeq === seq ? state.pendingCommitModelText : ""; const shouldCommit = commitAfterResult || Boolean(pendingText); if (shouldCommit) { committed = commitPrediction( pendingText || userText, finalPrediction, pendingModelText || requestText, ); } } return finalPrediction; } catch (error) { if (error.name === "AbortError") { return null; } markCall(state.activeCallNode, "error", "error"); state.errorText = error.message || "Prediction failed."; clearPendingLatency(seq); return null; } finally { if (seq === state.requestSeq) { state.inFlight = false; state.controller = null; state.activeCallNode = null; if (!committed && state.lockedSeq === seq) { unlockComposer(seq); } render(); } } } function applyPrediction(anchor, parsed, modelUserText = anchor) { const currentText = composer.value; const predictedCompletion = removeEchoedUserPrefix( currentText, parsed.user_completion || "", ); let remainingCompletion = predictedCompletion; const isSilenceRefresh = modelUserText.includes(SILENCE_TOKEN) && state.hasPrediction && state.predictionAnchor === anchor; if (currentText !== anchor) { if (!currentText.startsWith(anchor)) { return false; } const consumedCompletion = currentText.slice(anchor.length); if (!predictedCompletion.startsWith(consumedCompletion)) { return false; } remainingCompletion = predictedCompletion.slice(consumedCompletion.length); } if ( isSilenceRefresh && !parsed.complete && isRegressiveRefresh( state.remainingUserCompletion, remainingCompletion, state.assistantText, parsed.assistant_response || "", ) ) { return false; } state.predictionAnchor = anchor; state.predictionModelUserText = modelUserText; state.predictedUserCompletion = predictedCompletion; state.remainingUserCompletion = remainingCompletion; state.assistantText = parsed.assistant_response || ""; state.errorText = ""; state.hasPrediction = Boolean(parsed.has_user_completion_tag) || Boolean(parsed.has_assistant_response_tag); state.predictionComplete = Boolean(parsed.complete); state.predictionTurnDone = Boolean(parsed.user_completion_closed) && normalizeInline(state.remainingUserCompletion) === ""; render(); return true; } function removeEchoedUserPrefix(userText, predictedCompletion) { const normalizedUser = normalizeInline(stripSilenceTokens(userText)).toLowerCase(); if (!normalizedUser || !predictedCompletion.trim()) { return predictedCompletion; } let bestEnd = 0; for (let end = 1; end <= predictedCompletion.length; end += 1) { const prefix = normalizeInline(predictedCompletion.slice(0, end)).toLowerCase(); if (prefix && normalizedUser.endsWith(prefix)) { bestEnd = end; } } return bestEnd ? predictedCompletion.slice(bestEnd).trimStart() : predictedCompletion; } function isRegressiveRefresh( previousUserCompletion, nextUserCompletion, previousAssistant, nextAssistant, ) { return ( isRegressiveText(previousUserCompletion, nextUserCompletion) || isRegressiveText(previousAssistant, nextAssistant) ); } function isRegressiveText(previous, next) { if (!previous) { return false; } return previous.startsWith(next) && previous !== next; } async function commitCurrentTurn(reason) { if (state.locked) { return; } const userText = composer.value; if (!userText.trim()) { return; } const turnStoppedAt = performance.now(); state.typed = userText; clearTimeout(state.debounceTimer); state.debounceTimer = null; if (state.mode === MODE_BASELINE) { await commitBaselineTurn(userText, turnStoppedAt); return; } reconcilePrediction(); if ( state.hasPrediction && state.predictionTurnDone && state.inFlight ) { queuePendingCommit(state.requestSeq, userText, reason, turnStoppedAt); return; } if ( state.hasPrediction && state.predictionTurnDone && state.assistantText.trim() ) { beginLatencyMeasurement(state.requestSeq, reason, turnStoppedAt); observeAssistantFirstToken(state.requestSeq, turnStoppedAt); if (state.controller) { state.controller.abort(); markCall(state.activeCallNode, "canceled", "canceled"); } state.requestSeq += 1; commitPrediction( userText, { assistant_response: state.assistantText }, state.predictionModelUserText || userText, ); render(); return; } invalidatePrediction(); state.typed = userText; composer.value = userText; resizeComposer(); await runInference(reason, { userText, requestText: userText, commitAfterResult: true, lockDuringRequest: true, turnComplete: true, latencyStartedAt: turnStoppedAt, latencySource: reason, }); } async function commitBaselineTurn(userText, turnStoppedAt) { invalidatePrediction(); state.typed = userText; composer.value = userText; resizeComposer(); await runBaselineResponse(userText, turnStoppedAt); } async function runBaselineResponse(userText, latencyStartedAt) { state.requestSeq += 1; const seq = state.requestSeq; const controller = new AbortController(); state.controller = controller; state.inFlight = true; state.activeCallNode = addCall({ seq, reason: "non-interactive", text: userText, }); state.assistantText = ""; state.errorText = ""; beginLatencyMeasurement(seq, "baseline", latencyStartedAt); lockComposer(seq); render(); let assistantText = ""; let committed = false; try { const response = await fetch("/api/respond", { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ typed: userText, history: state.history }), signal: controller.signal, }); if (response.status === 401) { authView.classList.remove("hidden"); appView.classList.add("hidden"); return null; } if (!response.ok || !response.body) { throw new Error(`Response failed with HTTP ${response.status}`); } await readEventStream(response.body, (event, data) => { if (seq !== state.requestSeq) { return; } if (event === "meta") { updateCallMeta(state.activeCallNode, data); } if (event === "delta") { assistantText += data.text || ""; state.assistantText = assistantText; observeAssistantFirstToken(seq, performance.now()); render(); } if (event === "result") { assistantText = data.assistant_response || assistantText; state.assistantText = assistantText; observeAssistantFirstToken(seq, performance.now()); render(); } if (event === "error") { throw new Error(data.error || "Response failed."); } }); if (seq === state.requestSeq) { markCall(state.activeCallNode, "done", "done"); committed = commitPrediction(userText, { assistant_response: assistantText }); } return assistantText; } catch (error) { if (error.name === "AbortError") { return null; } markCall(state.activeCallNode, "error", "error"); state.errorText = error.message || "Response failed."; clearPendingLatency(seq); return null; } finally { if (seq === state.requestSeq) { state.inFlight = false; state.controller = null; state.activeCallNode = null; if (!committed && state.lockedSeq === seq) { unlockComposer(seq); } render(); } } } function queueCommitFromPrediction(source = "predicted", startedAt = performance.now()) { if ( !state.hasPrediction || !state.predictionTurnDone || !state.typed.trim() ) { return false; } if (state.inFlight) { queuePendingCommit( state.requestSeq, state.typed, source, startedAt, state.predictionModelUserText || state.typed, ); return true; } if (state.assistantText.trim()) { beginLatencyMeasurement(state.requestSeq, source, startedAt); observeAssistantFirstToken(state.requestSeq, startedAt); commitPrediction( state.typed, { assistant_response: state.assistantText }, state.predictionModelUserText || state.typed, ); return true; } return false; } function queuePendingCommit( seq, userText, source = "predicted", startedAt = performance.now(), modelUserText = userText, ) { if (!userText.trim()) { return; } state.pendingCommitSeq = seq; state.pendingCommitText = userText; state.pendingCommitModelText = modelUserText; beginLatencyMeasurement(seq, source, startedAt); observeAssistantFirstToken(seq, startedAt); lockComposer(seq); render(); } function commitPrediction(userText, prediction, modelUserText = userText) { const committedModelUser = modelUserText.trimEnd(); const committedAssistant = (prediction.assistant_response || "").trim(); if (!committedModelUser.trim()) { return false; } if (!committedAssistant) { state.errorText = "Assistant prediction was empty."; return false; } observeAssistantFirstToken(null, performance.now()); if (isInteractiveMode(state.mode)) { recordPredictionAccuracy(state.interactiveInferenceCount); } state.history.push({ role: "user", content: committedModelUser }); state.history.push({ role: "assistant", content: committedAssistant }); state.history = state.history.slice(-24); state.typed = ""; composer.value = ""; resizeComposer(); stopSilenceLoop(); clearPredictionState(); resetCurrentInteractiveMetrics(); unlockComposer(); renderHistory(); return true; } function clearPredictionState() { state.predictionAnchor = null; state.predictionModelUserText = ""; state.predictedUserCompletion = ""; state.remainingUserCompletion = ""; state.assistantText = ""; state.errorText = ""; state.hasPrediction = false; state.predictionComplete = false; state.predictionTurnDone = false; state.pendingCommitSeq = null; state.pendingCommitText = ""; state.pendingCommitModelText = ""; } function lockComposer(seq) { state.locked = true; state.lockedSeq = seq; } function unlockComposer(seq = null) { if (seq !== null && state.lockedSeq !== seq) { return; } state.locked = false; state.lockedSeq = null; state.pendingCommitSeq = null; state.pendingCommitText = ""; state.pendingCommitModelText = ""; window.requestAnimationFrame(() => { if (!state.locked && !appView.classList.contains("hidden")) { composer.focus(); } }); } function beginLatencyMeasurement(seq, source, startedAt) { if (state.measuredLatencySeqs.has(seq)) { return; } if ( state.pendingLatency && state.pendingLatency.seq === seq && !state.pendingLatency.recorded ) { return; } state.pendingLatency = { seq, source, startedAt, recorded: false, }; } function observeAssistantFirstToken(seq = null, observedAt = performance.now()) { if (!state.pendingLatency || state.pendingLatency.recorded) { return false; } if (seq !== null && state.pendingLatency.seq !== seq) { return false; } if (!state.assistantText.trim()) { return false; } const latencyMs = Math.max( 0, Math.round(observedAt - state.pendingLatency.startedAt), ); state.latencyMeasurements.push({ latencyMs, source: state.pendingLatency.source, }); state.latencyMeasurements = state.latencyMeasurements.slice(-100); state.measuredLatencySeqs.add(state.pendingLatency.seq); state.pendingLatency.recorded = true; state.pendingLatency = null; renderLatencyAverage(); return true; } function clearPendingLatency(seq) { if (!state.pendingLatency) { return; } if (seq === null || state.pendingLatency.seq === seq) { state.pendingLatency = null; } } function renderLatencyAverage() { if (!latencyAverage) { return; } if (!state.latencyMeasurements.length) { latencyAverage.textContent = "avg ttft --"; return; } const total = state.latencyMeasurements.reduce( (sum, measurement) => sum + measurement.latencyMs, 0, ); const average = total / state.latencyMeasurements.length; latencyAverage.textContent = `avg ttft ${formatLatency(average)} (${state.latencyMeasurements.length})`; } function recordPredictionAccuracy(inferenceCount) { const score = predictionAccuracyScore(inferenceCount); if (score === null) { return; } state.predictionAccuracies.push(score); state.predictionAccuracies = state.predictionAccuracies.slice(-100); renderAccuracyAverage(); } function predictionAccuracyScore(inferenceCount) { if (!inferenceCount) { return null; } return 1 / inferenceCount; } function resetCurrentInteractiveMetrics() { state.interactiveInferenceCount = 0; } function renderAccuracyAverage() { if (!accuracyAverage) { return; } accuracyAverage.classList.toggle("hidden", state.mode === MODE_BASELINE); if (!state.predictionAccuracies.length) { accuracyAverage.textContent = "avg accuracy --"; return; } const total = state.predictionAccuracies.reduce( (sum, score) => sum + score, 0, ); const average = total / state.predictionAccuracies.length; accuracyAverage.textContent = `avg accuracy ${formatPercent(average)} (${state.predictionAccuracies.length})`; } function formatPercent(value) { return `${Math.round(value * 100)}%`; } function formatLatency(milliseconds) { if (milliseconds < 1000) { return `${Math.round(milliseconds)} ms`; } if (milliseconds < 10_000) { return `${(milliseconds / 1000).toFixed(1)} s`; } return `${Math.round(milliseconds / 1000)} s`; } function normalizeInline(text) { return text.replace(/\s+/g, " ").trim(); } function stripSilenceTokens(text) { return text.replaceAll(SILENCE_TOKEN, "").replace(/\s+/g, " ").trimStart(); } function isInteractiveMode(mode) { return mode === MODE_INTERACTIVE_BASE || mode === MODE_INTERACTIVE_RL; } function predictionVariantForMode(mode) { return mode === MODE_INTERACTIVE_RL ? "rl" : "base"; } function interactiveCallReason(mode, reason) { const prefix = mode === MODE_INTERACTIVE_RL ? "interactive-rl" : "interactive"; return `${prefix} ${reason}`; } async function readEventStream(body, onEvent) { const reader = body.getReader(); const decoder = new TextDecoder(); let buffer = ""; while (true) { const { value, done } = await reader.read(); if (done) { break; } buffer += decoder.decode(value, { stream: true }); const frames = buffer.split("\n\n"); buffer = frames.pop() || ""; for (const frame of frames) { const parsed = parseSseFrame(frame); if (parsed) { onEvent(parsed.event, parsed.data); } } } if (buffer.trim()) { const parsed = parseSseFrame(buffer); if (parsed) { onEvent(parsed.event, parsed.data); } } } function parseSseFrame(frame) { const lines = frame.split(/\r?\n/); let event = "message"; const dataLines = []; for (const line of lines) { if (line.startsWith("event:")) { event = line.slice(6).trim(); } if (line.startsWith("data:")) { dataLines.push(line.slice(5).trimStart()); } } if (!dataLines.length) { return null; } return { event, data: JSON.parse(dataLines.join("\n")) }; } function render() { renderMode(); renderHistory(); renderLatencyAverage(); renderAccuracyAverage(); composer.disabled = state.locked; appView.classList.toggle("locked", state.locked); typedText.textContent = state.typed; predictedText.textContent = isInteractiveMode(state.mode) ? stripSilenceTokens(state.remainingUserCompletion) : ""; const isThinking = state.inFlight && !state.assistantText.trim() && (state.mode === MODE_BASELINE || !normalizeInline(state.remainingUserCompletion)); thinking.classList.toggle("active", isThinking); thinking.innerHTML = isThinking ? "..." : ""; assistantResponse.textContent = state.errorText || state.assistantText; assistantResponse.className = "assistant-response"; if (state.errorText) { assistantResponse.classList.add("error"); return; } if ( (isInteractiveMode(state.mode) && state.hasPrediction && state.predictionTurnDone) || (state.mode === MODE_BASELINE && state.assistantText.trim()) ) { assistantResponse.classList.add("ready"); } } function renderMode() { for (const button of modeButtons) { const active = button.dataset.mode === state.mode; button.classList.toggle("active", active); button.setAttribute("aria-pressed", String(active)); button.disabled = state.locked; } } function renderHistory() { historyView.replaceChildren(); historyView.classList.toggle("empty", state.history.length === 0); for (const message of state.history) { const row = document.createElement("div"); row.className = `history-line ${message.role}`; const role = document.createElement("span"); role.className = "history-role"; role.textContent = message.role === "user" ? "user" : "assistant"; const content = document.createElement("div"); content.className = "history-content"; content.textContent = message.role === "user" ? stripSilenceTokens(message.content) : message.content; row.append(role, content); historyView.append(row); } } function resizeComposer() { composer.style.height = "auto"; composer.style.height = `${Math.max(78, composer.scrollHeight)}px`; } function addCall({ seq, reason, text }) { const node = document.createElement("div"); node.className = "call-item running"; node.dataset.seq = String(seq); node.dataset.status = "starting"; const prompt = document.createElement("span"); prompt.className = "call-prompt"; prompt.textContent = ">"; const label = document.createElement("span"); label.className = "call-label"; label.textContent = `#${seq} ${reason}`; const meta = document.createElement("div"); meta.className = "call-meta"; meta.textContent = "starting"; const separator = document.createElement("span"); separator.className = "call-separator"; separator.textContent = "::"; const preview = document.createElement("div"); preview.className = "call-preview"; preview.textContent = stripSilenceTokens(text); node.append(prompt, label, meta, separator, preview); callList.prepend(node); return node; } function updateCallMeta(node, data) { if (!node) { return; } const meta = node.querySelector(".call-meta"); const model = data.model || "model"; const chars = data.chars ?? 0; const turns = data.history_turns ?? 0; node.dataset.model = model; node.dataset.chars = String(chars); node.dataset.turns = String(turns); node.dataset.status = "running"; meta.textContent = `${model} ${chars}c ${turns}t running`; } function markCall(node, className, label) { if (!node) { return; } node.classList.remove("running", "done", "error", "canceled"); node.classList.add(className); node.dataset.status = label; const meta = node.querySelector(".call-meta"); if (meta) { const model = node.dataset.model || "model"; const chars = node.dataset.chars || "?"; const turns = node.dataset.turns || "0"; meta.textContent = `${model} ${chars}c ${turns}t ${label}`; } } void boot();