Skip to content

Add host-side canvas declaration API and canvasProvider to Java SDK#1848

Open
jmoseley wants to merge 2 commits into
mainfrom
jmoseley-java-canvas-host-api
Open

Add host-side canvas declaration API and canvasProvider to Java SDK#1848
jmoseley wants to merge 2 commits into
mainfrom
jmoseley-java-canvas-host-api

Conversation

@jmoseley

Copy link
Copy Markdown
Contributor

Why

The Java SDK was the only SDK without host-side canvas declaration support. Unlike Rust/Node/.NET, Java's SessionConfig/ResumeSessionConfig had no canvases declaration field and no extensionInfo identity, so a host could not declare canvases on session create/resume, register a canvas handler, or supply provider identity. Java only had the read-side open-canvases snapshot. This brings Java to full parity and adds the new session-level canvasProvider identity from runtime PR #10519.

What

Five new public types in com.github.copilot.rpc (all @CopilotExperimental, Jackson-annotated, fluent setters + getters, Javadoc):

  • CanvasDeclaration{id, displayName, description, inputSchema?, actions?}
  • ExtensionInfo{source, name} provider identity
  • CanvasProviderIdentity{id, name?} (the new field)
  • CanvasHandler — interface with onOpen + default onClose/onAction
  • CanvasException — carries a code; noHandler() -> canvas_action_no_handler

Wiring:

  • Canvas declaration fields (canvases, requestCanvasRenderer, requestExtensions, extensionSdkPath, extensionInfo, canvasProvider) added to SessionConfig/ResumeSessionConfig and the Jackson wire DTOs (CreateSessionRequest/ResumeSessionRequest), mapped by SessionRequestBuilder.
  • Inbound canvas.open / canvas.close / canvas.action.invoke provider callbacks routed in RpcHandlerDispatcher to the registered CanvasHandler on CopilotSession (with CompletableFuture results and a structured error envelope).
  • Outbound driving reuses the existing generated session.getRpc().canvas API; no new accessor needed.

canvasProvider is added on both session.create and session.resume, serialized as canvasProvider with nested id/name (name omitted from the wire when null). It is a hand-registered JSON-RPC field (not in api.schema.json), matching the sibling Rust/Node/.NET work exactly. Fully optional and back-compatible.

Notes for reviewers

  • canvasHandler lives only on the config object and is never serialized (test-asserted).
  • No edits to java/src/generated/; tests use public APIs only.
  • The 6 read-side session.canvas.* stream events were already generated and surfaced through the typed event stream, so this PR only fills the host-side declaration gap.
  • Full mvn spotless:apply && mvn verify passes (checkstyle 0 violations, all unit tests + 16 IT including 3 new CanvasIT, multi-release JAR built). The full build requires JDK 25+.

Tests

  • CanvasHostApiTest (8 unit tests) asserts the create/resume wire JSON: extensionInfo, canvasProvider id/name, name-omitted-when-null, fields-omitted-when-unset, and that canvasHandler is never serialized; mirrors the Rust session tests.
  • CanvasIT (3 E2E tests) drives the full canvas round-trip (list, open, invoke action) against the live CLI via the replay proxy, with two new handcrafted snapshots.

Brings the Java SDK to parity with Rust/Node/.NET for the host-facing
canvas API. Previously Java only had the read-side open-canvases snapshot
and could not declare canvases, register a provider handler, or supply
provider identity.

Adds five public types in com.github.copilot.rpc (all @CopilotExperimental):
CanvasDeclaration, ExtensionInfo, CanvasProviderIdentity, CanvasHandler,
and CanvasException. Wires canvas declaration fields (canvases,
requestCanvasRenderer, requestExtensions, extensionSdkPath, extensionInfo,
canvasProvider) through SessionConfig/ResumeSessionConfig and the Jackson
wire DTOs, mapped by SessionRequestBuilder. Routes inbound canvas.open /
canvas.close / canvas.action.invoke provider callbacks in
RpcHandlerDispatcher to the registered CanvasHandler on CopilotSession.

Also adds the canvasProvider (CanvasProviderIdentity with required id and
optional name) field on session.create and session.resume, matching runtime
PR #10519 and the sibling Rust/Node/.NET work. It is a hand-registered
JSON-RPC field serialized as canvasProvider with nested id/name; name is
omitted from the wire when null. Fully optional and back-compatible.

Tests: CanvasHostApiTest asserts the create/resume wire JSON (extensionInfo,
canvasProvider id/name, name-omitted, fields-omitted-when-unset,
canvasHandler never serialized) mirroring the Rust session tests. CanvasIT
adds three E2E tests (list, open, invoke action) driving the full canvas
round-trip against the live CLI via the replay proxy, with two new
handcrafted snapshots.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 30, 2026 03:39
@jmoseley jmoseley requested a review from a team as a code owner June 30, 2026 03:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request brings the Java SDK up to parity with other Copilot SDKs by adding host-side canvas declaration support (create/resume wire fields + handler callbacks), plus the new session-level canvasProvider identity.

Changes:

  • Adds new experimental public canvas-related RPC types (CanvasDeclaration, ExtensionInfo, CanvasProviderIdentity, CanvasHandler, CanvasException) and threads them through SessionConfig / ResumeSessionConfig.
  • Extends session.create / session.resume request DTOs and SessionRequestBuilder mapping to include canvas declaration + identity fields.
  • Registers and routes inbound canvas.open / canvas.close / canvas.action.invoke requests to a session-registered CanvasHandler, including structured error responses; adds unit + E2E coverage and snapshots.
Show a summary per file
File Description
test/snapshots/canvas/canvas_open_round_trip.yaml Adds replay-proxy fixture for canvas open round-trip E2E scenario (no CAPI calls).
test/snapshots/canvas/canvas_invoke_action_round_trip.yaml Adds replay-proxy fixture for canvas action invocation E2E scenario (no CAPI calls).
java/src/test/java/com/github/copilot/CanvasIT.java New Java failsafe IT covering list/open/action canvas round-trips via replay proxy.
java/src/test/java/com/github/copilot/CanvasHostApiTest.java New unit tests asserting create/resume wire JSON mapping and handler/exception defaults.
java/src/main/java/com/github/copilot/SessionRequestBuilder.java Wires new canvas/extension/provider config fields into create/resume requests and registers handler on session.
java/src/main/java/com/github/copilot/RpcHandlerDispatcher.java Adds JSON-RPC handlers for canvas.open, canvas.close, canvas.action.invoke and structured error responses.
java/src/main/java/com/github/copilot/rpc/SessionConfig.java Adds canvas declarations, extension identity, provider identity, and non-serialized CanvasHandler.
java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java Adds resume-time canvas declarations, open-canvas snapshot, and non-serialized CanvasHandler.
java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java Adds new wire fields for canvas declarations/identities on session.create.
java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java Adds new wire fields for canvas declarations/identities on session.resume.
java/src/main/java/com/github/copilot/rpc/ExtensionInfo.java New public experimental type for stable extension/provider identity on the wire.
java/src/main/java/com/github/copilot/rpc/CanvasProviderIdentity.java New public experimental type for session-level canvas provider identity.
java/src/main/java/com/github/copilot/rpc/CanvasHandler.java New public experimental callback interface for inbound canvas lifecycle/action requests.
java/src/main/java/com/github/copilot/rpc/CanvasException.java New public experimental exception type carrying machine-readable error codes.
java/src/main/java/com/github/copilot/rpc/CanvasDeclaration.java New public experimental type describing declared canvases sent on create/resume.
java/src/main/java/com/github/copilot/JsonRpcClient.java Extends error response sending to optionally include structured data.
java/src/main/java/com/github/copilot/CopilotSession.java Stores/registers CanvasHandler and exposes internal routing helpers for canvas callbacks.

Review details

  • Files reviewed: 17/17 changed files
  • Comments generated: 3
  • Review effort level: Low

Comment thread java/src/main/java/com/github/copilot/RpcHandlerDispatcher.java
Comment thread java/src/main/java/com/github/copilot/RpcHandlerDispatcher.java
Comment thread java/src/main/java/com/github/copilot/RpcHandlerDispatcher.java
@github-actions

This comment has been minimized.

Address Cloud Code Review feedback: the canvas.open / canvas.close /
canvas.action.invoke handlers caught synchronous failures (e.g. malformed
params from treeToValue, or any logic before the CompletableFuture chain is
attached) but only logged them, leaving the caller without a response. Send
a structured canvas error reply in the catch block so the provider callback
always completes instead of hanging.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review ✅

Summary: This PR maintains excellent cross-SDK consistency and is part of a well-coordinated companion PR pair.

Companion PR relationship

This PR (#1848) and #1847 are designed to be merged together:

Together they bring all six SDKs to full parity on the canvas host-side declaration API.

Canvas host-side API parity check

Feature Node.js Python Go .NET Rust Java (this PR)
CanvasDeclaration type ✅ added
ExtensionInfo type ✅ added
CanvasHandler interface ✅ added
Canvas error type CanvasError CanvasError CanvasError CanvasException CanvasError CanvasException
canvases session field ✅ added
requestCanvasRenderer ✅ added
requestExtensions ✅ added
extensionSdkPath ✅ added
extensionInfo ✅ added
canvasProvider identity via #1847 via #1847 via #1847 via #1847 via #1847 ✅ added
Inbound canvas dispatch ✅ added

API naming consistency

Java's naming follows language conventions while remaining semantically parallel:

  • CanvasException (Java/NET) vs CanvasError (Node/Python/Go/Rust) — both pattern match .NET's convention of Exception
  • CanvasException.noHandler() matches CanvasError.noHandler() (Node), CanvasError.no_handler() (Python/Rust), CanvasErrorNoHandler() (Go) — consistent canvas_action_no_handler error code across all
  • Fluent setters (setCanvases(), setCanvasProvider(), etc.) mirror Java/NET PascalCase conventions, equivalent to named parameters in Python, builder methods in Rust, and struct fields in Go

No outstanding consistency gaps

Once both #1847 and #1848 are merged, all six SDKs will be fully consistent on the canvas host-side declaration API surface. There are no unaddressed inconsistencies in this PR.

Note: The new snapshot files (canvas_open_round_trip.yaml, canvas_invoke_action_round_trip.yaml) correctly contain empty conversations arrays — canvas round-trips make no model/CAPI calls so this matches the pattern used by the existing canvas_list_discovers_declared_canvases.yaml.

Generated by SDK Consistency Review Agent for issue #1848 · sonnet46 4.5M ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants