fix(mindspace): use lightweight page sync for background list refresh

Background page list sync was calling syncAndDeliver over the remote adapter,
which could exceed the 60s timeout for heavy users and block Portal. Route list
sync through pageSyncService only via listSync while keeping full delivery on
Finish and agent-run paths.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-09-16 08:53:58 +08:00
parent f9e105acd0
commit 264c213fbe
4 changed files with 26 additions and 10 deletions
+12 -1
View File
@@ -20,7 +20,18 @@ const contractSource = read('mindspace-server-adapter-contract.mjs');
assertIncludes(
'server.mjs syncUserGeneratedPages',
serverSource,
'await mindSpacePageSync.syncUserGeneratedPages(userId);',
'mindSpacePageSync.syncUserGeneratedPages(userId',
);
assertIncludes(
'server.mjs list page sync bypasses full delivery',
serverSource,
'listSync = false',
);
const pageCoreRoutesSource = read('server/portal-mindspace-page-core-routes.mjs');
assertIncludes(
'portal page list background sync',
pageCoreRoutesSource,
'syncUserGeneratedPages(userId, { listSync: true })',
);
if (serverSource.includes("mindSpaceServerRuntime.adapterKind === 'remote'")) {
throw new Error(
+5 -3
View File
@@ -1292,7 +1292,7 @@ async function listSessionPublicHtmlRelativePaths(userId, sessionId, { sinceMs =
async function syncUserGeneratedPages(
userId,
{ sessionId = null, sinceMs = null, onlyRelativePaths = null } = {},
{ sessionId = null, sinceMs = null, onlyRelativePaths = null, listSync = false } = {},
) {
if (!userId) return;
// Agent-run/Finish delivery must stay scoped to the current conversation.
@@ -1330,11 +1330,13 @@ async function syncUserGeneratedPages(
: sessionId
? (discoveredRelativePaths?.length ? discoveredRelativePaths : recentWorkspaceRelativePaths)
: null;
if (workspacePageDeliver?.syncAndDeliver) {
if (!listSync && workspacePageDeliver?.syncAndDeliver) {
return await workspacePageDeliver.syncAndDeliver(userId, { pageDataRelativePaths });
}
if (!mindSpacePageSync) return;
return await mindSpacePageSync.syncUserGeneratedPages(userId);
return await mindSpacePageSync.syncUserGeneratedPages(userId, {
onlyRelativePaths: pageDataRelativePaths,
});
}
async function resolveChatSaveBundle(user, h5Root, input = {}) {
+1 -1
View File
@@ -46,7 +46,7 @@ export function createMindSpacePageListSyncScheduler({
lastStartedAtByUser.set(userId, currentTime);
const task = Promise.resolve()
.then(() => syncUserGeneratedPages(userId))
.then(() => syncUserGeneratedPages(userId, { listSync: true }))
.catch((error) => {
logger?.warn?.(
`[MindSpace] background page list sync failed for user ${userId}:`,
@@ -230,8 +230,8 @@ test('page list background sync coalesces in-flight and throttled requests', asy
const scheduler = createMindSpacePageListSyncScheduler({
now: () => currentTime,
minIntervalMs: 30_000,
syncUserGeneratedPages(userId) {
calls.push(userId);
syncUserGeneratedPages(userId, options) {
calls.push({ userId, options });
return new Promise((resolve) => {
resolvers.push(resolve);
});
@@ -242,19 +242,22 @@ test('page list background sync coalesces in-flight and throttled requests', asy
const second = scheduler('user-1');
assert.equal(first, second);
await Promise.resolve();
assert.deepEqual(calls, ['user-1']);
assert.deepEqual(calls, [{ userId: 'user-1', options: { listSync: true } }]);
resolvers.shift()?.({ created: 0, updated: 0, skipped: 0 });
await first;
currentTime += 1_000;
assert.equal(scheduler('user-1'), null);
assert.deepEqual(calls, ['user-1']);
assert.deepEqual(calls, [{ userId: 'user-1', options: { listSync: true } }]);
currentTime += 30_000;
const third = scheduler('user-1');
await Promise.resolve();
assert.deepEqual(calls, ['user-1', 'user-1']);
assert.deepEqual(calls, [
{ userId: 'user-1', options: { listSync: true } },
{ userId: 'user-1', options: { listSync: true } },
]);
resolvers.shift()?.({ created: 0, updated: 0, skipped: 0 });
await third;
});