java: plumb AbortSignal through ToolInvocation for cooperative cancellation#1707
Open
gimenete wants to merge 3 commits into
Open
java: plumb AbortSignal through ToolInvocation for cooperative cancellation#1707gimenete wants to merge 3 commits into
gimenete wants to merge 3 commits into
Conversation
…lation Add AbortSignal to ToolInvocation so tool handlers can observe when session.abort() is called and stop in-flight work cooperatively. - New AbortSignal class in com.github.copilot.rpc with isAborted(), onAborted(Runnable), and abort() (SDK-internal) methods - ToolInvocation.getAbortSignal() returns the signal; initialized to a fresh (non-aborted) instance by default - CopilotSession tracks active signals per requestId; injects the signal into each ToolInvocation before calling the handler - CopilotSession.abort() now fires all tracked active tool signals before sending the RPC abort request, enabling cooperative cancellation - Signals are removed from the tracking map when a tool invocation completes (success or error) to avoid leaks - Unit tests for all AbortSignal behaviours added to ToolInvocationTest Fixes github#1433 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR introduces cooperative cancellation for tool executions by adding an AbortSignal that propagates from CopilotSession.abort() into each ToolInvocation.
Changes:
- Added
AbortSignaltype withisAborted(), listener registration, and idempotentabort(). - Wired per-tool
AbortSignalcreation/management intoCopilotSessionand exposed it viaToolInvocation#getAbortSignal(). - Added unit tests and package documentation for the new cancellation signal.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| java/src/main/java/com/github/copilot/rpc/AbortSignal.java | New cancellation signal implementation with listener support. |
| java/src/main/java/com/github/copilot/rpc/ToolInvocation.java | Adds AbortSignal to tool invocation API + docs. |
| java/src/main/java/com/github/copilot/CopilotSession.java | Tracks active tool signals and aborts them on session abort. |
| java/src/main/java/com/github/copilot/rpc/package-info.java | Documents AbortSignal in RPC package overview. |
| java/src/test/java/com/github/copilot/ToolInvocationTest.java | Adds tests covering AbortSignal and default signal behavior. |
- CopilotSession.cancelToolCall(toolCallId): fires the AbortSignal for only the named in-flight handler without aborting the agentic loop or other handlers; returns true if found and cancelled, false otherwise - activeToolSignals map is now keyed by toolCallId (falls back to requestId when toolCallId is null) so cancelToolCall can look up directly by the ID exposed on ToolInvocation - CancelToolCallTest: 3 unit tests covering targeted cancel, unknown id returns false, and signal removal from the tracking map - java/README.md: new 'Tool Handler Cancellation' section documenting both abort() (all handlers) and cancelToolCall() (single handler) with isAborted()/onAborted() handler examples Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix at-most-once callback delivery: wrap each onAborted() listener in an AtomicBoolean-guarded Runnable so it cannot fire twice even when abort() races with onAborted() registration - Catch Throwable (not just Exception) in listener invocations to align with the 'silently ignored' Javadoc contract - Add @JsonIgnore to setAbortSignal() so it is excluded from Jackson serialization/deserialization, matching the getter annotation - setAbortSignal(null) now silently preserves the existing signal for backwards compatibility, rather than throwing NullPointerException - Add tests: at-most-once callback guarantee; null setAbortSignal is ignored and leaves the existing signal in place Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Summary
Fixes #1433 for the Java SDK.
Adds
AbortSignaltoToolInvocationso tool handlers can observe whensession.abort()is called and stop in-flight work cooperatively — without needing OS-level process kills. Also addscancelToolCall(toolCallId)for single-handler cancellation, mirroring the Node.js reference (PR #1701).Changes
New:
AbortSignalclass (com.github.copilot.rpc)abort()is idempotent — the signal fires exactly once. Callbacks registered after the signal has already fired are invoked immediately.Updated:
ToolInvocation.getAbortSignal()Or register a callback:
Updated:
CopilotSession.abort()When
session.abort()is called, the SDK now:AbortSignalinstances for currently in-flight tool invocationssession.abortRPC as beforeNew:
CopilotSession.cancelToolCall(String toolCallId)Fires the
AbortSignalfor only the named in-flight handler — without aborting the agentic loop or other running handlers.The
activeToolSignalstracking map is keyed bytoolCallId(falls back torequestIdwhentoolCallIdis null) socancelToolCallcan do a direct map lookup.New: Java README "Tool Handler Cancellation" section
Documents both cancellation paths with handler examples.
Tests
ToolInvocationTest: 6 new unit tests forAbortSignalbehaviours (idempotent abort, immediate callback when already aborted, null listener rejection, etc.)CancelToolCallTest: 3 new unit tests — targeted cancel fires only signal A while signal B stays unaffected; returnsfalsefor unknown id; cancelled signal is removed from the mapConfirmed not in PR
java/mvnw— only modified locally (chmod), not committed