Add cancellation for native user-verification RPCs (#43925)

## Why

Canceling an elicitation does not stop a separate native verification RPC. Clients need a way to cancel that work and prevent a canceled proof from being delivered while it waits for outbound queue capacity.

## What changed

- Add experimental `userVerification/cancel` with `{requestId}` to signal cancellation of status, enroll, delete, or verify requests on the same connection.
- Return an empty acknowledgment without waiting for native work to exit. Unknown, finished, unrelated, and other-connection requests are no-ops.
- Retain request cancellation state until outbound capacity is available so canceled proofs are checked before enqueueing. Already enqueued responses and completed effects cannot be retracted.
- Document the API, request ID requirements, and independent lifetimes of elicitations and native verification RPCs.

## Testing

Add coverage for experimental opt-in, parameter validation, connection and request ID isolation, cancellation before native dispatch, repeated cancellation, worker-slot retention, and proof cancellation while waiting for outbound capacity.

GitOrigin-RevId: 03fcb83fcc5fab4169c68668822fc0bbb4970938
This commit is contained in:
riley-oai
2026-09-08 21:48:53 +00:00
committed by copyberry
parent 9ec33e1926
commit 82d4a98912
11 changed files with 455 additions and 7 deletions
+22 -3
View File
@@ -70,6 +70,7 @@ use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::JSONRPCNotification;
use codex_app_server_protocol::JSONRPCRequest;
use codex_app_server_protocol::JSONRPCResponse;
use codex_app_server_protocol::UserVerificationCancelResponse;
use codex_app_server_protocol::experimental_required_message;
use codex_arg0::Arg0DispatchPaths;
use codex_code_mode::CodeModeSessionProvider;
@@ -631,7 +632,12 @@ impl MessageProcessor {
traceparent: trace.traceparent.clone(),
tracestate: trace.tracestate.clone(),
});
let request_context = RequestContext::new(request_id.clone(), request_span, request_trace);
let request_context = RequestContext::new(
request_id.clone(),
request_method,
request_span,
request_trace,
);
Self::run_request_with_context(
Arc::clone(&self.outgoing),
request_context.clone(),
@@ -680,8 +686,12 @@ impl MessageProcessor {
};
let request_span =
crate::app_server_tracing::typed_request_span(&request, connection_id, &session);
let mut request_context =
RequestContext::new(request_id.clone(), request_span, /*parent_trace*/ None);
let mut request_context = RequestContext::new(
request_id.clone(),
request.method_name(),
request_span,
/*parent_trace*/ None,
);
request_context.cancellation = cancellation;
tracing::trace!(
?connection_id,
@@ -995,6 +1005,15 @@ impl MessageProcessor {
ClientRequest::Initialize { .. } => {
panic!("Initialize should be handled before initialized request dispatch");
}
ClientRequest::UserVerificationCancel { params, .. } => {
self.outgoing
.cancel_user_verification_request(&ConnectionRequestId {
connection_id,
request_id: params.request_id,
})
.await;
Ok(Some(UserVerificationCancelResponse {}.into()))
}
request @ (ClientRequest::UserVerificationStatus { .. }
| ClientRequest::UserVerificationEnroll { .. }
| ClientRequest::UserVerificationDelete { .. }