-
Notifications
You must be signed in to change notification settings - Fork 7
Merge reference implementation SDK changes (2026-05-05) and address review feedback #157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
edburns
merged 5 commits into
main
from
copilot/reference-impl-sync-expand-sdk-e2e-coverage
May 5, 2026
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
7b80d72
Initial plan
Copilot 0b6c28b
Port reference implementation changes: process cleanup, connect fallb…
Copilot 0a2157c
Update .lastmerge to c063458ecc3d606766f04cf203b11b08de672cc8, sync p…
Copilot ee55571
Address PR review feedback on cleanup and tests
Copilot a9c88d7
Refine timeout constants from validation feedback
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| ced6613253a595769d2e77547f9e1caf6bef6438 | ||
| c063458ecc3d606766f04cf203b11b08de672cc8 |
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -76,6 +76,7 @@ public final class CopilotClient implements AutoCloseable { | |
| * shutdown via {@link #stop()}. | ||
| */ | ||
| public static final int AUTOCLOSEABLE_TIMEOUT_SECONDS = 10; | ||
| private static final int FORCE_KILL_TIMEOUT_SECONDS = 10; | ||
| private final CopilotClientOptions options; | ||
| private final CliServerManager serverManager; | ||
| private final LifecycleEventManager lifecycleManager = new LifecycleEventManager(); | ||
|
|
@@ -216,7 +217,8 @@ private Connection startCoreBody() { | |
| } catch (Exception e) { | ||
| String stderr = serverManager.getStderrOutput(); | ||
| if (!stderr.isEmpty()) { | ||
| throw new CompletionException(new IOException("CLI process exited unexpectedly. stderr: " + stderr, e)); | ||
| throw new CompletionException(new IOException( | ||
| CliServerManager.formatCliExitedMessage("CLI process exited unexpectedly.", stderr), e)); | ||
| } | ||
| throw new CompletionException(e); | ||
| } | ||
|
|
@@ -244,7 +246,7 @@ private void verifyProtocolVersion(Connection connection) throws Exception { | |
| while (cause instanceof java.util.concurrent.ExecutionException || cause instanceof CompletionException) { | ||
| cause = cause.getCause(); | ||
| } | ||
| if (cause instanceof JsonRpcException rpcEx && rpcEx.getCode() == METHOD_NOT_FOUND_ERROR_CODE) { | ||
| if (cause instanceof JsonRpcException rpcEx && isUnsupportedConnectMethod(rpcEx)) { | ||
| // Legacy server without 'connect'; fall back to 'ping'. | ||
| // A token, if any, is silently dropped — the legacy server can't enforce one. | ||
| var params = new HashMap<String, Object>(); | ||
|
|
@@ -270,6 +272,10 @@ private void verifyProtocolVersion(Connection connection) throws Exception { | |
| } | ||
| } | ||
|
|
||
| private static boolean isUnsupportedConnectMethod(JsonRpcException ex) { | ||
| return ex.getCode() == METHOD_NOT_FOUND_ERROR_CODE || "Unhandled method connect".equals(ex.getMessage()); | ||
| } | ||
|
|
||
| /** | ||
| * Disconnects from the Copilot server and closes all active sessions. | ||
| * <p> | ||
|
|
@@ -348,8 +354,14 @@ private CompletableFuture<Void> cleanupConnection() { | |
| if (connection.process != null) { | ||
| try { | ||
| if (connection.process.isAlive()) { | ||
| connection.process.destroyForcibly(); | ||
| Process destroyedProcess = connection.process.destroyForcibly(); | ||
| if (!destroyedProcess.waitFor(FORCE_KILL_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { | ||
| LOG.fine("Process did not terminate within force kill timeout"); | ||
| } | ||
| } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| LOG.log(Level.FINE, "Interrupted while killing process", e); | ||
| } catch (Exception e) { | ||
| LOG.log(Level.FINE, "Error killing process", e); | ||
| } | ||
|
|
||
111 changes: 111 additions & 0 deletions
111
src/test/java/com/github/copilot/sdk/EventFidelityTest.java
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| package com.github.copilot.sdk; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.*; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| import org.junit.jupiter.api.AfterAll; | ||
| import org.junit.jupiter.api.BeforeAll; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import com.github.copilot.sdk.generated.AssistantUsageEvent; | ||
| import com.github.copilot.sdk.generated.SessionEvent; | ||
| import com.github.copilot.sdk.generated.SessionUsageInfoEvent; | ||
| import com.github.copilot.sdk.json.MessageOptions; | ||
| import com.github.copilot.sdk.json.PermissionHandler; | ||
| import com.github.copilot.sdk.json.SessionConfig; | ||
|
|
||
| /** | ||
| * E2E tests for event fidelity — verifying the shape, ordering, and presence of | ||
| * key events emitted from the runtime. | ||
| * | ||
| * <p> | ||
| * Snapshots are stored in {@code test/snapshots/event_fidelity/}. | ||
| * </p> | ||
| */ | ||
| public class EventFidelityTest { | ||
|
|
||
| private static E2ETestContext ctx; | ||
|
|
||
| @BeforeAll | ||
| static void setup() throws Exception { | ||
| ctx = E2ETestContext.create(); | ||
| } | ||
|
|
||
| @AfterAll | ||
| static void teardown() throws Exception { | ||
| if (ctx != null) { | ||
| ctx.close(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Verifies that an {@code assistant.usage} event is emitted after the model | ||
| * processes a prompt. | ||
| * | ||
| * @see Snapshot: | ||
| * event_fidelity/should_emit_assistant_usage_event_after_model_call | ||
| */ | ||
| @Test | ||
| void testShouldEmitAssistantUsageEventAfterModelCall() throws Exception { | ||
| ctx.configureForTest("event_fidelity", "should_emit_assistant_usage_event_after_model_call"); | ||
|
|
||
| try (CopilotClient client = ctx.createClient()) { | ||
| CopilotSession session = client | ||
| .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); | ||
|
|
||
| List<SessionEvent> events = new ArrayList<>(); | ||
| session.on(events::add); | ||
|
|
||
| session.sendAndWait(new MessageOptions().setPrompt("What is 5+5? Reply with just the number.")).get(60, | ||
| TimeUnit.SECONDS); | ||
|
|
||
| List<AssistantUsageEvent> usageEvents = events.stream().filter(e -> e instanceof AssistantUsageEvent) | ||
| .map(e -> (AssistantUsageEvent) e).toList(); | ||
|
|
||
| assertFalse(usageEvents.isEmpty(), "Should have received an assistant.usage event after model call"); | ||
|
|
||
| AssistantUsageEvent lastUsage = usageEvents.get(usageEvents.size() - 1); | ||
| assertNotNull(lastUsage.getData().model(), "Usage event should have a model field"); | ||
| assertFalse(lastUsage.getData().model().isEmpty(), "Model field should not be empty"); | ||
|
|
||
| session.close(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Verifies that a {@code session.usage_info} event is emitted after the model | ||
| * processes a prompt. | ||
| * | ||
| * @see Snapshot: | ||
| * event_fidelity/should_emit_session_usage_info_event_after_model_call | ||
| */ | ||
| @Test | ||
| void testShouldEmitSessionUsageInfoEventAfterModelCall() throws Exception { | ||
| ctx.configureForTest("event_fidelity", "should_emit_session_usage_info_event_after_model_call"); | ||
|
|
||
| try (CopilotClient client = ctx.createClient()) { | ||
| CopilotSession session = client | ||
| .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); | ||
|
|
||
| List<SessionEvent> events = new ArrayList<>(); | ||
| session.on(events::add); | ||
|
|
||
| session.sendAndWait(new MessageOptions().setPrompt("What is 5+5? Reply with just the number.")).get(60, | ||
| TimeUnit.SECONDS); | ||
|
|
||
| List<SessionUsageInfoEvent> usageInfoEvents = events.stream() | ||
| .filter(e -> e instanceof SessionUsageInfoEvent).map(e -> (SessionUsageInfoEvent) e).toList(); | ||
|
|
||
| assertFalse(usageInfoEvents.isEmpty(), "Should have received a session.usage_info event after model call"); | ||
|
|
||
| session.close(); | ||
| } | ||
| } | ||
| } |
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
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in ee55571: moved CLI exit message formatting into
CliServerManagerand updatedCopilotClientto call it, removing the reverse dependency.