python-client: client-initiated turn cancellation (cancel) - #463
Merged
Conversation
Mirror the TypeScript client's Stop-button surface (#459) in the Python async client. The wire protocol and all five servers already honor the `cancel` frame / `cancelled` event; the Python client had no way to send it. - SmoothAgentClient.cancel(request_id=, session_id=) sends the cancel frame. - MessageTurn.cancel() is the ergonomic "stop THIS turn" convenience, using the turn's own request_id + originating session_id. - A terminal `cancelled` event settles the matching turn as a user-stop: it RESOLVES (never raises), `await turn` yields the Cancelled event, the async iterator ends cleanly, and turn.cancelled is True so callers tell a stop from an error. - CancelRequest / Cancelled are now first-class in the ClientAction / ServerEvent unions and the validator's schema maps. - Idempotent: cancel with no active turn, or a cancelled with no matching turn, is a harmless no-op. Tests (mock transport), README stop-button snippet, and a changeset included. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YbN45JeWDbcjvFqGJvmVD3
🦋 Changeset detectedLatest commit: ec3fbe5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The wire protocol and all five servers already honor the
cancelframe /cancelledevent, but the Python async client had no way to send it — the missing half of the just-merged TypeScript client (#459). There was no "Stop button" surface.Solution
Mirror #459 in the Python client (
python/src/smooth_operator/):SmoothAgentClient.cancel(request_id=..., session_id=None)— sends the{action: "cancel", requestId, sessionId?}frame (sync send, matching itsconfirm_tool_action/verify_otpsiblings).MessageTurn.cancel()— ergonomic "stop THIS turn" convenience; cancels using the turn's ownrequest_id+ originatingsession_id.cancelledevent settles the matching turn as a user-stop: it resolves (never raises),await turnyields theCancelledevent, theasync foriterator ends cleanly, andturn.cancelledisTrueso a UI tells a stop apart from an error. Errors still raiseProtocolError.CancelRequest/Cancelledare now first-class in theClientAction/ServerEventdiscriminated unions, theActionType/EventTypeenums, and the validator's schema maps.cancelledwith no matching in-flight turn, is a harmless no-op.Reading user-stop vs error
Tests
5 new mock-transport tests in
python/tests/test_client.py: turn/clientcancelemit the correct frame,cancelledsettles the matching turn as a user-stop (resolves,turn.cancelled == True, iterator ends cleanly), and idempotent no-ops (nothing running; latecancelledafter completion). Full suite green (uv run pytest: 58 passed, 1 live-e2e skipped), ruff lint + format clean,compileallclean.🤖 Generated with Claude Code