diff --git a/src/viewer/index.html b/src/viewer/index.html
index a4bcd4389..5cfdccb04 100644
--- a/src/viewer/index.html
+++ b/src/viewer/index.html
@@ -1468,14 +1468,18 @@
agentmemory
}
var dashboardLoadPromise = null;
+ var dashboardRefreshPending = false;
async function loadDashboard() {
if (dashboardLoadPromise) return dashboardLoadPromise;
- dashboardLoadPromise = loadDashboardOnce();
- try {
- return await dashboardLoadPromise;
- } finally {
- dashboardLoadPromise = null;
- }
+ do {
+ dashboardRefreshPending = false;
+ dashboardLoadPromise = loadDashboardOnce();
+ try {
+ await dashboardLoadPromise;
+ } finally {
+ dashboardLoadPromise = null;
+ }
+ } while (dashboardRefreshPending);
}
async function loadDashboardOnce() {
@@ -1793,7 +1797,8 @@ agentmemory
var dashboardTimer = null;
function refreshDashboard() {
state.dashboard.loaded = false;
- loadDashboard();
+ if (dashboardLoadPromise) dashboardRefreshPending = true;
+ return loadDashboard();
}
function startDashboardAutoRefresh() {
if (dashboardTimer) clearInterval(dashboardTimer);
@@ -3810,8 +3815,7 @@ agentmemory
pollTimer = setInterval(function() {
tick++;
if (state.activeTab === 'dashboard') {
- state.dashboard.loaded = false;
- loadDashboard();
+ refreshDashboard();
} else if (state.activeTab === 'memories') {
state.memories.loaded = false;
loadMemories();
@@ -3975,8 +3979,7 @@ agentmemory
}
}
if (state.activeTab === 'dashboard') {
- state.dashboard.loaded = false;
- loadDashboard();
+ refreshDashboard();
}
if (state.activeTab === 'activity' && msg.observation) {
state.activity.observations.unshift(msg.observation);
diff --git a/test/viewer-session-id.test.ts b/test/viewer-session-id.test.ts
index c4a6a90c3..6c2f88834 100644
--- a/test/viewer-session-id.test.ts
+++ b/test/viewer-session-id.test.ts
@@ -208,6 +208,29 @@ describe("viewer session rendering", () => {
]);
});
+ it("runs one pending refresh after an in-flight dashboard load", async () => {
+ const { sandbox } = loadViewerSandbox();
+ const paths: string[] = [];
+ let releaseSessions: (() => void) | undefined;
+ sandbox.fetch = async (url: string) => {
+ paths.push(new URL(url).pathname);
+ if (paths.length === 2) {
+ await new Promise((resolve) => {
+ releaseSessions = resolve;
+ });
+ }
+ return { ok: true, json: async () => ({}) };
+ };
+
+ const initialLoad = sandbox.loadDashboard();
+ while (!releaseSessions) await Promise.resolve();
+ const pendingRefresh = sandbox.refreshDashboard();
+ releaseSessions();
+ await Promise.all([initialLoad, pendingRefresh]);
+
+ expect(paths).toHaveLength(20);
+ });
+
it("attaches the saved viewer bearer to API calls", async () => {
const { sandbox } = loadViewerSandbox();
const requests: Array<{ url: string; opts: { headers?: Record } }> = [];