chat: durable Postgres event queue + cursor replay on reconnect (Phase 2+3)
Completes the turn/session decoupling so nothing is lost across disconnects. Phase 2 (durability): - New chat_session_events table (global monotonic id = cursor) + queries appendChatEvent / getChatEventsSince / pruneChatEventsOlderThan. - Every durable outbound ServerMessage now goes through emitToSession: appended to the queue (even while the client is disconnected) and delivered live with its seq. Streaming deltas stay ephemeral (live-only, never persisted). Phase 3 (resilient transport): - New 'resume-cursor' client message → handleResumeCursor re-binds the socket to the (still-live) session (cancels idle-GC via attachWs) and replays every event since the client's cursor. - useChatWebSocket already auto-reconnects; added an onOpen hook. useChat tracks the max seq and, on every (re)connect with an established session, sends resume-cursor — so a dropped connection self-heals with no manual navigate away/back, and background task notifications that landed while offline replay. Verified end-to-end: disconnect after a turn's result but before a background task finishes, reconnect with the cursor → the missed task:notification is replayed from Postgres, no duplicates. Note: the DB is managed via drizzle push/direct DDL (no __drizzle_migrations table), so 0001 was applied directly; the generated migration is committed for the record. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,13 @@
|
||||
"when": 1785018112373,
|
||||
"tag": "0000_eager_sir_ram",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "7",
|
||||
"when": 1785110579165,
|
||||
"tag": "0001_omniscient_photon",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user