From 02ec0bc29264a80e1deaf75b9c39662652e47a7d Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 14 Oct 2026 12:09:37 -0400 Subject: [PATCH 1/5] Bring Java SDK cookbook to .NET parity Use the official SDK monorepo and released Java 1.0.21 APIs. Add the in-process runtime recipe, message provenance, deferred tool search, typed MCP configuration, and platform-aware safety and setup guidance. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3fdaadb1-b7cb-4313-8291-1bcc522981d7 --- cookbook/copilot-sdk/README.md | 14 +- cookbook/copilot-sdk/java/README.md | 8 +- .../copilot-sdk/java/accessibility-report.md | 28 ++-- cookbook/copilot-sdk/java/error-handling.md | 40 ++++- .../copilot-sdk/java/in-process-runtime.md | 134 +++++++++++++++ .../copilot-sdk/java/managing-local-files.md | 30 ++-- .../copilot-sdk/java/multiple-sessions.md | 6 +- .../copilot-sdk/java/persisting-sessions.md | 60 +++---- cookbook/copilot-sdk/java/pr-visualization.md | 45 ++++- cookbook/copilot-sdk/java/ralph-loop.md | 16 +- .../java/recipe/AccessibilityReport.java | 18 +- .../java/recipe/ErrorHandling.java | 8 +- .../java/recipe/InProcessRuntime.java | 45 +++++ .../java/recipe/ManagingLocalFiles.java | 20 +-- .../java/recipe/MultipleSessions.java | 6 +- .../java/recipe/PRVisualization.java | 20 ++- .../java/recipe/PersistingSessions.java | 8 +- cookbook/copilot-sdk/java/recipe/README.md | 155 +++++++++++++++++- .../copilot-sdk/java/recipe/RalphLoop.java | 8 +- 19 files changed, 530 insertions(+), 139 deletions(-) create mode 100644 cookbook/copilot-sdk/java/in-process-runtime.md create mode 100644 cookbook/copilot-sdk/java/recipe/InProcessRuntime.java diff --git a/cookbook/copilot-sdk/README.md b/cookbook/copilot-sdk/README.md index d9ac2c7586..98a13d138a 100644 --- a/cookbook/copilot-sdk/README.md +++ b/cookbook/copilot-sdk/README.md @@ -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 @@ -54,14 +54,15 @@ 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 @@ -97,6 +98,9 @@ go run .go ### Java +Follow the [Java recipe prerequisites](java/recipe/README.md#prerequisites) to +configure authentication and the CLI or platform runtime dependencies. + ```bash cd java/recipe jbang .java @@ -109,4 +113,4 @@ jbang .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. diff --git a/cookbook/copilot-sdk/java/README.md b/cookbook/copilot-sdk/java/README.md index fb335a1989..972f1d331b 100644 --- a/cookbook/copilot-sdk/java/README.md +++ b/cookbook/copilot-sdk/java/README.md @@ -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 @@ -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 diff --git a/cookbook/copilot-sdk/java/accessibility-report.md b/cookbook/copilot-sdk/java/accessibility-report.md index 83a1853c5a..cafc92f430 100644 --- a/cookbook/copilot-sdk/java/accessibility-report.md +++ b/cookbook/copilot-sdk/java/accessibility-report.md @@ -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.*; @@ -67,12 +67,10 @@ public class AccessibilityReport { client.start().get(); // Configure Playwright MCP server for browser automation - Map 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() @@ -168,12 +166,10 @@ public class AccessibilityReport { The recipe configures a local MCP server that runs alongside the session: ```java -Map 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() diff --git a/cookbook/copilot-sdk/java/error-handling.md b/cookbook/copilot-sdk/java/error-handling.md index a21c4f755c..75745ee16d 100644 --- a/cookbook/copilot-sdk/java/error-handling.md +++ b/cookbook/copilot-sdk/java/error-handling.md @@ -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) { @@ -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. @@ -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-"`; 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( diff --git a/cookbook/copilot-sdk/java/in-process-runtime.md b/cookbook/copilot-sdk/java/in-process-runtime.md new file mode 100644 index 0000000000..4aaa01e4cf --- /dev/null +++ b/cookbook/copilot-sdk/java/in-process-runtime.md @@ -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 + + + com.github + copilot-sdk-java + 1.0.21 + + + com.github + copilot-sdk-java-runtime + 1.0.21 + linux-x64 + + + net.java.dev.jna + jna + 5.19.1 + + +``` + +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. diff --git a/cookbook/copilot-sdk/java/managing-local-files.md b/cookbook/copilot-sdk/java/managing-local-files.md index d347c7feaf..7986835ef3 100644 --- a/cookbook/copilot-sdk/java/managing-local-files.md +++ b/cookbook/copilot-sdk/java/managing-local-files.md @@ -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; @@ -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; diff --git a/cookbook/copilot-sdk/java/multiple-sessions.md b/cookbook/copilot-sdk/java/multiple-sessions.md index 4508e565b4..7025f655b7 100644 --- a/cookbook/copilot-sdk/java/multiple-sessions.md +++ b/cookbook/copilot-sdk/java/multiple-sessions.md @@ -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 { diff --git a/cookbook/copilot-sdk/java/persisting-sessions.md b/cookbook/copilot-sdk/java/persisting-sessions.md index 5de9c0e2ae..b40ab9a48a 100644 --- a/cookbook/copilot-sdk/java/persisting-sessions.md +++ b/cookbook/copilot-sdk/java/persisting-sessions.md @@ -15,12 +15,12 @@ You want users to be able to continue a conversation even after closing and reop ## Creating a session with a custom ID ```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; public class CreateSessionWithId { public static void main(String[] args) throws Exception { @@ -55,12 +55,12 @@ public class CreateSessionWithId { ## Resuming a session ```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.ResumeSessionConfig; +//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.ResumeSessionConfig; public class ResumeSession { public static void main(String[] args) throws Exception { @@ -91,8 +91,8 @@ public class ResumeSession { ## Listing available sessions ```java -//DEPS com.github:copilot-sdk-java:0.2.1-java.1 -import com.github.copilot.sdk.CopilotClient; +//DEPS com.github:copilot-sdk-java:1.0.21 +import com.github.copilot.CopilotClient; public class ListSessions { public static void main(String[] args) throws Exception { @@ -111,8 +111,8 @@ public class ListSessions { ## Deleting a session permanently ```java -//DEPS com.github:copilot-sdk-java:0.2.1-java.1 -import com.github.copilot.sdk.CopilotClient; +//DEPS com.github:copilot-sdk-java:1.0.21 +import com.github.copilot.CopilotClient; public class DeleteSession { public static void main(String[] args) throws Exception { @@ -130,12 +130,12 @@ public class DeleteSession { ## Getting session history ```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.UserMessageEvent; -import com.github.copilot.sdk.json.PermissionHandler; -import com.github.copilot.sdk.json.ResumeSessionConfig; +//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.UserMessageEvent; +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.ResumeSessionConfig; public class SessionHistory { public static void main(String[] args) throws Exception { @@ -170,10 +170,10 @@ public class SessionHistory { This interactive example lets you create, resume, or list sessions from the command line. ```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.*; +//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.*; import java.util.Scanner; public class SessionManager { @@ -255,7 +255,7 @@ public class SessionManager { if (input.equalsIgnoreCase("exit")) break; // Use reflection-free approach: cast to the session type - var s = (com.github.copilot.sdk.CopilotSession) session; + var s = (com.github.copilot.CopilotSession) session; s.sendAndWait(new MessageOptions().setPrompt(input)).get(); } } @@ -265,9 +265,9 @@ public class SessionManager { ## Checking if a session exists ```java -//DEPS com.github:copilot-sdk-java:0.2.1-java.1 -import com.github.copilot.sdk.CopilotClient; -import com.github.copilot.sdk.json.*; +//DEPS com.github:copilot-sdk-java:1.0.21 +import com.github.copilot.CopilotClient; +import com.github.copilot.rpc.*; public class CheckSession { public static boolean sessionExists(CopilotClient client, String sessionId) { diff --git a/cookbook/copilot-sdk/java/pr-visualization.md b/cookbook/copilot-sdk/java/pr-visualization.md index 87f9976c2a..c6a36965f2 100644 --- a/cookbook/copilot-sdk/java/pr-visualization.md +++ b/cookbook/copilot-sdk/java/pr-visualization.md @@ -25,14 +25,15 @@ jbang recipe/PRVisualization.java github/copilot-sdk ## Full example: PRVisualization.java ```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.ToolExecutionStartEvent; -import com.github.copilot.sdk.json.MessageOptions; -import com.github.copilot.sdk.json.PermissionHandler; -import com.github.copilot.sdk.json.SessionConfig; -import com.github.copilot.sdk.json.SystemMessageConfig; +//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.ToolExecutionStartEvent; +import com.github.copilot.rpc.MessageOptions; +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.SessionConfig; +import com.github.copilot.rpc.SystemMessageConfig; +import com.github.copilot.rpc.ToolSearchConfig; import java.io.BufferedReader; import java.io.EOFException; import java.io.IOException; @@ -94,6 +95,7 @@ public class PRVisualization { var session = client.createSession( new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) .setModel("gpt-5") + .setToolSearch(new ToolSearchConfig().setEnabled(true).setDeferThreshold(20)) .setSystemMessage(new SystemMessageConfig().setContent(systemMessage)) ).get(); @@ -222,6 +224,33 @@ public class PRVisualization { | Chart types | What you coded | **Any type Copilot can generate** | | Data grouping | Hardcoded buckets | **Intelligent grouping** | +## Deferring tools with tool search + +The GitHub MCP Server can expose many tools alongside file and code-execution +tools. Configure `SessionConfig.setToolSearch(...)` to defer less-used tools +behind a search step rather than listing every tool on each turn: + +```java +import com.github.copilot.rpc.ToolSearchConfig; + +var session = client.createSession(new SessionConfig() + .setModel("auto") + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setToolSearch(new ToolSearchConfig() + .setEnabled(true) + .setDeferThreshold(20)) +).get(); +``` + +- `setEnabled(true)` explicitly enables tool search; leave it unset to use the runtime default. +- `setDeferThreshold(20)` sets the tool count above which MCP and external tools + are deferred behind `tool_search_tool`. The runtime default is 30 when unset. +- To replace the search implementation, register a tool named `"tool_search_tool"` + and call `.overridesBuiltInTool(true)` on its `ToolDefinition`. + +This configuration is per session, so tool-heavy recipes can use a different +threshold from lightweight sessions. + ## Best practices 1. **Start with auto-detection**: Let the tool detect the repository from the git remote before prompting the user diff --git a/cookbook/copilot-sdk/java/ralph-loop.md b/cookbook/copilot-sdk/java/ralph-loop.md index 84d77478bd..6da7a764b8 100644 --- a/cookbook/copilot-sdk/java/ralph-loop.md +++ b/cookbook/copilot-sdk/java/ralph-loop.md @@ -42,11 +42,11 @@ The minimal Ralph loop — the SDK equivalent of `while :; do cat PROMPT.md | co ```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.nio.file.*; public class SimpleRalphLoop { @@ -91,11 +91,11 @@ The full Ralph pattern with planning and building modes, matching the [Ralph Pla ```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.nio.file.*; import java.util.Arrays; diff --git a/cookbook/copilot-sdk/java/recipe/AccessibilityReport.java b/cookbook/copilot-sdk/java/recipe/AccessibilityReport.java index 34af05d505..efa3b7e472 100644 --- a/cookbook/copilot-sdk/java/recipe/AccessibilityReport.java +++ b/cookbook/copilot-sdk/java/recipe/AccessibilityReport.java @@ -1,9 +1,9 @@ ///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.*; @@ -43,12 +43,10 @@ public static void main(String[] args) throws Exception { client.start().get(); // Configure Playwright MCP server for browser automation - Map 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() diff --git a/cookbook/copilot-sdk/java/recipe/ErrorHandling.java b/cookbook/copilot-sdk/java/recipe/ErrorHandling.java index ab4e7fbf00..60d2d6f378 100644 --- a/cookbook/copilot-sdk/java/recipe/ErrorHandling.java +++ b/cookbook/copilot-sdk/java/recipe/ErrorHandling.java @@ -1,9 +1,9 @@ ///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.util.concurrent.ExecutionException; public class ErrorHandling { diff --git a/cookbook/copilot-sdk/java/recipe/InProcessRuntime.java b/cookbook/copilot-sdk/java/recipe/InProcessRuntime.java new file mode 100644 index 0000000000..a89515b721 --- /dev/null +++ b/cookbook/copilot-sdk/java/recipe/InProcessRuntime.java @@ -0,0 +1,45 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS com.github:copilot-sdk-java:1.0.21 +//DEPS net.java.dev.jna:jna:5.19.1 +//JAVA_OPTIONS --enable-native-access=ALL-UNNAMED + +import com.github.copilot.AllowCopilotExperimental; +import com.github.copilot.CopilotClient; +import com.github.copilot.rpc.CopilotClientOptions; +import com.github.copilot.rpc.MessageOptions; +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.RuntimeConnection; +import com.github.copilot.rpc.SessionConfig; +import java.util.concurrent.TimeUnit; + +@AllowCopilotExperimental +public class InProcessRuntime { + public static void main(String[] args) throws Exception { + if (args.length > 1 || (args.length == 1 && !args[0].equals("--ping"))) { + throw new IllegalArgumentException("Usage: InProcessRuntime.java [--ping]"); + } + + var options = new CopilotClientOptions() + .setConnection(RuntimeConnection.forInProcess()); + + try (var client = new CopilotClient(options)) { + client.start().get(30, TimeUnit.SECONDS); + + if (args.length == 1) { + var response = client.ping("in-process recipe").get(30, TimeUnit.SECONDS); + System.out.println(response.message()); + return; + } + + 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()); + } + } + } +} diff --git a/cookbook/copilot-sdk/java/recipe/ManagingLocalFiles.java b/cookbook/copilot-sdk/java/recipe/ManagingLocalFiles.java index 8a60f1f441..fcdd950ec2 100644 --- a/cookbook/copilot-sdk/java/recipe/ManagingLocalFiles.java +++ b/cookbook/copilot-sdk/java/recipe/ManagingLocalFiles.java @@ -1,14 +1,14 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//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.util.concurrent.CountDownLatch; public class ManagingLocalFiles { diff --git a/cookbook/copilot-sdk/java/recipe/MultipleSessions.java b/cookbook/copilot-sdk/java/recipe/MultipleSessions.java index 615dab80bf..317e17fee8 100644 --- a/cookbook/copilot-sdk/java/recipe/MultipleSessions.java +++ b/cookbook/copilot-sdk/java/recipe/MultipleSessions.java @@ -1,8 +1,8 @@ ///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.*; import java.util.concurrent.CompletableFuture; public class MultipleSessions { diff --git a/cookbook/copilot-sdk/java/recipe/PRVisualization.java b/cookbook/copilot-sdk/java/recipe/PRVisualization.java index 0ac79527d0..b56be6f947 100644 --- a/cookbook/copilot-sdk/java/recipe/PRVisualization.java +++ b/cookbook/copilot-sdk/java/recipe/PRVisualization.java @@ -1,13 +1,14 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//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.ToolExecutionStartEvent; -import com.github.copilot.sdk.json.MessageOptions; -import com.github.copilot.sdk.json.PermissionHandler; -import com.github.copilot.sdk.json.SessionConfig; -import com.github.copilot.sdk.json.SystemMessageConfig; +//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.ToolExecutionStartEvent; +import com.github.copilot.rpc.MessageOptions; +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.SessionConfig; +import com.github.copilot.rpc.SystemMessageConfig; +import com.github.copilot.rpc.ToolSearchConfig; import java.io.BufferedReader; import java.io.EOFException; import java.io.IOException; @@ -69,6 +70,7 @@ public static void main(String[] args) throws Exception { var session = client.createSession( new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) .setModel("gpt-5") + .setToolSearch(new ToolSearchConfig().setEnabled(true).setDeferThreshold(20)) .setSystemMessage(new SystemMessageConfig().setContent(systemMessage)) ).get(); diff --git a/cookbook/copilot-sdk/java/recipe/PersistingSessions.java b/cookbook/copilot-sdk/java/recipe/PersistingSessions.java index c2980e5e2a..62e0a009c3 100644 --- a/cookbook/copilot-sdk/java/recipe/PersistingSessions.java +++ b/cookbook/copilot-sdk/java/recipe/PersistingSessions.java @@ -1,9 +1,9 @@ ///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.*; public class PersistingSessions { public static void main(String[] args) throws Exception { diff --git a/cookbook/copilot-sdk/java/recipe/README.md b/cookbook/copilot-sdk/java/recipe/README.md index 38201fb7f0..2fb7804457 100644 --- a/cookbook/copilot-sdk/java/recipe/README.md +++ b/cookbook/copilot-sdk/java/recipe/README.md @@ -5,6 +5,8 @@ This folder contains standalone, executable Java examples for each cookbook reci ## Prerequisites - Java 17 or later +- GitHub Copilot authentication configured before running model requests +- Official GitHub Copilot SDK 1.0.21 (resolved automatically from `//DEPS`) - JBang installed: ```bash @@ -20,6 +22,22 @@ scoop install jbang For other installation methods, see the [JBang installation guide](https://www.jbang.dev/download/). +For the default child-process transport, either add the matching +`copilot-sdk-java-runtime` classifier with JBang's `--deps` option (see +[In-Process Runtime](../in-process-runtime.md#prerequisites) for classifiers), or +use an installed Copilot CLI by setting `COPILOT_CLI_PATH` before launching Java: + +```bash +export COPILOT_CLI_PATH="$(command -v copilot)" +``` + +```powershell +$env:COPILOT_CLI_PATH = (Get-Command copilot -ErrorAction Stop).Source +``` + +The in-process recipe additionally resolves JNA and requires a native runtime +artifact matching your platform. It does not need a separate CLI process. + ## Running Examples Each `.java` file is a complete, runnable program. Simply use: @@ -34,11 +52,12 @@ jbang .java | -------------------- | ------------------------------------ | ------------------------------------------ | | Error Handling | `jbang ErrorHandling.java` | Demonstrates error handling patterns | | Multiple Sessions | `jbang MultipleSessions.java` | Manages multiple independent conversations | -| Managing Local Files | `jbang ManagingLocalFiles.java` | Organizes files using AI grouping | -| PR Visualization | `jbang PRVisualization.java` | Generates PR age charts | +| Managing Local Files | `jbang ManagingLocalFiles.java` | Previews file organization; see safety notes | +| PR Visualization | `jbang PRVisualization.java` | Generates PR age charts; requires GitHub access | | Persisting Sessions | `jbang PersistingSessions.java` | Save and resume sessions across restarts | -| Ralph Loop | `jbang RalphLoop.java` | Autonomous AI task loop | -| Accessibility Report | `jbang AccessibilityReport.java` | WCAG accessibility report generator | +| Ralph Loop | `jbang RalphLoop.java` | Autonomous AI task loop; modifies files and may commit | +| Accessibility Report | `jbang AccessibilityReport.java` | WCAG reports; requires Node.js and Playwright MCP | +| In-Process Runtime | `jbang --deps com.github:copilot-sdk-java-runtime:1.0.21:linux-x64 InProcessRuntime.java` | Native runtime; choose your platform classifier | ### Examples with Arguments @@ -60,6 +79,131 @@ jbang ManagingLocalFiles.java /path/to/your/folder jbang RalphLoop.java PROMPT_build.md 20 ``` +## Safety & Prerequisites + +The examples use `PermissionHandler.APPROVE_ALL` for brevity. This gives the agent +permission to use tools without asking you; test in an isolated workspace and +replace it with a restrictive handler before processing untrusted prompts. + +
+Managing Local Files - Test on disposable copies + +The runnable example requests a preview and explicitly tells the model not to move +files. That prompt is not a filesystem security boundary, especially when adapting +the recipe to perform moves. Use a copy of the directory first. +Run the following from this recipe directory: + +```bash +recipeDir=$(pwd) +tempDir=$(mktemp -d) +touch "$tempDir/document1.txt" "$tempDir/image1.png" "$tempDir/data.json" +jbang "$recipeDir/ManagingLocalFiles.java" "$tempDir" +# Inspect the plan and files before testing on a real directory. +``` + +```powershell +$recipeDir = (Get-Location).Path +$tempDir = Join-Path ([IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString()) +New-Item -ItemType Directory -Path $tempDir | Out-Null +"document1.txt", "image1.png", "data.json" | ForEach-Object { + New-Item -ItemType File -Path (Join-Path $tempDir $_) | Out-Null +} +jbang "$recipeDir/ManagingLocalFiles.java" "$tempDir" +# Inspect the plan and files before testing on a real directory. +``` + +The command-line argument selects the target directory. Without it, the example +uses an `example-files` folder inside the system temporary directory. +
+ +
+Ralph Loop - Modifies files and can create commits + +Run the loop only in an isolated repository until you have reviewed its behavior. +It reads `PROMPT.md` by default and performs at most 50 iterations; pass a prompt +path and a small iteration limit for testing. +Run the following from this recipe directory: + +```bash +recipeDir=$(pwd) +tempDir=$(mktemp -d) +cd "$tempDir" +git init +printf '# Task\nCreate a small README. Do not commit changes.\n' > PROMPT.md +jbang "$recipeDir/RalphLoop.java" PROMPT.md 1 +git status --short +git diff +cd "$recipeDir" +``` + +```powershell +$recipeDir = (Get-Location).Path +$tempDir = Join-Path ([IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString()) +New-Item -ItemType Directory -Path $tempDir | Out-Null +Push-Location $tempDir +try { + git init + "# Task`nCreate a small README. Do not commit changes." | Set-Content PROMPT.md + jbang "$recipeDir/RalphLoop.java" PROMPT.md 1 + git status --short + git diff +} finally { + Pop-Location +} +``` + +Review any edits or commits before removing the test repository. Fresh SDK +sessions isolate model context, not filesystem access. +
+ +
+Accessibility Report - Requires Playwright MCP + +Install Node.js so `npx` is available. The recipe launches +`npx @playwright/mcp@latest` on demand; a global MCP installation is not required. +Allow time for package resolution and browser setup on the first run. + +```bash +jbang AccessibilityReport.java +``` + +The recipe prompts for a URL and can generate Playwright tests after the report. +
+ +
+PR Visualization - Requires GitHub API access + +Configure the Copilot CLI's GitHub MCP Server and authenticate for the repository +you want to analyze. Private repositories require appropriate access. +GitHub API requests are subject to +[rate limits](https://docs.github.com/rest/overview/rate-limits-for-the-rest-api). +The agent can run chart-generation code and write images to the working directory. + +```bash +jbang PRVisualization.java github/copilot-sdk +``` + +This Java recipe accepts `owner/repo` as a positional argument, unlike the .NET +recipe's `--repo` option. With no argument, it tries the current Git remote and +then prompts for a repository. Tool search is enabled with a deferral threshold +of 20 tools. +
+ +
+In-Process Runtime - Requires native dependencies + +Choose the classifier for your operating system and architecture. For example, +on Apple Silicon, check local startup and shutdown without sending a model request: + +```bash +jbang --deps com.github:copilot-sdk-java-runtime:1.0.21:darwin-arm64 InProcessRuntime.java --ping +``` + +Omit `--ping` to create a session and send the greeting. See +[In-Process Runtime](../in-process-runtime.md) for dependencies, experimental API +opt-in, supported platforms, and process-wide limitations. +
+ ## Why JBang? JBang lets you run Java files as scripts — no `pom.xml`, no `build.gradle`, no project scaffolding. Dependencies are declared inline with `//DEPS` comments and resolved automatically. @@ -67,5 +211,6 @@ JBang lets you run Java files as scripts — no `pom.xml`, no `build.gradle`, no ## Learning Resources - [JBang Documentation](https://www.jbang.dev/documentation/guide/latest/) -- [GitHub Copilot SDK for Java](https://github.com/github/copilot-sdk-java) +- [GitHub Copilot SDK for Java](https://github.com/github/copilot-sdk/tree/main/java) +- [GitHub Copilot CLI](https://github.com/features/copilot/cli) - [Parent Cookbook](../README.md) diff --git a/cookbook/copilot-sdk/java/recipe/RalphLoop.java b/cookbook/copilot-sdk/java/recipe/RalphLoop.java index 99e0465571..0c3f0d6f90 100644 --- a/cookbook/copilot-sdk/java/recipe/RalphLoop.java +++ b/cookbook/copilot-sdk/java/recipe/RalphLoop.java @@ -1,9 +1,9 @@ ///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.nio.file.*; /** From 516e59d3d613d53d47e66ba12a515e0402642f5c Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 14 Oct 2026 12:25:40 -0400 Subject: [PATCH 2/5] Make cookbook prerequisite guidance consistent across languages Replace the Java-only Running Examples note with shared links to all five language-specific prerequisite sections. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3fdaadb1-b7cb-4313-8291-1bcc522981d7 --- cookbook/copilot-sdk/README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cookbook/copilot-sdk/README.md b/cookbook/copilot-sdk/README.md index 98a13d138a..0fa487f3b6 100644 --- a/cookbook/copilot-sdk/README.md +++ b/cookbook/copilot-sdk/README.md @@ -66,6 +66,13 @@ This cookbook collects small, focused recipes showing how to accomplish common t ## 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 @@ -98,9 +105,6 @@ go run .go ### Java -Follow the [Java recipe prerequisites](java/recipe/README.md#prerequisites) to -configure authentication and the CLI or platform runtime dependencies. - ```bash cd java/recipe jbang .java From 5085948ec8453726657ab43da9214b728538ac1e Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 14 Oct 2026 12:28:13 -0400 Subject: [PATCH 3/5] Format cookbook prerequisite links as a list Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3fdaadb1-b7cb-4313-8291-1bcc522981d7 --- cookbook/copilot-sdk/README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cookbook/copilot-sdk/README.md b/cookbook/copilot-sdk/README.md index 0fa487f3b6..8f228233ec 100644 --- a/cookbook/copilot-sdk/README.md +++ b/cookbook/copilot-sdk/README.md @@ -67,11 +67,12 @@ This cookbook collects small, focused recipes showing how to accomplish common t ## 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](dotnet/recipe/README.md#prerequisites) + - [Node.js](nodejs/recipe/README.md#prerequisites) + - [Python](python/recipe/README.md#prerequisites) + - [Go](go/recipe/README.md#prerequisites) + - [Java](java/recipe/README.md#prerequisites) ### .NET From af16dbe6f42e708c96537c32314eedcc5148a550 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Fri, 16 Oct 2026 09:37:53 -0400 Subject: [PATCH 4/5] Address Java SDK cookbook review feedback Correct message-source behavior, mark unrestricted approval as demo-only, and clarify tool-search rules and runtime-owned defaults. Refine native transport dependencies, JDK guidance, lifecycle and test coverage, pin version-specific SDK references, and make the in-process stop path explicit. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3fdaadb1-b7cb-4313-8291-1bcc522981d7 --- cookbook/copilot-sdk/README.md | 8 +- cookbook/copilot-sdk/java/README.md | 2 +- .../copilot-sdk/java/accessibility-report.md | 4 + cookbook/copilot-sdk/java/error-handling.md | 47 ++++-- .../copilot-sdk/java/in-process-runtime.md | 143 +++++++++++++----- .../copilot-sdk/java/managing-local-files.md | 4 + .../copilot-sdk/java/multiple-sessions.md | 4 + .../copilot-sdk/java/persisting-sessions.md | 4 + cookbook/copilot-sdk/java/pr-visualization.md | 44 ++++-- cookbook/copilot-sdk/java/ralph-loop.md | 6 +- .../java/recipe/InProcessRuntime.java | 3 + cookbook/copilot-sdk/java/recipe/README.md | 45 ++++-- 12 files changed, 240 insertions(+), 74 deletions(-) diff --git a/cookbook/copilot-sdk/README.md b/cookbook/copilot-sdk/README.md index 8f228233ec..24ea472567 100644 --- a/cookbook/copilot-sdk/README.md +++ b/cookbook/copilot-sdk/README.md @@ -54,15 +54,15 @@ 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. +- [In-Process Runtime](java/in-process-runtime.md): Load the native Copilot runtime library into the JVM process instead of spawning a separate runtime 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 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) +- See existing examples and tests at the official SDK repository's `java/v1.0.21` tag for version-specific references: + - [Node.js example](https://github.com/github/copilot-sdk/blob/java/v1.0.21/nodejs/examples/basic-example.ts) + - E2E tests: [Go](https://github.com/github/copilot-sdk/tree/java/v1.0.21/go/internal/e2e), [Python](https://github.com/github/copilot-sdk/tree/java/v1.0.21/python/e2e), [Node.js](https://github.com/github/copilot-sdk/tree/java/v1.0.21/nodejs/test/e2e), [.NET](https://github.com/github/copilot-sdk/tree/java/v1.0.21/dotnet/test/Harness), [Java](https://github.com/github/copilot-sdk/tree/java/v1.0.21/java/sdk/src/test/java/com/github/copilot/e2e) ## Running Examples diff --git a/cookbook/copilot-sdk/java/README.md b/cookbook/copilot-sdk/java/README.md index 972f1d331b..cbc4ba45f7 100644 --- a/cookbook/copilot-sdk/java/README.md +++ b/cookbook/copilot-sdk/java/README.md @@ -16,7 +16,7 @@ Examples target the official SDK 1.0.21 and Java 17 or later. SDK 1.x uses - [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. +- [In-Process Runtime](in-process-runtime.md): Load the native Copilot runtime library into the JVM process instead of spawning a separate runtime process. ## Contributing diff --git a/cookbook/copilot-sdk/java/accessibility-report.md b/cookbook/copilot-sdk/java/accessibility-report.md index cafc92f430..0cf5b9ac24 100644 --- a/cookbook/copilot-sdk/java/accessibility-report.md +++ b/cookbook/copilot-sdk/java/accessibility-report.md @@ -1,5 +1,9 @@ # Generating Accessibility Reports +> **Demo permissions:** These examples use `PermissionHandler.APPROVE_ALL` only +> for isolated, trusted demonstrations. Replace it with a restrictive permission +> handler before using untrusted prompts or workspaces. + Build a CLI tool that analyzes web page accessibility using the Playwright MCP server and generates detailed WCAG-compliant reports with optional test generation. > **Runnable example:** [recipe/AccessibilityReport.java](recipe/AccessibilityReport.java) diff --git a/cookbook/copilot-sdk/java/error-handling.md b/cookbook/copilot-sdk/java/error-handling.md index 75745ee16d..f270878759 100644 --- a/cookbook/copilot-sdk/java/error-handling.md +++ b/cookbook/copilot-sdk/java/error-handling.md @@ -2,6 +2,10 @@ Handle errors gracefully in your Copilot SDK applications. +> **Demo permissions:** These examples use `PermissionHandler.APPROVE_ALL` only +> for isolated, trusted demonstrations. Replace it with a restrictive permission +> handler before using untrusted prompts or workspaces. + > **Runnable example:** [recipe/ErrorHandling.java](recipe/ErrorHandling.java) > > ```bash @@ -128,10 +132,12 @@ 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. +`client.stop()` returns a `CompletableFuture`; observe it to catch failures +surfaced by the SDK rather than relying only on `close()`, which suppresses +cleanup failures. In-process FFI cleanup can also log failures or continue +asynchronously, so a completed stop is not proof that every native teardown +phase succeeded. If a graceful stop exceeds your application's shutdown deadline, +use `client.forceStop()` to retire the connection and sessions locally. ## Try-with-resources (nested) @@ -155,8 +161,8 @@ try (var client = new CopilotClient()) { ## Tagging message provenance -Use `MessageOptions.setSource(...)` to distinguish human input from application -context or another agent's messages when diagnosing errors in a transcript: +Use `MessageOptions.setSource(...)` to distinguish user-attributed messages from +application context or another agent's messages when diagnosing unexpected turns: ```java import com.github.copilot.rpc.MessageSource; @@ -174,11 +180,32 @@ session.sendAndWait(new MessageOptions() .setSource(MessageSource.agent("incident-bot"))).get(); ``` -Omitting `source` retains the runtime's default user-input behavior. +Omitting `source` delegates to the selected runtime; the runtime packaged with +SDK 1.0.21 treats it as user provenance, equivalent to `MessageSource.USER`. +That attribution does not prove a person physically entered the message. `MessageSource.agent(id)` serializes as `"agent-"`; 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(...)`. +without adding that prefix. The factory rejects only `null`; it does not +normalize prefixes or validate blank, format, or length constraints. Validate +identifiers crossing a trust boundary. Source values and agent IDs can appear +in events, history, queue snapshots, and diagnostics, so do not put secrets in them. + +Source is caller-supplied provenance with behavioral effects, not authenticated +identity. The runtime can use it for queueing and steering, visibility and +filtering, inter-agent routing, conversation/context reconstruction, and +classification of human authorization evidence. It cannot itself grant, deny, +or respond to a permission request, but source classification can indirectly +affect auto-approval. Never authorize an action from the source value alone. + +`MessageSource.SYSTEM` is not a model system-role message: it does not change the +model-visible role, modify the session's system prompt, or elevate instruction +priority. The separate delivery `mode` controls queued admission (`enqueue`, the +default) versus attempting to steer an active turn (`immediate`); it is independent +of source even though source can affect runtime handling. + +`send(...)` and `sendAndWait(...)` preserve the same source values. Non-user +attribution is persisted in durable `user.message` events and restored on replay. +User provenance is stored as an absent source, so explicit `USER` and an omitted +source are indistinguishable after persistence. ## Handling tool errors diff --git a/cookbook/copilot-sdk/java/in-process-runtime.md b/cookbook/copilot-sdk/java/in-process-runtime.md index 4aaa01e4cf..564004a37d 100644 --- a/cookbook/copilot-sdk/java/in-process-runtime.md +++ b/cookbook/copilot-sdk/java/in-process-runtime.md @@ -1,6 +1,7 @@ # In-Process Runtime -Run the Copilot runtime inside your JVM instead of launching a separate Copilot CLI process. +Load the native Copilot runtime library into the JVM process instead of spawning +a separate runtime process. > **Runnable example:** [recipe/InProcessRuntime.java](recipe/InProcessRuntime.java) > @@ -11,15 +12,28 @@ Run the Copilot runtime inside your JVM instead of launching a separate Copilot ## 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. +By default, `CopilotClient` spawns a child process and communicates over stdio or TCP. +Hosts that need to avoid a separate runtime process can load the native Copilot +runtime library into the JVM process. + +In-process hosting only removes the separate Copilot runtime process. Enabled +built-in tools such as shell execution, configured local MCP servers, and +application-defined tool handlers may still start child processes according to +their implementation, configuration, permissions, and sandbox policy. Hooks and +extensions may also start subprocesses. Model requests still require authentication +and network access; in-process transport is not a sandbox. + +> **Demo permissions:** These examples use `PermissionHandler.APPROVE_ALL` only +> for isolated, trusted demonstrations. Replace it with a restrictive permission +> handler before using untrusted prompts or workspaces. ## 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: +Use Java 17 or later and SDK 1.0.21. JDK 25 or later is recommended, not required +for in-process operation. In addition to the SDK, explicitly declare JNA and a +platform-specific runtime-classifier JAR with the same version as the SDK. +The example is tested with JNA 5.19.1; neither JNA nor the native library is part +of the JDK. ```xml @@ -44,14 +58,24 @@ SDK, Java needs JNA and a platform-specific native runtime artifact of the same 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: +`darwin-x64` or `darwin-arm64` for macOS. The glibc Linux classifiers require +glibc 2.28 or newer. The musl classifiers dynamically link musl and are intended +for compatible musl-based distributions such as Alpine Linux. Only x64 and ARM64 +are supported. + +JBang resolves the SDK and JNA from the recipe's `//DEPS` directives; pass the +runtime-classifier JAR 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 ``` +The in-process API is available in Java SDK 1.0.21, with runtime classifiers +published for the platforms listed above. Artifact availability does not mean +this recipe was tested on every target. This cookbook's native smoke test was +run on macOS arm64, without model requests or MCP/browser end-to-end execution. +Validate startup, model requests, and shutdown on each deployment platform. + ## Using the in-process connection ```java @@ -78,50 +102,99 @@ public class InProcessExample { .get(60, TimeUnit.SECONDS); System.out.println(response.getData().content()); } + + client.stop().get(30, TimeUnit.SECONDS); } } } ``` -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. +The connection API is experimental. `@AllowCopilotExperimental` opts the class +into the declaration-level checks performed by the SDK's annotation processor; +expression-only uses inside method bodies are not inspected. The processor must +be enabled by the compiler or build tool. Projects can instead pass +`-Acopilot.experimental.allowed=true` to opt in every source processed in that +compiler invocation. See the [version-pinned experimental API guidance](https://github.com/github/copilot-sdk/blob/java/v1.0.21/java/README.md#using-experimental-apis) +for build-tool setup. + +In-process mode does not introduce separate Java APIs for sessions, events, +tools, permissions, or hooks. Shared APIs and transport plumbing do not establish +full end-to-end support in this experimental transport: verify the features you +depend on against the selected SDK and native-library version. Native worker +threads, callback lifetimes, failure isolation, and shutdown differ from +subprocess transport. Extension/plugin tools may also require an extension +launch provider and external runtimes. ## 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 SDK uses JNA to load the `runtime.node` native library and exchange JSON-RPC +frames and callbacks over its C ABI rather than a child-process transport. +The runtime-classifier JAR contains both the `copilot-runtime` (`copilot-runtime.exe` +on Windows) subprocess wrapper and the adjacent `runtime.node` native library. +In-process transport loads the library; subprocess transport launches the wrapper. +Adding the classifier or JNA to the classpath does not change transport selection. + +Files extracted from a runtime-classifier JAR are cached by SDK version, native +package version, and platform classifier. Explicitly configured or discovered +filesystem libraries can be loaded directly instead. -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. +The recipe grants native access with `//JAVA_OPTIONS --enable-native-access=ALL-UNNAMED` +because JBang places dependencies on the classpath. JNA performs the restricted +JNI/native operations, not the SDK directly. For a regular classpath application, +pass `--enable-native-access=ALL-UNNAMED` to `java`; for a module-path launch, use +`--enable-native-access=com.sun.jna`. JNA recommends its `jna-jpms` artifact for a +fully modular setup. -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. +On JDK 17-23 this flag is unnecessary for JNA/JNI loading. On JDK 24-25, the +default illegal-native-access mode warns, while `--illegal-native-access=deny` +requires the grant. A future JDK is expected to make denial the default. +Prefer JDK 25 over JDK 24 for the newer virtual-thread/native-call pinning behavior. -To check native startup, a local RPC round trip, and shutdown without making a model -request, add `--ping`: +The explicit `RuntimeConnection.forInProcess()` configuration is sufficient for +this recipe. Process-global transport overrides and their precedence belong in +the [SDK reference](https://github.com/github/copilot-sdk/blob/java/v1.0.21/java/sdk/src/main/java/com/github/copilot/CopilotClient.java). + +To check native startup and an in-process ping round trip over the FFI transport +without authenticating or making a model request, add `--ping`: ```bash jbang --deps com.github:copilot-sdk-java-runtime:1.0.21:linux-x64 recipe/InProcessRuntime.java --ping ``` +Ping performs no account authentication, model discovery, or network request. +The recipe explicitly selects the in-process connection, so successful startup +and ping demonstrate native-library loading and a JSON-RPC request/response over +FFI, not a connection through another transport. It then calls +`client.stop().get(30, TimeUnit.SECONDS)` to exercise the normal shutdown path. +Neither completion of `stop()` nor try-with-resources proves that every native +teardown phase succeeded: some FFI cleanup failures are logged or retried +asynchronously, and `close()` suppresses cleanup failures. + ## 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. +- **Shared library, distinct hosts**: clients share one loaded native library and + process-global infrastructure, including native workers. Each client creates + its own runtime host/server engine and FFI connection. Environment and + operating-system working directory are captured separately when each host + starts; later process changes do not update existing hosts or their defaults. +- **Rejected process options**: `setEnvironment`, `setTelemetry`, `setCwd`, and + `setCliArgs` on `CopilotClientOptions` are rejected synchronously by the client + constructor in in-process mode, not by the setters or only at startup. + CLI-path, URL, TCP, and non-stdio options also conflict with an explicit + in-process connection. Configure telemetry through the host process environment; + it cannot vary per client. +- **One native-library path per JVM**: clients can reuse the same library, but + the load guard compares normalized absolute paths, not build metadata or + contents. Even an identical library at a different path is rejected. The SDK + intentionally retains the library for the JVM's lifetime because native workers + may outlive clients; no unload operation is exposed. - **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. + and JNA must be available. Missing or mismatched libraries fail startup. +- **Session and client closure**: session close retires Java-side resources and + detaches that connection; the last detach destroys the in-memory session but + preserves resumable data. Client close attempts to close its sessions, shut + down its own runtime host, and close its FFI connection/host. It does not unload + the library or shut down other clients' hosts. ## Best practices diff --git a/cookbook/copilot-sdk/java/managing-local-files.md b/cookbook/copilot-sdk/java/managing-local-files.md index 7986835ef3..0abe127bf3 100644 --- a/cookbook/copilot-sdk/java/managing-local-files.md +++ b/cookbook/copilot-sdk/java/managing-local-files.md @@ -1,5 +1,9 @@ # Grouping Files by Metadata +> **Demo permissions:** These examples use `PermissionHandler.APPROVE_ALL` only +> for isolated, trusted demonstrations. Replace it with a restrictive permission +> handler before using untrusted prompts or workspaces. + Use Copilot to intelligently organize files in a folder based on their metadata. > **Runnable example:** [recipe/ManagingLocalFiles.java](recipe/ManagingLocalFiles.java) diff --git a/cookbook/copilot-sdk/java/multiple-sessions.md b/cookbook/copilot-sdk/java/multiple-sessions.md index 7025f655b7..56f385621b 100644 --- a/cookbook/copilot-sdk/java/multiple-sessions.md +++ b/cookbook/copilot-sdk/java/multiple-sessions.md @@ -1,5 +1,9 @@ # Working with Multiple Sessions +> **Demo permissions:** These examples use `PermissionHandler.APPROVE_ALL` only +> for isolated, trusted demonstrations. Replace it with a restrictive permission +> handler before using untrusted prompts or workspaces. + Manage multiple independent conversations simultaneously. > **Runnable example:** [recipe/MultipleSessions.java](recipe/MultipleSessions.java) diff --git a/cookbook/copilot-sdk/java/persisting-sessions.md b/cookbook/copilot-sdk/java/persisting-sessions.md index b40ab9a48a..29b690cd83 100644 --- a/cookbook/copilot-sdk/java/persisting-sessions.md +++ b/cookbook/copilot-sdk/java/persisting-sessions.md @@ -1,5 +1,9 @@ # Session Persistence and Resumption +> **Demo permissions:** These examples use `PermissionHandler.APPROVE_ALL` only +> for isolated, trusted demonstrations. Replace it with a restrictive permission +> handler before using untrusted prompts or workspaces. + Save and restore conversation sessions across application restarts. > **Runnable example:** [recipe/PersistingSessions.java](recipe/PersistingSessions.java) diff --git a/cookbook/copilot-sdk/java/pr-visualization.md b/cookbook/copilot-sdk/java/pr-visualization.md index c6a36965f2..b5df95dc58 100644 --- a/cookbook/copilot-sdk/java/pr-visualization.md +++ b/cookbook/copilot-sdk/java/pr-visualization.md @@ -1,5 +1,9 @@ # Generating PR Age Charts +> **Demo permissions:** These examples use `PermissionHandler.APPROVE_ALL` only +> for isolated, trusted demonstrations. Replace it with a restrictive permission +> handler before using untrusted prompts or workspaces. + Build an interactive CLI tool that visualizes pull request age distribution for a GitHub repository using Copilot's built-in capabilities. > **Runnable example:** [recipe/PRVisualization.java](recipe/PRVisualization.java) @@ -227,8 +231,9 @@ public class PRVisualization { ## Deferring tools with tool search The GitHub MCP Server can expose many tools alongside file and code-execution -tools. Configure `SessionConfig.setToolSearch(...)` to defer less-used tools -behind a search step rather than listing every tool on each turn: +tools. Configure `SessionConfig.setToolSearch(...)` to defer eligible MCP and +SDK-defined external tools by category and runtime policy, then discover them +through metadata-based search. Deferral does not track tool usage frequency: ```java import com.github.copilot.rpc.ToolSearchConfig; @@ -242,19 +247,36 @@ var session = client.createSession(new SessionConfig() ).get(); ``` -- `setEnabled(true)` explicitly enables tool search; leave it unset to use the runtime default. -- `setDeferThreshold(20)` sets the tool count above which MCP and external tools - are deferred behind `tool_search_tool`. The runtime default is 30 when unset. -- To replace the search implementation, register a tool named `"tool_search_tool"` - and call `.overridesBuiltInTool(true)` on its `ToolDefinition`. - -This configuration is per session, so tool-heavy recipes can use a different -threshold from lightweight sessions. +- `setEnabled(true)` requests tool search explicitly. Leaving it unset delegates + to runtime-controlled automatic enablement, which depends on model capabilities, + transport, runtime settings, kill switches, and experiments. Setting it to `false` + disables search deferral, not independent tool filters, policy, or availability. +- `setDeferThreshold(20)` considers deferral only when the effective tool catalog + **strictly exceeds 20** tools after tool-set and agent filtering. The count + includes built-ins, MCP tools, and SDK-defined tools, before adding the search + tool or hosted search mechanism. +- Crossing the threshold defers eligible MCP and SDK-defined external tools, + not all tools or just those beyond the first 20. Ordinary built-ins and exempt + tools remain eager, so there is no fixed number left directly visible. + Exemptions include tools marked `defer: "never"`, the search tool itself, + GitHub MCP `web_search`, Bluebird MCP tools, and explicitly selected agent + tools unless that agent opts into deferred loading. +- Leaving `enabled` or `deferThreshold` unset delegates to the selected runtime. + The runtime packaged with SDK 1.0.21 uses a threshold of 30; explicitly setting + these values avoids relying on version-specific defaults. An explicit threshold + takes effect only when the runtime resolves tool search as enabled. + +Configuration is session-scoped, including on resume. Subagents need their own +override if they should differ from their runtime default. Tool search is resolved +per turn against the current catalog; a stable result may be reused, but changes +to the model, transport, configuration, runtime gates, or experiments can trigger +re-resolution. Search ranks metadata, prioritizing names over descriptions and +parameter metadata, with namespace/server metadata used by some search paths. ## Best practices 1. **Start with auto-detection**: Let the tool detect the repository from the git remote before prompting the user 2. **Use system messages**: Provide context about the repo and working directory so Copilot can act autonomously -3. **Approve tool execution**: Use `PermissionHandler.APPROVE_ALL` to allow Copilot to run tools like the GitHub MCP Server without manual approval +3. **Restrict tool permissions**: Keep `PermissionHandler.APPROVE_ALL` limited to isolated, trusted demonstrations. Use a restrictive handler for untrusted prompts or workspaces, including GitHub MCP and chart-generation tools. 4. **Interactive follow-ups**: Let users refine the analysis conversationally instead of requiring restarts 5. **Save artifacts locally**: Direct Copilot to save generated charts to the current directory for easy access diff --git a/cookbook/copilot-sdk/java/ralph-loop.md b/cookbook/copilot-sdk/java/ralph-loop.md index 6da7a764b8..054a2f7b10 100644 --- a/cookbook/copilot-sdk/java/ralph-loop.md +++ b/cookbook/copilot-sdk/java/ralph-loop.md @@ -1,5 +1,9 @@ # Ralph Loop: Autonomous AI Task Loops +> **Demo permissions:** These examples use `PermissionHandler.APPROVE_ALL` only +> for isolated, trusted demonstrations. Replace it with a restrictive permission +> handler before using untrusted prompts or workspaces. + Build autonomous coding loops where an AI agent picks tasks, implements them, validates against backpressure (tests, builds), commits, and repeats — each iteration in a fresh context window. > **Runnable example:** [recipe/RalphLoop.java](recipe/RalphLoop.java) @@ -223,7 +227,7 @@ mvn compile 7. **Keep `AGENTS.md` brief**: It's loaded every iteration — operational info only, no progress notes 8. **Use a sandbox**: The agent runs autonomously with full tool access — isolate it 9. **Set `workingDirectory`**: Pin the session to your project root so tool operations resolve paths correctly -10. **Auto-approve permissions**: Use `PermissionHandler.APPROVE_ALL` to allow tool calls without interrupting the loop +10. **Restrict tool permissions**: Keep `PermissionHandler.APPROVE_ALL` limited to isolated, trusted demonstrations. Use a restrictive handler for untrusted prompts or workspaces; unattended execution is not a reason to bypass permission checks. ## When to Use a Ralph Loop diff --git a/cookbook/copilot-sdk/java/recipe/InProcessRuntime.java b/cookbook/copilot-sdk/java/recipe/InProcessRuntime.java index a89515b721..c5859044ad 100644 --- a/cookbook/copilot-sdk/java/recipe/InProcessRuntime.java +++ b/cookbook/copilot-sdk/java/recipe/InProcessRuntime.java @@ -28,6 +28,7 @@ public static void main(String[] args) throws Exception { if (args.length == 1) { var response = client.ping("in-process recipe").get(30, TimeUnit.SECONDS); System.out.println(response.message()); + client.stop().get(30, TimeUnit.SECONDS); return; } @@ -40,6 +41,8 @@ public static void main(String[] args) throws Exception { .get(60, TimeUnit.SECONDS); System.out.println(response.getData().content()); } + + client.stop().get(30, TimeUnit.SECONDS); } } } diff --git a/cookbook/copilot-sdk/java/recipe/README.md b/cookbook/copilot-sdk/java/recipe/README.md index 2fb7804457..cc937ef444 100644 --- a/cookbook/copilot-sdk/java/recipe/README.md +++ b/cookbook/copilot-sdk/java/recipe/README.md @@ -22,10 +22,15 @@ scoop install jbang For other installation methods, see the [JBang installation guide](https://www.jbang.dev/download/). -For the default child-process transport, either add the matching -`copilot-sdk-java-runtime` classifier with JBang's `--deps` option (see -[In-Process Runtime](../in-process-runtime.md#prerequisites) for classifiers), or -use an installed Copilot CLI by setting `COPILOT_CLI_PATH` before launching Java: +The default child-process transport spawns the `copilot-runtime` +(`copilot-runtime.exe` on Windows) wrapper from the platform's runtime-classifier +JAR. Add `com.github:copilot-sdk-java-runtime:1.0.21:` with JBang's +`--deps` option (see [In-Process Runtime](../in-process-runtime.md#prerequisites) +for published classifiers). This transport does not require JNA. + +Alternatively, explicitly select an external Copilot CLI executable. Having +`copilot` on `PATH` alone is insufficient. For these recipes, set +`COPILOT_CLI_PATH` before launching Java: ```bash export COPILOT_CLI_PATH="$(command -v copilot)" @@ -35,8 +40,21 @@ export COPILOT_CLI_PATH="$(command -v copilot)" $env:COPILOT_CLI_PATH = (Get-Command copilot -ErrorAction Stop).Source ``` -The in-process recipe additionally resolves JNA and requires a native runtime -artifact matching your platform. It does not need a separate CLI process. +Applications can instead use `CopilotClientOptions.setCliPath(...)` (including +`setCliPath("copilot")` to select a PATH-installed executable). For subprocess +executable selection, the programmatic path takes precedence over +`COPILOT_CLI_PATH`, which takes precedence over the classifier-provided wrapper. +An external CLI replaces the need for the runtime-classifier JAR in subprocess +mode. Configure a supported authentication source before model requests; no +particular credential file is required, and `COPILOT_HOME` is optional. + +The in-process recipe uses the same runtime-classifier JAR differently: it loads +the `runtime.node` native library through JNA instead of spawning the wrapper. +JNA must be declared explicitly and is tested here at 5.19.1. This transport needs +neither an external CLI nor a separate runtime process. A classpath containing the +SDK, matching runtime-classifier JAR, and JNA can serve either transport; merely +adding these dependencies does not select in-process mode. These runtime and +authentication rules also apply to Maven/Gradle applications. ## Running Examples @@ -81,9 +99,9 @@ jbang RalphLoop.java PROMPT_build.md 20 ## Safety & Prerequisites -The examples use `PermissionHandler.APPROVE_ALL` for brevity. This gives the agent -permission to use tools without asking you; test in an isolated workspace and -replace it with a restrictive handler before processing untrusted prompts. +The examples use `PermissionHandler.APPROVE_ALL` only for isolated, trusted +demonstrations. This gives the agent permission to use tools without asking you; +replace it with a restrictive handler before using untrusted prompts or workspaces.
Managing Local Files - Test on disposable copies @@ -193,15 +211,18 @@ of 20 tools. In-Process Runtime - Requires native dependencies Choose the classifier for your operating system and architecture. For example, -on Apple Silicon, check local startup and shutdown without sending a model request: +on Apple Silicon, check native startup and an in-process ping round trip over +FFI without authenticating or making a model request: ```bash jbang --deps com.github:copilot-sdk-java-runtime:1.0.21:darwin-arm64 InProcessRuntime.java --ping ``` -Omit `--ping` to create a session and send the greeting. See +The recipe then exercises the normal client shutdown path; this does not prove +that every native teardown phase succeeded. Omit `--ping` to create a session +and send the greeting. See [In-Process Runtime](../in-process-runtime.md) for dependencies, experimental API -opt-in, supported platforms, and process-wide limitations. +opt-in, published platform classifiers, tested coverage, and process-wide limitations.
## Why JBang? From bef4b86cd1fc23bc2852512c75b565418bbf86b8 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Fri, 16 Oct 2026 09:50:02 -0400 Subject: [PATCH 5/5] Avoid PowerShell shims in Windows SDK CLI setup Use the bare copilot command so the SDK can resolve native executables or CMD shims instead of passing a copilot.ps1 path to ProcessBuilder. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3fdaadb1-b7cb-4313-8291-1bcc522981d7 --- cookbook/copilot-sdk/java/recipe/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cookbook/copilot-sdk/java/recipe/README.md b/cookbook/copilot-sdk/java/recipe/README.md index cc937ef444..3391166c85 100644 --- a/cookbook/copilot-sdk/java/recipe/README.md +++ b/cookbook/copilot-sdk/java/recipe/README.md @@ -37,9 +37,15 @@ export COPILOT_CLI_PATH="$(command -v copilot)" ``` ```powershell -$env:COPILOT_CLI_PATH = (Get-Command copilot -ErrorAction Stop).Source +Get-Command copilot -ErrorAction Stop | Out-Null +$env:COPILOT_CLI_PATH = "copilot" ``` +On Windows, keep the bare command name so the SDK can resolve a native executable +or launch an npm CMD shim through `cmd /c`. Do not pass the absolute path returned +by PowerShell's `Get-Command`: it may point to `copilot.ps1`, which Java's +`ProcessBuilder` cannot execute directly. + Applications can instead use `CopilotClientOptions.setCliPath(...)` (including `setCliPath("copilot")` to select a PATH-installed executable). For subprocess executable selection, the programmatic path takes precedence over