Skip to content

Commit 9724b8e

Browse files
authored
Merge pull request #52 from github/copilot/sync-upstream-27-commits
Upstream sync: Port Commands, Elicitation, Session Capabilities, and getSessionMetadata (27 commits)
2 parents 3a74e47 + e365e05 commit 9724b8e

37 files changed

+2216
-8
lines changed

.lastmerge

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
40887393a9e687dacc141a645799441b0313ff15
1+
f7fd7577109d64e261456b16c49baa56258eae4e

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Java SDK for programmatic control of GitHub Copilot CLI, enabling you to build A
2525
### Requirements
2626

2727
- Java 17 or later. **JDK 25 recommended**. Selecting JDK 25 enables the use of virtual threads, as shown in the [Quick Start](#quick-start).
28-
- GitHub Copilot 1.0.15-0 or later installed and in `PATH` (or provide custom `cliPath`)
28+
- GitHub Copilot CLI 1.0.17 or later installed and in `PATH` (or provide custom `cliPath`)
2929

3030
### Maven
3131

src/main/java/com/github/copilot/sdk/CopilotClient.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.github.copilot.sdk.json.DeleteSessionResponse;
2525
import com.github.copilot.sdk.json.GetAuthStatusResponse;
2626
import com.github.copilot.sdk.json.GetLastSessionIdResponse;
27+
import com.github.copilot.sdk.json.GetSessionMetadataResponse;
2728
import com.github.copilot.sdk.json.GetModelsResponse;
2829
import com.github.copilot.sdk.json.GetStatusResponse;
2930
import com.github.copilot.sdk.json.ListSessionsResponse;
@@ -374,6 +375,7 @@ public CompletableFuture<CopilotSession> createSession(SessionConfig config) {
374375

375376
return connection.rpc.invoke("session.create", request, CreateSessionResponse.class).thenApply(response -> {
376377
session.setWorkspacePath(response.workspacePath());
378+
session.setCapabilities(response.capabilities());
377379
// If the server returned a different sessionId (e.g. a v2 CLI that ignores
378380
// the client-supplied ID), re-key the sessions map.
379381
String returnedId = response.sessionId();
@@ -444,6 +446,7 @@ public CompletableFuture<CopilotSession> resumeSession(String sessionId, ResumeS
444446

445447
return connection.rpc.invoke("session.resume", request, ResumeSessionResponse.class).thenApply(response -> {
446448
session.setWorkspacePath(response.workspacePath());
449+
session.setCapabilities(response.capabilities());
447450
// If the server returned a different sessionId than what was requested, re-key.
448451
String returnedId = response.sessionId();
449452
if (returnedId != null && !returnedId.equals(sessionId)) {
@@ -657,6 +660,34 @@ public CompletableFuture<List<SessionMetadata>> listSessions(SessionListFilter f
657660
});
658661
}
659662

663+
/**
664+
* Gets metadata for a specific session by ID.
665+
* <p>
666+
* This provides an efficient O(1) lookup of a single session's metadata instead
667+
* of listing all sessions.
668+
*
669+
* <h2>Example Usage</h2>
670+
*
671+
* <pre>{@code
672+
* var metadata = client.getSessionMetadata("session-123").get();
673+
* if (metadata != null) {
674+
* System.out.println("Session started at: " + metadata.getStartTime());
675+
* }
676+
* }</pre>
677+
*
678+
* @param sessionId
679+
* the ID of the session to look up
680+
* @return a future that resolves with the {@link SessionMetadata}, or
681+
* {@code null} if the session was not found
682+
* @see SessionMetadata
683+
* @since 1.0.0
684+
*/
685+
public CompletableFuture<SessionMetadata> getSessionMetadata(String sessionId) {
686+
return ensureConnected().thenCompose(connection -> connection.rpc
687+
.invoke("session.getMetadata", Map.of("sessionId", sessionId), GetSessionMetadataResponse.class)
688+
.thenApply(GetSessionMetadataResponse::session));
689+
}
690+
660691
/**
661692
* Gets the ID of the session currently displayed in the TUI.
662693
* <p>

src/main/java/com/github/copilot/sdk/CopilotSession.java

Lines changed: 373 additions & 0 deletions
Large diffs are not rendered by default.

src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.function.Function;
1111

1212
import com.github.copilot.sdk.json.CreateSessionRequest;
13+
import com.github.copilot.sdk.json.CommandWireDefinition;
1314
import com.github.copilot.sdk.json.ResumeSessionConfig;
1415
import com.github.copilot.sdk.json.ResumeSessionRequest;
1516
import com.github.copilot.sdk.json.SectionOverride;
@@ -122,6 +123,16 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess
122123
request.setDisabledSkills(config.getDisabledSkills());
123124
request.setConfigDir(config.getConfigDir());
124125

126+
if (config.getCommands() != null && !config.getCommands().isEmpty()) {
127+
var wireCommands = config.getCommands().stream()
128+
.map(c -> new CommandWireDefinition(c.getName(), c.getDescription()))
129+
.collect(java.util.stream.Collectors.toList());
130+
request.setCommands(wireCommands);
131+
}
132+
if (config.getOnElicitationRequest() != null) {
133+
request.setRequestElicitation(true);
134+
}
135+
125136
return request;
126137
}
127138

@@ -183,6 +194,16 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo
183194
request.setDisabledSkills(config.getDisabledSkills());
184195
request.setInfiniteSessions(config.getInfiniteSessions());
185196

197+
if (config.getCommands() != null && !config.getCommands().isEmpty()) {
198+
var wireCommands = config.getCommands().stream()
199+
.map(c -> new CommandWireDefinition(c.getName(), c.getDescription()))
200+
.collect(java.util.stream.Collectors.toList());
201+
request.setCommands(wireCommands);
202+
}
203+
if (config.getOnElicitationRequest() != null) {
204+
request.setRequestElicitation(true);
205+
}
206+
186207
return request;
187208
}
188209

@@ -211,6 +232,12 @@ static void configureSession(CopilotSession session, SessionConfig config) {
211232
if (config.getHooks() != null) {
212233
session.registerHooks(config.getHooks());
213234
}
235+
if (config.getCommands() != null) {
236+
session.registerCommands(config.getCommands());
237+
}
238+
if (config.getOnElicitationRequest() != null) {
239+
session.registerElicitationHandler(config.getOnElicitationRequest());
240+
}
214241
if (config.getOnEvent() != null) {
215242
session.on(config.getOnEvent());
216243
}
@@ -241,6 +268,12 @@ static void configureSession(CopilotSession session, ResumeSessionConfig config)
241268
if (config.getHooks() != null) {
242269
session.registerHooks(config.getHooks());
243270
}
271+
if (config.getCommands() != null) {
272+
session.registerCommands(config.getCommands());
273+
}
274+
if (config.getOnElicitationRequest() != null) {
275+
session.registerElicitationHandler(config.getOnElicitationRequest());
276+
}
244277
if (config.getOnEvent() != null) {
245278
session.on(config.getOnEvent());
246279
}

src/main/java/com/github/copilot/sdk/events/AbstractSessionEvent.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ public abstract sealed class AbstractSessionEvent permits
6565
ToolExecutionCompleteEvent,
6666
// Broadcast request/completion events (protocol v3)
6767
ExternalToolRequestedEvent, ExternalToolCompletedEvent, PermissionRequestedEvent, PermissionCompletedEvent,
68-
CommandQueuedEvent, CommandCompletedEvent, ExitPlanModeRequestedEvent, ExitPlanModeCompletedEvent,
69-
SystemNotificationEvent,
68+
CommandQueuedEvent, CommandCompletedEvent, CommandExecuteEvent, ElicitationRequestedEvent,
69+
CapabilitiesChangedEvent, ExitPlanModeRequestedEvent, ExitPlanModeCompletedEvent, SystemNotificationEvent,
7070
// User events
7171
UserMessageEvent, PendingMessagesModifiedEvent,
7272
// Skill events
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot.sdk.events;
6+
7+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
8+
import com.fasterxml.jackson.annotation.JsonProperty;
9+
10+
/**
11+
* Event: capabilities.changed
12+
* <p>
13+
* Broadcast when the host's session capabilities change. The SDK updates
14+
* {@link com.github.copilot.sdk.CopilotSession#getCapabilities()} accordingly.
15+
*
16+
* @since 1.0.0
17+
*/
18+
@JsonIgnoreProperties(ignoreUnknown = true)
19+
public final class CapabilitiesChangedEvent extends AbstractSessionEvent {
20+
21+
@JsonProperty("data")
22+
private CapabilitiesChangedData data;
23+
24+
@Override
25+
public String getType() {
26+
return "capabilities.changed";
27+
}
28+
29+
public CapabilitiesChangedData getData() {
30+
return data;
31+
}
32+
33+
public void setData(CapabilitiesChangedData data) {
34+
this.data = data;
35+
}
36+
37+
@JsonIgnoreProperties(ignoreUnknown = true)
38+
public record CapabilitiesChangedData(@JsonProperty("ui") CapabilitiesChangedUi ui) {
39+
}
40+
41+
@JsonIgnoreProperties(ignoreUnknown = true)
42+
public record CapabilitiesChangedUi(@JsonProperty("elicitation") Boolean elicitation) {
43+
}
44+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot.sdk.events;
6+
7+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
8+
import com.fasterxml.jackson.annotation.JsonProperty;
9+
10+
/**
11+
* Event: command.execute
12+
* <p>
13+
* Broadcast when the user executes a slash command registered by this client.
14+
* Clients that have a matching command handler should respond via
15+
* {@code session.commands.handlePendingCommand}.
16+
*
17+
* @since 1.0.0
18+
*/
19+
@JsonIgnoreProperties(ignoreUnknown = true)
20+
public final class CommandExecuteEvent extends AbstractSessionEvent {
21+
22+
@JsonProperty("data")
23+
private CommandExecuteData data;
24+
25+
@Override
26+
public String getType() {
27+
return "command.execute";
28+
}
29+
30+
public CommandExecuteData getData() {
31+
return data;
32+
}
33+
34+
public void setData(CommandExecuteData data) {
35+
this.data = data;
36+
}
37+
38+
@JsonIgnoreProperties(ignoreUnknown = true)
39+
public record CommandExecuteData(@JsonProperty("requestId") String requestId,
40+
@JsonProperty("command") String command, @JsonProperty("commandName") String commandName,
41+
@JsonProperty("args") String args) {
42+
}
43+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot.sdk.events;
6+
7+
import java.util.List;
8+
import java.util.Map;
9+
10+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
11+
import com.fasterxml.jackson.annotation.JsonProperty;
12+
13+
/**
14+
* Event: elicitation.requested
15+
* <p>
16+
* Broadcast when the server or an MCP tool requests structured input from the
17+
* user. Clients that have an elicitation handler should respond via
18+
* {@code session.ui.handlePendingElicitation}.
19+
*
20+
* @since 1.0.0
21+
*/
22+
@JsonIgnoreProperties(ignoreUnknown = true)
23+
public final class ElicitationRequestedEvent extends AbstractSessionEvent {
24+
25+
@JsonProperty("data")
26+
private ElicitationRequestedData data;
27+
28+
@Override
29+
public String getType() {
30+
return "elicitation.requested";
31+
}
32+
33+
public ElicitationRequestedData getData() {
34+
return data;
35+
}
36+
37+
public void setData(ElicitationRequestedData data) {
38+
this.data = data;
39+
}
40+
41+
@JsonIgnoreProperties(ignoreUnknown = true)
42+
public record ElicitationRequestedData(@JsonProperty("requestId") String requestId,
43+
@JsonProperty("toolCallId") String toolCallId, @JsonProperty("elicitationSource") String elicitationSource,
44+
@JsonProperty("message") String message, @JsonProperty("mode") String mode,
45+
@JsonProperty("requestedSchema") ElicitationRequestedSchema requestedSchema,
46+
@JsonProperty("url") String url) {
47+
}
48+
49+
@JsonIgnoreProperties(ignoreUnknown = true)
50+
public record ElicitationRequestedSchema(@JsonProperty("type") String type,
51+
@JsonProperty("properties") Map<String, Object> properties,
52+
@JsonProperty("required") List<String> required) {
53+
}
54+
}

src/main/java/com/github/copilot/sdk/events/PermissionRequestedEvent.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public void setData(PermissionRequestedData data) {
3838

3939
@JsonIgnoreProperties(ignoreUnknown = true)
4040
public record PermissionRequestedData(@JsonProperty("requestId") String requestId,
41-
@JsonProperty("permissionRequest") PermissionRequest permissionRequest) {
41+
@JsonProperty("permissionRequest") PermissionRequest permissionRequest,
42+
@JsonProperty("resolvedByHook") Boolean resolvedByHook) {
4243
}
4344
}

0 commit comments

Comments
 (0)