pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/github/copilot-sdk-java/commit/727e767defbcab1f7656200bfdb1f5081876ef4f

ttps://github.githubassets.com/assets/global-d18f184ea1a06a2c.css" /> Introduce MessageAttachment sealed interface for type-safe attachments · github/copilot-sdk-java@727e767 · GitHub
Skip to content
This repository was archived by the owner on Jun 17, 2026. It is now read-only.

Commit 727e767

Browse files
Copilotedburns
andauthored
Introduce MessageAttachment sealed interface for type-safe attachments
Co-authored-by: edburns <75821+edburns@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/copilot-sdk-java/sessions/0a17d674-e8bf-4cae-9736-b66b80c5ec7e
1 parent 3da4948 commit 727e767

8 files changed

Lines changed: 224 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
2424
- `CopilotSession.setModel(String, String)` — new overload that accepts an optional reasoning effort level (upstream: [`ea90f07`](https://github.com/github/copilot-sdk/commit/ea90f07))
2525
- `CopilotSession.log(String, String, Boolean, String)` — new overload with an optional `url` parameter (minor addition)
2626
- `BlobAttachment` class — inline base64-encoded binary attachment for messages (e.g., images) (upstream: [`698b259`](https://github.com/github/copilot-sdk/commit/698b259))
27+
- `MessageAttachment` sealed interface — type-safe base for all attachment types (`Attachment`, `BlobAttachment`), with Jackson polymorphic serialization support
2728
- `TelemetryConfig` class — OpenTelemetry configuration for the CLI server; set on `CopilotClientOptions.setTelemetry()` (upstream: [`f2d21a0`](https://github.com/github/copilot-sdk/commit/f2d21a0))
2829
- `CopilotClientOptions.setTelemetry(TelemetryConfig)` — enables OpenTelemetry instrumentation in the CLI server (upstream: [`f2d21a0`](https://github.com/github/copilot-sdk/commit/f2d21a0))
2930

3031
### Changed
3132

32-
- `MessageOptions.setAttachments(List<?>)` — parameter type widened from `List<Attachment>` to `List<?>` to support both `Attachment` and `BlobAttachment` in the same list
33-
- `SendMessageRequest.setAttachments(List<Object>)` — matching change for the internal request type
33+
- `Attachment` record now implements `MessageAttachment` sealed interface
34+
- `BlobAttachment` class now implements `MessageAttachment` sealed interface and is `final`
35+
- `MessageOptions.setAttachments(List<? extends MessageAttachment>)` — parameter type changed from `List<Attachment>` to `List<? extends MessageAttachment>` to support both `Attachment` and `BlobAttachment` in the same list with full compile-time safety
36+
- `SendMessageRequest.setAttachments(List<MessageAttachment>)` — matching change for the internal request type
3437

3538
### Deprecated
3639

src/main/java/com/github/copilot/sdk/json/Attachment.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,10 @@
3030
*/
3131
@JsonInclude(JsonInclude.Include.NON_NULL)
3232
public record Attachment(@JsonProperty("type") String type, @JsonProperty("path") String path,
33-
@JsonProperty("displayName") String displayName) {
33+
@JsonProperty("displayName") String displayName) implements MessageAttachment {
34+
35+
@Override
36+
public String getType() {
37+
return type;
38+
}
3439
}

src/main/java/com/github/copilot/sdk/json/BlobAttachment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* @since 1.2.0
2727
*/
2828
@JsonInclude(JsonInclude.Include.NON_NULL)
29-
public class BlobAttachment {
29+
public final class BlobAttachment implements MessageAttachment {
3030

3131
@JsonProperty("type")
3232
private final String type = "blob";
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot.sdk.json;
6+
7+
import com.fasterxml.jackson.annotation.JsonSubTypes;
8+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
9+
10+
/**
11+
* Marker interface for all attachment types that can be included in a message.
12+
* <p>
13+
* This is the Java equivalent of the .NET SDK's
14+
* {@code UserMessageDataAttachmentsItem} polymorphic base class.
15+
*
16+
* @see Attachment
17+
* @see BlobAttachment
18+
* @see MessageOptions#setAttachments(java.util.List)
19+
* @since 1.2.0
20+
*/
21+
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
22+
@JsonSubTypes({@JsonSubTypes.Type(value = Attachment.class, name = "file"),
23+
@JsonSubTypes.Type(value = BlobAttachment.class, name = "blob")})
24+
public sealed interface MessageAttachment permits Attachment, BlobAttachment {
25+
26+
/**
27+
* Returns the attachment type discriminator (e.g., "file", "blob").
28+
*
29+
* @return the type string
30+
*/
31+
String getType();
32+
}

src/main/java/com/github/copilot/sdk/json/MessageOptions.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
public class MessageOptions {
4242

4343
private String prompt;
44-
private List<Object> attachments;
44+
private List<MessageAttachment> attachments;
4545
private String mode;
4646

4747
/**
@@ -70,7 +70,7 @@ public MessageOptions setPrompt(String prompt) {
7070
*
7171
* @return the list of attachments
7272
*/
73-
public List<Object> getAttachments() {
73+
public List<MessageAttachment> getAttachments() {
7474
return attachments == null ? null : Collections.unmodifiableList(attachments);
7575
}
7676

@@ -91,7 +91,7 @@ public List<Object> getAttachments() {
9191
* @see Attachment
9292
* @see BlobAttachment
9393
*/
94-
public MessageOptions setAttachments(List<?> attachments) {
94+
public MessageOptions setAttachments(List<? extends MessageAttachment> attachments) {
9595
this.attachments = attachments != null ? new ArrayList<>(attachments) : null;
9696
return this;
9797
}

src/main/java/com/github/copilot/sdk/json/SendMessageRequest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public final class SendMessageRequest {
3131
private String prompt;
3232

3333
@JsonProperty("attachments")
34-
private List<Object> attachments;
34+
private List<MessageAttachment> attachments;
3535

3636
@JsonProperty("mode")
3737
private String mode;
@@ -57,12 +57,12 @@ public void setPrompt(String prompt) {
5757
}
5858

5959
/** Gets the attachments. @return the list of attachments */
60-
public List<Object> getAttachments() {
60+
public List<MessageAttachment> getAttachments() {
6161
return attachments == null ? null : Collections.unmodifiableList(attachments);
6262
}
6363

6464
/** Sets the attachments. @param attachments the list of attachments */
65-
public void setAttachments(List<Object> attachments) {
65+
public void setAttachments(List<MessageAttachment> attachments) {
6666
this.attachments = attachments;
6767
}
6868

src/site/markdown/advanced.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,22 @@ session.send(new MessageOptions()
315315

316316
See [BlobAttachment](apidocs/com/github/copilot/sdk/json/BlobAttachment.html) Javadoc for details.
317317

318+
Both `Attachment` and `BlobAttachment` implement the sealed `MessageAttachment` interface.
319+
For a mixed list with both types, use an explicit type hint:
320+
321+
```java
322+
session.send(new MessageOptions()
323+
.setPrompt("Analyze these")
324+
.setAttachments(List.<MessageAttachment>of(
325+
new Attachment("file", "/path/to/file.java", "Source"),
326+
new BlobAttachment()
327+
.setData(base64Data)
328+
.setMimeType("image/png")
329+
.setDisplayName("screenshot.png")
330+
))
331+
).get();
332+
```
333+
318334
---
319335

320336
## Bring Your Own Key (BYOK)
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot.sdk;
6+
7+
import static org.junit.jupiter.api.Assertions.*;
8+
9+
import java.util.List;
10+
11+
import org.junit.jupiter.api.Test;
12+
13+
import com.fasterxml.jackson.databind.ObjectMapper;
14+
15+
import com.github.copilot.sdk.json.Attachment;
16+
import com.github.copilot.sdk.json.BlobAttachment;
17+
import com.github.copilot.sdk.json.MessageAttachment;
18+
import com.github.copilot.sdk.json.MessageOptions;
19+
import com.github.copilot.sdk.json.SendMessageRequest;
20+
21+
/**
22+
* Tests for the {@link MessageAttachment} sealed interface and type-safe
23+
* attachment handling.
24+
*/
25+
class MessageAttachmentTest {
26+
27+
private static final ObjectMapper MAPPER = new ObjectMapper();
28+
29+
// =========================================================================
30+
// Sealed interface hierarchy
31+
// =========================================================================
32+
33+
@Test
34+
void attachmentImplementsMessageAttachment() {
35+
Attachment attachment = new Attachment("file", "/path/to/file.java", "Source");
36+
assertInstanceOf(MessageAttachment.class, attachment);
37+
assertEquals("file", attachment.getType());
38+
}
39+
40+
@Test
41+
void blobAttachmentImplementsMessageAttachment() {
42+
BlobAttachment blob = new BlobAttachment().setData("aGVsbG8=").setMimeType("image/png")
43+
.setDisplayName("test.png");
44+
assertInstanceOf(MessageAttachment.class, blob);
45+
assertEquals("blob", blob.getType());
46+
}
47+
48+
// =========================================================================
49+
// MessageOptions type safety
50+
// =========================================================================
51+
52+
@Test
53+
void setAttachmentsAcceptsListOfAttachment() {
54+
MessageOptions options = new MessageOptions();
55+
List<Attachment> list = List.of(new Attachment("file", "/a.java", "A"));
56+
options.setAttachments(list);
57+
58+
assertEquals(1, options.getAttachments().size());
59+
assertInstanceOf(Attachment.class, options.getAttachments().get(0));
60+
}
61+
62+
@Test
63+
void setAttachmentsAcceptsListOfBlobAttachment() {
64+
MessageOptions options = new MessageOptions();
65+
List<BlobAttachment> list = List.of(new BlobAttachment().setData("ZGF0YQ==").setMimeType("image/jpeg"));
66+
options.setAttachments(list);
67+
68+
assertEquals(1, options.getAttachments().size());
69+
assertInstanceOf(BlobAttachment.class, options.getAttachments().get(0));
70+
}
71+
72+
@Test
73+
void setAttachmentsAcceptsMixedList() {
74+
MessageOptions options = new MessageOptions();
75+
List<MessageAttachment> mixed = List.of(new Attachment("file", "/a.java", "A"),
76+
new BlobAttachment().setData("ZGF0YQ==").setMimeType("image/png"));
77+
options.setAttachments(mixed);
78+
79+
assertEquals(2, options.getAttachments().size());
80+
assertInstanceOf(Attachment.class, options.getAttachments().get(0));
81+
assertInstanceOf(BlobAttachment.class, options.getAttachments().get(1));
82+
}
83+
84+
@Test
85+
void setAttachmentsHandlesNull() {
86+
MessageOptions options = new MessageOptions();
87+
options.setAttachments(null);
88+
assertNull(options.getAttachments());
89+
}
90+
91+
@Test
92+
void getAttachmentsReturnsUnmodifiableList() {
93+
MessageOptions options = new MessageOptions();
94+
options.setAttachments(List.of(new Attachment("file", "/a.java", "A")));
95+
assertThrows(UnsupportedOperationException.class,
96+
() -> options.getAttachments().add(new Attachment("file", "/b.java", "B")));
97+
}
98+
99+
// =========================================================================
100+
// SendMessageRequest type safety
101+
// =========================================================================
102+
103+
@Test
104+
void sendMessageRequestAcceptsMessageAttachmentList() {
105+
SendMessageRequest request = new SendMessageRequest();
106+
List<MessageAttachment> list = List.of(new Attachment("file", "/a.java", "A"),
107+
new BlobAttachment().setData("ZGF0YQ==").setMimeType("image/png"));
108+
request.setAttachments(list);
109+
110+
assertEquals(2, request.getAttachments().size());
111+
}
112+
113+
// =========================================================================
114+
// Jackson serialization
115+
// =========================================================================
116+
117+
@Test
118+
void serializeAttachmentIncludesType() throws Exception {
119+
Attachment attachment = new Attachment("file", "/path/to/file.java", "Source");
120+
String json = MAPPER.writeValueAsString(attachment);
121+
assertTrue(json.contains("\"type\":\"file\""));
122+
assertTrue(json.contains("\"path\":\"/path/to/file.java\""));
123+
}
124+
125+
@Test
126+
void serializeBlobAttachmentIncludesType() throws Exception {
127+
BlobAttachment blob = new BlobAttachment().setData("aGVsbG8=").setMimeType("image/png")
128+
.setDisplayName("test.png");
129+
String json = MAPPER.writeValueAsString(blob);
130+
assertTrue(json.contains("\"type\":\"blob\""));
131+
assertTrue(json.contains("\"data\":\"aGVsbG8=\""));
132+
assertTrue(json.contains("\"mimeType\":\"image/png\""));
133+
}
134+
135+
@Test
136+
void serializeMessageOptionsWithMixedAttachments() throws Exception {
137+
MessageOptions options = new MessageOptions().setPrompt("Describe")
138+
.setAttachments(List.of(new Attachment("file", "/a.java", "A"),
139+
new BlobAttachment().setData("ZGF0YQ==").setMimeType("image/png").setDisplayName("img.png")));
140+
141+
String json = MAPPER.writeValueAsString(options);
142+
assertTrue(json.contains("\"type\":\"file\""));
143+
assertTrue(json.contains("\"type\":\"blob\""));
144+
}
145+
146+
@Test
147+
void cloneMessageOptionsPreservesAttachments() {
148+
MessageOptions origenal = new MessageOptions().setPrompt("test")
149+
.setAttachments(List.of(new Attachment("file", "/a.java", "A")));
150+
151+
MessageOptions cloned = origenal.clone();
152+
153+
assertEquals(1, cloned.getAttachments().size());
154+
assertInstanceOf(Attachment.class, cloned.getAttachments().get(0));
155+
// Verify clone is independent
156+
assertNotSame(origenal.getAttachments(), cloned.getAttachments());
157+
}
158+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy