Drain artifact watcher before closing WS on end_session_message#29
Drain artifact watcher before closing WS on end_session_message#29jackcbrown89 merged 2 commits intomainfrom
Conversation
Previously ws.close() fired immediately on end_session_message and the 3s chokidar drain ran in the close handler. Late artifact_upload_request messages produced during the drain were silently dropped because send() is gated on readyState === OPEN. Move the drain into a memoized helper invoked before close() on the orderly path; the close handler still drains for unexpected disconnects.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 79977a7. Configure here.
| this.logger.log("Received end_session_message. Closing session."); | ||
| // Drain the artifact watcher *before* closing so any late chokidar | ||
| // events still have an open socket to send upload requests through. | ||
| await this.drain(); |
There was a problem hiding this comment.
Drain failure prevents ws.close() and caches rejected promise
Low Severity
If drain() rejects (e.g., stopWatching() / chokidar watcher.close() throws), ws.close() on the next line is never reached, leaving the WebSocket open. Worse, the memoized drainPromise caches the rejection permanently, so when the close handler later calls await this.drain(), it re-throws — resolve() is never called and run() hangs forever. Wrapping ws.close() in a finally (or catching drain errors) would ensure the socket is always closed. In the old code, ws.close() was called unconditionally before any drain logic.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 79977a7. Configure here.


Summary
end_session_message,ws.close()was called immediately and the 3s chokidar drain + pending-request wait ran in the close handler. Anyartifact_upload_request_messageproduced by a late chokidar event during the drain was silently dropped becausesend()is gated onreadyState === OPEN.drain()helper invoked beforews.close()on the orderly path. Theclosehandler still callsdrain()for unexpected disconnects; memoization prevents double-draining.Test plan
npx vitest run src/session.test.ts— all 29 tests passNote
Medium Risk
Changes session shutdown ordering and introduces memoized draining, which can affect artifact upload completeness and session close timing if edge cases exist (e.g., multiple close paths or long-running uploads). Scope is contained to
WebSocketSessionteardown logic plus a patch version bump.Overview
Ensures
end_session_messagedrains the artifact watcher and waits for pending artifact uploads before callingws.close(), so late chokidar events can still enqueueartifact_upload_request_messagewhile the socket is open.Refactors the shutdown sequence into a memoized
drain()helper that is invoked from both the orderly end-session path and the WSclosehandler (for unexpected disconnects), preventing duplicate drain work.Bumps
runtimeuseversion from0.11.0to0.11.1.Reviewed by Cursor Bugbot for commit 79977a7. Bugbot is set up for automated code reviews on this repo. Configure here.