diff --git a/.github/workflows/codegen-check.yml b/.github/workflows/codegen-check.yml
index 0bef14531..78927f160 100644
--- a/.github/workflows/codegen-check.yml
+++ b/.github/workflows/codegen-check.yml
@@ -14,6 +14,8 @@ on:
- 'go/generated_*.go'
- 'go/rpc/**'
- 'rust/src/generated/**'
+ - 'sdk-protocol-version.json'
+ - 'java/src/main/java/com/github/copilot/SdkProtocolVersion.java'
- '.github/workflows/codegen-check.yml'
workflow_dispatch:
@@ -78,4 +80,13 @@ jobs:
git diff
exit 1
fi
+
+ - name: Verify Java protocol version matches
+ run: |
+ EXPECTED=$(jq -r '.version' sdk-protocol-version.json)
+ ACTUAL=$(grep -oP 'LATEST\(\K[0-9]+' java/src/main/java/com/github/copilot/SdkProtocolVersion.java)
+ if [ "$EXPECTED" != "$ACTUAL" ]; then
+ echo "::error::Java SDK protocol version ($ACTUAL) does not match sdk-protocol-version.json ($EXPECTED). Java manages its own SdkProtocolVersion.java via java/scripts/codegen/. Update it to match."
+ exit 1
+ fi
echo "✅ Generated files are up-to-date"
diff --git a/.github/workflows/docs-validation.yml b/.github/workflows/docs-validation.yml
index e7f64be5b..5d244a23f 100644
--- a/.github/workflows/docs-validation.yml
+++ b/.github/workflows/docs-validation.yml
@@ -9,6 +9,7 @@ on:
- 'python/copilot/**'
- 'go/**/*.go'
- 'dotnet/src/**'
+ - 'java/src/**'
- 'scripts/docs-validation/**'
- '.github/workflows/docs-validation.yml'
workflow_dispatch:
@@ -126,3 +127,32 @@ jobs:
- name: Extract and validate C#
working-directory: scripts/docs-validation
run: npm run extract && npm run validate:cs
+
+ validate-java:
+ name: "Validate Java"
+ if: github.event.repository.fork == false
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v6
+
+ - uses: actions/setup-node@v6
+ with:
+ node-version: 22
+
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+ cache: 'maven'
+
+ - name: Install SDK to local repo
+ working-directory: java
+ run: mvn install -DskipTests -q
+
+ - name: Install validation dependencies
+ working-directory: scripts/docs-validation
+ run: npm ci
+
+ - name: Extract and validate Java
+ working-directory: scripts/docs-validation
+ run: npm run extract && npm run validate:java
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 8d5642595..105fec4d7 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -19,5 +19,6 @@
},
"[go]": {
"editor.defaultFormatter": "golang.go"
- }
+ },
+ "java.configuration.updateBuildConfiguration": "automatic"
}
diff --git a/README.md b/README.md
index 277586b2d..8714d85c8 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ The GitHub Copilot SDK exposes the same engine behind Copilot CLI: a production-
| **Go** | [`go/`](./go/) | [Cookbook](https://github.com/github/awesome-copilot/blob/main/cookbook/copilot-sdk/go/README.md) | `go get github.com/github/copilot-sdk/go` |
| **.NET** | [`dotnet/`](./dotnet/) | [Cookbook](https://github.com/github/awesome-copilot/blob/main/cookbook/copilot-sdk/dotnet/README.md) | `dotnet add package GitHub.Copilot.SDK` |
| **Rust** | [`rust/`](./rust/) | — | `cargo add github-copilot-sdk` |
-| **Java** | [`github/copilot-sdk-java`](https://github.com/github/copilot-sdk-java) | [Cookbook](https://github.com/github/awesome-copilot/blob/main/cookbook/copilot-sdk/java/README.md) | Maven coordinates
`com.github:copilot-sdk-java`
See instructions for [Maven](https://github.com/github/copilot-sdk-java?tab=readme-ov-file#maven) and [Gradle](https://github.com/github/copilot-sdk-java?tab=readme-ov-file#gradle) |
+| **Java** | [`java/`](./java/) | [Cookbook](https://github.com/github/awesome-copilot/blob/main/cookbook/copilot-sdk/java/README.md) | Maven coordinates
`com.github:copilot-sdk-java`
See instructions for [Maven](https://github.com/github/copilot-sdk-java?tab=readme-ov-file#maven) and [Gradle](https://github.com/github/copilot-sdk-java?tab=readme-ov-file#gradle) |
See the individual SDK READMEs for installation, usage examples, and API reference.
@@ -37,7 +37,7 @@ Quick steps:
1. **(Optional) Install the Copilot CLI**
For Node.js, Python, and .NET SDKs, the Copilot CLI is bundled automatically and no separate installation is required.
-For the Go and Rust SDKs, [install the CLI manually](https://github.com/features/copilot/cli) or ensure `copilot` is available in your PATH unless you opt into their application-level CLI bundling features.
+For the Go, Java and Rust SDKs, [install the CLI manually](https://github.com/features/copilot/cli) or ensure `copilot` is available in your PATH unless you opt into their application-level CLI bundling features.
2. **Install your preferred SDK** using the commands above.
@@ -88,7 +88,7 @@ See the **[Authentication documentation](./docs/auth/index.md)** for details on
No — for Node.js, Python, and .NET SDKs, the Copilot CLI is bundled automatically as a dependency. You do not need to install it separately.
-For Go and Rust SDKs, the CLI is not bundled by default. Install the CLI manually, ensure `copilot` is available in your PATH, or opt into their application-level CLI bundling features.
+For Go, Java and Rust SDKs, the CLI is **not** bundled by default. Install the CLI manually, ensure `copilot` is available in your PATH, or opt into their application-level CLI bundling features.
Advanced: You can override the CLI binary or connect to an external server. See the individual SDK README for language-specific options.
@@ -109,7 +109,7 @@ Yes, check out the custom instructions and SDK-specific guidance:
- **[.NET](https://github.com/github/awesome-copilot/blob/main/instructions/copilot-sdk-csharp.instructions.md)**
- **[Go](https://github.com/github/awesome-copilot/blob/main/instructions/copilot-sdk-go.instructions.md)**
- **[Rust](./rust/README.md)** (SDK guidance; custom instructions not yet published)
-- **[Java](https://github.com/github/copilot-sdk-java/blob/main/instructions/copilot-sdk-java.instructions.md)**
+- **[Java](https://github.com/github/awesome-copilot/blob/main/instructions/copilot-sdk-java.instructions.md)**
### What models are supported?
diff --git a/docs/auth/authenticate.md b/docs/auth/authenticate.md
index d2fb11603..36bc855f5 100644
--- a/docs/auth/authenticate.md
+++ b/docs/auth/authenticate.md
@@ -89,7 +89,7 @@ await using var client = new CopilotClient();
Java
```java
-import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.CopilotClient;
// Default: uses logged-in user credentials
var client = new CopilotClient();
@@ -205,9 +205,10 @@ await using var client = new CopilotClient(new CopilotClientOptions
Java
+
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
var client = new CopilotClient(new CopilotClientOptions()
.setGitHubToken(userAccessToken) // Token from OAuth flow
@@ -384,8 +385,8 @@ await using var client = new CopilotClient(new CopilotClientOptions
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
var client = new CopilotClient(new CopilotClientOptions()
.setUseLoggedInUser(false) // Only use explicit tokens
diff --git a/docs/auth/byok.md b/docs/auth/byok.md
index 4afb149e8..8bfc5d50c 100644
--- a/docs/auth/byok.md
+++ b/docs/auth/byok.md
@@ -170,9 +170,8 @@ Console.WriteLine(response?.Data.Content);
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
var client = new CopilotClient();
client.start().get();
@@ -450,8 +449,8 @@ var client = new CopilotClient(new CopilotClientOptions
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
diff --git a/docs/features/custom-agents.md b/docs/features/custom-agents.md
index 5329f163e..d0f209649 100644
--- a/docs/features/custom-agents.md
+++ b/docs/features/custom-agents.md
@@ -210,9 +210,8 @@ await using var session = await client.CreateSessionAsync(new SessionConfig
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
import java.util.List;
try (var client = new CopilotClient()) {
@@ -387,7 +386,7 @@ var session = await client.CreateSessionAsync(new SessionConfig
```java
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.rpc.*;
import java.util.List;
var session = client.createSession(
@@ -656,6 +655,7 @@ await session.SendAndWaitAsync(new MessageOptions
Java
+
```java
session.on(event -> {
if (event instanceof SubagentStartedEvent e) {
@@ -980,4 +980,4 @@ session.on((event) => {
// Show error in UI, retry, or fall back to parent agent
}
});
-```
+```
\ No newline at end of file
diff --git a/docs/features/hooks.md b/docs/features/hooks.md
index b348488a0..6af5232a6 100644
--- a/docs/features/hooks.md
+++ b/docs/features/hooks.md
@@ -212,9 +212,8 @@ var session = await client.CreateSessionAsync(new SessionConfig
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
import java.util.concurrent.CompletableFuture;
try (var client = new CopilotClient()) {
@@ -430,14 +429,15 @@ var session = await client.CreateSessionAsync(new SessionConfig
Java
+
```java
import java.util.Set;
import java.util.concurrent.CompletableFuture;
-import com.github.copilot.sdk.PermissionHandler;
-import com.github.copilot.sdk.SessionConfig;
-import com.github.copilot.sdk.SessionHooks;
-import com.github.copilot.sdk.json.PreToolUseHookOutput;
+import com.github.copilot.rpc.PermissionHandler;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SessionHooks;
+import com.github.copilot.rpc.PreToolUseHookOutput;
var readOnlyTools = Set.of("read_file", "glob", "grep", "view");
var hooks = new SessionHooks()
@@ -1063,4 +1063,4 @@ For full type definitions, input/output field tables, and additional examples fo
- [Getting Started](../getting-started.md)
- [Custom Agents & Sub-Agent Orchestration](./custom-agents.md)
- [Streaming Session Events](./streaming-events.md)
-- [Debugging Guide](../troubleshooting/debugging.md)
+- [Debugging Guide](../troubleshooting/debugging.md)
\ No newline at end of file
diff --git a/docs/features/image-input.md b/docs/features/image-input.md
index cf2dee518..b850a4c1b 100644
--- a/docs/features/image-input.md
+++ b/docs/features/image-input.md
@@ -225,9 +225,8 @@ await session.SendAsync(new MessageOptions
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
import java.util.List;
try (var client = new CopilotClient()) {
@@ -434,9 +433,8 @@ await session.SendAsync(new MessageOptions
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
import java.util.List;
try (var client = new CopilotClient()) {
diff --git a/docs/features/skills.md b/docs/features/skills.md
index bac35e39e..6db955e74 100644
--- a/docs/features/skills.md
+++ b/docs/features/skills.md
@@ -145,9 +145,8 @@ await session.SendAndWaitAsync(new MessageOptions
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
import java.util.List;
try (var client = new CopilotClient()) {
@@ -280,8 +279,9 @@ var session = await client.CreateSessionAsync(new SessionConfig
Java
+
```java
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.rpc.*;
import java.util.List;
var session = client.createSession(
@@ -422,4 +422,4 @@ If multiple skills provide conflicting instructions:
* [Custom Agents](../getting-started.md#create-custom-agents) - Define specialized AI personas
* [Custom Tools](../getting-started.md#step-4-add-a-custom-tool) - Build your own tools
-* [MCP Servers](./mcp.md) - Connect external tool providers
+* [MCP Servers](./mcp.md) - Connect external tool providers
\ No newline at end of file
diff --git a/docs/features/steering-and-queueing.md b/docs/features/steering-and-queueing.md
index 3b32f678d..7dbdc17b7 100644
--- a/docs/features/steering-and-queueing.md
+++ b/docs/features/steering-and-queueing.md
@@ -183,9 +183,8 @@ await session.SendAsync(new MessageOptions
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
try (var client = new CopilotClient()) {
client.start().get();
@@ -427,9 +426,8 @@ await session.SendAsync(new MessageOptions
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
try (var client = new CopilotClient()) {
client.start().get();
diff --git a/docs/features/streaming-events.md b/docs/features/streaming-events.md
index 9b61108ed..3388d6a1a 100644
--- a/docs/features/streaming-events.md
+++ b/docs/features/streaming-events.md
@@ -195,6 +195,7 @@ session.On(evt =>
Java
+
```java
// All events
session.on(event -> System.out.println(event.getType()));
@@ -793,4 +794,4 @@ session.idle → Ready for next message (ephemeral)
| `command.queued` | ✅ | Command | `requestId`, `command` |
| `command.completed` | ✅ | Command | `requestId` |
| `exit_plan_mode.requested` | ✅ | Plan Mode | `requestId`, `summary`, `planContent`, `actions` |
-| `exit_plan_mode.completed` | ✅ | Plan Mode | `requestId` |
+| `exit_plan_mode.completed` | ✅ | Plan Mode | `requestId` |
\ No newline at end of file
diff --git a/docs/getting-started.md b/docs/getting-started.md
index b5df45100..80c9541e4 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -325,10 +325,10 @@ dotnet run
Create `HelloCopilot.java`:
+
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
public class HelloCopilot {
public static void main(String[] args) throws Exception {
@@ -590,10 +590,10 @@ await session.SendAndWaitAsync(new MessageOptions { Prompt = "Tell me a short jo
Update `HelloCopilot.java`:
+
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
public class HelloCopilot {
public static void main(String[] args) throws Exception {
@@ -856,6 +856,7 @@ unsubscribe.Dispose();
Java
+
```java
// Subscribe to all events
var unsubscribe = session.on(event -> {
@@ -1214,10 +1215,10 @@ await session.SendAndWaitAsync(new MessageOptions
Update `HelloCopilot.java`:
+
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
import java.util.List;
import java.util.Map;
@@ -1719,10 +1720,10 @@ dotnet run
Create `WeatherAssistant.java`:
+
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
import java.util.List;
import java.util.Map;
@@ -2085,8 +2086,8 @@ await using var session = await client.CreateSessionAsync(new()
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
var client = new CopilotClient(
new CopilotClientOptions().setCliUrl("localhost:4321")
@@ -2207,8 +2208,8 @@ No extra dependencies—uses built-in `System.Diagnostics.Activity`.
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
var client = new CopilotClient(new CopilotClientOptions()
.setTelemetry(new TelemetryConfig()
@@ -2273,4 +2274,4 @@ Trace context is propagated automatically—no manual instrumentation is needed:
* ✅ Streaming for real-time output
* ✅ Defining custom tools that Copilot can call
-Now go build something amazing! 🚀
+Now go build something amazing! 🚀
\ No newline at end of file
diff --git a/docs/hooks/error-handling.md b/docs/hooks/error-handling.md
index f36573f31..471c9c426 100644
--- a/docs/hooks/error-handling.md
+++ b/docs/hooks/error-handling.md
@@ -102,6 +102,7 @@ public delegate Task ErrorOccurredHandler(
Java
+
```java
// Note: Java SDK does not have an onErrorOccurred hook.
// Use EventErrorPolicy and EventErrorHandler instead:
@@ -271,9 +272,10 @@ var session = await client.CreateSessionAsync(new SessionConfig
Java
+
```java
-import com.github.copilot.sdk.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.*;
+import com.github.copilot.rpc.*;
// Note: Java SDK does not have an onErrorOccurred hook.
// Use EventErrorPolicy and EventErrorHandler instead:
@@ -514,4 +516,4 @@ const session = await client.createSession({
* [Hooks Overview](./index.md)
* [Session Lifecycle Hooks](./session-lifecycle.md)
-* [Debugging Guide](../troubleshooting/debugging.md)
+* [Debugging Guide](../troubleshooting/debugging.md)
\ No newline at end of file
diff --git a/docs/hooks/hooks-overview.md b/docs/hooks/hooks-overview.md
index 27628a5cc..ad9a2eb52 100644
--- a/docs/hooks/hooks-overview.md
+++ b/docs/hooks/hooks-overview.md
@@ -161,8 +161,8 @@ var session = await client.CreateSessionAsync(new SessionConfig
Java
```java
-import com.github.copilot.sdk.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.*;
+import com.github.copilot.rpc.*;
import java.util.concurrent.CompletableFuture;
try (var client = new CopilotClient()) {
diff --git a/docs/hooks/post-tool-use.md b/docs/hooks/post-tool-use.md
index 96e9ee982..86473c0a2 100644
--- a/docs/hooks/post-tool-use.md
+++ b/docs/hooks/post-tool-use.md
@@ -121,10 +121,25 @@ public delegate Task PostToolUseHandler(
Java
+
```java
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.rpc.*;
+import java.util.concurrent.CompletableFuture;
-PostToolUseHandler postToolUseHandler;
+public class PostToolUseSignature {
+ PostToolUseHandler handler = (PostToolUseHookInput input, HookInvocation invocation) ->
+ CompletableFuture.completedFuture(null);
+ public static void main(String[] args) {}
+}
+```
+
+```java
+@FunctionalInterface
+public interface PostToolUseHandler {
+ CompletableFuture handle(
+ PostToolUseHookInput input,
+ HookInvocation invocation);
+}
```
@@ -289,9 +304,10 @@ var session = await client.CreateSessionAsync(new SessionConfig
Java
+
```java
-import com.github.copilot.sdk.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.*;
+import com.github.copilot.rpc.*;
import java.util.concurrent.CompletableFuture;
var hooks = new SessionHooks()
@@ -493,4 +509,4 @@ const session = await client.createSession({
- [Hooks Overview](./index.md)
- [Pre-Tool Use Hook](./pre-tool-use.md)
-- [Error Handling Hook](./error-handling.md)
+- [Error Handling Hook](./error-handling.md)
\ No newline at end of file
diff --git a/docs/hooks/pre-tool-use.md b/docs/hooks/pre-tool-use.md
index 6e568d8a2..fe373cc2f 100644
--- a/docs/hooks/pre-tool-use.md
+++ b/docs/hooks/pre-tool-use.md
@@ -102,10 +102,25 @@ public delegate Task PreToolUseHandler(
Java
+
```java
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.rpc.*;
+import java.util.concurrent.CompletableFuture;
-PreToolUseHandler preToolUseHandler;
+public class PreToolUseSignature {
+ PreToolUseHandler handler = (PreToolUseHookInput input, HookInvocation invocation) ->
+ CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
+ public static void main(String[] args) {}
+}
+```
+
+```java
+@FunctionalInterface
+public interface PreToolUseHandler {
+ CompletableFuture handle(
+ PreToolUseHookInput input,
+ HookInvocation invocation);
+}
```
@@ -275,9 +290,10 @@ var session = await client.CreateSessionAsync(new SessionConfig
Java
+
```java
-import com.github.copilot.sdk.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.*;
+import com.github.copilot.rpc.*;
import java.util.concurrent.CompletableFuture;
var hooks = new SessionHooks()
@@ -423,4 +439,4 @@ const session = await client.createSession({
* [Hooks Overview](./index.md)
* [Post-Tool Use Hook](./post-tool-use.md)
-* [Debugging Guide](../troubleshooting/debugging.md)
+* [Debugging Guide](../troubleshooting/debugging.md)
\ No newline at end of file
diff --git a/docs/hooks/session-lifecycle.md b/docs/hooks/session-lifecycle.md
index 83b75a32f..aed71132e 100644
--- a/docs/hooks/session-lifecycle.md
+++ b/docs/hooks/session-lifecycle.md
@@ -106,10 +106,25 @@ public delegate Task SessionStartHandler(
Java
+
```java
-import com.github.copilot.sdk.json.*;
-
-SessionStartHandler sessionStartHandler;
+import com.github.copilot.rpc.*;
+import java.util.concurrent.CompletableFuture;
+
+public class SessionStartSignature {
+ SessionStartHandler handler = (SessionStartHookInput input, HookInvocation invocation) ->
+ CompletableFuture.completedFuture(null);
+ public static void main(String[] args) {}
+}
+```
+
+```java
+@FunctionalInterface
+public interface SessionStartHandler {
+ CompletableFuture handle(
+ SessionStartHookInput input,
+ HookInvocation invocation);
+}
```
@@ -316,10 +331,25 @@ public delegate Task SessionEndHandler(
Java
+
```java
-import com.github.copilot.sdk.json.*;
-
-SessionEndHandler sessionEndHandler;
+import com.github.copilot.rpc.*;
+import java.util.concurrent.CompletableFuture;
+
+public class SessionEndSignature {
+ SessionEndHandler handler = (SessionEndHookInput input, HookInvocation invocation) ->
+ CompletableFuture.completedFuture(null);
+ public static void main(String[] args) {}
+}
+```
+
+```java
+@FunctionalInterface
+public interface SessionEndHandler {
+ CompletableFuture handle(
+ SessionEndHookInput input,
+ HookInvocation invocation);
+}
```
diff --git a/docs/hooks/user-prompt-submitted.md b/docs/hooks/user-prompt-submitted.md
index 79e34249d..edb6c7a3f 100644
--- a/docs/hooks/user-prompt-submitted.md
+++ b/docs/hooks/user-prompt-submitted.md
@@ -102,10 +102,25 @@ public delegate Task UserPromptSubmittedHandler(
Java
+
```java
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.rpc.*;
+import java.util.concurrent.CompletableFuture;
-UserPromptSubmittedHandler userPromptSubmittedHandler;
+public class UserPromptSubmittedSignature {
+ UserPromptSubmittedHandler handler = (UserPromptSubmittedHookInput input, HookInvocation invocation) ->
+ CompletableFuture.completedFuture(null);
+ public static void main(String[] args) {}
+}
+```
+
+```java
+@FunctionalInterface
+public interface UserPromptSubmittedHandler {
+ CompletableFuture handle(
+ UserPromptSubmittedHookInput input,
+ HookInvocation invocation);
+}
```
@@ -250,9 +265,10 @@ var session = await client.CreateSessionAsync(new SessionConfig
Java
+
```java
-import com.github.copilot.sdk.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.*;
+import com.github.copilot.rpc.*;
import java.util.concurrent.CompletableFuture;
var hooks = new SessionHooks()
@@ -482,4 +498,4 @@ const session = await client.createSession({
* [Hooks Overview](./index.md)
* [Session Lifecycle Hooks](./session-lifecycle.md)
-* [Pre-Tool Use Hook](./pre-tool-use.md)
+* [Pre-Tool Use Hook](./pre-tool-use.md)
\ No newline at end of file
diff --git a/docs/integrations/microsoft-agent-framework.md b/docs/integrations/microsoft-agent-framework.md
index 1802ddd4b..3d6d99086 100644
--- a/docs/integrations/microsoft-agent-framework.md
+++ b/docs/integrations/microsoft-agent-framework.md
@@ -116,9 +116,8 @@ async def main():
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
var client = new CopilotClient();
client.start().get();
@@ -232,9 +231,8 @@ await session.sendAndWait({ prompt: "What's the weather like in Seattle?" });
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@@ -353,9 +351,8 @@ async def main():
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
// Java uses the standard SDK directly — no MAF orchestrator needed
var client = new CopilotClient();
@@ -428,9 +425,8 @@ Console.WriteLine(combinedResult);
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
import java.util.concurrent.CompletableFuture;
// Java uses CompletableFuture for concurrent execution
@@ -539,10 +535,10 @@ await session.sendAndWait({ prompt: "Write a quicksort implementation in TypeScr
Java
+
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
var client = new CopilotClient();
client.start().get();
@@ -650,4 +646,4 @@ catch (AgentException ex)
* [Custom Agents](../features/custom-agents.md): define specialized sub-agents within the SDK
* [Custom Skills](../features/skills.md): reusable prompt modules
* [Microsoft Agent Framework documentation](https://learn.microsoft.com/en-us/agent-framework/agents/providers/github-copilot): official MAF docs for the Copilot provider
-* [Blog: Build AI Agents with GitHub Copilot SDK and Microsoft Agent Framework](https://devblogs.microsoft.com/semantic-kernel/build-ai-agents-with-github-copilot-sdk-and-microsoft-agent-framework/)
+* [Blog: Build AI Agents with GitHub Copilot SDK and Microsoft Agent Framework](https://devblogs.microsoft.com/semantic-kernel/build-ai-agents-with-github-copilot-sdk-and-microsoft-agent-framework/)
\ No newline at end of file
diff --git a/docs/observability/opentelemetry.md b/docs/observability/opentelemetry.md
index 1f9581ba5..ee2014efb 100644
--- a/docs/observability/opentelemetry.md
+++ b/docs/observability/opentelemetry.md
@@ -73,8 +73,8 @@ var client = new CopilotClient(new CopilotClientOptions
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
var client = new CopilotClient(new CopilotClientOptions()
.setTelemetry(new TelemetryConfig()
diff --git a/docs/setup/backend-services.md b/docs/setup/backend-services.md
index dfe9c19af..2dc2c47d1 100644
--- a/docs/setup/backend-services.md
+++ b/docs/setup/backend-services.md
@@ -260,9 +260,8 @@ var response = await session.SendAndWaitAsync(
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
var userId = "user1";
var message = "Hello!";
diff --git a/docs/setup/bundled-cli.md b/docs/setup/bundled-cli.md
index 8f036ee8b..94bb61754 100644
--- a/docs/setup/bundled-cli.md
+++ b/docs/setup/bundled-cli.md
@@ -140,9 +140,8 @@ Console.WriteLine(response?.Data.Content);
> The Java SDK does not bundle or embed the Copilot CLI. You must install the CLI separately and configure its path via `Connection` or the `COPILOT_CLI_PATH` environment variable.
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
var client = new CopilotClient(new CopilotClientOptions()
// Point to the CLI binary installed on the system
diff --git a/docs/setup/github-oauth.md b/docs/setup/github-oauth.md
index 6cba4a5b7..aea6b22b9 100644
--- a/docs/setup/github-oauth.md
+++ b/docs/setup/github-oauth.md
@@ -278,10 +278,10 @@ var response = await session.SendAndWaitAsync(
Java
+
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.*;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
CopilotClient createClientForUser(String userToken) throws Exception {
var client = new CopilotClient(new CopilotClientOptions()
diff --git a/docs/troubleshooting/debugging.md b/docs/troubleshooting/debugging.md
index 95c9b3a7d..77f950552 100644
--- a/docs/troubleshooting/debugging.md
+++ b/docs/troubleshooting/debugging.md
@@ -96,8 +96,8 @@ var client = new CopilotClient(new CopilotClientOptions
Java
```java
-import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.json.*;
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.*;
var client = new CopilotClient(new CopilotClientOptions()
.setLogLevel("debug")
@@ -180,6 +180,7 @@ var client = new CopilotClient(new CopilotClientOptions
Java
+
```java
// The Java SDK does not currently support passing extra CLI arguments.
// For custom log directories, run the CLI manually with --log-dir
diff --git a/scripts/docs-validation/extract.ts b/scripts/docs-validation/extract.ts
index b8d7cb089..df1c358de 100644
--- a/scripts/docs-validation/extract.ts
+++ b/scripts/docs-validation/extract.ts
@@ -23,6 +23,7 @@ const LANGUAGE_MAP: Record = {
csharp: "csharp",
"c#": "csharp",
cs: "csharp",
+ java: "java",
};
interface CodeBlock {
@@ -152,6 +153,8 @@ function getExtension(language: string): string {
return ".go";
case "csharp":
return ".cs";
+ case "java":
+ return ".java";
default:
return ".txt";
}
@@ -185,6 +188,18 @@ function shouldSkipFragment(block: CodeBlock): boolean {
}
}
+ // Java: Skip interface definitions, annotations-only, or method signatures without bodies
+ if (block.language === "java") {
+ // Just an annotation
+ if (/^@\w+/.test(code) && !code.includes("{")) {
+ return true;
+ }
+ // Method signature without body
+ if (/^(public|private|protected)?\s*(static\s+)?[\w<>\[\]]+\s+\w+\([^)]*\)\s*(throws\s+[\w,\s]+)?;\s*$/.test(code)) {
+ return true;
+ }
+ }
+
return false;
}
@@ -352,6 +367,61 @@ ${indentedCode}
}
}
+ // Java: wrap in a class for compilation
+ if (block.language === "java") {
+ const hasClass =
+ code.includes("class ") ||
+ code.includes("interface ") ||
+ code.includes("enum ");
+
+ if (!hasClass) {
+ // Extract any existing import statements
+ const lines = code.split("\n");
+ const imports: string[] = [];
+ const rest: string[] = [];
+
+ for (const line of lines) {
+ if (line.trim().startsWith("import ")) {
+ imports.push(line);
+ } else {
+ rest.push(line);
+ }
+ }
+
+ // Add default imports if no SDK imports are present
+ const hasAnyCopilotImport = imports.some(i =>
+ i.includes("com.github.copilot"),
+ );
+ if (!hasAnyCopilotImport) {
+ imports.push("import com.github.copilot.*;");
+ imports.push("import com.github.copilot.rpc.*;");
+ imports.push("import java.util.*;");
+ imports.push("import java.util.concurrent.*;");
+ }
+
+ // Generate a unique class name from block.file and block.line
+ let className = `${block.file.replace(/[^a-zA-Z0-9]/g, "_")}_${block.line}`;
+ if (/^\d/.test(className)) {
+ className = "Snippet_" + className;
+ }
+
+ const indentedCode = rest.map(l => " " + l).join("\n");
+
+ code = `${imports.join("\n")}
+
+public class ${className} {
+ public static void main(String[] args) throws Exception {
+${indentedCode}
+ }
+}`;
+ } else {
+ // Has class structure. Only add SDK imports if not already present.
+ if (!code.includes("import com.github.copilot")) {
+ code = "import com.github.copilot.*;\nimport com.github.copilot.rpc.*;\nimport java.util.*;\nimport java.util.concurrent.*;\n" + code;
+ }
+ }
+ }
+
return code;
}
@@ -365,7 +435,7 @@ async function main() {
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
// Create language subdirectories
- for (const lang of ["typescript", "python", "go", "csharp"]) {
+ for (const lang of ["typescript", "python", "go", "csharp", "java"]) {
fs.mkdirSync(path.join(OUTPUT_DIR, lang), { recursive: true });
}
@@ -414,10 +484,19 @@ async function main() {
}
const fileName = generateFileName(block, totalBlocks, langCounts);
- const outputPath = path.join(OUTPUT_DIR, block.language, fileName);
-
const wrappedCode = wrapCodeForValidation(block);
+ // For Java, filename must match the public class name
+ let actualFileName = fileName;
+ if (block.language === "java") {
+ const classMatch = wrappedCode.match(/public class (\w+)/);
+ if (classMatch) {
+ actualFileName = classMatch[1] + ".java";
+ }
+ }
+
+ const outputPath = path.join(OUTPUT_DIR, block.language, actualFileName);
+
// Add source location comment
const sourceComment = getSourceComment(
block.language,
@@ -429,11 +508,11 @@ async function main() {
fs.writeFileSync(outputPath, finalCode);
manifest.blocks.push({
- id: `${block.language}/${fileName}`,
+ id: `${block.language}/${actualFileName}`,
sourceFile: block.file,
sourceLine: block.line,
language: block.language,
- outputFile: `${block.language}/${fileName}`,
+ outputFile: `${block.language}/${actualFileName}`,
});
totalBlocks++;
@@ -469,7 +548,10 @@ function getSourceComment(
file: string,
line: number
): string {
- const location = `Source: ${file}:${line}`;
+ // Normalize path separators to forward slashes to avoid issues
+ // (e.g., Java interprets \u as a unicode escape sequence)
+ const normalizedFile = file.replace(/\\/g, "/");
+ const location = `Source: ${normalizedFile}:${line}`;
switch (language) {
case "typescript":
case "go":
diff --git a/scripts/docs-validation/package.json b/scripts/docs-validation/package.json
index 976df1de5..b802f0ba3 100644
--- a/scripts/docs-validation/package.json
+++ b/scripts/docs-validation/package.json
@@ -9,7 +9,8 @@
"validate:ts": "tsx validate.ts --lang typescript",
"validate:py": "tsx validate.ts --lang python",
"validate:go": "tsx validate.ts --lang go",
- "validate:cs": "tsx validate.ts --lang csharp"
+ "validate:cs": "tsx validate.ts --lang csharp",
+ "validate:java": "tsx validate.ts --lang java"
},
"dependencies": {
"glob": "^11.0.0",
diff --git a/scripts/docs-validation/validate.ts b/scripts/docs-validation/validate.ts
index bf11baa6e..fdc270e1f 100644
--- a/scripts/docs-validation/validate.ts
+++ b/scripts/docs-validation/validate.ts
@@ -3,10 +3,10 @@
* Runs language-specific type/compile checks.
*/
+import { execFileSync, execSync } from "child_process";
import * as fs from "fs";
-import * as path from "path";
-import { execFileSync } from "child_process";
import { glob } from "glob";
+import * as path from "path";
const ROOT_DIR = path.resolve(import.meta.dirname, "../..");
const VALIDATION_DIR = path.join(ROOT_DIR, "docs/.validation");
@@ -33,7 +33,7 @@ function loadManifest(): Manifest {
const manifestPath = path.join(VALIDATION_DIR, "manifest.json");
if (!fs.existsSync(manifestPath)) {
console.error(
- "❌ No manifest found. Run extraction first: npm run extract"
+ "❌ No manifest found. Run extraction first: npm run extract",
);
process.exit(1);
}
@@ -86,7 +86,7 @@ async function validateTypeScript(): Promise {
for (const file of files) {
if (file === "tsconfig.json") continue;
const block = manifest.blocks.find(
- (b) => b.outputFile === `typescript/${file}`
+ (b) => b.outputFile === `typescript/${file}`,
);
results.push({
file: `typescript/${file}`,
@@ -122,7 +122,7 @@ async function validateTypeScript(): Promise {
if (file === "tsconfig.json") continue;
const fullPath = path.join(tsDir, file);
const block = manifest.blocks.find(
- (b) => b.outputFile === `typescript/${file}`
+ (b) => b.outputFile === `typescript/${file}`,
);
const errors = fileErrors.get(fullPath) || fileErrors.get(file) || [];
@@ -154,7 +154,7 @@ async function validatePython(): Promise {
for (const file of files) {
const fullPath = path.join(pyDir, file);
const block = manifest.blocks.find(
- (b) => b.outputFile === `python/${file}`
+ (b) => b.outputFile === `python/${file}`,
);
const errors: string[] = [];
@@ -172,8 +172,14 @@ async function validatePython(): Promise {
try {
execFileSync(
"python3",
- ["-m", "mypy", fullPath, "--ignore-missing-imports", "--no-error-summary"],
- { encoding: "utf-8" }
+ [
+ "-m",
+ "mypy",
+ fullPath,
+ "--ignore-missing-imports",
+ "--no-error-summary",
+ ],
+ { encoding: "utf-8" },
);
} catch (err: any) {
const output = err.stdout || err.stderr || err.message || "";
@@ -183,7 +189,7 @@ async function validatePython(): Promise {
.filter(
(l: string) =>
l.includes(": error:") &&
- !l.includes("Cannot find implementation")
+ !l.includes("Cannot find implementation"),
);
if (typeErrors.length > 0) {
errors.push(...typeErrors);
@@ -253,7 +259,9 @@ replace github.com/github/copilot-sdk/go => ${path.join(ROOT_DIR, "go")}
} catch (err: any) {
const output = err.stdout || err.stderr || err.message || "";
errors.push(
- ...output.split("\n").filter((l: string) => l.trim() && !l.startsWith("#"))
+ ...output
+ .split("\n")
+ .filter((l: string) => l.trim() && !l.startsWith("#")),
);
}
@@ -299,15 +307,19 @@ async function validateCSharp(): Promise {
// Compile all files together
try {
- execFileSync("dotnet", ["build", path.join(csDir, "DocsValidation.csproj")], {
- encoding: "utf-8",
- cwd: csDir,
- });
+ execFileSync(
+ "dotnet",
+ ["build", path.join(csDir, "DocsValidation.csproj")],
+ {
+ encoding: "utf-8",
+ cwd: csDir,
+ },
+ );
// All files passed
for (const file of files) {
const block = manifest.blocks.find(
- (b) => b.outputFile === `csharp/${file}`
+ (b) => b.outputFile === `csharp/${file}`,
);
results.push({
file: `csharp/${file}`,
@@ -336,7 +348,7 @@ async function validateCSharp(): Promise {
for (const file of files) {
const block = manifest.blocks.find(
- (b) => b.outputFile === `csharp/${file}`
+ (b) => b.outputFile === `csharp/${file}`,
);
const errors = fileErrors.get(file) || [];
@@ -353,7 +365,148 @@ async function validateCSharp(): Promise {
return results;
}
-function printResults(results: ValidationResult[], language: string): { failed: number; passed: number; failures: ValidationResult[] } {
+async function validateJava(): Promise {
+ const results: ValidationResult[] = [];
+ const javaDir = path.join(VALIDATION_DIR, "java");
+ const manifest = loadManifest();
+
+ if (!fs.existsSync(javaDir)) {
+ console.log(" No Java files to validate");
+ return results;
+ }
+
+ // Create a minimal Maven project structure
+ const srcDir = path.join(javaDir, "src", "main", "java");
+ fs.mkdirSync(srcDir, { recursive: true });
+
+ // Move all .java files into src/main/java/
+ const files = await glob("*.java", { cwd: javaDir });
+ for (const file of files) {
+ fs.renameSync(path.join(javaDir, file), path.join(srcDir, file));
+ }
+
+ // Read the SDK version from java/pom.xml
+ const sdkPomPath = path.join(ROOT_DIR, "java", "pom.xml");
+ const sdkPomContent = fs.readFileSync(sdkPomPath, "utf-8");
+ const versionMatch = sdkPomContent.match(
+ /copilot-sdk-java<\/artifactId>\s*([^<]+)<\/version>/,
+ );
+ const sdkVersion = versionMatch ? versionMatch[1] : "1.0.0-SNAPSHOT";
+
+ // Create pom.xml that references the local SDK
+ const pomXml = `
+
+ 4.0.0
+ docs
+ docs-validation-java
+ 1.0.0
+
+ 17
+ 17
+ UTF-8
+
+
+
+ com.github
+ copilot-sdk-java
+ ${sdkVersion}
+
+
+`;
+
+ fs.writeFileSync(path.join(javaDir, "pom.xml"), pomXml);
+
+ // First, install the local SDK into the local Maven repo
+ const pomPath = path.join(ROOT_DIR, "java", "pom.xml");
+ try {
+ execSync(`mvn install -f "${pomPath}" -DskipTests -q`, {
+ encoding: "utf-8",
+ cwd: path.join(ROOT_DIR, "java"),
+ });
+ } catch (err: any) {
+ // If SDK install fails, all Java snippets fail
+ const errorMsg = `SDK install failed: ${(err.stderr || err.message || "").slice(0, 200)}`;
+ for (const file of files) {
+ const block = manifest.blocks.find(
+ (b) => b.outputFile === `java/${file}`,
+ );
+ results.push({
+ file: `java/${file}`,
+ sourceFile: block?.sourceFile || "unknown",
+ sourceLine: block?.sourceLine || 0,
+ success: false,
+ errors: [errorMsg],
+ });
+ }
+ return results;
+ }
+
+ // Compile the validation project
+ try {
+ const validationPom = path.join(javaDir, "pom.xml");
+ execSync(`mvn compile -f "${validationPom}" -q`, {
+ encoding: "utf-8",
+ cwd: javaDir,
+ });
+
+ // All files passed
+ for (const file of files) {
+ const block = manifest.blocks.find(
+ (b) => b.outputFile === `java/${file}`,
+ );
+ results.push({
+ file: `java/${file}`,
+ sourceFile: block?.sourceFile || "unknown",
+ sourceLine: block?.sourceLine || 0,
+ success: true,
+ errors: [],
+ });
+ }
+ } catch (err: any) {
+ const output = err.stdout || err.stderr || err.message || "";
+
+ // Parse javac errors from Maven output
+ // Format: [ERROR] /path/to/File.java:[line,col] error: message
+ const fileErrors = new Map();
+
+ for (const line of output.split("\n")) {
+ const match = line.match(
+ /\[ERROR\]\s+.*[/\\]([^/\\]+\.java):\[(\d+),(\d+)\]\s*(.*)/,
+ );
+ if (match) {
+ const fileName = match[1];
+ if (!fileErrors.has(fileName)) {
+ fileErrors.set(fileName, []);
+ }
+ fileErrors.get(fileName)!.push(`${fileName}:${match[2]}: ${match[4]}`);
+ }
+ }
+
+ for (const file of files) {
+ const block = manifest.blocks.find(
+ (b) => b.outputFile === `java/${file}`,
+ );
+ const errors = fileErrors.get(file) || [];
+
+ results.push({
+ file: `java/${file}`,
+ sourceFile: block?.sourceFile || "unknown",
+ sourceLine: block?.sourceLine || 0,
+ success: errors.length === 0,
+ errors,
+ });
+ }
+ }
+
+ return results;
+}
+
+function printResults(
+ results: ValidationResult[],
+ language: string,
+): { failed: number; passed: number; failures: ValidationResult[] } {
const failed = results.filter((r) => !r.success);
const passed = results.filter((r) => r.success);
@@ -379,7 +532,14 @@ function printResults(results: ValidationResult[], language: string): { failed:
return { failed: failed.length, passed: passed.length, failures: failed };
}
-function writeGitHubSummary(summaryData: { language: string; passed: number; failed: number; failures: ValidationResult[] }[]) {
+function writeGitHubSummary(
+ summaryData: {
+ language: string;
+ passed: number;
+ failed: number;
+ failures: ValidationResult[];
+ }[],
+) {
const summaryFile = process.env.GITHUB_STEP_SUMMARY;
if (!summaryFile) return;
@@ -432,13 +592,19 @@ async function main() {
}
let totalFailed = 0;
- const summaryData: { language: string; passed: number; failed: number; failures: ValidationResult[] }[] = [];
+ const summaryData: {
+ language: string;
+ passed: number;
+ failed: number;
+ failures: ValidationResult[];
+ }[] = [];
const validators: [string, () => Promise][] = [
["TypeScript", validateTypeScript],
["Python", validatePython],
["Go", validateGo],
["C#", validateCSharp],
+ ["Java", validateJava],
];
for (const [name, validator] of validators) {