fix(socket): avoid replaying resumed thread on reconnect

NI
1mo ago
+355-27

Summary

Avoid replaying a persisted thread when a live WebSocket session reconnects, while still completing an initial restore that was pending when the first socket disconnected.

Serialize resume processing per session. Cache the completed server restore so a failed client notification can be retried without rerunning on_chat_resume or adding historical messages again. Clear the pending marker after successful delivery or a terminal not-found result. Existing on_chat_start behavior is preserved.

Regression coverage includes reconnecting before the first confirmation event, propagated callback failures, notification failure, concurrent reconnects, and avoiding repeated terminal errors.

Fixes #3061

Verification

  • pytest backend/tests/test_socket.py backend/tests/test_session.py backend/tests/test_context.py backend/tests/test_emitter.py -q -p no:cacheprovider: 107 passed (one existing Pydantic deprecation warning).
  • Ruff lint and format checks on the four changed files: passed.
  • git diff --check: passed.
  • Full pytest -q collection is blocked by eight import errors for optional storage and integration dependencies (google.auth, boto3, asyncpg, sqlalchemy, langchain_core, and llama_index).

Prepared with Codex assistance.

Review follow-up

Keep completed server initialization pending when Socket.IO observes a disconnect before or during resume notification, or when the target SID changes while the emit is suspended. A connected retry receives the cached thread and interaction metadata without rerunning the resume callback or adding historical messages again.

Regression tests reproduce the silent no-op delivery (not just a raised exception), SID replacement during delivery, and an interaction marker lost across reconnection while the callback is running. Related socket/session/context/emitter tests: 107 passed on Python 3.13, with one existing Pydantic deprecation warning. Ruff lint and format checks and diff whitespace checks passed. Socket.IO connection state detects server-observed disconnects; this does not introduce a browser acknowledgement protocol.


Summary by cubic

Fixes socket reconnects so a live session no longer replays a resumed thread's messages into the chat context. on_chat_resume and persisted message loading now run at most once per session.

  • Reconnecting the same session skips on_chat_resume and re-adding persisted messages.
  • A pending resume (for example, when the first socket disconnects before an interrupted on_chat_resume completes) is finished on reconnect before the session is treated as live.
  • Resume delivery is retained across silent disconnects: initialized thread state stays pending until the receiving Socket.IO session is confirmed connected, and interaction metadata is re-emitted on the new socket.
  • Concurrent reconnects wait for the in-progress resume instead of replaying it.
  • Missing-thread errors are reported only once per session.
  • on_chat_start still runs for restored sessions without a first interaction.
  • Adds regression tests for resume-once, pending-reconnect, concurrent-handler, and missing-thread cases.

Fixes #3061.