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(); + } + } +}