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
+35 -1
View File
@@ -198,16 +198,50 @@ function evaluateCanHandle({
return { ok: true, reason: null };
}
export function isPortalDirectChatSnapshot(snapshot) {
export function isPortalDirectChatSnapshot(snapshot, { sessionId = null } = {}) {
if (!snapshot) return false;
const messages = Array.isArray(snapshot?.messages) ? snapshot.messages : [];
const lastAssistant = [...messages].reverse().find((message) => message?.role === 'assistant');
if (lastAssistant?.metadata?.source !== 'portal-direct-chat') return false;
// Mixed agent sessions: a user turn after the portal-direct-chat assistant means
// execution moved to goosed — never replay the stale direct-chat snapshot.
if (sessionId && !isDirectChatSessionId(sessionId)) {
const lastAssistantIndex = messages.indexOf(lastAssistant);
const hasUserTurnAfterDirectChat = messages
.slice(lastAssistantIndex + 1)
.some((message) => message?.role === 'user');
if (hasUserTurnAfterDirectChat) return false;
}
const updatedAtRaw = snapshot?.session?.updated_at ?? snapshot?.meta?.saved_at ?? 0;
const updatedAt = Date.parse(String(updatedAtRaw)) || Number(updatedAtRaw) || 0;
if (!updatedAt) return true;
return Date.now() - updatedAt <= 5 * 60 * 1000;
}
export async function shouldExpirePortalDirectChatSnapshot(pool, sessionId, snapshot) {
if (!pool || !sessionId || !snapshot) return false;
if (isDirectChatSessionId(sessionId)) return false;
if (!isPortalDirectChatSnapshot(snapshot, { sessionId })) return false;
const syncedAt = Number(snapshot?.meta?.synced_at ?? 0);
if (!syncedAt) return false;
const [rows] = await pool.query(
`SELECT r.id
FROM h5_agent_runs r
WHERE r.agent_session_id = ?
AND r.status = 'succeeded'
AND r.completed_at > ?
AND NOT EXISTS (
SELECT 1
FROM h5_agent_run_events e
WHERE e.run_id = r.id
AND e.event_type = 'direct_chat_completed'
)
LIMIT 1`,
[sessionId, syncedAt],
);
return rows.length > 0;
}
function normalizeUsageForBilling(usage, previousState) {
if (!usage) return null;
const input = Math.max(0, Number(usage.inputTokens ?? 0) || 0);