From abfc92e4670a372f3c68919ef6a315175017c5cf Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 27 May 2026 15:15:16 -0400 Subject: [PATCH 01/11] feat(java): add JDK 25 default executor Use a multi-release JAR to select virtual threads as the default internal executor on JDK 25+, while retaining the common pool on older JDKs. Keep user-provided executors caller-owned and only shut down SDK-owned defaults. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/pom.xml | 46 +++++++ .../com/github/copilot/CopilotClient.java | 39 +++--- .../copilot/DefaultExecutorProvider.java | 22 ++++ .../copilot/rpc/CopilotClientOptions.java | 21 +-- .../copilot/DefaultExecutorProvider.java | 23 ++++ .../copilot/DefaultExecutorProviderTest.java | 124 ++++++++++++++++++ 6 files changed, 250 insertions(+), 25 deletions(-) create mode 100644 java/src/main/java/com/github/copilot/DefaultExecutorProvider.java create mode 100644 java/src/main/java25/com/github/copilot/DefaultExecutorProvider.java create mode 100644 java/src/test/java/com/github/copilot/DefaultExecutorProviderTest.java diff --git a/java/pom.xml b/java/pom.xml index a21ae9013..801dda9b5 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -447,6 +447,10 @@ ${project.build.directory}/jacoco-test-results/sdk-tests.exec ${project.reporting.outputDirectory}/jacoco-coverage + + + META-INF/versions/**/*.class + @@ -507,6 +511,48 @@ -XX:+EnableDynamicAgentLoading + + java25-multi-release + + [25,) + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile-java25 + compile + + compile + + + 25 + false + + ${project.basedir}/src/main/java25 + + true + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + + + + + + + skip-test-harness diff --git a/java/src/main/java/com/github/copilot/CopilotClient.java b/java/src/main/java/com/github/copilot/CopilotClient.java index 662b66c7b..5b36d969c 100644 --- a/java/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/src/main/java/com/github/copilot/CopilotClient.java @@ -14,6 +14,7 @@ import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -81,6 +82,8 @@ public final class CopilotClient implements AutoCloseable { public static final int AUTOCLOSEABLE_TIMEOUT_SECONDS = 10; private static final int FORCE_KILL_TIMEOUT_SECONDS = 10; private final CopilotClientOptions options; + private final Executor executor; + private final ExecutorService ownedExecutor; private final CliServerManager serverManager; private final LifecycleEventManager lifecycleManager = new LifecycleEventManager(); private final Map sessions = new ConcurrentHashMap<>(); @@ -168,6 +171,11 @@ public CopilotClient(CopilotClientOptions options) { this.optionsPort = null; } + Executor providedExecutor = this.options.getExecutor(); + this.executor = providedExecutor != null ? providedExecutor : DefaultExecutorProvider.create(); + this.ownedExecutor = providedExecutor == null && DefaultExecutorProvider.isOwned(this.executor) + && this.executor instanceof ExecutorService executorService ? executorService : null; + this.serverManager = new CliServerManager(this.options); this.serverManager.setConnectionToken(this.effectiveConnectionToken); } @@ -191,11 +199,8 @@ public CompletableFuture start() { private CompletableFuture startCore() { LOG.fine("Starting Copilot client"); - Executor exec = options.getExecutor(); try { - return exec != null - ? CompletableFuture.supplyAsync(this::startCoreBody, exec) - : CompletableFuture.supplyAsync(this::startCoreBody); + return CompletableFuture.supplyAsync(this::startCoreBody, executor); } catch (RejectedExecutionException e) { return CompletableFuture.failedFuture(e); } @@ -224,8 +229,7 @@ private Connection startCoreBody() { Connection connection = new Connection(rpc, process, new ServerRpc(rpc::invoke)); // Register handlers for server-to-client calls - RpcHandlerDispatcher dispatcher = new RpcHandlerDispatcher(sessions, lifecycleManager::dispatch, - options.getExecutor()); + RpcHandlerDispatcher dispatcher = new RpcHandlerDispatcher(sessions, lifecycleManager::dispatch, executor); dispatcher.registerHandlers(rpc); // Verify protocol version @@ -323,7 +327,6 @@ private static boolean isUnsupportedConnectMethod(JsonRpcException ex) { */ public CompletableFuture stop() { var closeFutures = new ArrayList>(); - Executor exec = options.getExecutor(); for (CopilotSession session : new ArrayList<>(sessions.values())) { Runnable closeTask = () -> { @@ -335,9 +338,7 @@ public CompletableFuture stop() { }; CompletableFuture future; try { - future = exec != null - ? CompletableFuture.runAsync(closeTask, exec) - : CompletableFuture.runAsync(closeTask); + future = CompletableFuture.runAsync(closeTask, executor); } catch (RejectedExecutionException e) { LOG.log(Level.WARNING, "Executor rejected session close task; closing inline", e); closeTask.run(); @@ -359,7 +360,7 @@ public CompletableFuture stop() { public CompletableFuture forceStop() { disposed = true; sessions.clear(); - return cleanupConnection(); + return cleanupConnection().whenComplete((ignored, error) -> shutdownOwnedExecutor()); } private CompletableFuture cleanupConnection() { @@ -451,9 +452,7 @@ public CompletableFuture createSession(SessionConfig config) { long setupNanos = System.nanoTime(); var session = new CopilotSession(sessionId, connection.rpc); - if (options.getExecutor() != null) { - session.setExecutor(options.getExecutor()); - } + session.setExecutor(executor); SessionRequestBuilder.configureSession(session, config); sessions.put(sessionId, session); LoggingHelpers.logTiming(LOG, Level.FINE, @@ -565,9 +564,7 @@ public CompletableFuture resumeSession(String sessionId, ResumeS // Register the session before the RPC call to avoid missing early events. long setupNanos = System.nanoTime(); var session = new CopilotSession(sessionId, connection.rpc); - if (options.getExecutor() != null) { - session.setExecutor(options.getExecutor()); - } + session.setExecutor(executor); SessionRequestBuilder.configureSession(session, config); sessions.put(sessionId, session); LoggingHelpers.logTiming(LOG, Level.FINE, @@ -1111,6 +1108,14 @@ public void close() { stop().get(AUTOCLOSEABLE_TIMEOUT_SECONDS, TimeUnit.SECONDS); } catch (Exception e) { LOG.log(Level.FINE, "Error during close", e); + } finally { + shutdownOwnedExecutor(); + } + } + + private void shutdownOwnedExecutor() { + if (ownedExecutor != null) { + ownedExecutor.shutdown(); } } diff --git a/java/src/main/java/com/github/copilot/DefaultExecutorProvider.java b/java/src/main/java/com/github/copilot/DefaultExecutorProvider.java new file mode 100644 index 000000000..6967cdeb9 --- /dev/null +++ b/java/src/main/java/com/github/copilot/DefaultExecutorProvider.java @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import java.util.concurrent.Executor; +import java.util.concurrent.ForkJoinPool; + +final class DefaultExecutorProvider { + + private DefaultExecutorProvider() { + } + + static Executor create() { + return ForkJoinPool.commonPool(); + } + + static boolean isOwned(Executor executor) { + return false; + } +} diff --git a/java/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java b/java/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java index 69464aa72..941467059 100644 --- a/java/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java +++ b/java/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java @@ -288,9 +288,11 @@ public CopilotClientOptions setEnvironment(Map environment) { /** * Gets the executor used for internal asynchronous operations. + *

+ * Returns {@code null} if no executor has been explicitly set, indicating that + * the SDK should use its default executor strategy. * - * @return the executor, or {@code null} to use the default - * {@code ForkJoinPool.commonPool()} + * @return the executor, or {@code null} if using SDK defaults */ public Executor getExecutor() { return executor; @@ -300,15 +302,18 @@ public Executor getExecutor() { * Sets the executor used for internal asynchronous operations. *

* When provided, the SDK uses this executor for all internal - * {@code CompletableFuture} combinators instead of the default - * {@code ForkJoinPool.commonPool()}. This allows callers to isolate SDK work - * onto a dedicated thread pool or integrate with container-managed threading. + * {@code CompletableFuture} combinators. This allows callers to isolate SDK + * work onto a dedicated thread pool or integrate with container-managed + * threading. *

- * Passing {@code null} reverts to the default {@code ForkJoinPool.commonPool()} - * behavior. + * The SDK will not shut down a user-provided executor. If you pass a custom + * {@code ExecutorService}, you remain responsible for shutting it down. + *

+ * If not set (or set to {@code null}), the SDK uses its default executor: + * virtual threads on JDK 25+, {@code ForkJoinPool.commonPool()} on older JDKs. * * @param executor - * the executor to use, or {@code null} for the default + * the executor to use, or {@code null} for SDK defaults * @return this options instance for fluent chaining */ public CopilotClientOptions setExecutor(Executor executor) { diff --git a/java/src/main/java25/com/github/copilot/DefaultExecutorProvider.java b/java/src/main/java25/com/github/copilot/DefaultExecutorProvider.java new file mode 100644 index 000000000..a0a4740bb --- /dev/null +++ b/java/src/main/java25/com/github/copilot/DefaultExecutorProvider.java @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +final class DefaultExecutorProvider { + + private DefaultExecutorProvider() { + } + + static Executor create() { + return Executors.newVirtualThreadPerTaskExecutor(); + } + + static boolean isOwned(Executor executor) { + return executor instanceof ExecutorService; + } +} diff --git a/java/src/test/java/com/github/copilot/DefaultExecutorProviderTest.java b/java/src/test/java/com/github/copilot/DefaultExecutorProviderTest.java new file mode 100644 index 000000000..a0702e46a --- /dev/null +++ b/java/src/test/java/com/github/copilot/DefaultExecutorProviderTest.java @@ -0,0 +1,124 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.TimeUnit; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import org.junit.jupiter.api.Test; + +import com.github.copilot.rpc.CopilotClientOptions; + +class DefaultExecutorProviderTest { + + @Test + void baseProviderUsesCommonPoolWithoutOwnership() { + Executor executor = DefaultExecutorProvider.create(); + + assertSame(ForkJoinPool.commonPool(), executor); + assertFalse(DefaultExecutorProvider.isOwned(executor)); + } + + @Test + void clientDoesNotShutDownUserProvidedExecutor() { + ExecutorService executor = Executors.newSingleThreadExecutor(); + try { + try (var client = new CopilotClient(new CopilotClientOptions().setAutoStart(false).setExecutor(executor))) { + assertNotNull(client); + } + + assertFalse(executor.isShutdown()); + } finally { + executor.shutdownNow(); + } + } + + @Test + void multiReleaseJarUsesOwnedVirtualThreadExecutorOnJdk25() throws Exception { + if (Runtime.version().feature() < 25) { + return; + } + + Path classes = Path.of("target", "classes"); + Path baseClass = classes.resolve("com/github/copilot/DefaultExecutorProvider.class"); + Path java25Class = classes.resolve("META-INF/versions/25/com/github/copilot/DefaultExecutorProvider.class"); + assertTrue(Files.exists(baseClass), "Base DefaultExecutorProvider class must be compiled"); + assertTrue(Files.exists(java25Class), "JDK 25 build must compile the multi-release executor provider"); + + Path jar = Files.createTempFile("copilot-sdk-default-executor", ".jar"); + try { + createProviderJar(jar, baseClass, java25Class); + + try (var loader = new URLClassLoader(new URL[]{jar.toUri().toURL()}, null)) { + Class provider = Class.forName("com.github.copilot.DefaultExecutorProvider", true, loader); + Method create = provider.getDeclaredMethod("create"); + Method isOwned = provider.getDeclaredMethod("isOwned", Executor.class); + create.setAccessible(true); + isOwned.setAccessible(true); + + Executor executor = (Executor) create.invoke(null); + try { + assertTrue((Boolean) isOwned.invoke(null, executor)); + CompletableFuture virtualThreadUsed = new CompletableFuture<>(); + executor.execute(() -> virtualThreadUsed.complete(isCurrentThreadVirtual())); + + assertTrue(virtualThreadUsed.get(5, TimeUnit.SECONDS)); + } finally { + if (executor instanceof ExecutorService executorService) { + executorService.shutdownNow(); + } + } + } + } finally { + Files.deleteIfExists(jar); + } + } + + private static boolean isCurrentThreadVirtual() { + try { + Method isVirtual = Thread.class.getMethod("isVirtual"); + return (Boolean) isVirtual.invoke(Thread.currentThread()); + } catch (ReflectiveOperationException e) { + return false; + } + } + + private static void createProviderJar(Path jar, Path baseClass, Path java25Class) throws IOException { + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); + attributes.putValue("Multi-Release", "true"); + + try (JarOutputStream output = new JarOutputStream(Files.newOutputStream(jar), manifest)) { + addClass(output, "com/github/copilot/DefaultExecutorProvider.class", baseClass); + addClass(output, "META-INF/versions/25/com/github/copilot/DefaultExecutorProvider.class", java25Class); + } + } + + private static void addClass(JarOutputStream output, String entryName, Path classFile) throws IOException { + output.putNextEntry(new JarEntry(entryName)); + Files.copy(classFile, output); + output.closeEntry(); + } +} From bb2e64cbb0ce153fb7c5f4a40a6827c51d9ec173 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 27 May 2026 15:23:06 -0400 Subject: [PATCH 02/11] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../java/com/github/copilot/CopilotClient.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/java/src/main/java/com/github/copilot/CopilotClient.java b/java/src/main/java/com/github/copilot/CopilotClient.java index 5b36d969c..3c50e53e1 100644 --- a/java/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/src/main/java/com/github/copilot/CopilotClient.java @@ -1114,8 +1114,21 @@ public void close() { } private void shutdownOwnedExecutor() { - if (ownedExecutor != null) { - ownedExecutor.shutdown(); + if (ownedExecutor == null) { + return; + } + + ownedExecutor.shutdown(); + try { + if (!ownedExecutor.awaitTermination(AUTOCLOSEABLE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { + LOG.log(Level.FINE, "Owned executor did not terminate within {0} seconds; forcing shutdown.", + AUTOCLOSEABLE_TIMEOUT_SECONDS); + ownedExecutor.shutdownNow(); + } + } catch (InterruptedException e) { + ownedExecutor.shutdownNow(); + Thread.currentThread().interrupt(); + LOG.log(Level.FINE, "Interrupted while waiting for owned executor to terminate", e); } } From 12c04de393fe23e8b1d553bde441e5de66645946 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 27 May 2026 15:29:39 -0400 Subject: [PATCH 03/11] test(java): cover owned default executor shutdown Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../copilot/DefaultExecutorProviderTest.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/java/src/test/java/com/github/copilot/DefaultExecutorProviderTest.java b/java/src/test/java/com/github/copilot/DefaultExecutorProviderTest.java index a0702e46a..65365809b 100644 --- a/java/src/test/java/com/github/copilot/DefaultExecutorProviderTest.java +++ b/java/src/test/java/com/github/copilot/DefaultExecutorProviderTest.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.lang.reflect.Method; +import java.lang.reflect.Field; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; @@ -95,6 +96,38 @@ void multiReleaseJarUsesOwnedVirtualThreadExecutorOnJdk25() throws Exception { } } + @Test + void clientCloseShutsDownOwnedDefaultExecutorOnJdk25() throws Exception { + if (Runtime.version().feature() < 25) { + return; + } + + Path classes = Path.of("target", "classes"); + Path jar = Files.createTempFile("copilot-sdk-client-default-executor", ".jar"); + try { + createClassesJar(jar, classes); + + try (var loader = new URLClassLoader(new URL[]{jar.toUri().toURL()}, null)) { + Class clientClass = Class.forName("com.github.copilot.CopilotClient", true, loader); + AutoCloseable client = (AutoCloseable) clientClass.getConstructor().newInstance(); + Field ownedExecutorField = clientClass.getDeclaredField("ownedExecutor"); + ownedExecutorField.setAccessible(true); + ExecutorService ownedExecutor = (ExecutorService) ownedExecutorField.get(client); + + assertNotNull(ownedExecutor); + assertFalse(ownedExecutor.isShutdown()); + + client.close(); + + assertTrue(ownedExecutor.isShutdown()); + assertTrue(ownedExecutor.awaitTermination(5, TimeUnit.SECONDS)); + assertTrue(ownedExecutor.isTerminated()); + } + } finally { + Files.deleteIfExists(jar); + } + } + private static boolean isCurrentThreadVirtual() { try { Method isVirtual = Thread.class.getMethod("isVirtual"); @@ -116,6 +149,31 @@ private static void createProviderJar(Path jar, Path baseClass, Path java25Class } } + private static void createClassesJar(Path jar, Path classes) throws IOException { + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); + attributes.putValue("Multi-Release", "true"); + + try (JarOutputStream output = new JarOutputStream(Files.newOutputStream(jar), manifest); + var files = Files.walk(classes)) { + var iterator = files.iterator(); + while (iterator.hasNext()) { + Path file = iterator.next(); + if (!Files.isRegularFile(file)) { + continue; + } + + String entryName = classes.relativize(file).toString().replace('\\', '/'); + if ("META-INF/MANIFEST.MF".equals(entryName)) { + continue; + } + + addClass(output, entryName, file); + } + } + } + private static void addClass(JarOutputStream output, String entryName, Path classFile) throws IOException { output.putNextEntry(new JarEntry(entryName)); Files.copy(classFile, output); From 7819e3f682f52d39f22bf34fba37009869947ec6 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 27 May 2026 16:26:11 -0400 Subject: [PATCH 04/11] refactor(java): make default executor provider internal Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../com/github/copilot/CopilotClient.java | 4 +-- ...der.java => InternalExecutorProvider.java} | 4 +-- ...der.java => InternalExecutorProvider.java} | 4 +-- ...java => InternalExecutorProviderTest.java} | 26 ++++++++++--------- 4 files changed, 20 insertions(+), 18 deletions(-) rename java/src/main/java/com/github/copilot/{DefaultExecutorProvider.java => InternalExecutorProvider.java} (86%) rename java/src/main/java25/com/github/copilot/{DefaultExecutorProvider.java => InternalExecutorProvider.java} (88%) rename java/src/test/java/com/github/copilot/{DefaultExecutorProviderTest.java => InternalExecutorProviderTest.java} (88%) diff --git a/java/src/main/java/com/github/copilot/CopilotClient.java b/java/src/main/java/com/github/copilot/CopilotClient.java index 3c50e53e1..f004ba715 100644 --- a/java/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/src/main/java/com/github/copilot/CopilotClient.java @@ -172,8 +172,8 @@ public CopilotClient(CopilotClientOptions options) { } Executor providedExecutor = this.options.getExecutor(); - this.executor = providedExecutor != null ? providedExecutor : DefaultExecutorProvider.create(); - this.ownedExecutor = providedExecutor == null && DefaultExecutorProvider.isOwned(this.executor) + this.executor = providedExecutor != null ? providedExecutor : InternalExecutorProvider.create(); + this.ownedExecutor = providedExecutor == null && InternalExecutorProvider.isOwned(this.executor) && this.executor instanceof ExecutorService executorService ? executorService : null; this.serverManager = new CliServerManager(this.options); diff --git a/java/src/main/java/com/github/copilot/DefaultExecutorProvider.java b/java/src/main/java/com/github/copilot/InternalExecutorProvider.java similarity index 86% rename from java/src/main/java/com/github/copilot/DefaultExecutorProvider.java rename to java/src/main/java/com/github/copilot/InternalExecutorProvider.java index 6967cdeb9..8657027e8 100644 --- a/java/src/main/java/com/github/copilot/DefaultExecutorProvider.java +++ b/java/src/main/java/com/github/copilot/InternalExecutorProvider.java @@ -7,9 +7,9 @@ import java.util.concurrent.Executor; import java.util.concurrent.ForkJoinPool; -final class DefaultExecutorProvider { +final class InternalExecutorProvider { - private DefaultExecutorProvider() { + private InternalExecutorProvider() { } static Executor create() { diff --git a/java/src/main/java25/com/github/copilot/DefaultExecutorProvider.java b/java/src/main/java25/com/github/copilot/InternalExecutorProvider.java similarity index 88% rename from java/src/main/java25/com/github/copilot/DefaultExecutorProvider.java rename to java/src/main/java25/com/github/copilot/InternalExecutorProvider.java index a0a4740bb..257d0f61e 100644 --- a/java/src/main/java25/com/github/copilot/DefaultExecutorProvider.java +++ b/java/src/main/java25/com/github/copilot/InternalExecutorProvider.java @@ -8,9 +8,9 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -final class DefaultExecutorProvider { +final class InternalExecutorProvider { - private DefaultExecutorProvider() { + private InternalExecutorProvider() { } static Executor create() { diff --git a/java/src/test/java/com/github/copilot/DefaultExecutorProviderTest.java b/java/src/test/java/com/github/copilot/InternalExecutorProviderTest.java similarity index 88% rename from java/src/test/java/com/github/copilot/DefaultExecutorProviderTest.java rename to java/src/test/java/com/github/copilot/InternalExecutorProviderTest.java index 65365809b..7ec4a420d 100644 --- a/java/src/test/java/com/github/copilot/DefaultExecutorProviderTest.java +++ b/java/src/test/java/com/github/copilot/InternalExecutorProviderTest.java @@ -10,8 +10,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; -import java.lang.reflect.Method; import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; @@ -31,14 +32,15 @@ import com.github.copilot.rpc.CopilotClientOptions; -class DefaultExecutorProviderTest { +class InternalExecutorProviderTest { @Test void baseProviderUsesCommonPoolWithoutOwnership() { - Executor executor = DefaultExecutorProvider.create(); + Executor executor = InternalExecutorProvider.create(); assertSame(ForkJoinPool.commonPool(), executor); - assertFalse(DefaultExecutorProvider.isOwned(executor)); + assertFalse(InternalExecutorProvider.isOwned(executor)); + assertFalse(Modifier.isPublic(InternalExecutorProvider.class.getModifiers())); } @Test @@ -62,17 +64,17 @@ void multiReleaseJarUsesOwnedVirtualThreadExecutorOnJdk25() throws Exception { } Path classes = Path.of("target", "classes"); - Path baseClass = classes.resolve("com/github/copilot/DefaultExecutorProvider.class"); - Path java25Class = classes.resolve("META-INF/versions/25/com/github/copilot/DefaultExecutorProvider.class"); - assertTrue(Files.exists(baseClass), "Base DefaultExecutorProvider class must be compiled"); + Path baseClass = classes.resolve("com/github/copilot/InternalExecutorProvider.class"); + Path java25Class = classes.resolve("META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class"); + assertTrue(Files.exists(baseClass), "Base InternalExecutorProvider class must be compiled"); assertTrue(Files.exists(java25Class), "JDK 25 build must compile the multi-release executor provider"); - Path jar = Files.createTempFile("copilot-sdk-default-executor", ".jar"); + Path jar = Files.createTempFile("copilot-sdk-internal-executor", ".jar"); try { createProviderJar(jar, baseClass, java25Class); try (var loader = new URLClassLoader(new URL[]{jar.toUri().toURL()}, null)) { - Class provider = Class.forName("com.github.copilot.DefaultExecutorProvider", true, loader); + Class provider = Class.forName("com.github.copilot.InternalExecutorProvider", true, loader); Method create = provider.getDeclaredMethod("create"); Method isOwned = provider.getDeclaredMethod("isOwned", Executor.class); create.setAccessible(true); @@ -103,7 +105,7 @@ void clientCloseShutsDownOwnedDefaultExecutorOnJdk25() throws Exception { } Path classes = Path.of("target", "classes"); - Path jar = Files.createTempFile("copilot-sdk-client-default-executor", ".jar"); + Path jar = Files.createTempFile("copilot-sdk-client-internal-executor", ".jar"); try { createClassesJar(jar, classes); @@ -144,8 +146,8 @@ private static void createProviderJar(Path jar, Path baseClass, Path java25Class attributes.putValue("Multi-Release", "true"); try (JarOutputStream output = new JarOutputStream(Files.newOutputStream(jar), manifest)) { - addClass(output, "com/github/copilot/DefaultExecutorProvider.class", baseClass); - addClass(output, "META-INF/versions/25/com/github/copilot/DefaultExecutorProvider.class", java25Class); + addClass(output, "com/github/copilot/InternalExecutorProvider.class", baseClass); + addClass(output, "META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class", java25Class); } } From 9fa7db5d576fd13f4a9dd714386afd1480735f72 Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Wed, 27 May 2026 17:34:09 -0700 Subject: [PATCH 05/11] Rebase and settings --- .vscode/settings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index d0d8465c3..c4ae9c761 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -25,5 +25,6 @@ "[go]": { "editor.defaultFormatter": "golang.go" }, - "java.configuration.updateBuildConfiguration": "automatic" + "java.configuration.updateBuildConfiguration": "automatic", + "java.compile.nullAnalysis.mode": "automatic" } From 8d2a11e0d7b032f65cb0cffaa0599249d9a914bd Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Wed, 27 May 2026 22:07:14 -0700 Subject: [PATCH 06/11] WIP: Operationalize sketch from @brunoborges --- .github/workflows/java-publish-maven.yml | 4 +- .github/workflows/java-publish-snapshot.yml | 4 +- .github/workflows/java-sdk-tests.yml | 26 +- java/20261027-2202-job-logs.txt | 1208 +++++++++++++++++++ java/pom.xml | 50 + 5 files changed, 1287 insertions(+), 5 deletions(-) create mode 100644 java/20261027-2202-job-logs.txt diff --git a/.github/workflows/java-publish-maven.yml b/.github/workflows/java-publish-maven.yml index 2f150f1b1..20b9b2054 100644 --- a/.github/workflows/java-publish-maven.yml +++ b/.github/workflows/java-publish-maven.yml @@ -54,10 +54,10 @@ jobs: - uses: ./.github/actions/setup-copilot - - name: Set up JDK 17 + - name: Set up JDK 25 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 with: - java-version: "17" + java-version: "25" distribution: "microsoft" cache: "maven" server-id: central diff --git a/.github/workflows/java-publish-snapshot.yml b/.github/workflows/java-publish-snapshot.yml index 7bc231c73..8c957627f 100644 --- a/.github/workflows/java-publish-snapshot.yml +++ b/.github/workflows/java-publish-snapshot.yml @@ -30,10 +30,10 @@ jobs: - uses: ./.github/actions/setup-copilot - - name: Set up JDK 17 + - name: Set up JDK 25 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 with: - java-version: "17" + java-version: "25" distribution: "microsoft" cache: "maven" server-id: central diff --git a/.github/workflows/java-sdk-tests.yml b/.github/workflows/java-sdk-tests.yml index 5e9b504fd..682331992 100644 --- a/.github/workflows/java-sdk-tests.yml +++ b/.github/workflows/java-sdk-tests.yml @@ -28,6 +28,12 @@ on: - "!**/*.gif" - "!**/*.svg" workflow_dispatch: + inputs: + test-jdk17: + description: "Also run tests on JDK 17 (compatibility check)" + type: boolean + required: false + default: false merge_group: permissions: @@ -52,7 +58,7 @@ jobs: - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 with: - java-version: "17" + java-version: "25" distribution: "microsoft" cache: "maven" @@ -126,3 +132,21 @@ jobs: java/target/surefire-reports/ java/target/surefire-reports-isolated/ retention-days: 7 + + - name: Switch to JDK 17 + if: inputs.test-jdk17 == true + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 + with: + java-version: "17" + distribution: "microsoft" + + - name: Re-run tests on JDK 17 (no recompilation) + if: inputs.test-jdk17 == true + env: + CI: "true" + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_CLI_PATH: ${{ steps.setup-copilot.outputs.cli-path }} + run: | + echo "Running tests against JDK 25-built classes using JDK 17 runtime..." + java -version + mvn surefire:test -Denforcer.skip=true diff --git a/java/20261027-2202-job-logs.txt b/java/20261027-2202-job-logs.txt new file mode 100644 index 000000000..86bf20178 --- /dev/null +++ b/java/20261027-2202-job-logs.txt @@ -0,0 +1,1208 @@ +-*- mode: compilation; default-directory: "~/workareas/copilot-sdk/java/" -*- +Compilation started at Wed May 27 21:55:34 + +mvn clean verify +WARNING: A terminally deprecated method in sun.misc.Unsafe has been called +WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper (file:/usr/share/maven/lib/guava.jar) +WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper +WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release +[INFO] Scanning for projects... +[INFO] Inspecting build with total of 1 modules +[INFO] Installing Central Publishing features +[INFO] +[INFO] --------------------< com.github:copilot-sdk-java >--------------------- +[INFO] Building GitHub Copilot SDK :: Java 1.0.0-beta-java.5-SNAPSHOT +[INFO] --------------------------------[ jar ]--------------------------------- +[INFO] +[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ copilot-sdk-java --- +[INFO] Deleting /home/edburns/workareas/copilot-sdk/java/target +[INFO] +[INFO] --- maven-checkstyle-plugin:3.6.0:check (validate) @ copilot-sdk-java --- +[INFO] Starting audit... +Audit done. +[INFO] You have 0 Checkstyle violations. +[INFO] +[INFO] --- maven-enforcer-plugin:3.5.0:enforce (enforce-jdk25) @ copilot-sdk-java --- +Downloading from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-xml/3.0.0/plexus-xml-3.0.0.pom +Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-xml/3.0.0/plexus-xml-3.0.0.pom (3.7 kB at 3.9 kB/s) +Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/enforcer/enforcer-api/3.5.0/enforcer-api-3.5.0.pom +Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/enforcer/enforcer-api/3.5.0/enforcer-api-3.5.0.pom (3.8 kB at 34 kB/s) +Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/enforcer/enforcer-rules/3.5.0/enforcer-rules-3.5.0.pom +Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/enforcer/enforcer-rules/3.5.0/enforcer-rules-3.5.0.pom (5.4 kB at 76 kB/s) +Downloading from central: https://repo.maven.apache.org/maven2/commons-codec/commons-codec/1.17.0/commons-codec-1.17.0.pom +Downloaded from central: https://repo.maven.apache.org/maven2/commons-codec/commons-codec/1.17.0/commons-codec-1.17.0.pom (18 kB at 255 kB/s) +Downloading from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-xml/3.0.0/plexus-xml-3.0.0.jar +Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/enforcer/enforcer-api/3.5.0/enforcer-api-3.5.0.jar +Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/enforcer/enforcer-rules/3.5.0/enforcer-rules-3.5.0.jar +Downloading from central: https://repo.maven.apache.org/maven2/commons-codec/commons-codec/1.17.0/commons-codec-1.17.0.jar +Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-xml/3.0.0/plexus-xml-3.0.0.jar (93 kB at 782 kB/s) +Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/enforcer/enforcer-api/3.5.0/enforcer-api-3.5.0.jar (15 kB at 91 kB/s) +Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/enforcer/enforcer-rules/3.5.0/enforcer-rules-3.5.0.jar (147 kB at 699 kB/s) +Downloaded from central: https://repo.maven.apache.org/maven2/commons-codec/commons-codec/1.17.0/commons-codec-1.17.0.jar (373 kB at 1.4 MB/s) +[INFO] Rule 0: org.apache.maven.enforcer.rules.version.RequireJavaVersion passed +[INFO] +[INFO] --- jacoco-maven-plugin:0.8.14:prepare-agent (wire-up-coverage-instrumentation) @ copilot-sdk-java --- +[INFO] testExecutionAgentArgs set to -javaagent:/home/edburns/.m2/repository/org/jacoco/org.jacoco.agent/0.8.14/org.jacoco.agent-0.8.14-runtime.jar=destfile=/home/edburns/workareas/copilot-sdk/java/target/jacoco-test-results/sdk-tests.exec,includes=com/github/copilot/**,excludes=com/github/copilot/E2ETestContext*:com/github/copilot/CapiProxy* +[INFO] +[INFO] --- build-helper-maven-plugin:3.6.1:add-source (add-generated-source) @ copilot-sdk-java --- +[INFO] Source directory: /home/edburns/workareas/copilot-sdk/java/src/generated/java added. +[INFO] +[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ copilot-sdk-java --- +[INFO] Using 'UTF-8' encoding to copy filtered resources. +[INFO] skip non existing resourceDirectory /home/edburns/workareas/copilot-sdk/java/src/main/resources +[INFO] +[INFO] --- maven-compiler-plugin:3.15.0:compile (default-compile) @ copilot-sdk-java --- +[INFO] Recompiling the module because of changed source code. +[INFO] Compiling 853 source files with javac [debug release 17 module-path] to target/classes +[INFO] /home/edburns/workareas/copilot-sdk/java/src/main/java/com/github/copilot/CopilotSession.java: Some input files use or override a deprecated API. +[INFO] /home/edburns/workareas/copilot-sdk/java/src/main/java/com/github/copilot/CopilotSession.java: Recompile with -Xlint:deprecation for details. +[INFO] +[INFO] --- maven-compiler-plugin:3.15.0:compile (compile-java25) @ copilot-sdk-java --- +[INFO] Compiling 1 source file with javac [debug release 25] to target/classes/META-INF/versions/25 +[INFO] +[INFO] --- maven-antrun-plugin:3.2.0:run (clone-or-update-copilot-sdk) @ copilot-sdk-java --- +[INFO] Executing tasks +[WARNING] [echo] Cloning copilot-sdk repository at commit 60104052cd914949ddf8c7a31e1856cd6db0a57c... +[INFO] [exec] Cloning into '/home/edburns/workareas/copilot-sdk/java/target/copilot-sdk'... +[INFO] [exec] From https://github.com/github/copilot-sdk +[INFO] [exec] * branch 60104052cd914949ddf8c7a31e1856cd6db0a57c -> FETCH_HEAD +[INFO] [exec] HEAD is now at 6010405 Multitenancy hardening: Client Mode (#1428) +[INFO] Executed tasks +[INFO] +[INFO] --- exec-maven-plugin:3.6.3:exec (install-harness-dependencies) @ copilot-sdk-java --- + +added 146 packages, and audited 147 packages in 24s + +45 packages are looking for funding + run `npm fund` for details + +found 0 vulnerabilities +[INFO] +[INFO] --- exec-maven-plugin:3.6.3:exec (install-nodejs-cli-dependencies) @ copilot-sdk-java --- + +added 203 packages, and audited 204 packages in 15s + +62 packages are looking for funding + run `npm fund` for details + +found 0 vulnerabilities +[INFO] +[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ copilot-sdk-java --- +[INFO] Using 'UTF-8' encoding to copy filtered resources. +[INFO] Copying 2 resources +[INFO] +[INFO] --- maven-compiler-plugin:3.15.0:testCompile (default-testCompile) @ copilot-sdk-java --- +[INFO] Recompiling the module because of changed dependency. +[INFO] Compiling 63 source files with javac [debug release 17 module-path] to target/test-classes +[INFO] /home/edburns/workareas/copilot-sdk/java/src/test/java/com/github/copilot/CommandsTest.java: Some input files use or override a deprecated API. +[INFO] /home/edburns/workareas/copilot-sdk/java/src/test/java/com/github/copilot/CommandsTest.java: Recompile with -Xlint:deprecation for details. +[INFO] +[INFO] --- maven-antrun-plugin:3.2.0:run (print-test-jdk-banner) @ copilot-sdk-java --- +[INFO] Executing tasks +[WARNING] [echo] ⚠⚠⚠ Using Java 25.0.2 (Microsoft) to run tests. +[INFO] Executed tasks +[INFO] +[INFO] --- maven-surefire-plugin:3.5.5:test (default-test) @ copilot-sdk-java --- +[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider +[INFO] +[INFO] ------------------------------------------------------- +[INFO] T E S T S +[INFO] ------------------------------------------------------- +[INFO] Running com.github.copilot.AgentInfoTest +[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.146 s -- in com.github.copilot.AgentInfoTest +[INFO] Running com.github.copilot.AgentModeTest +[INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.546 s -- in com.github.copilot.AgentModeTest +[INFO] Running com.github.copilot.AskUserTest +May 27, 2026 9:56:52 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'ask_user/should_handle_freeform_user_input_response' expects prompts: [Ask me a question using ask_user and then include my answer in your response. The question should be 'What is] +May 27, 2026 9:56:56 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'ask_user/should_receive_choices_in_user_input_request' expects prompts: ["Use the ask_user tool to ask me to pick between exactly two options: 'Red' and 'Blue'. These should be] +May 27, 2026 9:56:58 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'ask_user/should_invoke_user_input_handler_when_model_uses_ask_user_tool' expects prompts: [Ask me to choose between 'Option A' and 'Option B' using the ask_user tool. Wait for my response before] +[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 10.29 s -- in com.github.copilot.AskUserTest +[INFO] Running com.github.copilot.CliServerManagerTest +[INFO] Tests run: 22, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.166 s -- in com.github.copilot.CliServerManagerTest +[INFO] Running com.github.copilot.ClosedSessionGuardTest +May 27, 2026 9:57:02 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:04 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:06 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:08 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:10 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:12 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:14 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:16 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:18 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:20 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:22 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:25 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:27 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:31 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:34 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:57:36 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +[INFO] Tests run: 16, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 38.13 s -- in com.github.copilot.ClosedSessionGuardTest +[INFO] Running com.github.copilot.CommandsTest +[INFO] Tests run: 11, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.078 s -- in com.github.copilot.CommandsTest +[INFO] Running com.github.copilot.CompactionTest +May 27, 2026 9:57:41 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'compaction/should_not_emit_compaction_events_when_infinite_sessions_disabled' expects prompts: [What is 2+2?] +[WARNING] Tests run: 2, Failures: 0, Errors: 0, Skipped: 1, Time elapsed: 5.485 s -- in com.github.copilot.CompactionTest +[INFO] Running com.github.copilot.ConfigCloneTest +[INFO] Tests run: 27, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.118 s -- in com.github.copilot.ConfigCloneTest +[INFO] Running com.github.copilot.CopilotClientModeTest +[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.020 s -- in com.github.copilot.CopilotClientModeTest +[INFO] Running com.github.copilot.CopilotClientTest +May 27, 2026 9:58:17 PM com.github.copilot.LoggingHelpers logTiming +WARNING: CopilotClient.start failed. Elapsed=PT30.004S +java.util.concurrent.TimeoutException + at java.base/java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1981) + at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2116) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CopilotClient.verifyProtocolVersion(CopilotClient.java:272) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CopilotClient.startCoreBody(CopilotClient.java:236) + at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1789) + at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1781) + at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:511) + at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1450) + at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:2019) + at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:187) + +May 27, 2026 9:58:17 PM com.github.copilot.LoggingHelpers logTiming +WARNING: CopilotClient.start failed. Elapsed=PT0.002S +java.io.IOException: Cannot run program "/nonexistent/path/to/cli": Exec failed, error: 2 (No such file or directory) + at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1112) + at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1046) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CliServerManager.startCliServer(CliServerManager.java:168) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CopilotClient.startCoreBody(CopilotClient.java:220) + at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1789) + at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1781) + at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:511) + at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1450) + at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:2019) + at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:187) +Caused by: java.io.IOException: Exec failed, error: 2 (No such file or directory) + at java.base/java.lang.ProcessImpl.forkAndExec(Native Method) + at java.base/java.lang.ProcessImpl.(ProcessImpl.java:300) + at java.base/java.lang.ProcessImpl.start(ProcessImpl.java:231) + at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1078) + ... 9 more + +May 27, 2026 9:58:22 PM com.github.copilot.LoggingHelpers logTiming +WARNING: CopilotClient.start failed. Elapsed=PT0.002S +java.io.IOException: Cannot run program "/nonexistent/path/to/cli": Exec failed, error: 2 (No such file or directory) + at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1112) + at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1046) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CliServerManager.startCliServer(CliServerManager.java:168) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CopilotClient.startCoreBody(CopilotClient.java:220) + at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1789) + at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1781) + at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:511) + at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1450) + at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:2019) + at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:187) +Caused by: java.io.IOException: Exec failed, error: 2 (No such file or directory) + at java.base/java.lang.ProcessImpl.forkAndExec(Native Method) + at java.base/java.lang.ProcessImpl.(ProcessImpl.java:300) + at java.base/java.lang.ProcessImpl.start(ProcessImpl.java:231) + at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1078) + ... 9 more + +May 27, 2026 9:58:22 PM com.github.copilot.LoggingHelpers logTiming +WARNING: CopilotClient.start failed. Elapsed=PT0.012S +java.io.IOException: Cannot run program "/nonexistent/path/to/cli": Exec failed, error: 2 (No such file or directory) + at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1112) + at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1046) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CliServerManager.startCliServer(CliServerManager.java:168) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CopilotClient.startCoreBody(CopilotClient.java:220) + at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1789) + at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1781) + at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:511) + at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1450) + at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:2019) + at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:187) +Caused by: java.io.IOException: Exec failed, error: 2 (No such file or directory) + at java.base/java.lang.ProcessImpl.forkAndExec(Native Method) + at java.base/java.lang.ProcessImpl.(ProcessImpl.java:300) + at java.base/java.lang.ProcessImpl.start(ProcessImpl.java:231) + at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1078) + ... 9 more + +May 27, 2026 9:58:22 PM com.github.copilot.LoggingHelpers logTiming +WARNING: CopilotClient.start failed. Elapsed=PT0.003S +java.io.IOException: Cannot run program "/nonexistent/path/to/cli": Exec failed, error: 2 (No such file or directory) + at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1112) + at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1046) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CliServerManager.startCliServer(CliServerManager.java:168) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CopilotClient.startCoreBody(CopilotClient.java:220) + at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1789) + at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1781) + at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:511) + at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1450) + at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:2019) + at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:187) +Caused by: java.io.IOException: Exec failed, error: 2 (No such file or directory) + at java.base/java.lang.ProcessImpl.forkAndExec(Native Method) + at java.base/java.lang.ProcessImpl.(ProcessImpl.java:300) + at java.base/java.lang.ProcessImpl.start(ProcessImpl.java:231) + at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1078) + ... 9 more + +[INFO] Tests run: 38, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 40.30 s -- in com.github.copilot.CopilotClientTest +[INFO] Running com.github.copilot.CopilotSessionTest +May 27, 2026 9:58:26 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_get_session_metadata_by_id' expects prompts: [Say hello] +May 27, 2026 9:58:29 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_create_a_session_with_defaultagent_excludedtools' expects prompts: [What is 1+1?] +May 27, 2026 9:58:31 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:58:33 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_list_sessions' expects prompts: [Say hello, Say goodbye] +May 27, 2026 9:58:36 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_delete_session' expects prompts: [Hello] +May 27, 2026 9:58:38 PM com.github.copilot.LoggingHelpers logTiming +WARNING: JsonRpc.invoke JSON-RPC request finished. Elapsed=PT0.016S, Method=session.resume, RequestId=5, Status=Failed +java.util.concurrent.CompletionException: com.github.copilot.JsonRpcException: Request session.resume failed with message: Session not found: 6727df67-df52-40ca-8b50-7eb9f21d6f3f + at java.base/java.util.concurrent.CompletableFuture.wrapInCompletionException(CompletableFuture.java:323) + at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:376) + at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:391) + at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:657) + at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:531) + at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2221) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.handleMessage(JsonRpcClient.java:281) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:256) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) +Caused by: com.github.copilot.JsonRpcException: Request session.resume failed with message: Session not found: 6727df67-df52-40ca-8b50-7eb9f21d6f3f + ... 7 more + +May 27, 2026 9:58:38 PM com.github.copilot.LoggingHelpers logTiming +WARNING: CopilotClient.resumeSession failed. Elapsed=PT0.019S, SessionId=6727df67-df52-40ca-8b50-7eb9f21d6f3f +java.util.concurrent.CompletionException: com.github.copilot.JsonRpcException: Request session.resume failed with message: Session not found: 6727df67-df52-40ca-8b50-7eb9f21d6f3f + at java.base/java.util.concurrent.CompletableFuture.wrapInCompletionException(CompletableFuture.java:323) + at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:376) + at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:391) + at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:657) + at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:531) + at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2221) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.handleMessage(JsonRpcClient.java:281) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:256) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) +Caused by: com.github.copilot.JsonRpcException: Request session.resume failed with message: Session not found: 6727df67-df52-40ca-8b50-7eb9f21d6f3f + ... 7 more + +May 27, 2026 9:58:38 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_get_last_session_id' expects prompts: [Say hello] +May 27, 2026 9:58:40 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/sendandwait_blocks_until_session_idle_and_returns_final_assistant_message' expects prompts: [What is 2+2?] +May 27, 2026 9:58:43 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/sendandwait_throws_on_timeout' expects prompts: [Run 'sleep 2 && echo done'] +May 27, 2026 9:58:44 PM com.github.copilot.LoggingHelpers logTiming +WARNING: CopilotSession.sendAndWait failed. Elapsed=PT0.100S, SessionId=94092f76-75c0-4a68-bbb1-c11b78c0f282, CompletedBy=timeout +java.util.concurrent.TimeoutException: sendAndWait timed out after 100ms + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CopilotSession.lambda$sendAndWait$2(CopilotSession.java:559) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:309) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) + +May 27, 2026 9:58:44 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_abort_a_session' expects prompts: [run the shell command 'sleep 100' (note this works on both bash and PowerShell), What is 2+2?] +May 27, 2026 9:58:47 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_create_a_session_with_availabletools' expects prompts: [What is 1+1?] +May 27, 2026 9:58:49 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_list_sessions' expects prompts: [Say hello, Say goodbye] +May 27, 2026 9:58:51 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_create_session_with_custom_config_dir' expects prompts: [What is 1+1?] +May 27, 2026 9:58:53 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_create_a_session_with_appended_systemmessage_config' expects prompts: [What is your full name?] +May 27, 2026 9:58:54 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_create_session_with_custom_tool' expects prompts: [What is the secret number for key ALPHA?] +May 27, 2026 9:58:56 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:58:58 PM com.github.copilot.LoggingHelpers logTiming +WARNING: JsonRpc.invoke JSON-RPC request finished. Elapsed=PT0.014S, Method=session.resume, RequestId=2, Status=Failed +java.util.concurrent.CompletionException: com.github.copilot.JsonRpcException: Request session.resume failed with message: Session not found: non-existent-session-id + at java.base/java.util.concurrent.CompletableFuture.wrapInCompletionException(CompletableFuture.java:323) + at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:376) + at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:391) + at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:657) + at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:531) + at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2221) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.handleMessage(JsonRpcClient.java:281) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:256) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) +Caused by: com.github.copilot.JsonRpcException: Request session.resume failed with message: Session not found: non-existent-session-id + ... 7 more + +May 27, 2026 9:58:58 PM com.github.copilot.LoggingHelpers logTiming +WARNING: CopilotClient.resumeSession failed. Elapsed=PT0.015S, SessionId=non-existent-session-id +java.util.concurrent.CompletionException: com.github.copilot.JsonRpcException: Request session.resume failed with message: Session not found: non-existent-session-id + at java.base/java.util.concurrent.CompletableFuture.wrapInCompletionException(CompletableFuture.java:323) + at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:376) + at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:391) + at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:657) + at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:531) + at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2221) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.handleMessage(JsonRpcClient.java:281) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:256) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) +Caused by: com.github.copilot.JsonRpcException: Request session.resume failed with message: Session not found: non-existent-session-id + ... 7 more + +May 27, 2026 9:58:58 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_have_stateful_conversation' expects prompts: [What is 1+1?, Now if you double that, what do you get?] +May 27, 2026 9:59:00 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +May 27, 2026 9:59:02 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_resume_a_session_using_the_same_client' expects prompts: [What is 1+1?, Now if you double that, what do you get?] +May 27, 2026 9:59:04 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_create_a_session_with_excludedtools' expects prompts: [What is 1+1?] +May 27, 2026 9:59:06 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_create_a_session_with_replaced_systemmessage_config' expects prompts: [What is your full name?] +May 27, 2026 9:59:08 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_list_sessions' expects prompts: [Say hello, Say goodbye] +May 27, 2026 9:59:09 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/send_returns_immediately_while_events_stream_in_background' expects prompts: [Run 'sleep 2 && echo done'] +May 27, 2026 10:00:11 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'session/should_receive_session_events' expects prompts: [What is 100+200?] +[ERROR] Tests run: 23, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 107.9 s <<< FAILURE! -- in com.github.copilot.CopilotSessionTest +[ERROR] com.github.copilot.CopilotSessionTest.testSendReturnsImmediatelyWhileEventsStreamInBackground -- Time elapsed: 61.59 s <<< ERROR! +java.util.concurrent.TimeoutException + at java.base/java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1981) + at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2116) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CopilotSessionTest.testSendReturnsImmediatelyWhileEventsStreamInBackground(CopilotSessionTest.java:215) + at java.base/java.lang.reflect.Method.invoke(Method.java:565) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) + +[INFO] Running com.github.copilot.CreateSessionReKeyEntryTest +May 27, 2026 10:00:13 PM com.github.copilot.LoggingHelpers logTiming +WARNING: JsonRpc.invoke JSON-RPC request finished. Elapsed=PT0.085S, Method=session.options.update, RequestId=2, Status=Failed +java.util.concurrent.CompletionException: com.github.copilot.JsonRpcException: simulated options update failure + at java.base/java.util.concurrent.CompletableFuture.wrapInCompletionException(CompletableFuture.java:323) + at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:376) + at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:391) + at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:657) + at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:531) + at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2221) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.handleMessage(JsonRpcClient.java:281) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:256) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) +Caused by: com.github.copilot.JsonRpcException: simulated options update failure + ... 7 more + +May 27, 2026 10:00:13 PM com.github.copilot.CopilotClient lambda$updateSessionOptionsForMode$1 +WARNING: session.options.update failed for session server-returned-id +java.util.concurrent.CompletionException: com.github.copilot.JsonRpcException: simulated options update failure + at java.base/java.util.concurrent.CompletableFuture.wrapInCompletionException(CompletableFuture.java:323) + at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:376) + at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:391) + at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:657) + at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:531) + at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2221) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.handleMessage(JsonRpcClient.java:281) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:256) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) +Caused by: com.github.copilot.JsonRpcException: simulated options update failure + ... 7 more + +May 27, 2026 10:00:18 PM com.github.copilot.LoggingHelpers logTiming +WARNING: CopilotClient.createSession failed. Elapsed=PT5.099S, SessionId=client-supplied-id +java.util.concurrent.CompletionException: com.github.copilot.JsonRpcException: simulated options update failure + at java.base/java.util.concurrent.CompletableFuture.wrapInCompletionException(CompletableFuture.java:323) + at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:376) + at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:391) + at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:657) + at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:531) + at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2221) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.handleMessage(JsonRpcClient.java:281) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:256) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) +Caused by: com.github.copilot.JsonRpcException: simulated options update failure + ... 7 more + +[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.303 s -- in com.github.copilot.CreateSessionReKeyEntryTest +[INFO] Running com.github.copilot.DataObjectCoverageTest +[INFO] Tests run: 24, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.055 s -- in com.github.copilot.DataObjectCoverageTest +[INFO] Running com.github.copilot.DocumentationSamplesTest +[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.008 s -- in com.github.copilot.DocumentationSamplesTest +[INFO] Running com.github.copilot.ElicitationTest +[INFO] Tests run: 14, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.052 s -- in com.github.copilot.ElicitationTest +[INFO] Running com.github.copilot.ErrorHandlingTest +May 27, 2026 10:00:19 PM com.github.copilot.ErrorHandlingTest testPermissionHandlerErrors_sessionErrorEventContainsDetails +INFO: Running test: testPermissionHandlerErrors_sessionErrorEventContainsDetails +May 27, 2026 10:00:19 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'permissions/permission_handler_errors' expects prompts: [Run 'echo test'. If you can't, say 'failed'.] +May 27, 2026 10:00:22 PM com.github.copilot.CopilotSession lambda$executePermissionAndRespondAsync$0 +WARNING: Error executing permission handler for requestId=e0603035-c8e3-4d93-813a-ecb5fb1da5e9 +java.lang.RuntimeException: Test error message + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.ErrorHandlingTest.lambda$testPermissionHandlerErrors_sessionErrorEventContainsDetails$0(ErrorHandlingTest.java:178) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CopilotSession.lambda$executePermissionAndRespondAsync$0(CopilotSession.java:940) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1825) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1817) + at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:511) + at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1450) + at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:2019) + at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:187) + +May 27, 2026 10:00:22 PM com.github.copilot.ErrorHandlingTest testHandlesToolCallingErrors_sessionContinuesAfterToolError +INFO: Running test: testHandlesToolCallingErrors_sessionContinuesAfterToolError +May 27, 2026 10:00:22 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'tools/handles_tool_calling_errors' expects prompts: [What is my location? If you can't find out, just say 'unknown'.] +May 27, 2026 10:00:24 PM com.github.copilot.ErrorHandlingTest testHandlesToolCallingErrors_toolReturnsFailureResult +INFO: Running test: testHandlesToolCallingErrors_toolReturnsFailureResult +May 27, 2026 10:00:24 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'tools/handles_tool_calling_errors' expects prompts: [What is my location? If you can't find out, just say 'unknown'.] +May 27, 2026 10:00:26 PM com.github.copilot.ErrorHandlingTest testHandlesToolCallingErrors_toolErrorDoesNotCrashSession +INFO: Running test: testHandlesToolCallingErrors_toolErrorDoesNotCrashSession +May 27, 2026 10:00:26 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'tools/handles_tool_calling_errors' expects prompts: [What is my location? If you can't find out, just say 'unknown'.] +May 27, 2026 10:00:28 PM com.github.copilot.ErrorHandlingTest testShouldHandlePermissionHandlerErrorsGracefully_deniesPermission +INFO: Running test: testShouldHandlePermissionHandlerErrorsGracefully_deniesPermission +May 27, 2026 10:00:28 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'permissions/should_handle_permission_handler_errors_gracefully' expects prompts: [Run 'echo test'. If you can't, say 'failed'.] +May 27, 2026 10:00:30 PM com.github.copilot.CopilotSession lambda$executePermissionAndRespondAsync$0 +WARNING: Error executing permission handler for requestId=4a93893d-a35e-417c-8892-a7cf926f855f +java.lang.RuntimeException: Permission handler crashed + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.ErrorHandlingTest.lambda$testShouldHandlePermissionHandlerErrorsGracefully_deniesPermission$0(ErrorHandlingTest.java:139) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.CopilotSession.lambda$executePermissionAndRespondAsync$0(CopilotSession.java:940) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1825) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1817) + at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:511) + at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1450) + at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:2019) + at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:187) + +[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 11.89 s -- in com.github.copilot.ErrorHandlingTest +[INFO] Running com.github.copilot.EventFidelityTest +May 27, 2026 10:00:31 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'event_fidelity/should_emit_assistant_usage_event_after_model_call' expects prompts: [What is 5+5? Reply with just the number.] +May 27, 2026 10:00:33 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'event_fidelity/should_emit_session_usage_info_event_after_model_call' expects prompts: [What is 5+5? Reply with just the number.] +[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.690 s -- in com.github.copilot.EventFidelityTest +[INFO] Running com.github.copilot.ExecutorWiringTest +May 27, 2026 10:00:37 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'ask_user/should_invoke_user_input_handler_when_model_uses_ask_user_tool' expects prompts: [Ask me to choose between 'Option A' and 'Option B' using the ask_user tool. Wait for my response before] +May 27, 2026 10:00:39 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'tools/invokes_custom_tool' expects prompts: [Use encrypt_string to encrypt this string: Hello] +May 27, 2026 10:00:41 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'hooks/invoke_pre_tool_use_hook_when_model_runs_a_tool' expects prompts: [Read the contents of hello.txt and tell me what it says] +May 27, 2026 10:00:43 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'permissions/permission_handler_for_write_operations' expects prompts: [Edit test.txt and replace 'original' with 'modified'] +May 27, 2026 10:00:46 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'tools/invokes_custom_tool' expects prompts: [Use encrypt_string to encrypt this string: Hello] +May 27, 2026 10:00:48 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'tools/invokes_custom_tool' expects prompts: [Use encrypt_string to encrypt this string: Hello] +[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 14.33 s -- in com.github.copilot.ExecutorWiringTest +[INFO] Running com.github.copilot.ForwardCompatibilityTest +[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.017 s -- in com.github.copilot.ForwardCompatibilityTest +[INFO] Running com.github.copilot.HooksTest +May 27, 2026 10:00:51 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'hooks/invoke_both_hooks_for_single_tool_call' expects prompts: [Read the contents of both.txt] +May 27, 2026 10:00:54 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'hooks/invoke_pre_tool_use_hook_when_model_runs_a_tool' expects prompts: [Read the contents of hello.txt and tell me what it says] +May 27, 2026 10:00:56 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'hooks/invoke_post_tool_use_hook_after_model_runs_a_tool' expects prompts: [Read the contents of world.txt and tell me what it says] +May 27, 2026 10:00:58 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'hooks/deny_tool_execution_when_pre_tool_use_returns_deny' expects prompts: [Edit protected.txt and replace 'Original' with 'Modified'] +[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 10.81 s -- in com.github.copilot.HooksTest +[INFO] Running com.github.copilot.InternalExecutorProviderTest +[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.415 s -- in com.github.copilot.InternalExecutorProviderTest +[INFO] Running com.github.copilot.JsonIncludeNonNullTest +[INFO] Tests run: 18, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.041 s -- in com.github.copilot.JsonIncludeNonNullTest +[INFO] Running com.github.copilot.JsonRpcClientTest +May 27, 2026 10:01:01 PM com.github.copilot.LoggingHelpers logTiming +WARNING: JsonRpc.invoke JSON-RPC request finished. Elapsed=PT0.001S, Method=test, RequestId=1, Status=Failed +java.util.concurrent.CompletionException: com.github.copilot.JsonRpcException: Unknown error + at java.base/java.util.concurrent.CompletableFuture.wrapInCompletionException(CompletableFuture.java:323) + at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:376) + at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:391) + at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:657) + at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:531) + at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2221) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.handleMessage(JsonRpcClient.java:281) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:256) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) +Caused by: com.github.copilot.JsonRpcException: Unknown error + ... 7 more + +May 27, 2026 10:01:01 PM com.github.copilot.JsonRpcClient handleMessage +SEVERE: Error handling method fail.method +java.lang.RuntimeException: handler error + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClientTest.lambda$testHandlerThrowsExceptionWithId$0(JsonRpcClientTest.java:317) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.handleMessage(JsonRpcClient.java:299) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:256) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) + +May 27, 2026 10:01:01 PM com.github.copilot.JsonRpcClient handleMessage +SEVERE: Error parsing JSON-RPC message +com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'not': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false') + at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] + at com.fasterxml.jackson.core@2.21.3/com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2675) + at com.fasterxml.jackson.core@2.21.3/com.fasterxml.jackson.core.json.ReaderBasedJsonParser._reportInvalidToken(ReaderBasedJsonParser.java:3025) + at com.fasterxml.jackson.core@2.21.3/com.fasterxml.jackson.core.json.ReaderBasedJsonParser._reportInvalidToken(ReaderBasedJsonParser.java:2995) + at com.fasterxml.jackson.core@2.21.3/com.fasterxml.jackson.core.json.ReaderBasedJsonParser._matchToken(ReaderBasedJsonParser.java:2760) + at com.fasterxml.jackson.core@2.21.3/com.fasterxml.jackson.core.json.ReaderBasedJsonParser._matchNull(ReaderBasedJsonParser.java:2746) + at com.fasterxml.jackson.core@2.21.3/com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:750) + at com.fasterxml.jackson.databind@2.21.3/com.fasterxml.jackson.databind.ObjectMapper._readTreeAndClose(ObjectMapper.java:5081) + at com.fasterxml.jackson.databind@2.21.3/com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:3349) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.handleMessage(JsonRpcClient.java:268) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:256) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) + +May 27, 2026 10:01:01 PM com.github.copilot.JsonRpcClient handleMessage +SEVERE: Error handling method fail.notify +java.lang.RuntimeException: notify error + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClientTest.lambda$testHandlerThrowsExceptionWithoutId$0(JsonRpcClientTest.java:336) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.handleMessage(JsonRpcClient.java:299) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:256) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) + +May 27, 2026 10:01:02 PM com.github.copilot.JsonRpcClient lambda$startReader$0 +SEVERE: Error in JSON-RPC reader +java.io.IOException: Stream closed + at java.base/java.io.BufferedInputStream.ensureOpen(BufferedInputStream.java:207) + at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:379) + at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:289) + at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:308) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:211) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) + +May 27, 2026 10:01:02 PM com.github.copilot.JsonRpcClient lambda$startReader$0 +SEVERE: Error in JSON-RPC reader +java.net.SocketException: Socket closed + at java.base/sun.nio.ch.NioSocketImpl.ensureOpenAndConnected(NioSocketImpl.java:162) + at java.base/sun.nio.ch.NioSocketImpl.beginRead(NioSocketImpl.java:225) + at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:293) + at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:354) + at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:798) + at java.base/java.net.Socket$SocketInputStream.implRead(Socket.java:974) + at java.base/java.net.Socket$SocketInputStream.read(Socket.java:964) + at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:289) + at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:308) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:211) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) + +May 27, 2026 10:01:02 PM com.github.copilot.LoggingHelpers logTiming +WARNING: JsonRpc.invoke JSON-RPC request finished. Elapsed=PT0.000S, Method=test, RequestId=1, Status=Failed +java.util.concurrent.CompletionException: java.net.SocketException: Socket closed + at java.base/java.util.concurrent.CompletableFuture.wrapInCompletionException(CompletableFuture.java:323) + at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:376) + at java.base/java.util.concurrent.CompletableFuture.uniApplyNow(CompletableFuture.java:695) + at java.base/java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:683) + at java.base/java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:2227) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.invoke(JsonRpcClient.java:125) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClientTest.testInvokeWithSendFailure(JsonRpcClientTest.java:204) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) + at java.base/java.lang.reflect.Method.invoke(Method.java:565) + at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:786) + at org.junit.platform.commons.support.ReflectionSupport.invokeMethod(ReflectionSupport.java:514) + at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) + at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:161) + at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:152) + at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:91) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:112) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:94) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:93) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:87) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$4(TestMethodTestDescriptor.java:221) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:217) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:159) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:70) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:157) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.executeEngine(EngineExecutionOrchestrator.java:230) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.failOrExecuteEngine(EngineExecutionOrchestrator.java:204) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:172) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:101) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:64) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:150) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:63) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:109) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:91) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47) + at org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$1(InterceptingLauncher.java:39) + at org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25) + at org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:38) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.executeWithoutCancellationToken(LauncherAdapter.java:60) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.execute(LauncherAdapter.java:52) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.execute(JUnitPlatformProvider.java:203) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:168) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:136) + at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385) + at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162) + at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507) + at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495) +Caused by: java.net.SocketException: Socket closed + at java.base/sun.nio.ch.NioSocketImpl.ensureOpenAndConnected(NioSocketImpl.java:162) + at java.base/sun.nio.ch.NioSocketImpl.beginWrite(NioSocketImpl.java:370) + at java.base/sun.nio.ch.NioSocketImpl.implWrite(NioSocketImpl.java:415) + at java.base/sun.nio.ch.NioSocketImpl.write(NioSocketImpl.java:448) + at java.base/sun.nio.ch.NioSocketImpl$2.write(NioSocketImpl.java:821) + at java.base/java.net.Socket$SocketOutputStream.implWrite(Socket.java:1086) + at java.base/java.net.Socket$SocketOutputStream.write(Socket.java:1076) + at java.base/java.io.OutputStream.write(OutputStream.java:124) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.sendMessage(JsonRpcClient.java:189) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.invoke(JsonRpcClient.java:119) + ... 77 more + +May 27, 2026 10:01:02 PM com.github.copilot.LoggingHelpers logTiming +WARNING: JsonRpc.invoke JSON-RPC request finished. Elapsed=PT0.000S, Method=test, RequestId=1, Status=Failed +java.util.concurrent.CompletionException: java.io.IOException: Client closed + at java.base/java.util.concurrent.CompletableFuture.wrapInCompletionException(CompletableFuture.java:323) + at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:376) + at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:391) + at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:657) + at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:531) + at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2221) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$close$0(JsonRpcClient.java:332) + at java.base/java.util.concurrent.ConcurrentHashMap.forEach(ConcurrentHashMap.java:1619) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.close(JsonRpcClient.java:332) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClientTest.testCloseWithPendingRequests(JsonRpcClientTest.java:449) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) + at java.base/java.lang.reflect.Method.invoke(Method.java:565) + at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:786) + at org.junit.platform.commons.support.ReflectionSupport.invokeMethod(ReflectionSupport.java:514) + at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) + at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:161) + at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:152) + at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:91) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:112) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:94) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:93) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:87) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$4(TestMethodTestDescriptor.java:221) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:217) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:159) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:70) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:157) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.executeEngine(EngineExecutionOrchestrator.java:230) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.failOrExecuteEngine(EngineExecutionOrchestrator.java:204) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:172) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:101) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:64) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:150) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:63) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:109) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:91) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47) + at org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$1(InterceptingLauncher.java:39) + at org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25) + at org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:38) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.executeWithoutCancellationToken(LauncherAdapter.java:60) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.execute(LauncherAdapter.java:52) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.execute(JUnitPlatformProvider.java:203) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:168) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:136) + at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385) + at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162) + at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507) + at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495) +Caused by: java.io.IOException: Client closed + ... 80 more + +May 27, 2026 10:01:02 PM com.github.copilot.LoggingHelpers logTiming +WARNING: JsonRpc.invoke JSON-RPC request finished. Elapsed=PT0.001S, Method=test, RequestId=1, Status=Failed +java.util.concurrent.CompletionException: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.lang.Integer` from Object value (token `JsonToken.START_OBJECT`) + at [Source: UNKNOWN; byte offset: #UNKNOWN] + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$invoke$0(JsonRpcClient.java:137) + at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:667) + at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:531) + at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2206) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.handleMessage(JsonRpcClient.java:283) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:256) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) +Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.lang.Integer` from Object value (token `JsonToken.START_OBJECT`) + at [Source: UNKNOWN; byte offset: #UNKNOWN] + at com.fasterxml.jackson.databind@2.21.3/com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:72) + at com.fasterxml.jackson.databind@2.21.3/com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1814) + at com.fasterxml.jackson.databind@2.21.3/com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1588) + at com.fasterxml.jackson.databind@2.21.3/com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1535) + at com.fasterxml.jackson.databind@2.21.3/com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseInteger(StdDeserializer.java:867) + at com.fasterxml.jackson.databind@2.21.3/com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer.deserialize(NumberDeserializers.java:543) + at com.fasterxml.jackson.databind@2.21.3/com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer.deserialize(NumberDeserializers.java:518) + at com.fasterxml.jackson.databind@2.21.3/com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342) + at com.fasterxml.jackson.databind@2.21.3/com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:5023) + at com.fasterxml.jackson.databind@2.21.3/com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3104) + at com.fasterxml.jackson.databind@2.21.3/com.fasterxml.jackson.databind.ObjectMapper.treeToValue(ObjectMapper.java:3572) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$invoke$0(JsonRpcClient.java:129) + ... 10 more + +May 27, 2026 10:01:02 PM com.github.copilot.LoggingHelpers logTiming +WARNING: JsonRpc.invoke JSON-RPC request finished. Elapsed=PT0.000S, Method=test, RequestId=1, Status=Failed +java.util.concurrent.CompletionException: com.github.copilot.JsonRpcException: bad request + at java.base/java.util.concurrent.CompletableFuture.wrapInCompletionException(CompletableFuture.java:323) + at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:376) + at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:391) + at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:657) + at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:531) + at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2221) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.handleMessage(JsonRpcClient.java:281) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.JsonRpcClient.lambda$startReader$0(JsonRpcClient.java:256) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) + at java.base/java.lang.Thread.run(Thread.java:1474) +Caused by: com.github.copilot.JsonRpcException: bad request + ... 7 more + +[INFO] Tests run: 23, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.433 s -- in com.github.copilot.JsonRpcClientTest +[INFO] Running com.github.copilot.LifecycleEventManagerTest +May 27, 2026 10:01:02 PM com.github.copilot.LifecycleEventManager dispatch +WARNING: Lifecycle handler error +java.lang.RuntimeException: wildcard handler error + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.LifecycleEventManagerTest.lambda$wildcardHandlerExceptionDoesNotPreventOtherHandlers$0(LifecycleEventManagerTest.java:147) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.LifecycleEventManager.dispatch(LifecycleEventManager.java:98) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.LifecycleEventManagerTest.lambda$wildcardHandlerExceptionDoesNotPreventOtherHandlers$1(LifecycleEventManagerTest.java:151) + at org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:49) + at org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:36) + at org.junit.jupiter.api.Assertions.assertDoesNotThrow(Assertions.java:3199) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.LifecycleEventManagerTest.wildcardHandlerExceptionDoesNotPreventOtherHandlers(LifecycleEventManagerTest.java:151) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) + at java.base/java.lang.reflect.Method.invoke(Method.java:565) + at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:786) + at org.junit.platform.commons.support.ReflectionSupport.invokeMethod(ReflectionSupport.java:514) + at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) + at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:161) + at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:152) + at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:91) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:112) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:94) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:93) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:87) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$4(TestMethodTestDescriptor.java:221) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:217) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:159) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:70) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:157) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.executeEngine(EngineExecutionOrchestrator.java:230) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.failOrExecuteEngine(EngineExecutionOrchestrator.java:204) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:172) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:101) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:64) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:150) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:63) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:109) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:91) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47) + at org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$1(InterceptingLauncher.java:39) + at org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25) + at org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:38) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.executeWithoutCancellationToken(LauncherAdapter.java:60) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.execute(LauncherAdapter.java:52) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.execute(JUnitPlatformProvider.java:203) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:168) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:136) + at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385) + at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162) + at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507) + at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495) + +May 27, 2026 10:01:02 PM com.github.copilot.LifecycleEventManager dispatch +WARNING: Lifecycle handler error +java.lang.RuntimeException: typed error + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.LifecycleEventManagerTest.lambda$typedAndWildcardErrorsDoNotAffectEachOther$0(LifecycleEventManagerTest.java:161) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.LifecycleEventManager.dispatch(LifecycleEventManager.java:90) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.LifecycleEventManagerTest.lambda$typedAndWildcardErrorsDoNotAffectEachOther$1(LifecycleEventManagerTest.java:166) + at org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:49) + at org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:36) + at org.junit.jupiter.api.Assertions.assertDoesNotThrow(Assertions.java:3199) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.LifecycleEventManagerTest.typedAndWildcardErrorsDoNotAffectEachOther(LifecycleEventManagerTest.java:166) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) + at java.base/java.lang.reflect.Method.invoke(Method.java:565) + at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:786) + at org.junit.platform.commons.support.ReflectionSupport.invokeMethod(ReflectionSupport.java:514) + at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) + at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:161) + at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:152) + at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:91) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:112) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:94) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:93) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:87) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$4(TestMethodTestDescriptor.java:221) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:217) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:159) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:70) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:157) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.executeEngine(EngineExecutionOrchestrator.java:230) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.failOrExecuteEngine(EngineExecutionOrchestrator.java:204) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:172) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:101) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:64) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:150) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:63) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:109) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:91) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47) + at org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$1(InterceptingLauncher.java:39) + at org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25) + at org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:38) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.executeWithoutCancellationToken(LauncherAdapter.java:60) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.execute(LauncherAdapter.java:52) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.execute(JUnitPlatformProvider.java:203) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:168) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:136) + at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385) + at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162) + at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507) + at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495) + +May 27, 2026 10:01:02 PM com.github.copilot.LifecycleEventManager dispatch +WARNING: Lifecycle handler error +java.lang.RuntimeException: typed handler error + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.LifecycleEventManagerTest.lambda$typedHandlerExceptionDoesNotPreventOtherHandlers$0(LifecycleEventManagerTest.java:133) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.LifecycleEventManager.dispatch(LifecycleEventManager.java:90) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.LifecycleEventManagerTest.lambda$typedHandlerExceptionDoesNotPreventOtherHandlers$1(LifecycleEventManagerTest.java:138) + at org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:49) + at org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:36) + at org.junit.jupiter.api.Assertions.assertDoesNotThrow(Assertions.java:3199) + at com.github.copilot.java@1.0.0-beta-java.5-SNAPSHOT/com.github.copilot.LifecycleEventManagerTest.typedHandlerExceptionDoesNotPreventOtherHandlers(LifecycleEventManagerTest.java:138) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) + at java.base/java.lang.reflect.Method.invoke(Method.java:565) + at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:786) + at org.junit.platform.commons.support.ReflectionSupport.invokeMethod(ReflectionSupport.java:514) + at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) + at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:161) + at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:152) + at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:91) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:112) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:94) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) + at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:93) + at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:87) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$4(TestMethodTestDescriptor.java:221) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:217) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:159) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:70) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:157) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1604) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.executeEngine(EngineExecutionOrchestrator.java:230) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.failOrExecuteEngine(EngineExecutionOrchestrator.java:204) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:172) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:101) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:64) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:150) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:63) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:109) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:91) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47) + at org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$1(InterceptingLauncher.java:39) + at org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25) + at org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:38) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.executeWithoutCancellationToken(LauncherAdapter.java:60) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.execute(LauncherAdapter.java:52) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.execute(JUnitPlatformProvider.java:203) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:168) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:136) + at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385) + at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162) + at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507) + at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495) + +[INFO] Tests run: 12, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.038 s -- in com.github.copilot.LifecycleEventManagerTest +[INFO] Running com.github.copilot.McpAndAgentsTest +May 27, 2026 10:01:04 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mcp_and_agents/should_accept_mcp_server_configuration_on_session_resume' expects prompts: [What is 1+1?] +May 27, 2026 10:01:07 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mcp_and_agents/should_accept_mcp_server_configuration_on_session_create' expects prompts: [What is 2+2?] +May 27, 2026 10:01:10 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mcp_and_agents/should_accept_mcp_server_configuration_on_session_create' expects prompts: [What is 2+2?] +May 27, 2026 10:01:14 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mcp_and_agents/should_accept_custom_agent_configuration_on_session_create' expects prompts: [What is 5+5?] +May 27, 2026 10:01:16 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mcp_and_agents/should_accept_custom_agent_configuration_on_session_create' expects prompts: [What is 5+5?] +May 27, 2026 10:01:18 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mcp_and_agents/should_accept_both_mcp_servers_and_custom_agents' expects prompts: [What is 7+7?] +May 27, 2026 10:01:20 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mcp_and_agents/should_accept_custom_agent_configuration_on_session_resume' expects prompts: [What is 1+1?, What is 6+6?] +May 27, 2026 10:01:23 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mcp_and_agents/should_accept_custom_agent_configuration_on_session_create' expects prompts: [What is 5+5?] +May 27, 2026 10:01:24 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mcp_and_agents/should_accept_both_mcp_servers_and_custom_agents' expects prompts: [What is 7+7?] +May 27, 2026 10:01:27 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mcp_and_agents/should_accept_defaultagent_configuration_on_session_resume' expects prompts: [What is 3+3?, What is 4+4?] +May 27, 2026 10:01:29 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mcp_and_agents/should_hide_excluded_tools_from_default_agent' expects prompts: [Do you have access to a tool called secret_tool? Answer yes or no.] +May 27, 2026 10:01:31 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mcp_and_agents/should_accept_mcp_server_configuration_on_session_create' expects prompts: [What is 2+2?] +[INFO] Tests run: 12, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 30.27 s -- in com.github.copilot.McpAndAgentsTest +[INFO] Running com.github.copilot.MessageAttachmentTest +[INFO] Tests run: 12, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.029 s -- in com.github.copilot.MessageAttachmentTest +[INFO] Running com.github.copilot.MetadataApiTest +[INFO] Tests run: 11, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.999 s -- in com.github.copilot.MetadataApiTest +[INFO] Running com.github.copilot.ModeHandlersTest +May 27, 2026 10:01:40 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mode_handlers/should_invoke_exit_plan_mode_handler_when_model_uses_tool' expects prompts: [Create a brief implementation plan for adding a greeting.txt file, then request approval with exit_plan_mode.] +May 27, 2026 10:01:42 PM com.github.copilot.E2ETestContext configureForTest +INFO: Configured snapshot 'mode_handlers/should_invoke_auto_mode_switch_handler_when_rate_limited' expects prompts: [Explain that auto mode recovered from a rate limit in one short sentence.] +[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 12.15 s -- in com.github.copilot.ModeHandlersTest +[INFO] Running com.github.copilot.ModelInfoTest +[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.007 s -- in com.github.copilot.ModelInfoTest +[INFO] Running com.github.copilot.ModuleDescriptorTest +[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 s -- in com.github.copilot.ModuleDescriptorTest +[INFO] Running com.github.copilot.OptionalApiAndJacksonTest +[INFO] Tests run: 51, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.067 s -- in com.github.copilot.OptionalApiAndJacksonTest +[INFO] Running com.github.copilot.PerSessionAuthTest +[CapiProxy stderr] Error in proxying request: Error: ReplayingCapiProxy not yet initialized. Either pass filePath and workDir to the constructor, or post configuration to /config before making other HTTP requests. +[CapiProxy stderr] at (/home/edburns/workareas/copilot-sdk/java/target/copilot-sdk/test/harness/replayingCapiProxy.ts:244:17) +[CapiProxy stderr] at iife (/home/edburns/workareas/copilot-sdk/java/target/copilot-sdk/test/harness/util.ts:6:10) +[CapiProxy stderr] at ReplayingCapiProxy.performRequest (/home/edburns/workareas/copilot-sdk/java/target/copilot-sdk/test/harness/replayingCapiProxy.ts:172:10) +[CapiProxy stderr] at IncomingMessage. (/home/edburns/workareas/copilot-sdk/java/target/copilot-sdk/test/harness/capturingHttpProxy.ts:58:14) +[CapiProxy stderr] at IncomingMessage.emit (node:events:508:28) +[CapiProxy stderr] at endReadableNT (node:internal/streams/readable:1729:12) +[CapiProxy stderr] at process.processTicksAndRejections (node:internal/process/task_queues:90:21) diff --git a/java/pom.xml b/java/pom.xml index 801dda9b5..ccf1dc942 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -245,6 +245,18 @@ + + print-test-jdk-banner + process-test-classes + + run + + + + + + + @@ -493,6 +505,44 @@ true + + org.apache.maven.plugins + maven-enforcer-plugin + 3.5.0 + + + enforce-jdk25 + + enforce + + + + + [25,) + JDK 25+ is required to build the Multi-Release JAR with the virtual-thread overlay. + + + + + + verify-multi-release-overlay + verify + + enforce + + + + + + ${project.build.outputDirectory}/META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class + + Multi-Release JAR overlay missing: META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class was not compiled. Ensure the build runs on JDK 25+. + + + + + + From ca560bd40f27f50a4fc63a6ae47080bf211806b7 Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Thu, 28 May 2026 08:50:54 -0700 Subject: [PATCH 07/11] Make it so the tests run under 17. --- .github/workflows/java-sdk-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/java-sdk-tests.yml b/.github/workflows/java-sdk-tests.yml index 682331992..cadc221ba 100644 --- a/.github/workflows/java-sdk-tests.yml +++ b/.github/workflows/java-sdk-tests.yml @@ -149,4 +149,4 @@ jobs: run: | echo "Running tests against JDK 25-built classes using JDK 17 runtime..." java -version - mvn surefire:test -Denforcer.skip=true + mvn antrun:run@print-test-jdk-banner surefire:test -Denforcer.skip=true -DtestExecutionAgentArgs= From 6ab626283e5a866c2b710cdc6d4cbdb844b81d55 Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Thu, 28 May 2026 09:38:17 -0700 Subject: [PATCH 08/11] feat(java): incorporate PR #1478 improvements Cherry-pick code changes from brunoborges/java-jdk25-default-executor: - Instance-based InternalExecutorProvider with canBeShutdown() API - SHUTDOWN_DISPATCHER to avoid deadlock in forceStop() - Idempotent shutdownOwnedExecutor() (short-circuits if already shut down) - maven-failsafe-plugin + InternalExecutorProviderIT (spawns child JVM against packaged JAR) - InternalExecutorProviderProbe for IT verification - Antrun verify-java25-overlay (checks actual JAR zip entry in java25-multi-release profile) - Simplified README (no manual executor configuration needed) - Split unit tests into single-assertion methods Co-authored-by: Bruno Borges --- java/README.md | 11 +- java/pom.xml | 67 ++++++++ .../com/github/copilot/CopilotClient.java | 58 +++++-- .../copilot/InternalExecutorProvider.java | 46 +++++- .../copilot/InternalExecutorProvider.java | 54 +++++- .../copilot/InternalExecutorProviderIT.java | 108 ++++++++++++ .../InternalExecutorProviderProbe.java | 74 +++++++++ .../copilot/InternalExecutorProviderTest.java | 155 ++---------------- 8 files changed, 401 insertions(+), 172 deletions(-) create mode 100644 java/src/test/java/com/github/copilot/InternalExecutorProviderIT.java create mode 100644 java/src/test/java/com/github/copilot/InternalExecutorProviderProbe.java diff --git a/java/README.md b/java/README.md index 61e59c8d6..9b23400eb 100644 --- a/java/README.md +++ b/java/README.md @@ -21,7 +21,7 @@ Java SDK for programmatic control of GitHub Copilot CLI, enabling you to build A ### Requirements -- Java 17 or later. **JDK 25 recommended**. Selecting JDK 25 enables the use of virtual threads, as shown in the [Quick Start](#quick-start). +- Java 17 or later. **JDK 25 recommended**. On JDK 25 and later, the SDK automatically uses virtual threads for its default internal executor. - GitHub Copilot CLI 1.0.17 or later installed and in `PATH` (or provide custom `cliPath`) ### Maven @@ -66,23 +66,16 @@ implementation 'com.github:copilot-sdk-java:1.0.0-beta-java.4' import com.github.copilot.CopilotClient; import com.github.copilot.generated.AssistantMessageEvent; import com.github.copilot.generated.SessionUsageInfoEvent; -import com.github.copilot.rpc.CopilotClientOptions; import com.github.copilot.rpc.MessageOptions; import com.github.copilot.rpc.PermissionHandler; import com.github.copilot.rpc.SessionConfig; -import java.util.concurrent.Executors; - public class CopilotSDK { public static void main(String[] args) throws Exception { var lastMessage = new String[]{null}; // Create and start client - try (var client = new CopilotClient()) { // JDK 25+: comment out this line - // JDK 25+: uncomment the following 3 lines for virtual thread support - // var options = new CopilotClientOptions() - // .setExecutor(Executors.newVirtualThreadPerTaskExecutor()); - // try (var client = new CopilotClient(options)) { + try (var client = new CopilotClient()) { client.start().get(); // Create a session diff --git a/java/pom.xml b/java/pom.xml index ccf1dc942..9ab34b022 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -319,6 +319,33 @@ + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.5.5 + + + + integration-test + verify + + + + + + ${project.build.directory} + ${project.build.finalName} + ${project.build.testOutputDirectory} + + + org.apache.maven.plugins maven-surefire-plugin @@ -600,6 +627,46 @@ + + + org.apache.maven.plugins + maven-antrun-plugin + + + verify-java25-overlay + package + + run + + + + + + + + + +JDK 25 multi-release overlay class is missing from the packaged JAR. +Expected entry: META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class +JAR: ${project.build.directory}/${project.build.finalName}.jar + +This usually means the 'java25-multi-release' Maven profile did not activate +(e.g. the build is running on a JDK older than 25) or maven-compiler-plugin +did not produce the multi-release output. Re-build on JDK 25+ and verify the +'compile-java25' execution ran during the 'compile' phase. + + + + + + diff --git a/java/src/main/java/com/github/copilot/CopilotClient.java b/java/src/main/java/com/github/copilot/CopilotClient.java index f004ba715..00d5ea749 100644 --- a/java/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/src/main/java/com/github/copilot/CopilotClient.java @@ -81,9 +81,24 @@ public final class CopilotClient implements AutoCloseable { */ public static final int AUTOCLOSEABLE_TIMEOUT_SECONDS = 10; private static final int FORCE_KILL_TIMEOUT_SECONDS = 10; + + /** + * One-shot dispatcher used to run the owned-executor shutdown off any caller + * thread that might itself belong to that executor (e.g. the + * {@link #forceStop()} continuation, which is chained off async work scheduled + * on the internal executor). Spawning a fresh daemon thread guarantees + * {@link java.util.concurrent.ExecutorService#awaitTermination(long, TimeUnit)} + * is never called from inside the very executor it is waiting on. + */ + private static final Executor SHUTDOWN_DISPATCHER = runnable -> { + Thread t = new Thread(runnable, "copilot-client-shutdown"); + t.setDaemon(true); + t.start(); + }; + private final CopilotClientOptions options; private final Executor executor; - private final ExecutorService ownedExecutor; + private final boolean executorCanBeShutdown; private final CliServerManager serverManager; private final LifecycleEventManager lifecycleManager = new LifecycleEventManager(); private final Map sessions = new ConcurrentHashMap<>(); @@ -171,10 +186,9 @@ public CopilotClient(CopilotClientOptions options) { this.optionsPort = null; } - Executor providedExecutor = this.options.getExecutor(); - this.executor = providedExecutor != null ? providedExecutor : InternalExecutorProvider.create(); - this.ownedExecutor = providedExecutor == null && InternalExecutorProvider.isOwned(this.executor) - && this.executor instanceof ExecutorService executorService ? executorService : null; + InternalExecutorProvider executorProvider = new InternalExecutorProvider(this.options.getExecutor()); + this.executor = executorProvider.get(); + this.executorCanBeShutdown = executorProvider.canBeShutdown(); this.serverManager = new CliServerManager(this.options); this.serverManager.setConnectionToken(this.effectiveConnectionToken); @@ -360,7 +374,12 @@ public CompletableFuture stop() { public CompletableFuture forceStop() { disposed = true; sessions.clear(); - return cleanupConnection().whenComplete((ignored, error) -> shutdownOwnedExecutor()); + // Dispatch the blocking shutdownOwnedExecutor() on a dedicated thread: + // cleanupConnection() is chained off async work running on the owned + // executor, so a plain whenComplete(...) here could land the awaitTermination + // call on one of the very threads it is waiting to drain, forcing the full + // AUTOCLOSEABLE_TIMEOUT_SECONDS timeout followed by shutdownNow(). + return cleanupConnection().whenCompleteAsync((ignored, error) -> shutdownOwnedExecutor(), SHUTDOWN_DISPATCHER); } private CompletableFuture cleanupConnection() { @@ -1114,19 +1133,36 @@ public void close() { } private void shutdownOwnedExecutor() { - if (ownedExecutor == null) { + if (!executorCanBeShutdown) { + return; + } + + ExecutorService serviceToShutdown = executor instanceof ExecutorService es ? es : null; + if (serviceToShutdown == null) { + LOG.log(Level.FINE, "Executor is not an ExecutorService; skipping shutdown"); + return; + } + + // Short-circuit when the owned executor is already shut down. close() and + // forceStop() can each call this method (e.g. forceStop() invoked before a + // subsequent close() in user code), and re-entering shutdown() + + // awaitTermination() + // is redundant. Logging at FINE aids diagnostics without spamming normal + // output. + if (serviceToShutdown.isShutdown()) { + LOG.log(Level.FINE, "Owned executor was already shut down; skipping redundant shutdown call."); return; } - ownedExecutor.shutdown(); + serviceToShutdown.shutdown(); try { - if (!ownedExecutor.awaitTermination(AUTOCLOSEABLE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { + if (!serviceToShutdown.awaitTermination(AUTOCLOSEABLE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { LOG.log(Level.FINE, "Owned executor did not terminate within {0} seconds; forcing shutdown.", AUTOCLOSEABLE_TIMEOUT_SECONDS); - ownedExecutor.shutdownNow(); + serviceToShutdown.shutdownNow(); } } catch (InterruptedException e) { - ownedExecutor.shutdownNow(); + serviceToShutdown.shutdownNow(); Thread.currentThread().interrupt(); LOG.log(Level.FINE, "Interrupted while waiting for owned executor to terminate", e); } diff --git a/java/src/main/java/com/github/copilot/InternalExecutorProvider.java b/java/src/main/java/com/github/copilot/InternalExecutorProvider.java index 8657027e8..284965513 100644 --- a/java/src/main/java/com/github/copilot/InternalExecutorProvider.java +++ b/java/src/main/java/com/github/copilot/InternalExecutorProvider.java @@ -7,16 +7,54 @@ import java.util.concurrent.Executor; import java.util.concurrent.ForkJoinPool; +/** + * Resolves the {@link Executor} used by {@link CopilotClient} for internal + * asynchronous work. + * + *

+ * This is the baseline (JDK 17+) implementation. When no + * user-provided executor is supplied, it falls back to + * {@link ForkJoinPool#commonPool()}, which is shared with the rest of the JVM + * and therefore never owned by the SDK. + * + *

+ * Multi-release JAR contract. This class has a sibling variant + * at {@code src/main/java25/com/github/copilot/InternalExecutorProvider.java} + * that is compiled with {@code --release 25} into {@code META-INF/versions/25/} + * and selected automatically by the JVM on JDK 25+. Any change to the + * package-private surface of this class + * ({@link #InternalExecutorProvider(Executor) constructor}, {@link #get()}, + * {@link #canBeShutdown()}) must be mirrored in both source + * trees. The two implementations must remain behaviourally + * interchangeable from the caller's perspective; only the default-executor + * strategy and ownership semantics differ. + * + * @implNote Maintainers: when editing this file, also edit + * {@code src/main/java25/com/github/copilot/InternalExecutorProvider.java}. + * The packaged JAR is verified at build time (see the + * {@code java25-multi-release} profile in {@code pom.xml}) to ensure + * the JDK 25 overlay is present. + */ final class InternalExecutorProvider { - private InternalExecutorProvider() { + private final Executor executor; + + InternalExecutorProvider(Executor userProvided) { + if (userProvided != null) { + this.executor = userProvided; + } else { + this.executor = ForkJoinPool.commonPool(); + } } - static Executor create() { - return ForkJoinPool.commonPool(); + Executor get() { + return executor; } - static boolean isOwned(Executor executor) { + boolean canBeShutdown() { + // Since we are using ForkJoinPool.commonPool() or user provided only, + // we should not attempt to shut it down return false; } + } diff --git a/java/src/main/java25/com/github/copilot/InternalExecutorProvider.java b/java/src/main/java25/com/github/copilot/InternalExecutorProvider.java index 257d0f61e..10878bb0c 100644 --- a/java/src/main/java25/com/github/copilot/InternalExecutorProvider.java +++ b/java/src/main/java25/com/github/copilot/InternalExecutorProvider.java @@ -5,19 +5,61 @@ package com.github.copilot; import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ForkJoinPool; +/** + * Resolves the {@link Executor} used by {@link CopilotClient} for internal + * asynchronous work. + * + *

This is the JDK 25+ multi-release variant. It is + * compiled with {@code --release 25} into + * {@code META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class} + * inside the packaged JAR and is automatically loaded in preference to the + * baseline class when the JVM runtime feature version is 25 or greater. + * When no user-provided executor is supplied, it creates an SDK-owned + * {@link Executors#newVirtualThreadPerTaskExecutor() virtual-thread executor} + * that is shut down by {@link CopilotClient#close()}. + * + *

Multi-release JAR contract. This class is the + * JDK 25 sibling of the baseline implementation at + * {@code src/main/java/com/github/copilot/InternalExecutorProvider.java}. + * The package-private surface of both classes + * ({@link #InternalExecutorProvider(Executor) constructor}, + * {@link #get()}, {@link #canBeShutdown()}) must be kept in + * lock-step; only the default-executor strategy and ownership + * semantics differ. + * + * @implNote + * Maintainers: when editing this file, also edit + * {@code src/main/java/com/github/copilot/InternalExecutorProvider.java}. + * The packaged JAR is verified at build time (see the + * {@code java25-multi-release} profile in {@code pom.xml}) to ensure this + * overlay class is present. + */ final class InternalExecutorProvider { - private InternalExecutorProvider() { + private final Executor executor; + private final boolean owned; + + InternalExecutorProvider(Executor userProvided) { + if (userProvided != null) { + this.executor = userProvided; + this.owned = false; + } else { + this.executor = Executors.newVirtualThreadPerTaskExecutor(); + this.owned = true; + } } - static Executor create() { - return Executors.newVirtualThreadPerTaskExecutor(); + Executor get() { + return executor; } - static boolean isOwned(Executor executor) { - return executor instanceof ExecutorService; + boolean canBeShutdown() { + // We can only shut down the executor if we created it (i.e., if it's owned) + // such as when using Executors.newVirtualThreadPerTaskExecutor(), + // which creates an executor that we are responsible for shutting down. + return owned; } } diff --git a/java/src/test/java/com/github/copilot/InternalExecutorProviderIT.java b/java/src/test/java/com/github/copilot/InternalExecutorProviderIT.java new file mode 100644 index 000000000..1cc6b482e --- /dev/null +++ b/java/src/test/java/com/github/copilot/InternalExecutorProviderIT.java @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; + +/** + * Failsafe integration test that asserts the multi-release behaviour of + * {@link InternalExecutorProvider} against the actually packaged JAR. + *

+ * Runs after {@code package}, when {@code target/${finalName}.jar} exists with + * its real {@code Multi-Release: true} manifest and (on JDK 25+ builds) the + * {@code META-INF/versions/25/} override produced by {@code maven-jar-plugin}. + *

+ * The test spawns a child JVM with the packaged JAR plus {@code test-classes} + * on the classpath, runs {@link InternalExecutorProviderProbe}, and asserts + * that the executor selected for the current runtime matches expectations. + */ +class InternalExecutorProviderIT { + + @Test + void packagedJarSelectsExecutorPerRuntimeVersion() throws Exception { + Path packagedJar = locatePackagedJar(); + Path testClasses = locateTestClassesDir(); + String javaBin = locateJavaBinary(); + + String classpath = packagedJar.toString() + File.pathSeparator + testClasses.toString(); + Process process = new ProcessBuilder(javaBin, "-cp", classpath, + "com.github.copilot.InternalExecutorProviderProbe").redirectErrorStream(true).start(); + + String output; + try { + output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); + assertTrue(process.waitFor(30, TimeUnit.SECONDS), "Probe JVM did not exit within 30s. Output:\n" + output); + } finally { + if (process.isAlive()) { + process.destroyForcibly(); + } + } + + assertEquals(0, process.exitValue(), "Probe exited non-zero. Output:\n" + output); + + Map kv = parseKeyValues(output); + String featureRaw = kv.get("feature"); + assertNotNull(featureRaw, "Probe did not report 'feature'. Output:\n" + output); + int feature = Integer.parseInt(featureRaw); + + boolean expectOwnedVirtual = feature >= 25; + assertEquals(String.valueOf(expectOwnedVirtual), kv.get("canBeShutdown"), + "canBeShutdown mismatch for JDK feature=" + feature + ". Output:\n" + output); + assertEquals(String.valueOf(expectOwnedVirtual), kv.get("virtual"), + "virtual mismatch for JDK feature=" + feature + ". Output:\n" + output); + } + + private static Path locatePackagedJar() { + String buildDir = System.getProperty("project.build.directory"); + String finalName = System.getProperty("project.build.finalName"); + assertNotNull(buildDir, "System property 'project.build.directory' must be set by failsafe"); + assertNotNull(finalName, "System property 'project.build.finalName' must be set by failsafe"); + Path jar = Path.of(buildDir, finalName + ".jar"); + assertTrue(Files.isRegularFile(jar), "Packaged JAR must exist: " + jar); + return jar; + } + + private static Path locateTestClassesDir() { + String testOutput = System.getProperty("project.build.testOutputDirectory"); + assertNotNull(testOutput, "System property 'project.build.testOutputDirectory' must be set by failsafe"); + Path dir = Path.of(testOutput); + assertTrue(Files.isDirectory(dir), "test-classes dir must exist: " + dir); + return dir; + } + + private static String locateJavaBinary() { + Path javaHome = Path.of(System.getProperty("java.home")); + Path candidate = javaHome.resolve("bin").resolve(isWindows() ? "java.exe" : "java"); + assertTrue(Files.isExecutable(candidate), "java binary must be executable: " + candidate); + return candidate.toString(); + } + + private static boolean isWindows() { + return System.getProperty("os.name", "").toLowerCase().contains("win"); + } + + private static Map parseKeyValues(String output) { + Map map = new HashMap<>(); + for (String line : output.split("\\R")) { + int eq = line.indexOf('='); + if (eq > 0) { + map.put(line.substring(0, eq).trim(), line.substring(eq + 1).trim()); + } + } + return map; + } +} diff --git a/java/src/test/java/com/github/copilot/InternalExecutorProviderProbe.java b/java/src/test/java/com/github/copilot/InternalExecutorProviderProbe.java new file mode 100644 index 000000000..85d12f14f --- /dev/null +++ b/java/src/test/java/com/github/copilot/InternalExecutorProviderProbe.java @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import java.lang.reflect.Method; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Diagnostic main launched as a separate JVM by + * {@code InternalExecutorProviderIT} to inspect the multi-release behaviour of + * {@link InternalExecutorProvider} against the actually packaged JAR. + *

+ * Lives in the same package as {@link InternalExecutorProvider} so it can use + * its package-private API directly, without reflection. + *

+ * Output format (key=value, one per line): + * + *

+ *   feature=<JDK feature version>
+ *   canBeShutdown=<true|false>
+ *   virtual=<true|false>
+ * 
+ */ +final class InternalExecutorProviderProbe { + + private InternalExecutorProviderProbe() { + } + + public static void main(String[] args) throws Exception { + InternalExecutorProvider provider = new InternalExecutorProvider(null); + Executor executor = provider.get(); + boolean canBeShutdown = provider.canBeShutdown(); + + AtomicBoolean virtual = new AtomicBoolean(); + CountDownLatch latch = new CountDownLatch(1); + executor.execute(() -> { + try { + virtual.set(isCurrentThreadVirtual()); + } finally { + latch.countDown(); + } + }); + + try { + if (!latch.await(5, TimeUnit.SECONDS)) { + System.out.println("error=task-timeout"); + System.exit(2); + } + } finally { + if (executor instanceof ExecutorService es) { + es.shutdownNow(); + } + } + + System.out.println("feature=" + Runtime.version().feature()); + System.out.println("canBeShutdown=" + canBeShutdown); + System.out.println("virtual=" + virtual.get()); + } + + private static boolean isCurrentThreadVirtual() { + try { + Method isVirtual = Thread.class.getMethod("isVirtual"); + return (Boolean) isVirtual.invoke(Thread.currentThread()); + } catch (ReflectiveOperationException e) { + return false; + } + } +} diff --git a/java/src/test/java/com/github/copilot/InternalExecutorProviderTest.java b/java/src/test/java/com/github/copilot/InternalExecutorProviderTest.java index 7ec4a420d..f1d854cb5 100644 --- a/java/src/test/java/com/github/copilot/InternalExecutorProviderTest.java +++ b/java/src/test/java/com/github/copilot/InternalExecutorProviderTest.java @@ -7,26 +7,12 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.TimeUnit; -import java.util.jar.Attributes; -import java.util.jar.JarEntry; -import java.util.jar.JarOutputStream; -import java.util.jar.Manifest; import org.junit.jupiter.api.Test; @@ -35,11 +21,21 @@ class InternalExecutorProviderTest { @Test - void baseProviderUsesCommonPoolWithoutOwnership() { - Executor executor = InternalExecutorProvider.create(); + void baseProviderReturnsCommonPool() { + Executor executor = new InternalExecutorProvider(null).get(); assertSame(ForkJoinPool.commonPool(), executor); - assertFalse(InternalExecutorProvider.isOwned(executor)); + } + + @Test + void userProvidedExecutorIsNotOwned() { + Executor executor = ForkJoinPool.commonPool(); + + assertFalse(new InternalExecutorProvider(executor).canBeShutdown()); + } + + @Test + void providerIsPackagePrivate() { assertFalse(Modifier.isPublic(InternalExecutorProvider.class.getModifiers())); } @@ -56,129 +52,4 @@ void clientDoesNotShutDownUserProvidedExecutor() { executor.shutdownNow(); } } - - @Test - void multiReleaseJarUsesOwnedVirtualThreadExecutorOnJdk25() throws Exception { - if (Runtime.version().feature() < 25) { - return; - } - - Path classes = Path.of("target", "classes"); - Path baseClass = classes.resolve("com/github/copilot/InternalExecutorProvider.class"); - Path java25Class = classes.resolve("META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class"); - assertTrue(Files.exists(baseClass), "Base InternalExecutorProvider class must be compiled"); - assertTrue(Files.exists(java25Class), "JDK 25 build must compile the multi-release executor provider"); - - Path jar = Files.createTempFile("copilot-sdk-internal-executor", ".jar"); - try { - createProviderJar(jar, baseClass, java25Class); - - try (var loader = new URLClassLoader(new URL[]{jar.toUri().toURL()}, null)) { - Class provider = Class.forName("com.github.copilot.InternalExecutorProvider", true, loader); - Method create = provider.getDeclaredMethod("create"); - Method isOwned = provider.getDeclaredMethod("isOwned", Executor.class); - create.setAccessible(true); - isOwned.setAccessible(true); - - Executor executor = (Executor) create.invoke(null); - try { - assertTrue((Boolean) isOwned.invoke(null, executor)); - CompletableFuture virtualThreadUsed = new CompletableFuture<>(); - executor.execute(() -> virtualThreadUsed.complete(isCurrentThreadVirtual())); - - assertTrue(virtualThreadUsed.get(5, TimeUnit.SECONDS)); - } finally { - if (executor instanceof ExecutorService executorService) { - executorService.shutdownNow(); - } - } - } - } finally { - Files.deleteIfExists(jar); - } - } - - @Test - void clientCloseShutsDownOwnedDefaultExecutorOnJdk25() throws Exception { - if (Runtime.version().feature() < 25) { - return; - } - - Path classes = Path.of("target", "classes"); - Path jar = Files.createTempFile("copilot-sdk-client-internal-executor", ".jar"); - try { - createClassesJar(jar, classes); - - try (var loader = new URLClassLoader(new URL[]{jar.toUri().toURL()}, null)) { - Class clientClass = Class.forName("com.github.copilot.CopilotClient", true, loader); - AutoCloseable client = (AutoCloseable) clientClass.getConstructor().newInstance(); - Field ownedExecutorField = clientClass.getDeclaredField("ownedExecutor"); - ownedExecutorField.setAccessible(true); - ExecutorService ownedExecutor = (ExecutorService) ownedExecutorField.get(client); - - assertNotNull(ownedExecutor); - assertFalse(ownedExecutor.isShutdown()); - - client.close(); - - assertTrue(ownedExecutor.isShutdown()); - assertTrue(ownedExecutor.awaitTermination(5, TimeUnit.SECONDS)); - assertTrue(ownedExecutor.isTerminated()); - } - } finally { - Files.deleteIfExists(jar); - } - } - - private static boolean isCurrentThreadVirtual() { - try { - Method isVirtual = Thread.class.getMethod("isVirtual"); - return (Boolean) isVirtual.invoke(Thread.currentThread()); - } catch (ReflectiveOperationException e) { - return false; - } - } - - private static void createProviderJar(Path jar, Path baseClass, Path java25Class) throws IOException { - Manifest manifest = new Manifest(); - Attributes attributes = manifest.getMainAttributes(); - attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); - attributes.putValue("Multi-Release", "true"); - - try (JarOutputStream output = new JarOutputStream(Files.newOutputStream(jar), manifest)) { - addClass(output, "com/github/copilot/InternalExecutorProvider.class", baseClass); - addClass(output, "META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class", java25Class); - } - } - - private static void createClassesJar(Path jar, Path classes) throws IOException { - Manifest manifest = new Manifest(); - Attributes attributes = manifest.getMainAttributes(); - attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); - attributes.putValue("Multi-Release", "true"); - - try (JarOutputStream output = new JarOutputStream(Files.newOutputStream(jar), manifest); - var files = Files.walk(classes)) { - var iterator = files.iterator(); - while (iterator.hasNext()) { - Path file = iterator.next(); - if (!Files.isRegularFile(file)) { - continue; - } - - String entryName = classes.relativize(file).toString().replace('\\', '/'); - if ("META-INF/MANIFEST.MF".equals(entryName)) { - continue; - } - - addClass(output, entryName, file); - } - } - } - - private static void addClass(JarOutputStream output, String entryName, Path classFile) throws IOException { - output.putNextEntry(new JarEntry(entryName)); - Files.copy(classFile, output); - output.closeEntry(); - } } From 88f0fdfe622448e3f66ef884370c6123ab5a151f Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Thu, 28 May 2026 09:58:21 -0700 Subject: [PATCH 09/11] Make it so the JDK 17 variant runs every time by default. --- .github/workflows/java-sdk-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/java-sdk-tests.yml b/.github/workflows/java-sdk-tests.yml index cadc221ba..38b0ba6a2 100644 --- a/.github/workflows/java-sdk-tests.yml +++ b/.github/workflows/java-sdk-tests.yml @@ -33,7 +33,7 @@ on: description: "Also run tests on JDK 17 (compatibility check)" type: boolean required: false - default: false + default: true merge_group: permissions: @@ -134,14 +134,14 @@ jobs: retention-days: 7 - name: Switch to JDK 17 - if: inputs.test-jdk17 == true + if: inputs.test-jdk17 == true || inputs.test-jdk17 == null uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 with: java-version: "17" distribution: "microsoft" - name: Re-run tests on JDK 17 (no recompilation) - if: inputs.test-jdk17 == true + if: inputs.test-jdk17 == true || inputs.test-jdk17 == null env: CI: "true" COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} From ce30282d3233a9c33a1d8b6e749a32c1f13be11a Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Thu, 28 May 2026 10:37:45 -0700 Subject: [PATCH 10/11] fix(java): use resolved executor unconditionally in initializeSession After merging upstream/main (which introduced the initializeSession lambda with a conditional options.getExecutor() guard), restore PR 1477's intent: always set the resolved executor field on every session. This ensures the JDK 25 virtual-thread default is propagated even when no user-provided executor is supplied. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/src/main/java/com/github/copilot/CopilotClient.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/java/src/main/java/com/github/copilot/CopilotClient.java b/java/src/main/java/com/github/copilot/CopilotClient.java index 293e2f8f9..2780a481f 100644 --- a/java/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/src/main/java/com/github/copilot/CopilotClient.java @@ -490,9 +490,7 @@ public CompletableFuture createSession(SessionConfig config) { java.util.function.Function initializeSession = sid -> { long setupNanos = System.nanoTime(); var s = new CopilotSession(sid, connection.rpc); - if (options.getExecutor() != null) { - s.setExecutor(options.getExecutor()); - } + s.setExecutor(executor); SessionRequestBuilder.configureSession(s, config); if (extracted.transformCallbacks() != null) { s.registerTransformCallbacks(extracted.transformCallbacks()); From 8471e1e25d981442e6fd4257ef6ae7fc956e6507 Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Thu, 28 May 2026 10:40:08 -0700 Subject: [PATCH 11/11] ci(java): separate test report sections for JDK 25 and JDK 17 Add title and show-coverage inputs to java-test-report action so the summary clearly identifies which JDK produced each set of results. The JDK 25 section includes coverage; the JDK 17 section shows only test counts. --- .github/actions/java-test-report/action.yml | 11 ++++++++++- .github/workflows/java-sdk-tests.yml | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/actions/java-test-report/action.yml b/.github/actions/java-test-report/action.yml index d5f6d2709..7e46c0310 100644 --- a/.github/actions/java-test-report/action.yml +++ b/.github/actions/java-test-report/action.yml @@ -1,6 +1,10 @@ name: "Java Test Report" description: "Generate and publish test reports with summary for Java SDK tests." inputs: + title: + description: "Heading to display in the summary (identifies the JDK variant)" + required: false + default: "Copilot Java SDK :: Test Results" report-path: description: "Path to the test report XML files (glob pattern)" required: false @@ -13,6 +17,10 @@ inputs: description: "Path to the JaCoCo CSV report" required: false default: "java/target/site/jacoco-coverage/jacoco.csv" + show-coverage: + description: "Whether to include JaCoCo coverage in the summary" + required: false + default: "true" check-name: description: "Name for the check run" required: false @@ -23,7 +31,7 @@ runs: - name: Generate Test Summary shell: bash run: | - echo "## 🧪 Copilot Java SDK :: Test Results" >> $GITHUB_STEP_SUMMARY + echo "## 🧪 ${{ inputs.title }}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY if ls ${{ inputs.report-path }} 1>/dev/null 2>&1; then @@ -81,6 +89,7 @@ runs: fi - name: Generate Coverage Summary + if: inputs.show-coverage == 'true' shell: bash run: | JACOCO_XML="${{ inputs.jacoco-path }}" diff --git a/.github/workflows/java-sdk-tests.yml b/.github/workflows/java-sdk-tests.yml index 38b0ba6a2..7c2223886 100644 --- a/.github/workflows/java-sdk-tests.yml +++ b/.github/workflows/java-sdk-tests.yml @@ -122,6 +122,8 @@ jobs: - name: Generate Test Report Summary if: always() uses: ./.github/actions/java-test-report + with: + title: "Copilot Java SDK :: Test Results (JDK 25)" - name: Upload test results on failure if: failure() @@ -150,3 +152,10 @@ jobs: echo "Running tests against JDK 25-built classes using JDK 17 runtime..." java -version mvn antrun:run@print-test-jdk-banner surefire:test -Denforcer.skip=true -DtestExecutionAgentArgs= + + - name: Generate Test Report Summary (JDK 17) + if: always() && (inputs.test-jdk17 == true || inputs.test-jdk17 == null) + uses: ./.github/actions/java-test-report + with: + title: "Copilot Java SDK :: Test Results (JDK 17)" + show-coverage: "false"