You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 37682e1
Browse filesBrowse the repository at this point in the historyBrowse files
fix(connection): support $/cancel_request request cancellation (#158)
* fix(connection): honor $/cancel_request and reply -32800 to cancelled requests
ACP v1 defines the `$/cancel_request` notification (part of the stable v1
schema vendored in schema/, 1.23.0; see
https://agentclientprotocol.com/protocol/v1/cancellation), and the SDK
already ships `PROTOCOL_METHODS["cancel_request"]`, but `Connection` never
handled it:
- An incoming `$/cancel_request` fell through to the router, which logged
an ERROR traceback (method not found) while the targeted handler kept
running and the peer never received the required response.
- A handler that ended with `CancelledError` (cancelled from inside the
agent) produced no response at all, leaving the peer waiting forever.
- Cancelling the task awaiting `send_request` only dropped the local
future; the peer kept working on the request.
- `close()` hung forever when a handler answered its own cancellation with
a result, because the response was queued on an already-closed sender.
`Connection` now tracks in-flight incoming requests by id, cancels the
handler task on `$/cancel_request`, answers cancelled requests with
`-32800 Request cancelled` (a handler may still return a partial result),
sends `$/cancel_request` when a still-pending outgoing request is
cancelled locally, and skips replies once the connection is closed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(connection): keep request responses out of the handler's cancellation
Two races could still lose the response to a cancelled request:
- A `$/cancel_request` read in the same buffer as its request cancelled the
request task before it first ran, so the coroutine that would have sent
`-32800` never executed.
- A late or repeated `$/cancel_request` arriving while the response was being
sent cancelled that send, so the peer got no response at all.
Each incoming request now runs its handler in its own task, which is the only
thing `$/cancel_request` targets. A separate supervised task waits for the
handler with `asyncio.wait` and sends its result, or `-32800` if the handler
was cancelled. `close()` still cancels both, so shutdown never waits on a
blocked send, and the `Task.uncancel()` / closed-send special cases are no
longer needed.
Also document request cancellation in the quickstart and state that a
cancelled handler answers with its result or `-32800`.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(connection): only cancel requests named by a valid requestId
`$/cancel_request` requires a `requestId`, and a request id is `null`, an
integer or a string. The handler did not check either, so a malformed
cancellation could abort an unrelated request:
- A notification without `params`, with non-object `params` or without
`requestId` was treated as `requestId: null` and cancelled a pending
request whose id is `null`.
- `requestId: true` / `false` cancelled pending requests `1` / `0`, and
`1.0` cancelled `1`, because Python equates these dict keys. Likewise a
request sent with a non-integer id such as `true` or `1.0` could be
cancelled by a cancellation for `1`.
Cancellations without a `requestId`, or with one that is not a valid request
id, are now ignored, and only requests with a valid id are tracked for
cancellation. An explicit `requestId: null` still cancels a `null` request.
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
0 commit comments