Skip to content

Add open-canvases snapshot tracking to the Java SDK#1606

Open
jmoseley wants to merge 2 commits into
mainfrom
jmoseley/java-open-canvases-snapshot
Open

Add open-canvases snapshot tracking to the Java SDK#1606
jmoseley wants to merge 2 commits into
mainfrom
jmoseley/java-open-canvases-snapshot

Conversation

@jmoseley

@jmoseley jmoseley commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

What

Brings the Java SDK to parity with the other five SDK languages (Rust, Node, Python, Go, .NET) by maintaining an in-memory snapshot of the canvas instances currently open for a session. Java previously had none of this feature — only the generated event/data types existed — so this is net-new.

Behavior (mirrors the other 5 languages' contract exactly)

  • CopilotSession keeps a lock-guarded List<OpenCanvasInstance> and exposes it via a new public accessor getOpenCanvases() (returns an immutable defensive copy).
  • session.canvas.openedupsert by instanceId. A provider-unregister re-emit arrives as another opened event with availability=stale and replaces the prior entry rather than duplicating it.
  • session.canvas.closedremove by instanceId (idempotent — removing an absent id is a no-op).
  • Validation guardrails: opened requires non-empty instanceId/canvasId/extensionId and non-null availability; closed requires a non-empty instanceId. Invalid/empty/null payloads log the canonical failed to deserialize session.canvas.{opened,closed} payload warning and no-op. opened/stale events are never treated as removals.
  • The update runs inside handleBroadcastEventAsync alongside the existing capabilities.changed passive state update, before user handlers observe the event, wrapped best-effort so snapshot upkeep can never disrupt event delivery.
  • Resume/create seeding: the snapshot is seeded from the session.create / session.resume responses, which already carry openCanvases on the wire. Added the field to the hand-written CreateSessionResponse / ResumeSessionResponse records and seed via session.setOpenCanvases(...) after setCapabilities, matching .NET.

Tests

New SessionCanvasSnapshotTest (12 cases): open two → both present; close one → gone, other remains; idempotent absent close; empty + null instanceId no-op; missing-required-field opened ignored; stale re-emit replaces (no duplicate); getOpenCanvases() returns an immutable copy; seed via setOpenCanvases (incl. null-element filtering); null clears; plus Jackson deserialization tests for both response records. mvn spotless:apply && mvn verify is green.

Context

Constraints honored

Java only. No edits to generated files (java/src/generated/) or other languages.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Bring the Java SDK to parity with the other five SDK languages
(Rust, Node, Python, Go, .NET) by maintaining an in-memory snapshot of
the canvas instances currently open for a session.

- CopilotSession now keeps a lock-guarded List<OpenCanvasInstance> and
  exposes it via getOpenCanvases() (immutable defensive copy).
- session.canvas.opened upserts by instanceId (a stale re-emit from a
  provider unregister replaces the prior entry rather than duplicating
  it); session.canvas.closed removes by instanceId. Both are validated
  against the canonical contract and are best-effort so snapshot upkeep
  never disrupts event delivery. The update runs in
  handleBroadcastEventAsync alongside the capabilities.changed state
  update, before user handlers observe the event.
- The snapshot is seeded from the session.create / session.resume
  responses, which already carry openCanvases on the wire.

Mirrors PR #1604, which landed the same opened-upsert + closed-remove
behavior for Rust/Node/Python/Go/.NET, and the runtime events from
copilot-agent-runtime #9489 (CLI 1.0.60). The consumer is github-app's
sticky-canvas fix.

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

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

Adds Java-side in-memory tracking of “open canvas instances” for a session, bringing the Java SDK in line with the existing snapshot behavior in the other language SDKs.

Changes:

  • Added a lock-guarded open-canvases snapshot to CopilotSession, updated via session.canvas.opened (upsert) and session.canvas.closed (remove), and exposed via getOpenCanvases().
  • Seeded the snapshot from session.create / session.resume RPC responses by adding openCanvases to the corresponding response records and wiring seeding in CopilotClient.
  • Added unit tests covering upsert/remove, guardrails, stale re-emit replacement, seeding, and response deserialization.
Show a summary per file
File Description
java/src/main/java/com/github/copilot/CopilotSession.java Maintains and exposes the open-canvases snapshot; updates it during event dispatch.
java/src/main/java/com/github/copilot/CopilotClient.java Seeds the session’s open-canvases snapshot from create/resume responses.
java/src/main/java/com/github/copilot/rpc/CreateSessionResponse.java Adds openCanvases to the create-session response shape for seeding.
java/src/main/java/com/github/copilot/rpc/ResumeSessionResponse.java Adds openCanvases to the resume-session response shape for seeding.
java/src/test/java/com/github/copilot/SessionCanvasSnapshotTest.java New unit coverage for snapshot behavior + response deserialization.

Copilot's findings

  • Files reviewed: 5/5 changed files
  • Comments generated: 4

Comment thread java/src/main/java/com/github/copilot/CopilotSession.java
… test

- getOpenCanvases() @SInCE 1.0.0 -> 1.0.1 (new public API)
- Note openCanvases component added in 1.0.1 on Create/ResumeSessionResponse
  (the record types themselves predate this PR, so type-level @SInCE stays 1.0.0)
- getOpenCanvasesReturnsImmutableCopy now dispatches a later event and asserts
  the previously-returned list is unchanged, proving it is a point-in-time
  snapshot rather than a live view

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

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review ✅

This PR correctly brings the Java SDK to full parity with the other five SDK implementations. I reviewed the implementation against the current main branch (which already includes PR #1604's changes for Rust/Node/Python/Go/.NET).

Feature parity across all SDKs

SDK Public accessor opened → upsert closed → remove Seeded on create/resume Thread-safe
Node.js openCanvases (getter)
Python open_canvases (property)
Go OpenCanvases()
.NET OpenCanvases (property)
Rust open_canvases()
Java (this PR) getOpenCanvases()

API naming consistency

Java uses getOpenCanvases() — idiomatic Java getter convention — which correctly mirrors the property/accessor pattern used in all other languages. Validation guardrails (non-empty instanceId/canvasId/extensionId, non-null availability for opened; non-empty instanceId for closed) match the other SDKs exactly. The method returns an immutable defensive copy (List.copyOf), consistent with Node.js ([...spread]), Go (copy), Python (list(...)), and .NET (AsReadOnly()).

No inconsistencies found. 🎉

Generated by SDK Consistency Review Agent for issue #1606 · sonnet46 3M ·

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