Skip to content

Latest commit

 

History

History
106 lines (87 loc) · 4.07 KB

File metadata and controls

106 lines (87 loc) · 4.07 KB

Request Cancellation

The SDK exposes the ACP $/cancel_request notification behind the unstable_cancel_request feature. The notification is protocol-level: either side may send it to ask the peer to cancel one outstanding JSON-RPC request by ID.

Enable the feature when depending on the crate:

agent-client-protocol = { version = "...", features = ["unstable_cancel_request"] }

To cancel a request sent through ConnectionTo::send_request, keep the returned SentRequest and call cancel on it:

# use agent_client_protocol::{ConnectionTo, Error, UntypedRole};
# use agent_client_protocol_test::MyRequest;
# async fn example(cx: ConnectionTo<UntypedRole>) -> Result<(), Error> {
let request = cx.send_request(MyRequest {});
request.cancel()?;
# Ok(())
# }

The SentRequest remembers the peer and any proxy wrapping used for the original request, so this also works for requests sent through ConnectionTo::send_request_to.

Dropping a SentRequest before the SDK receives a response also sends $/cancel_request. This covers abandoned request handles and futures. Once the SDK routes a response to the waiting request handle, automatic cancellation is disarmed, even if caller code has not yet consumed it with block_task, on_receiving_result, or forward_response_to.

If you already have the JSON-RPC request ID, send the notification directly:

# use agent_client_protocol::{ConnectionTo, Error, UntypedRole};
# async fn example(cx: ConnectionTo<UntypedRole>) -> Result<(), Error> {
cx.send_cancel_request("request-id".to_string())?;
# Ok(())
# }

For incoming requests, get the request-local cancellation marker from the Responder. This keeps cancellation handling next to the request work it controls:

# use agent_client_protocol::{ConnectionTo, Error, Responder, UntypedRole};
# use agent_client_protocol_test::{MyRequest, MyResponse};
# async fn example(request: MyRequest, responder: Responder<MyResponse>, cx: ConnectionTo<UntypedRole>) -> Result<(), Error> {
# async fn run_request(_request: MyRequest) -> Result<MyResponse, Error> { todo!() }
let cancellation = responder.cancellation();

cx.spawn(async move {
    let response = cancellation.run_until_cancelled(run_request(request)).await;
    responder.respond_with_result(response)
})?;
Ok(())
# }

run_until_cancelled is the simple path for handlers that should stop work and reply with the standard cancellation error as soon as cancellation is requested. If the handler needs cleanup, partial results, or custom cancellation behavior, use cancellation.cancelled() or cancellation.is_cancelled() directly inside the request work instead.

Cancellation markers are only updated when the connection can process the incoming $/cancel_request notification. Long-running handlers should return quickly and move work into ConnectionTo::spawn, SentRequest callbacks, or another task.

When proxying with SentRequest::forward_response_to, the SDK observes the upstream Responder cancellation marker and forwards cancellation to the downstream request automatically.

Register CancelRequestNotification or ProtocolLevelNotification directly only when you need low-level access to cancellation notifications, such as custom routing or protocol tracing:

# use agent_client_protocol::{ConnectionTo, Error, UntypedRole};
use agent_client_protocol::schema::CancelRequestNotification;

# fn builder() -> agent_client_protocol::Builder<UntypedRole> {
UntypedRole.builder()
    .on_receive_notification(
        async |cancel: CancelRequestNotification, _cx: ConnectionTo<UntypedRole>| {
            let request_id = cancel.request_id;
            // Mark the matching in-flight operation cancelled.
            Ok(())
        },
        agent_client_protocol::on_receive_notification!(),
    )
# }

Cancellation is cooperative. A peer may ignore $/cancel_request, may finish with normal data, or may respond to the original request with Error::request_cancelled() (-32800). The SDK ignores unhandled $/... notifications so unsupported protocol-level notifications do not produce method-not-found errors.