fix: unblock agent events after portal direct chat escalation

When a session starts with LLM direct chat then escalates to goosed,
invalidate the portal-direct-chat snapshot and stop replaying stale SSE
so page-generation turns stream and finish correctly.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-04 23:15:44 +08:00
parent e45c9300bf
commit a29158d589
5 changed files with 231 additions and 5 deletions
+17 -4
View File
@@ -164,7 +164,7 @@ import { createScheduleService } from './schedule-service.mjs';
import { createFeedbackService } from './user-feedback.mjs';
import { startScheduleReminderWorker } from './schedule-reminder-worker.mjs';
import { createLlmProviderService, RELAY_BOOTSTRAP } from './llm-providers.mjs';
import { createDirectChatService, isDirectChatSessionId, isPortalDirectChatSnapshot, sendDirectChatSessionEvents } from './direct-chat-service.mjs';
import { createDirectChatService, isDirectChatSessionId, isPortalDirectChatSnapshot, sendDirectChatSessionEvents, shouldExpirePortalDirectChatSnapshot } from './direct-chat-service.mjs';
import { createManagedChatIntentRouter } from './chat-intent-router.mjs';
import { createSessionSnapshotService } from './session-snapshot.mjs';
import { createConversationMemoryService } from './conversation-memory.mjs';
@@ -580,6 +580,7 @@ async function bootstrapUserAuth() {
toolGateway,
directChatService,
chatIntentRouter,
sessionSnapshotService,
autoDispatch: ['1', 'true', 'yes', 'on'].includes(
String(process.env.MEMIND_AGENT_RUN_AUTODISPATCH ?? '1').trim().toLowerCase(),
),
@@ -4470,7 +4471,13 @@ api.get('/sessions/:sessionId', async (req, res, next) => {
try {
if (sessionSnapshotService?.isEnabled()) {
const snapshot = await sessionSnapshotService.get(sessionId);
let snapshot = await sessionSnapshotService.get(sessionId);
if (snapshot) {
if (authPool && await shouldExpirePortalDirectChatSnapshot(authPool, sessionId, snapshot)) {
await sessionSnapshotService.remove(sessionId).catch(() => {});
snapshot = null;
}
}
if (snapshot) {
// REGRESSION GUARD: without both hints, stale snapshot can wipe mid-turn chat.
const canUseSnapshotCache = hintMc != null && hintUa != null;
@@ -4478,7 +4485,7 @@ api.get('/sessions/:sessionId', async (req, res, next) => {
const uaMatch = snapshot.meta.source_updated_at === hintUa;
if (
isDirectChatSessionId(sessionId) ||
isPortalDirectChatSnapshot(snapshot) ||
isPortalDirectChatSnapshot(snapshot, { sessionId }) ||
(canUseSnapshotCache && mcMatch && uaMatch)
) {
const sanitizedMessages = sanitizeSessionConversationPublicHtmlLinks(
@@ -4581,7 +4588,13 @@ api.get('/sessions/:sessionId/events', async (req, res, next) => {
return sendDirectChatSessionEvents(req, res, snapshot);
}
const portalDirectSnapshot = await sessionSnapshotService?.get(sessionId).catch(() => null);
if (isPortalDirectChatSnapshot(portalDirectSnapshot)) {
if (
portalDirectSnapshot &&
authPool &&
await shouldExpirePortalDirectChatSnapshot(authPool, sessionId, portalDirectSnapshot)
) {
await sessionSnapshotService?.remove(sessionId).catch(() => {});
} else if (portalDirectSnapshot && isPortalDirectChatSnapshot(portalDirectSnapshot, { sessionId })) {
return sendDirectChatSessionEvents(req, res, portalDirectSnapshot);
}
const publishDir = resolveMindSpaceUserPublishDir(__dirname, { id: req.currentUser.id });