Skip to content

Add end_session_confirm_message handshake#31

Merged
jackcbrown89 merged 2 commits intomainfrom
jb-end-session-confirm-message
Apr 20, 2026
Merged

Add end_session_confirm_message handshake#31
jackcbrown89 merged 2 commits intomainfrom
jb-end-session-confirm-message

Conversation

@jackcbrown89
Copy link
Copy Markdown
Contributor

@jackcbrown89 jackcbrown89 commented Apr 18, 2026

Summary

Previously the Python client sent end_session_message and immediately closed the WS. Any artifact_upload_request_message the runtime produced after that (from chokidar events that fired post-terminal — e.g. files created by post-agent commands like `zip`) was sent on a half-closed socket and the upload response could never make it back. Symptom:

```
Executing post-agent command: zip ...
Received new WS message
Received end_session_message. Closing session.
Waiting 3000ms for artifact watcher to drain...
WebSocket connection closed ← client closed while we were draining
Requesting upload for artifact: deterministic-test.zip ← dropped
Waiting for 1 artifact round-trips... ← hangs until timeout
```

New protocol:

  • Runtime receives `end_session_message`, drains the artifact watcher, sends `end_session_confirm_message`, then closes.
  • Python client sends `end_session_message`, then pumps the receive loop — dispatching late `artifact_upload_request_message`s to the session's most-recently-used callback — until the confirm arrives (or 60s timeout).

Bumps both packages to 0.12.0. No backwards compatibility.

Test plan

  • `npx vitest run` — all 140 TS tests pass
  • `uv run pytest test/test_client.py` — all 44 Python tests pass

Note

Medium Risk
Introduces a new WebSocket protocol handshake and changes session teardown semantics across both the TS runtime and Python client, which can break compatibility with older peers and affect connection lifecycle behavior.

Overview
Adds an end_session_confirm_message handshake so sessions close gracefully: the runtime drains the artifact watcher after receiving end_session_message, then confirms and closes, allowing late artifact_upload_request_messages to complete.

Updates the Python persistent-session flow to send end_session_message on context exit and keep pumping messages (responding to late artifact upload requests using the most-recent callback) until confirmation or timeout; transports now expose a new end_session() API to support this drain/confirm loop.

Bumps package versions to 0.13.0 (Python and Node) and updates test fakes to implement the new end_session method.

Reviewed by Cursor Bugbot for commit bdf9873. Bugbot is set up for automated code reviews on this repo. Configure here.

Previously the Python client sent end_session_message and immediately
closed the WS. Any artifact_upload_request_message the runtime produced
after that (from chokidar events that fired post-terminal, e.g. files
created by post-agent commands) was sent on a half-closed socket and
the upload_response could never make it back.

New protocol:
- Runtime receives end_session_message, drains the artifact watcher,
  sends end_session_confirm_message, then closes.
- Python client sends end_session_message, then pumps the receive
  loop — handling late artifact_upload_request_messages with the
  session's most-recent callback — until confirm arrives (or timeout).

Breaking change: older clients that close without waiting for confirm
will no longer get late artifacts uploaded. Both packages bumped to
0.12.0.
@jackcbrown89 jackcbrown89 force-pushed the jb-end-session-confirm-message branch from d94c8db to 612fdd3 Compare April 20, 2026 22:02
@jackcbrown89 jackcbrown89 merged commit dca04fe into main Apr 20, 2026
6 checks passed
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bdf9873. Configure here.

try:
yield runtime_session
finally:
await runtime_session._end_session()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unprotected cleanup in finally may suppress user exceptions

Medium Severity

_end_session() is called in a bare finally block without a try/except wrapper. While end_session() in the transport catches TimeoutError and ConnectionClosed, it does not catch all exceptions — for example, if the server sends valid JSON that isn't an object (e.g., an array), msg.get("message_type") raises AttributeError, which propagates uncaught. If the user's code also raised an exception, the cleanup exception from _end_session() would suppress the original error, making debugging very difficult.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit bdf9873. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant