Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions cookbook/copilot-sdk/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# GitHub Copilot SDK Cookbook

This cookbook collects small, focused recipes showing how to accomplish common tasks with the GitHub Copilot SDK across languages. Each recipe is intentionally short and practical, with copy‑pasteable snippets and pointers to fuller examples and tests.
This cookbook collects small, focused recipes showing how to accomplish common tasks with the [GitHub Copilot SDK](https://github.com/github/copilot-sdk) across languages. Each recipe is intentionally short and practical, with copy‑pasteable snippets and pointers to fuller examples and tests.

## Recipes by Language

Expand Down Expand Up @@ -54,17 +54,25 @@ This cookbook collects small, focused recipes showing how to accomplish common t
- [PR Visualization](java/pr-visualization.md): Generate interactive PR age charts using GitHub MCP Server.
- [Persisting Sessions](java/persisting-sessions.md): Save and resume sessions across restarts.
- [Accessibility Report](java/accessibility-report.md): Generate WCAG accessibility reports using the Playwright MCP server.
- [In-Process Runtime](java/in-process-runtime.md): Run the native Copilot runtime inside the JVM instead of launching a separate CLI process.

## How to Use

- Browse your language section above and open the recipe links
- Each recipe includes runnable examples in a `recipe/` subfolder with language-specific tooling
- See existing examples and tests for working references:
- Node.js examples: `nodejs/examples/basic-example.ts`
- E2E tests: `go/e2e`, `python/e2e`, `nodejs/test/e2e`, `dotnet/test/Harness`
- See existing examples and tests in the official SDK repository for working references:
- [Node.js example](https://github.com/github/copilot-sdk/blob/main/nodejs/examples/basic-example.ts)
- E2E tests: [Go](https://github.com/github/copilot-sdk/tree/main/go/internal/e2e), [Python](https://github.com/github/copilot-sdk/tree/main/python/e2e), [Node.js](https://github.com/github/copilot-sdk/tree/main/nodejs/test/e2e), [.NET](https://github.com/github/copilot-sdk/tree/main/dotnet/test/Harness), [Java](https://github.com/github/copilot-sdk/tree/main/java/sdk/src/test/java/com/github/copilot/e2e)

## Running Examples

Before running examples, follow the prerequisites in your language's recipe README:
[.NET](dotnet/recipe/README.md#prerequisites),
[Node.js](nodejs/recipe/README.md#prerequisites),
[Python](python/recipe/README.md#prerequisites),
[Go](go/recipe/README.md#prerequisites), or
[Java](java/recipe/README.md#prerequisites).

### .NET

```bash
Expand Down Expand Up @@ -109,4 +117,4 @@ jbang <FileName>.java

## Status

Cookbook structure is complete with 7 recipes across all 5 supported languages. Each recipe includes both markdown documentation and runnable examples.
The cookbook covers 5 languages, with 8 recipes for .NET and Java and 7 shared recipes for Node.js, Python, and Go. Each recipe includes both markdown documentation and runnable examples.
8 changes: 7 additions & 1 deletion cookbook/copilot-sdk/java/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# GitHub Copilot SDK Cookbook — Java

This folder hosts short, practical recipes for using the GitHub Copilot SDK with Java. Each recipe is concise, copy‑pasteable, and points to fuller examples and tests. All examples can be run directly with [JBang](https://www.jbang.dev/).
This folder hosts short, practical recipes for using the [GitHub Copilot SDK for Java](https://github.com/github/copilot-sdk/tree/main/java). Each recipe is concise, copy‑pasteable, and points to fuller examples and tests. All examples can be run directly with [JBang](https://www.jbang.dev/).

Examples target the official SDK 1.0.21 and Java 17 or later. SDK 1.x uses
`com.github.copilot` for clients, `com.github.copilot.generated` for events, and
`com.github.copilot.rpc` for configuration instead of the archived SDK's
`com.github.copilot.sdk` packages.

## Recipes

Expand All @@ -11,6 +16,7 @@ This folder hosts short, practical recipes for using the GitHub Copilot SDK with
- [PR Visualization](pr-visualization.md): Generate interactive PR age charts using GitHub MCP Server.
- [Persisting Sessions](persisting-sessions.md): Save and resume sessions across restarts.
- [Accessibility Report](accessibility-report.md): Generate WCAG accessibility reports using the Playwright MCP server.
- [In-Process Runtime](in-process-runtime.md): Run the native Copilot runtime inside the JVM instead of launching a separate CLI process.

## Contributing

Expand Down
28 changes: 12 additions & 16 deletions cookbook/copilot-sdk/java/accessibility-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ jbang recipe/AccessibilityReport.java

```java
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.github:copilot-sdk-java:0.2.1-java.1
//DEPS com.github:copilot-sdk-java:1.0.21

import com.github.copilot.sdk.*;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
import com.github.copilot.*;
import com.github.copilot.generated.*;
import com.github.copilot.rpc.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
Expand Down Expand Up @@ -67,12 +67,10 @@ public class AccessibilityReport {
client.start().get();

// Configure Playwright MCP server for browser automation
Map<String, Object> mcpConfig = Map.of(
"type", "local",
"command", "npx",
"args", List.of("@playwright/mcp@latest"),
"tools", List.of("*")
);
var mcpConfig = new McpStdioServerConfig()
.setCommand("npx")
.setArgs(List.of("@playwright/mcp@latest"))
.setTools(List.of("*"));

var session = client.createSession(
new SessionConfig()
Expand Down Expand Up @@ -168,12 +166,10 @@ public class AccessibilityReport {
The recipe configures a local MCP server that runs alongside the session:

```java
Map<String, Object> mcpConfig = Map.of(
"type", "local",
"command", "npx",
"args", List.of("@playwright/mcp@latest"),
"tools", List.of("*")
);
var mcpConfig = new McpStdioServerConfig()
.setCommand("npx")
.setArgs(List.of("@playwright/mcp@latest"))
.setTools(List.of("*"));

var session = client.createSession(
new SessionConfig()
Expand Down
40 changes: 36 additions & 4 deletions cookbook/copilot-sdk/java/error-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ You need to handle various error conditions like connection failures, timeouts,
Java's `try-with-resources` ensures the client is always cleaned up, even when exceptions occur.

```java
//DEPS com.github:copilot-sdk-java:0.2.1-java.1
//DEPS com.github:copilot-sdk-java:1.0.21

import com.github.copilot.sdk.*;
import com.github.copilot.sdk.json.*;
import com.github.copilot.*;
import com.github.copilot.rpc.*;

public class BasicErrorHandling {
public static void main(String[] args) {
Expand Down Expand Up @@ -128,6 +128,11 @@ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
}));
```

`client.stop()` returns a `CompletableFuture` that fails if graceful cleanup
encounters an error; log the cause rather than treating it as a successful stop.
If a graceful stop exceeds your application's shutdown deadline, use
`client.forceStop()` to retire the connection and sessions locally.

## Try-with-resources (nested)

When working with multiple sessions, nest `try-with-resources` blocks to guarantee each resource is closed.
Expand All @@ -148,12 +153,39 @@ try (var client = new CopilotClient()) {
} // client is closed here
```

## Tagging message provenance

Use `MessageOptions.setSource(...)` to distinguish human input from application
context or another agent's messages when diagnosing errors in a transcript:

```java
import com.github.copilot.rpc.MessageSource;

session.sendAndWait(new MessageOptions()
.setPrompt("What changed in the last release?")
.setSource(MessageSource.USER)).get();

session.sendAndWait(new MessageOptions()
.setPrompt("Re-run the failed step and report the result.")
.setSource(MessageSource.SYSTEM)).get();

session.sendAndWait(new MessageOptions()
.setPrompt("Summarize the open incidents.")
.setSource(MessageSource.agent("incident-bot"))).get();
```

Omitting `source` retains the runtime's default user-input behavior.
`MessageSource.agent(id)` serializes as `"agent-<id>"`; pass the identifier
without adding that prefix. The source records provenance only: it does not
replace the session's system prompt, change delivery mode, or grant permissions.
The same source values work with `send(...)`.

## Handling tool errors

When defining tools, return an error string to signal a failure back to the model instead of throwing.

```java
import com.github.copilot.sdk.json.ToolDefinition;
import com.github.copilot.rpc.ToolDefinition;
import java.util.concurrent.CompletableFuture;

var readFileTool = ToolDefinition.create(
Expand Down
134 changes: 134 additions & 0 deletions cookbook/copilot-sdk/java/in-process-runtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# In-Process Runtime

Run the Copilot runtime inside your JVM instead of launching a separate Copilot CLI process.

> **Runnable example:** [recipe/InProcessRuntime.java](recipe/InProcessRuntime.java)
>
> ```bash
> # Linux x64 (glibc); choose the classifier for your platform below.
> jbang --deps com.github:copilot-sdk-java-runtime:1.0.21:linux-x64 recipe/InProcessRuntime.java
> ```

## Example scenario

By default, `CopilotClient` manages a child process and communicates over stdio or TCP.
Hosts that need to avoid a separate process for the SDK transport can load the native
Copilot runtime directly into the JVM instead. The runtime's tools can still launch
subprocesses; in-process transport is not a sandbox.

## Prerequisites

Use Java 17 or later and SDK 1.0.21. JDK 25 or later is recommended. In addition to the
SDK, Java needs JNA and a platform-specific native runtime artifact of the same SDK version:

```xml
<dependencies>
<dependency>
<groupId>com.github</groupId>
<artifactId>copilot-sdk-java</artifactId>
<version>1.0.21</version>
</dependency>
<dependency>
<groupId>com.github</groupId>
<artifactId>copilot-sdk-java-runtime</artifactId>
<version>1.0.21</version>
<classifier>linux-x64</classifier>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.19.1</version>
</dependency>
</dependencies>
```

Choose `linux-x64` or `linux-arm64` for glibc Linux, `linuxmusl-x64` or
`linuxmusl-arm64` for musl Linux, `win32-x64` or `win32-arm64` for Windows, and
`darwin-x64` or `darwin-arm64` for macOS. JBang resolves the SDK and JNA from the
recipe's `//DEPS` directives; pass the native artifact using `--deps` as shown above.
For example, on Apple Silicon:

```bash
jbang --deps com.github:copilot-sdk-java-runtime:1.0.21:darwin-arm64 recipe/InProcessRuntime.java
```

## Using the in-process connection

```java
import com.github.copilot.AllowCopilotExperimental;
import com.github.copilot.CopilotClient;
import com.github.copilot.rpc.*;
import java.util.concurrent.TimeUnit;

@AllowCopilotExperimental
public class InProcessExample {
public static void main(String[] args) throws Exception {
var options = new CopilotClientOptions()
.setConnection(RuntimeConnection.forInProcess());

try (var client = new CopilotClient(options)) {
client.start().get(30, TimeUnit.SECONDS);

try (var session = client.createSession(new SessionConfig()
.setModel("auto")
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL))
.get(30, TimeUnit.SECONDS)) {
var response = session.sendAndWait(new MessageOptions()
.setPrompt("Hello from the in-process runtime!"))
.get(60, TimeUnit.SECONDS);
System.out.println(response.getData().content());
}
}
}
}
```

The connection API is experimental. `@AllowCopilotExperimental` opts the class in;
projects can instead pass `-Acopilot.experimental.allowed=true` to the SDK's
annotation processor. See the [official experimental API guidance](https://github.com/github/copilot-sdk/blob/main/java/README.md#using-experimental-apis).
Session events, tools, permissions, and hooks use the same Java APIs as the default transport.

## How it works

The SDK loads `runtime.node` through JNA and exchanges JSON-RPC messages over the
native C ABI rather than a child-process transport. The platform classifier JAR
provides the runtime library; the SDK extracts it to a versioned cache before loading it.

The recipe enables native access with `//JAVA_OPTIONS --enable-native-access=ALL-UNNAMED`.
For a regular Java application, pass `--enable-native-access=ALL-UNNAMED` to `java`
when JNA is on the classpath, or allow its module when using the module path.

You can also set `COPILOT_SDK_DEFAULT_CONNECTION=inprocess` before starting the
JVM to select this transport for a client with no explicit connection. Explicit
subprocess settings such as a CLI path, server URL, or TCP port take precedence
over that environment default. The native artifact and JNA are still required.

To check native startup, a local RPC round trip, and shutdown without making a model
request, add `--ping`:

```bash
jbang --deps com.github:copilot-sdk-java-runtime:1.0.21:linux-x64 recipe/InProcessRuntime.java --ping
```

## Limitations

- **Shared process state**: clients share the JVM's native runtime library, environment,
and operating-system working directory.
- **Rejected process options**: do not set per-client `cwd`, environment, telemetry,
or CLI arguments for this connection. Configure the host process before startup.
- **One native runtime per process**: do not try to load different native runtime
builds into the same JVM. The library remains loaded after the client closes.
- **Platform dependencies**: the classifier must match the OS and architecture,
and JNA must be on the classpath. Missing or mismatched libraries fail startup.
- **Permissions still matter**: the example uses `PermissionHandler.APPROVE_ALL`
for brevity. Use an explicit, restrictive permission handler for untrusted requests.

## Best practices

1. **Test each deployment platform**: verify startup, model turns, and shutdown
on every OS and architecture you support.
2. **Prefer the default transport** unless avoiding the SDK's child process is a requirement.
3. **Close resources**: use nested try-with-resources for the session and client.
Closing the connection does not unload the native library.
4. **Configure process-wide values early**: set the environment and working directory
before starting the JVM, rather than assuming per-client overrides work.
30 changes: 15 additions & 15 deletions cookbook/copilot-sdk/java/managing-local-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ jbang recipe/ManagingLocalFiles.java

**Code:**
```java
//DEPS com.github:copilot-sdk-java:0.2.1-java.1
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.AssistantMessageEvent;
import com.github.copilot.sdk.events.SessionIdleEvent;
import com.github.copilot.sdk.events.ToolExecutionCompleteEvent;
import com.github.copilot.sdk.events.ToolExecutionStartEvent;
import com.github.copilot.sdk.json.MessageOptions;
import com.github.copilot.sdk.json.PermissionHandler;
import com.github.copilot.sdk.json.SessionConfig;
//DEPS com.github:copilot-sdk-java:1.0.21
import com.github.copilot.CopilotClient;
import com.github.copilot.generated.AssistantMessageEvent;
import com.github.copilot.generated.SessionIdleEvent;
import com.github.copilot.generated.ToolExecutionCompleteEvent;
import com.github.copilot.generated.ToolExecutionStartEvent;
import com.github.copilot.rpc.MessageOptions;
import com.github.copilot.rpc.PermissionHandler;
import com.github.copilot.rpc.SessionConfig;
import java.nio.file.Paths;
import java.util.concurrent.CountDownLatch;

Expand Down Expand Up @@ -152,12 +152,12 @@ session.send(new MessageOptions().setPrompt(prompt));
## Interactive file organization

```java
//DEPS com.github:copilot-sdk-java:0.2.1-java.1
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.AssistantMessageEvent;
import com.github.copilot.sdk.json.MessageOptions;
import com.github.copilot.sdk.json.PermissionHandler;
import com.github.copilot.sdk.json.SessionConfig;
//DEPS com.github:copilot-sdk-java:1.0.21
import com.github.copilot.CopilotClient;
import com.github.copilot.generated.AssistantMessageEvent;
import com.github.copilot.rpc.MessageOptions;
import com.github.copilot.rpc.PermissionHandler;
import com.github.copilot.rpc.SessionConfig;
import java.io.BufferedReader;
import java.io.InputStreamReader;

Expand Down
6 changes: 3 additions & 3 deletions cookbook/copilot-sdk/java/multiple-sessions.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ You need to run multiple conversations in parallel, each with its own context an

```java
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.github:copilot-sdk-java:0.2.1-java.1
//DEPS com.github:copilot-sdk-java:1.0.21

import com.github.copilot.sdk.*;
import com.github.copilot.sdk.json.*;
import com.github.copilot.*;
import com.github.copilot.rpc.*;

public class MultipleSessions {
public static void main(String[] args) throws Exception {
Expand Down
Loading
Loading