From 84077289f17d51ae3049adcec2e5ad99e814984b Mon Sep 17 00:00:00 2001 From: iris Date: Sun, 21 Jun 2026 21:23:44 +0200 Subject: [PATCH 1/2] fix(dashboard): call.js renderQuestions referenced tabs.js-only helper renderQuestions (call.js) called snapshotOpenDetails()/restoreOpenDetails() which are closure-locals in tabs.js (built on MANAGED_SECTION_IDS) and not in call.js's module scope. On a /dashboard.html (Y3R C4LL tab) refresh this threw 'ReferenceError: snapshotOpenDetails is not defined' and aborted refreshState entirely. Give call.js its own section-scoped snapshot/restore pair operating on the questions-section root (the only section renderQuestions manages), so open
state still survives an SSE re-render without reaching into tabs.js internals. --- frontend/packages/dashboard/src/call.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/frontend/packages/dashboard/src/call.js b/frontend/packages/dashboard/src/call.js index 88ea4588..c0b478ff 100644 --- a/frontend/packages/dashboard/src/call.js +++ b/frontend/packages/dashboard/src/call.js @@ -615,6 +615,27 @@ function buildQuestionLi(q) { return li; } +// Snapshot / restore open `
` state across a re-render, scoped to +// one section root. renderQuestions only manages `#questions-section`, so it +// keeps its own section-local pair rather than reaching into tabs.js's +// `MANAGED_SECTION_IDS`-based helpers (which aren't in this module's scope — +// referencing them threw `ReferenceError: snapshotOpenDetails is not defined` +// and aborted refreshState). Sections that should survive a refresh carry a +// stable `data-restore-key`. +function snapshotOpenDetails(root) { + const open = new Set(); + for (const d of root.querySelectorAll('details[data-restore-key]')) { + if (d.open) open.add(d.dataset.restoreKey); + } + return open; +} +function restoreOpenDetails(root, open) { + if (!open.size) return; + for (const d of root.querySelectorAll('details[data-restore-key]')) { + if (open.has(d.dataset.restoreKey)) d.open = true; + } +} + export function renderQuestions() { const root = $('questions-section'); // #questions-section only lives on /dashboard.html (Y3R C4LL tab); @@ -625,7 +646,7 @@ export function renderQuestions() { // any expanded sections. The keyed-cache approach reuses question //
  • nodes (preserving textarea/checkbox state) and only rebuilds // cache-miss rows, so we no longer wipe the DOM at the start. - const openDetails = snapshotOpenDetails(); + const openDetails = snapshotOpenDetails(root); const fmt = (n) => new Date(n * 1000).toISOString().replace('T', ' ').slice(0, 19); const allPending = questionsState.pending; @@ -750,7 +771,7 @@ export function renderQuestions() { const histEl = root.querySelector('.q-history'); if (histEl) histEl.open = true; } - restoreOpenDetails(openDetails); + restoreOpenDetails(root, openDetails); } // Format a remaining-seconds count as the `⏳ …` TTL chip text on a From 5eacc99146f5ce79bbbeb77eed1d1190ba37ddf3 Mon Sep 17 00:00:00 2001 From: iris Date: Sun, 21 Jun 2026 21:27:28 +0200 Subject: [PATCH 2/2] =?UTF-8?q?fix(dashboard):=20ctx=C2=B7Nk=20card=20badg?= =?UTF-8?q?e=20showed=200k=20=E2=80=94=20use=20context=5Ftokens()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /api/dashboard-state reported ctx_tokens from raw ctx_usage.input_tokens, which is only the *uncached* input. With prompt caching the bulk of the prompt is cache-read, so input_tokens is ~0 and every SW4RM card showed 'ctx·0k'. Use ctx_usage.context_tokens() (input + cache-read + cache-creation = the real window size) to match the agent page (ships the full ctx_usage) and turn.rs's cache-TTL check. Doc comment updated. --- hive-ag3nt/src/web_ui.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hive-ag3nt/src/web_ui.rs b/hive-ag3nt/src/web_ui.rs index 113da430..f2835c1d 100644 --- a/hive-ag3nt/src/web_ui.rs +++ b/hive-ag3nt/src/web_ui.rs @@ -664,8 +664,9 @@ struct DashboardState { /// `None` when no status is set. #[serde(skip_serializing_if = "Option::is_none")] status_set_at: Option, - /// Input token count from the most recent completed turn (`ctx_usage.input_tokens`). - /// `None` until the first turn finishes. + /// Full context-window size from the most recent completed turn + /// (`ctx_usage.context_tokens()` = input + cache-read + cache-creation). + /// `None` until the first turn finishes. Drives the `ctx·Nk` card badge. #[serde(skip_serializing_if = "Option::is_none")] ctx_tokens: Option, /// Effective context-window budget for the current model. Same @@ -714,7 +715,12 @@ async fn api_dashboard_state(State(state): State) -> axum::Json