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/.github/workflows/scenario-builds.yml b/.github/workflows/scenario-builds.yml
index 8114176e7..ddf2581d3 100644
--- a/.github/workflows/scenario-builds.yml
+++ b/.github/workflows/scenario-builds.yml
@@ -11,6 +11,7 @@ on:
- "dotnet/src/**"
- "rust/src/**"
- "rust/Cargo.toml"
+ - "java/src/**"
- ".github/workflows/scenario-builds.yml"
push:
branches:
@@ -235,3 +236,46 @@ jobs:
echo -e "Failures:$FAILURES"
exit 1
fi
+
+ # ── Java ────────────────────────────────────────────────────────────
+ build-java:
+ name: "Java scenarios"
+ if: github.event.repository.fork == false
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v6
+
+ - uses: actions/setup-java@v4
+ with:
+ distribution: "temurin"
+ java-version: "17"
+ cache: "maven"
+
+ # Install SDK to local Maven repo so scenarios can resolve it
+ - name: Install SDK to local repo
+ working-directory: java
+ run: mvn install -DskipTests -q
+
+ - name: Build all Java scenarios
+ run: |
+ PASS=0; FAIL=0; FAILURES=""
+ for pom in $(find test/scenarios -path '*/java/pom.xml' | sort); do
+ dir=$(dirname "$pom")
+ scenario="${dir#test/scenarios/}"
+ echo "::group::$scenario"
+ if (cd "$dir" && mvn compile -q 2>&1); then
+ echo "✅ $scenario"
+ PASS=$((PASS + 1))
+ else
+ echo "❌ $scenario"
+ FAIL=$((FAIL + 1))
+ FAILURES="$FAILURES\n $scenario"
+ fi
+ echo "::endgroup::"
+ done
+ echo ""
+ echo "Java builds: $PASS passed, $FAIL failed"
+ if [ "$FAIL" -gt 0 ]; then
+ echo -e "Failures:$FAILURES"
+ exit 1
+ fi
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) {
diff --git a/test/scenarios/auth/byok-anthropic/java/pom.xml b/test/scenarios/auth/byok-anthropic/java/pom.xml
new file mode 100644
index 000000000..45756555f
--- /dev/null
+++ b/test/scenarios/auth/byok-anthropic/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-auth-byok-anthropic
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/auth/byok-anthropic/java/src/main/java/Main.java b/test/scenarios/auth/byok-anthropic/java/src/main/java/Main.java
new file mode 100644
index 000000000..849b9aca6
--- /dev/null
+++ b/test/scenarios/auth/byok-anthropic/java/src/main/java/Main.java
@@ -0,0 +1,46 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.ProviderConfig;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SystemMessageConfig;
+import com.github.copilot.SystemMessageMode;
+
+import java.util.List;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var apiKey = System.getenv("ANTHROPIC_API_KEY");
+ var model = System.getenv("ANTHROPIC_MODEL");
+ if (model == null) model = "claude-sonnet-4-20250514";
+ var baseUrl = System.getenv("ANTHROPIC_BASE_URL");
+ if (baseUrl == null) baseUrl = "https://api.anthropic.com";
+
+ if (apiKey == null || apiKey.isEmpty()) {
+ System.err.println("Missing ANTHROPIC_API_KEY.");
+ System.exit(1);
+ }
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel(model)
+ .setProvider(new ProviderConfig()
+ .setType("anthropic")
+ .setBaseUrl(baseUrl)
+ .setApiKey(apiKey))
+ .setAvailableTools(List.of())
+ .setSystemMessage(new SystemMessageConfig()
+ .setMode(SystemMessageMode.REPLACE)
+ .setContent("You are a helpful assistant. Answer concisely.")))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/auth/byok-azure/java/pom.xml b/test/scenarios/auth/byok-azure/java/pom.xml
new file mode 100644
index 000000000..6366107be
--- /dev/null
+++ b/test/scenarios/auth/byok-azure/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-auth-byok-azure
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/auth/byok-azure/java/src/main/java/Main.java b/test/scenarios/auth/byok-azure/java/src/main/java/Main.java
new file mode 100644
index 000000000..3812eb392
--- /dev/null
+++ b/test/scenarios/auth/byok-azure/java/src/main/java/Main.java
@@ -0,0 +1,50 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.AzureOptions;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.ProviderConfig;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SystemMessageConfig;
+import com.github.copilot.SystemMessageMode;
+
+import java.util.List;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var endpoint = System.getenv("AZURE_OPENAI_ENDPOINT");
+ var apiKey = System.getenv("AZURE_OPENAI_API_KEY");
+ var model = System.getenv("AZURE_OPENAI_MODEL");
+ if (model == null) model = "claude-haiku-4.5";
+ var apiVersion = System.getenv("AZURE_API_VERSION");
+ if (apiVersion == null) apiVersion = "2024-10-21";
+
+ if (endpoint == null || endpoint.isEmpty() || apiKey == null || apiKey.isEmpty()) {
+ System.err.println("Required: AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_API_KEY");
+ System.exit(1);
+ }
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel(model)
+ .setProvider(new ProviderConfig()
+ .setType("azure")
+ .setBaseUrl(endpoint)
+ .setApiKey(apiKey)
+ .setAzure(new AzureOptions()
+ .setApiVersion(apiVersion)))
+ .setAvailableTools(List.of())
+ .setSystemMessage(new SystemMessageConfig()
+ .setMode(SystemMessageMode.REPLACE)
+ .setContent("You are a helpful assistant. Answer concisely.")))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/auth/byok-openai/java/pom.xml b/test/scenarios/auth/byok-openai/java/pom.xml
new file mode 100644
index 000000000..41e87dfac
--- /dev/null
+++ b/test/scenarios/auth/byok-openai/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-auth-byok-openai
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/auth/byok-openai/java/src/main/java/Main.java b/test/scenarios/auth/byok-openai/java/src/main/java/Main.java
new file mode 100644
index 000000000..1aa45299d
--- /dev/null
+++ b/test/scenarios/auth/byok-openai/java/src/main/java/Main.java
@@ -0,0 +1,38 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.ProviderConfig;
+import com.github.copilot.rpc.SessionConfig;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var apiKey = System.getenv("OPENAI_API_KEY");
+ var model = System.getenv("OPENAI_MODEL");
+ if (model == null) model = "claude-haiku-4.5";
+ var baseUrl = System.getenv("OPENAI_BASE_URL");
+ if (baseUrl == null) baseUrl = "https://api.openai.com/v1";
+
+ if (apiKey == null || apiKey.isEmpty()) {
+ System.err.println("Missing OPENAI_API_KEY.");
+ System.exit(1);
+ }
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel(model)
+ .setProvider(new ProviderConfig()
+ .setType("openai")
+ .setBaseUrl(baseUrl)
+ .setApiKey(apiKey)))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/auth/gh-app/java/pom.xml b/test/scenarios/auth/gh-app/java/pom.xml
new file mode 100644
index 000000000..c33be4a68
--- /dev/null
+++ b/test/scenarios/auth/gh-app/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-auth-gh-app
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/auth/gh-app/java/src/main/java/Main.java b/test/scenarios/auth/gh-app/java/src/main/java/Main.java
new file mode 100644
index 000000000..4e1db52be
--- /dev/null
+++ b/test/scenarios/auth/gh-app/java/src/main/java/Main.java
@@ -0,0 +1,98 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.CopilotClientOptions;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var clientId = System.getenv("GITHUB_OAUTH_CLIENT_ID");
+ if (clientId == null || clientId.isEmpty()) {
+ System.err.println("Missing GITHUB_OAUTH_CLIENT_ID");
+ System.exit(1);
+ }
+
+ var mapper = new ObjectMapper();
+ var httpClient = HttpClient.newHttpClient();
+
+ // Step 1: Request device code
+ var deviceCodeReq = HttpRequest.newBuilder()
+ .uri(URI.create("https://github.com/login/device/code"))
+ .header("Accept", "application/json")
+ .header("User-Agent", "copilot-sdk-java")
+ .POST(HttpRequest.BodyPublishers.ofString("client_id=" + clientId))
+ .header("Content-Type", "application/x-www-form-urlencoded")
+ .build();
+ var deviceCodeResp = httpClient.send(deviceCodeReq, HttpResponse.BodyHandlers.ofString());
+ var deviceCode = mapper.readTree(deviceCodeResp.body());
+
+ var userCode = deviceCode.get("user_code").asText();
+ var verificationUri = deviceCode.get("verification_uri").asText();
+ var code = deviceCode.get("device_code").asText();
+ var interval = deviceCode.get("interval").asInt();
+
+ System.out.println("Please visit: " + verificationUri);
+ System.out.println("Enter code: " + userCode);
+
+ // Step 2: Poll for access token
+ String accessToken = null;
+ while (accessToken == null) {
+ Thread.sleep(interval * 1000L);
+ var tokenReq = HttpRequest.newBuilder()
+ .uri(URI.create("https://github.com/login/oauth/access_token"))
+ .header("Accept", "application/json")
+ .header("Content-Type", "application/x-www-form-urlencoded")
+ .POST(HttpRequest.BodyPublishers.ofString(
+ "client_id=" + clientId
+ + "&device_code=" + code
+ + "&grant_type=urn:ietf:params:oauth:grant-type:device_code"))
+ .build();
+ var tokenResp = httpClient.send(tokenReq, HttpResponse.BodyHandlers.ofString());
+ var tokenData = mapper.readTree(tokenResp.body());
+
+ if (tokenData.has("access_token")) {
+ accessToken = tokenData.get("access_token").asText();
+ } else if (tokenData.has("error")) {
+ var err = tokenData.get("error").asText();
+ if ("authorization_pending".equals(err)) continue;
+ if ("slow_down".equals(err)) { interval += 5; continue; }
+ throw new RuntimeException("OAuth error: " + err);
+ }
+ }
+
+ // Step 3: Verify authentication
+ var userReq = HttpRequest.newBuilder()
+ .uri(URI.create("https://api.github.com/user"))
+ .header("Authorization", "Bearer " + accessToken)
+ .header("User-Agent", "copilot-sdk-java")
+ .GET()
+ .build();
+ var userResp = httpClient.send(userReq, HttpResponse.BodyHandlers.ofString());
+ var userData = mapper.readTree(userResp.body());
+ System.out.println("Authenticated as: " + userData.get("login").asText());
+
+ // Step 4: Use the token with Copilot
+ try (var client = new CopilotClient(new CopilotClientOptions()
+ .setGitHubToken(accessToken))) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5"))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/bundling/app-direct-server/java/pom.xml b/test/scenarios/bundling/app-direct-server/java/pom.xml
new file mode 100644
index 000000000..572907bb2
--- /dev/null
+++ b/test/scenarios/bundling/app-direct-server/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-bundling-app-direct-server
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/bundling/app-direct-server/java/src/main/java/Main.java b/test/scenarios/bundling/app-direct-server/java/src/main/java/Main.java
new file mode 100644
index 000000000..92bf08dd5
--- /dev/null
+++ b/test/scenarios/bundling/app-direct-server/java/src/main/java/Main.java
@@ -0,0 +1,31 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.CopilotClientOptions;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var cliUrl = System.getenv("COPILOT_CLI_URL");
+ if (cliUrl == null) {
+ cliUrl = "localhost:3000";
+ }
+
+ try (var client = new CopilotClient(new CopilotClientOptions().setCliUrl(cliUrl))) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5"))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ } else {
+ System.err.println("No response content received");
+ System.exit(1);
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/bundling/fully-bundled/java/pom.xml b/test/scenarios/bundling/fully-bundled/java/pom.xml
new file mode 100644
index 000000000..4bdfa0501
--- /dev/null
+++ b/test/scenarios/bundling/fully-bundled/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-bundling-fully-bundled
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/bundling/fully-bundled/java/src/main/java/Main.java b/test/scenarios/bundling/fully-bundled/java/src/main/java/Main.java
new file mode 100644
index 000000000..80dba10f3
--- /dev/null
+++ b/test/scenarios/bundling/fully-bundled/java/src/main/java/Main.java
@@ -0,0 +1,29 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.CopilotClientOptions;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var cliPath = System.getenv("COPILOT_CLI_PATH");
+ var options = new CopilotClientOptions();
+ if (cliPath != null) {
+ options.setCliPath(cliPath);
+ }
+
+ try (var client = new CopilotClient(options)) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5"))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/callbacks/hooks/java/pom.xml b/test/scenarios/callbacks/hooks/java/pom.xml
new file mode 100644
index 000000000..a6a2fc9ba
--- /dev/null
+++ b/test/scenarios/callbacks/hooks/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-callbacks-hooks
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/callbacks/hooks/java/src/main/java/Main.java b/test/scenarios/callbacks/hooks/java/src/main/java/Main.java
new file mode 100644
index 000000000..cb8541c07
--- /dev/null
+++ b/test/scenarios/callbacks/hooks/java/src/main/java/Main.java
@@ -0,0 +1,63 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.HookInvocation;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.PermissionHandler;
+import com.github.copilot.rpc.PostToolUseHookOutput;
+import com.github.copilot.rpc.PreToolUseHookOutput;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SessionEndHookOutput;
+import com.github.copilot.rpc.SessionHooks;
+import com.github.copilot.rpc.SessionStartHookOutput;
+import com.github.copilot.rpc.UserPromptSubmittedHookOutput;
+
+import java.util.ArrayList;
+import java.util.concurrent.CompletableFuture;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var hookLog = new ArrayList();
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setHooks(new SessionHooks()
+ .setOnSessionStart((input, invocation) -> {
+ hookLog.add("onSessionStart");
+ return CompletableFuture.completedFuture(null);
+ })
+ .setOnSessionEnd((input, invocation) -> {
+ hookLog.add("onSessionEnd");
+ return CompletableFuture.completedFuture(null);
+ })
+ .setOnPreToolUse((input, invocation) -> {
+ hookLog.add("onPreToolUse:" + input.getToolName());
+ return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
+ })
+ .setOnPostToolUse((input, invocation) -> {
+ hookLog.add("onPostToolUse:" + input.getToolName());
+ return CompletableFuture.completedFuture(null);
+ })
+ .setOnUserPromptSubmitted((input, invocation) -> {
+ hookLog.add("onUserPromptSubmitted");
+ return CompletableFuture.completedFuture(null);
+ })))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt(
+ "List the files in the current directory using the glob tool with pattern '*.md'."))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ System.out.println("\n--- Hook execution log ---");
+ for (var entry : hookLog) {
+ System.out.println(" " + entry);
+ }
+ System.out.println("\nTotal hooks fired: " + hookLog.size());
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/callbacks/permissions/java/pom.xml b/test/scenarios/callbacks/permissions/java/pom.xml
new file mode 100644
index 000000000..24e6cd843
--- /dev/null
+++ b/test/scenarios/callbacks/permissions/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-callbacks-permissions
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/callbacks/permissions/java/src/main/java/Main.java b/test/scenarios/callbacks/permissions/java/src/main/java/Main.java
new file mode 100644
index 000000000..e22b3a445
--- /dev/null
+++ b/test/scenarios/callbacks/permissions/java/src/main/java/Main.java
@@ -0,0 +1,48 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.PermissionHandler;
+import com.github.copilot.rpc.PermissionRequest;
+import com.github.copilot.rpc.PermissionRequestResult;
+import com.github.copilot.rpc.PermissionRequestResultKind;
+import com.github.copilot.rpc.PreToolUseHookOutput;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SessionHooks;
+
+import java.util.ArrayList;
+import java.util.concurrent.CompletableFuture;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var permissionLog = new ArrayList();
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setOnPermissionRequest((request, invocation) -> {
+ permissionLog.add("approved:" + request.getKind());
+ return CompletableFuture.completedFuture(
+ new PermissionRequestResult()
+ .setKind(PermissionRequestResultKind.APPROVED));
+ })
+ .setHooks(new SessionHooks()
+ .setOnPreToolUse((input, invocation) ->
+ CompletableFuture.completedFuture(PreToolUseHookOutput.allow()))))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt(
+ "List the files in the current directory using glob with pattern '*.md'."))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ System.out.println("\n--- Permission request log ---");
+ for (var entry : permissionLog) {
+ System.out.println(" " + entry);
+ }
+ System.out.println("\nTotal permission requests: " + permissionLog.size());
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/callbacks/user-input/java/pom.xml b/test/scenarios/callbacks/user-input/java/pom.xml
new file mode 100644
index 000000000..be6a8c8ef
--- /dev/null
+++ b/test/scenarios/callbacks/user-input/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-callbacks-user-input
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/callbacks/user-input/java/src/main/java/Main.java b/test/scenarios/callbacks/user-input/java/src/main/java/Main.java
new file mode 100644
index 000000000..f581910f5
--- /dev/null
+++ b/test/scenarios/callbacks/user-input/java/src/main/java/Main.java
@@ -0,0 +1,48 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.PermissionHandler;
+import com.github.copilot.rpc.PreToolUseHookOutput;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SessionHooks;
+import com.github.copilot.rpc.UserInputHandler;
+import com.github.copilot.rpc.UserInputResponse;
+
+import java.util.ArrayList;
+import java.util.concurrent.CompletableFuture;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var inputLog = new ArrayList();
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setOnUserInputRequest((request, invocation) -> {
+ inputLog.add("question: " + request.getQuestion());
+ return CompletableFuture.completedFuture(
+ new UserInputResponse().setAnswer("Paris").setWasFreeform(true));
+ })
+ .setHooks(new SessionHooks()
+ .setOnPreToolUse((input, invocation) ->
+ CompletableFuture.completedFuture(PreToolUseHookOutput.allow()))))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt(
+ "I want to learn about a city. Use the ask_user tool to ask me which city "
+ + "I'm interested in. Then tell me about that city."))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ System.out.println("\n--- User input log ---");
+ for (var entry : inputLog) {
+ System.out.println(" " + entry);
+ }
+ System.out.println("\nTotal user input requests: " + inputLog.size());
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/modes/default/java/pom.xml b/test/scenarios/modes/default/java/pom.xml
new file mode 100644
index 000000000..3d3f738dd
--- /dev/null
+++ b/test/scenarios/modes/default/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-modes-default
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/modes/default/java/src/main/java/Main.java b/test/scenarios/modes/default/java/src/main/java/Main.java
new file mode 100644
index 000000000..89e68f778
--- /dev/null
+++ b/test/scenarios/modes/default/java/src/main/java/Main.java
@@ -0,0 +1,24 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5"))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt(
+ "Use the grep tool to search for the word 'SDK' in README.md and show the matching lines."))
+ .get();
+ if (response != null) {
+ System.out.println("Response: " + response.getData().content());
+ }
+ System.out.println("Default mode test complete");
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/modes/minimal/java/pom.xml b/test/scenarios/modes/minimal/java/pom.xml
new file mode 100644
index 000000000..4c4cbe61a
--- /dev/null
+++ b/test/scenarios/modes/minimal/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-modes-minimal
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/modes/minimal/java/src/main/java/Main.java b/test/scenarios/modes/minimal/java/src/main/java/Main.java
new file mode 100644
index 000000000..0c3f4fe21
--- /dev/null
+++ b/test/scenarios/modes/minimal/java/src/main/java/Main.java
@@ -0,0 +1,32 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SystemMessageConfig;
+import com.github.copilot.SystemMessageMode;
+
+import java.util.List;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setAvailableTools(List.of())
+ .setSystemMessage(new SystemMessageConfig()
+ .setMode(SystemMessageMode.REPLACE)
+ .setContent("You have no tools. Respond with text only.")))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt(
+ "Use the grep tool to search for 'SDK' in README.md."))
+ .get();
+ if (response != null) {
+ System.out.println("Response: " + response.getData().content());
+ }
+ System.out.println("Minimal mode test complete");
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/prompts/attachments/java/pom.xml b/test/scenarios/prompts/attachments/java/pom.xml
new file mode 100644
index 000000000..542f1c7fe
--- /dev/null
+++ b/test/scenarios/prompts/attachments/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-prompts-attachments
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/prompts/attachments/java/src/main/java/Main.java b/test/scenarios/prompts/attachments/java/src/main/java/Main.java
new file mode 100644
index 000000000..acd5235b7
--- /dev/null
+++ b/test/scenarios/prompts/attachments/java/src/main/java/Main.java
@@ -0,0 +1,39 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.Attachment;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SystemMessageConfig;
+import com.github.copilot.SystemMessageMode;
+
+import java.nio.file.Path;
+import java.util.List;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setSystemMessage(new SystemMessageConfig()
+ .setMode(SystemMessageMode.REPLACE)
+ .setContent("You are a helpful assistant. Answer questions about attached files concisely."))
+ .setAvailableTools(List.of()))
+ .get();
+
+ var sampleFile = Path.of(System.getProperty("user.dir"), "..", "sample-data.txt")
+ .toAbsolutePath().normalize().toString();
+
+ var response = session.sendAndWait(
+ new MessageOptions()
+ .setPrompt("What languages are listed in the attached file?")
+ .setAttachments(List.of(
+ new Attachment("file", sampleFile, "sample-data.txt"))))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/prompts/reasoning-effort/java/pom.xml b/test/scenarios/prompts/reasoning-effort/java/pom.xml
new file mode 100644
index 000000000..414c2c6fe
--- /dev/null
+++ b/test/scenarios/prompts/reasoning-effort/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-prompts-reasoning-effort
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/prompts/reasoning-effort/java/src/main/java/Main.java b/test/scenarios/prompts/reasoning-effort/java/src/main/java/Main.java
new file mode 100644
index 000000000..a8a8a7854
--- /dev/null
+++ b/test/scenarios/prompts/reasoning-effort/java/src/main/java/Main.java
@@ -0,0 +1,32 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SystemMessageConfig;
+import com.github.copilot.SystemMessageMode;
+
+import java.util.List;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-opus-4.6")
+ .setReasoningEffort("low")
+ .setAvailableTools(List.of())
+ .setSystemMessage(new SystemMessageConfig()
+ .setMode(SystemMessageMode.REPLACE)
+ .setContent("You are a helpful assistant. Answer concisely.")))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+ if (response != null) {
+ System.out.println("Reasoning effort: low");
+ System.out.println("Response: " + response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/prompts/system-message/java/pom.xml b/test/scenarios/prompts/system-message/java/pom.xml
new file mode 100644
index 000000000..09b921253
--- /dev/null
+++ b/test/scenarios/prompts/system-message/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-prompts-system-message
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/prompts/system-message/java/src/main/java/Main.java b/test/scenarios/prompts/system-message/java/src/main/java/Main.java
new file mode 100644
index 000000000..59015d0ad
--- /dev/null
+++ b/test/scenarios/prompts/system-message/java/src/main/java/Main.java
@@ -0,0 +1,33 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SystemMessageConfig;
+import com.github.copilot.SystemMessageMode;
+
+import java.util.List;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var piratePrompt = "You are a pirate. Always respond in pirate speak. "
+ + "Say 'Arrr!' in every response. Use nautical terms and pirate slang throughout.";
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setSystemMessage(new SystemMessageConfig()
+ .setMode(SystemMessageMode.REPLACE)
+ .setContent(piratePrompt))
+ .setAvailableTools(List.of()))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/sessions/concurrent-sessions/java/pom.xml b/test/scenarios/sessions/concurrent-sessions/java/pom.xml
new file mode 100644
index 000000000..8a6591a04
--- /dev/null
+++ b/test/scenarios/sessions/concurrent-sessions/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-sessions-concurrent-sessions
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/sessions/concurrent-sessions/java/src/main/java/Main.java b/test/scenarios/sessions/concurrent-sessions/java/src/main/java/Main.java
new file mode 100644
index 000000000..e63fb4774
--- /dev/null
+++ b/test/scenarios/sessions/concurrent-sessions/java/src/main/java/Main.java
@@ -0,0 +1,48 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SystemMessageConfig;
+import com.github.copilot.SystemMessageMode;
+
+import java.util.List;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var session1 = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setSystemMessage(new SystemMessageConfig()
+ .setMode(SystemMessageMode.REPLACE)
+ .setContent("You are a pirate. Always say Arrr!"))
+ .setAvailableTools(List.of()))
+ .get();
+
+ var session2 = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setSystemMessage(new SystemMessageConfig()
+ .setMode(SystemMessageMode.REPLACE)
+ .setContent("You are a robot. Always say BEEP BOOP!"))
+ .setAvailableTools(List.of()))
+ .get();
+
+ var response1 = session1.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+ var response2 = session2.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+
+ if (response1 != null) {
+ System.out.println("Session 1 (pirate): " + response1.getData().content());
+ }
+ if (response2 != null) {
+ System.out.println("Session 2 (robot): " + response2.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/sessions/infinite-sessions/java/pom.xml b/test/scenarios/sessions/infinite-sessions/java/pom.xml
new file mode 100644
index 000000000..caf707ad2
--- /dev/null
+++ b/test/scenarios/sessions/infinite-sessions/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-sessions-infinite-sessions
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/sessions/infinite-sessions/java/src/main/java/Main.java b/test/scenarios/sessions/infinite-sessions/java/src/main/java/Main.java
new file mode 100644
index 000000000..c5d889c46
--- /dev/null
+++ b/test/scenarios/sessions/infinite-sessions/java/src/main/java/Main.java
@@ -0,0 +1,45 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.InfiniteSessionConfig;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SystemMessageConfig;
+import com.github.copilot.SystemMessageMode;
+
+import java.util.List;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setAvailableTools(List.of())
+ .setSystemMessage(new SystemMessageConfig()
+ .setMode(SystemMessageMode.REPLACE)
+ .setContent("You are a helpful assistant. Answer concisely in one sentence."))
+ .setInfiniteSessions(new InfiniteSessionConfig()
+ .setEnabled(true)
+ .setBackgroundCompactionThreshold(0.80)
+ .setBufferExhaustionThreshold(0.95)))
+ .get();
+
+ var prompts = List.of(
+ "What is the capital of France?",
+ "What is the capital of Japan?",
+ "What is the capital of Brazil?");
+
+ for (var prompt : prompts) {
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt(prompt))
+ .get();
+ if (response != null) {
+ System.out.println("Q: " + prompt);
+ System.out.println("A: " + response.getData().content() + "\n");
+ }
+ }
+ System.out.println("Infinite sessions test complete — all messages processed successfully");
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/sessions/session-resume/java/pom.xml b/test/scenarios/sessions/session-resume/java/pom.xml
new file mode 100644
index 000000000..d788a211d
--- /dev/null
+++ b/test/scenarios/sessions/session-resume/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-sessions-session-resume
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/sessions/session-resume/java/src/main/java/Main.java b/test/scenarios/sessions/session-resume/java/src/main/java/Main.java
new file mode 100644
index 000000000..ef1f2dd36
--- /dev/null
+++ b/test/scenarios/sessions/session-resume/java/src/main/java/Main.java
@@ -0,0 +1,47 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.PermissionHandler;
+import com.github.copilot.rpc.ResumeSessionConfig;
+import com.github.copilot.rpc.SessionConfig;
+
+import java.util.List;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try (var client = new CopilotClient()) {
+ client.start().get();
+
+ // 1. Create a session
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setAvailableTools(List.of()))
+ .get();
+
+ // 2. Send the secret word
+ session.sendAndWait(
+ new MessageOptions().setPrompt("Remember this: the secret word is PINEAPPLE."))
+ .get();
+
+ // 3. Get the session ID
+ var sessionId = session.getSessionId();
+
+ // 4. Resume the session with the same ID
+ var resumed = client.resumeSession(sessionId,
+ new ResumeSessionConfig()
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL))
+ .get();
+ System.out.println("Session resumed");
+
+ // 5. Ask for the secret word
+ var response = resumed.sendAndWait(
+ new MessageOptions().setPrompt("What was the secret word I told you?"))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/sessions/streaming/java/pom.xml b/test/scenarios/sessions/streaming/java/pom.xml
new file mode 100644
index 000000000..d43b2f24e
--- /dev/null
+++ b/test/scenarios/sessions/streaming/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-sessions-streaming
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/sessions/streaming/java/src/main/java/Main.java b/test/scenarios/sessions/streaming/java/src/main/java/Main.java
new file mode 100644
index 000000000..dba7f9560
--- /dev/null
+++ b/test/scenarios/sessions/streaming/java/src/main/java/Main.java
@@ -0,0 +1,27 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.generated.AssistantMessageDeltaEvent;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setStreaming(true))
+ .get();
+ int[] chunkCount = {0};
+ session.on(AssistantMessageDeltaEvent.class, evt -> chunkCount[0]++);
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ System.out.println("\nStreaming chunks received: " + chunkCount[0]);
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/tools/custom-agents/java/pom.xml b/test/scenarios/tools/custom-agents/java/pom.xml
new file mode 100644
index 000000000..5653a8ec4
--- /dev/null
+++ b/test/scenarios/tools/custom-agents/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-tools-custom-agents
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/tools/custom-agents/java/src/main/java/Main.java b/test/scenarios/tools/custom-agents/java/src/main/java/Main.java
new file mode 100644
index 000000000..07146ea39
--- /dev/null
+++ b/test/scenarios/tools/custom-agents/java/src/main/java/Main.java
@@ -0,0 +1,57 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.CustomAgentConfig;
+import com.github.copilot.rpc.DefaultAgentConfig;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.PermissionHandler;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.ToolDefinition;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var parameters = Map.of(
+ "type", "object",
+ "properties", Map.of(
+ "query", Map.of("type", "string", "description", "Analysis query")),
+ "required", List.of("query"));
+
+ var analyzeTool = ToolDefinition.create("analyze-codebase",
+ "Performs deep analysis of the codebase", parameters,
+ invocation -> {
+ String query = (String) invocation.getArguments().get("query");
+ return CompletableFuture.completedFuture("Analysis result for: " + query);
+ });
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setTools(List.of(analyzeTool))
+ .setDefaultAgent(new DefaultAgentConfig()
+ .setExcludedTools(List.of("analyze-codebase")))
+ .setCustomAgents(List.of(
+ new CustomAgentConfig()
+ .setName("researcher")
+ .setDisplayName("Research Agent")
+ .setDescription("A research agent that can only read and search files, not modify them")
+ .setTools(List.of("grep", "glob", "view", "analyze-codebase"))
+ .setPrompt("You are a research assistant. You can search and read files "
+ + "but cannot modify anything. When asked about your capabilities, "
+ + "list the tools you have access to."))))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt(
+ "What custom agents are available? Describe the researcher agent and its capabilities."))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/tools/mcp-servers/java/pom.xml b/test/scenarios/tools/mcp-servers/java/pom.xml
new file mode 100644
index 000000000..9c2fac801
--- /dev/null
+++ b/test/scenarios/tools/mcp-servers/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-tools-mcp-servers
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/tools/mcp-servers/java/src/main/java/Main.java b/test/scenarios/tools/mcp-servers/java/src/main/java/Main.java
new file mode 100644
index 000000000..32fe53647
--- /dev/null
+++ b/test/scenarios/tools/mcp-servers/java/src/main/java/Main.java
@@ -0,0 +1,55 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.McpStdioServerConfig;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SystemMessageConfig;
+import com.github.copilot.SystemMessageMode;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var mcpServers = new HashMap();
+ var mcpServerCmd = System.getenv("MCP_SERVER_CMD");
+ if (mcpServerCmd != null && !mcpServerCmd.isEmpty()) {
+ var mcpArgs = System.getenv("MCP_SERVER_ARGS");
+ var serverConfig = new McpStdioServerConfig()
+ .setCommand(mcpServerCmd)
+ .setTools(List.of("*"));
+ if (mcpArgs != null && !mcpArgs.isEmpty()) {
+ serverConfig.setArgs(List.of(mcpArgs.split(" ")));
+ }
+ mcpServers.put("example", serverConfig);
+ }
+
+ var config = new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setAvailableTools(List.of())
+ .setSystemMessage(new SystemMessageConfig()
+ .setMode(SystemMessageMode.REPLACE)
+ .setContent("You are a helpful assistant. Answer questions concisely."));
+
+ if (!mcpServers.isEmpty()) {
+ config.setMcpServers(mcpServers);
+ }
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(config).get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ if (!mcpServers.isEmpty()) {
+ System.out.println("\nMCP servers configured: " + String.join(", ", mcpServers.keySet()));
+ } else {
+ System.out.println("\nNo MCP servers configured (set MCP_SERVER_CMD to test with a real server)");
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/tools/no-tools/java/pom.xml b/test/scenarios/tools/no-tools/java/pom.xml
new file mode 100644
index 000000000..4bd91ffa6
--- /dev/null
+++ b/test/scenarios/tools/no-tools/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-tools-no-tools
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/tools/no-tools/java/src/main/java/Main.java b/test/scenarios/tools/no-tools/java/src/main/java/Main.java
new file mode 100644
index 000000000..9424f5534
--- /dev/null
+++ b/test/scenarios/tools/no-tools/java/src/main/java/Main.java
@@ -0,0 +1,37 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SystemMessageConfig;
+import com.github.copilot.SystemMessageMode;
+
+import java.util.List;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var systemPrompt = """
+ You are a minimal assistant with no tools available.
+ You cannot execute code, read files, edit files, search, or perform any actions.
+ You can only respond with text based on your training data.
+ If asked about your capabilities or tools, clearly state that you have no tools available.
+ """;
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setSystemMessage(new SystemMessageConfig()
+ .setMode(SystemMessageMode.REPLACE)
+ .setContent(systemPrompt))
+ .setAvailableTools(List.of()))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("Use the bash tool to run 'echo hello'."))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/tools/skills/java/pom.xml b/test/scenarios/tools/skills/java/pom.xml
new file mode 100644
index 000000000..8439198f3
--- /dev/null
+++ b/test/scenarios/tools/skills/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-tools-skills
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/tools/skills/java/src/main/java/Main.java b/test/scenarios/tools/skills/java/src/main/java/Main.java
new file mode 100644
index 000000000..109980b0c
--- /dev/null
+++ b/test/scenarios/tools/skills/java/src/main/java/Main.java
@@ -0,0 +1,38 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.PermissionHandler;
+import com.github.copilot.rpc.PreToolUseHookOutput;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SessionHooks;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var skillsDir = Path.of(System.getProperty("user.dir"), "..", "sample-skills")
+ .toAbsolutePath().normalize().toString();
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setSkillDirectories(List.of(skillsDir))
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setHooks(new SessionHooks()
+ .setOnPreToolUse((input, invocation) ->
+ CompletableFuture.completedFuture(PreToolUseHookOutput.allow()))))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("Use the greeting skill to greet someone named Alice."))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ System.out.println("\nSkill directories configured successfully");
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/tools/tool-filtering/java/pom.xml b/test/scenarios/tools/tool-filtering/java/pom.xml
new file mode 100644
index 000000000..b35d71c23
--- /dev/null
+++ b/test/scenarios/tools/tool-filtering/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-tools-tool-filtering
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/tools/tool-filtering/java/src/main/java/Main.java b/test/scenarios/tools/tool-filtering/java/src/main/java/Main.java
new file mode 100644
index 000000000..98bcc3810
--- /dev/null
+++ b/test/scenarios/tools/tool-filtering/java/src/main/java/Main.java
@@ -0,0 +1,31 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.SystemMessageConfig;
+import com.github.copilot.SystemMessageMode;
+
+import java.util.List;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setSystemMessage(new SystemMessageConfig()
+ .setMode(SystemMessageMode.REPLACE)
+ .setContent("You are a helpful assistant. You have access to a limited set of tools. "
+ + "When asked about your tools, list exactly which tools you have available."))
+ .setAvailableTools(List.of("grep", "glob", "view")))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What tools do you have available? List each one by name."))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/tools/tool-overrides/java/pom.xml b/test/scenarios/tools/tool-overrides/java/pom.xml
new file mode 100644
index 000000000..de749506b
--- /dev/null
+++ b/test/scenarios/tools/tool-overrides/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-tools-tool-overrides
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/tools/tool-overrides/java/src/main/java/Main.java b/test/scenarios/tools/tool-overrides/java/src/main/java/Main.java
new file mode 100644
index 000000000..8b7165747
--- /dev/null
+++ b/test/scenarios/tools/tool-overrides/java/src/main/java/Main.java
@@ -0,0 +1,43 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.PermissionHandler;
+import com.github.copilot.rpc.SessionConfig;
+import com.github.copilot.rpc.ToolDefinition;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var parameters = Map.of(
+ "type", "object",
+ "properties", Map.of(
+ "query", Map.of("type", "string", "description", "Search query")),
+ "required", List.of("query"));
+
+ var customGrep = ToolDefinition.createOverride("grep",
+ "A custom grep implementation that overrides the built-in", parameters,
+ invocation -> {
+ String query = (String) invocation.getArguments().get("query");
+ return CompletableFuture.completedFuture("CUSTOM_GREP_RESULT: " + query);
+ });
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setTools(List.of(customGrep)))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("Use grep to search for the word 'hello'"))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/transport/stdio/java/pom.xml b/test/scenarios/transport/stdio/java/pom.xml
new file mode 100644
index 000000000..69f72ce97
--- /dev/null
+++ b/test/scenarios/transport/stdio/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-transport-stdio
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/transport/stdio/java/src/main/java/Main.java b/test/scenarios/transport/stdio/java/src/main/java/Main.java
new file mode 100644
index 000000000..80dba10f3
--- /dev/null
+++ b/test/scenarios/transport/stdio/java/src/main/java/Main.java
@@ -0,0 +1,29 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.CopilotClientOptions;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var cliPath = System.getenv("COPILOT_CLI_PATH");
+ var options = new CopilotClientOptions();
+ if (cliPath != null) {
+ options.setCliPath(cliPath);
+ }
+
+ try (var client = new CopilotClient(options)) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5"))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ }
+ client.stop().get();
+ }
+ }
+}
diff --git a/test/scenarios/transport/tcp/java/pom.xml b/test/scenarios/transport/tcp/java/pom.xml
new file mode 100644
index 000000000..d2f41f547
--- /dev/null
+++ b/test/scenarios/transport/tcp/java/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.github.copilot.sdk.scenarios
+ scenario-transport-tcp
+ 1.0.0
+ jar
+
+
+ 17
+ UTF-8
+
+
+
+
+ com.github
+ copilot-sdk-java
+ 1.0.0-beta-java.5-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/test/scenarios/transport/tcp/java/src/main/java/Main.java b/test/scenarios/transport/tcp/java/src/main/java/Main.java
new file mode 100644
index 000000000..38612c699
--- /dev/null
+++ b/test/scenarios/transport/tcp/java/src/main/java/Main.java
@@ -0,0 +1,30 @@
+import com.github.copilot.CopilotClient;
+import com.github.copilot.rpc.CopilotClientOptions;
+import com.github.copilot.rpc.MessageOptions;
+import com.github.copilot.rpc.SessionConfig;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ var cliUrl = System.getenv("COPILOT_CLI_URL");
+ if (cliUrl == null) {
+ cliUrl = "localhost:3000";
+ }
+
+ try (var client = new CopilotClient(new CopilotClientOptions().setCliUrl(cliUrl))) {
+ client.start().get();
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("claude-haiku-4.5"))
+ .get();
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What is the capital of France?"))
+ .get();
+ if (response != null) {
+ System.out.println(response.getData().content());
+ } else {
+ System.err.println("(no response)");
+ }
+ client.stop().get();
+ }
+ }
+}