re
return future.thenApply(result -> {
try {
- if (responseType == Void.class || responseType == void.class) {
- return null;
+ T value = null;
+ if (responseType != Void.class && responseType != void.class) {
+ value = MAPPER.treeToValue(result, responseType);
}
- return MAPPER.treeToValue(result, responseType);
+ LoggingHelpers.logTiming(LOG, Level.FINE,
+ "JsonRpc.invoke JSON-RPC request finished. Elapsed={Elapsed}, Method=" + method + ", RequestId="
+ + id + ", Status=Succeeded",
+ timingNanos);
+ return value;
} catch (JsonProcessingException e) {
throw new CompletionException(e);
}
+ }).exceptionally(ex -> {
+ LoggingHelpers.logTiming(LOG, Level.WARNING, ex,
+ "JsonRpc.invoke JSON-RPC request finished. Elapsed={Elapsed}, Method=" + method + ", RequestId="
+ + id + ", Status=Failed",
+ timingNanos);
+ throw ex instanceof RuntimeException re ? re : new RuntimeException(ex);
});
}
diff --git a/src/main/java/com/github/copilot/sdk/LoggingHelpers.java b/src/main/java/com/github/copilot/sdk/LoggingHelpers.java
new file mode 100644
index 0000000000..654494ef13
--- /dev/null
+++ b/src/main/java/com/github/copilot/sdk/LoggingHelpers.java
@@ -0,0 +1,77 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk;
+
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Internal helper for timing-based diagnostic logging.
+ */
+final class LoggingHelpers {
+
+ private LoggingHelpers() {
+ // Utility class
+ }
+
+ /**
+ * Formats elapsed time as a human-readable duration string.
+ *
+ * @param startNanos
+ * the start time from {@link System#nanoTime()}
+ * @return formatted duration (e.g. "PT0.123S")
+ */
+ static String formatElapsed(long startNanos) {
+ long elapsedNanos = System.nanoTime() - startNanos;
+ long millis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos);
+ return String.format("PT%d.%03dS", millis / 1000, millis % 1000);
+ }
+
+ /**
+ * Logs a timing message at the given level if the logger accepts it.
+ *
+ * @param logger
+ * the logger to use
+ * @param level
+ * the log level
+ * @param message
+ * the message template
+ * @param startNanos
+ * the start time from {@link System#nanoTime()}
+ */
+ static void logTiming(Logger logger, Level level, String message, long startNanos) {
+ if (!logger.isLoggable(level)) {
+ return;
+ }
+ logger.log(level, message.replace("{Elapsed}", formatElapsed(startNanos)));
+ }
+
+ /**
+ * Logs a timing message at the given level with an exception.
+ *
+ * @param logger
+ * the logger to use
+ * @param level
+ * the log level
+ * @param exception
+ * the exception, may be {@code null}
+ * @param message
+ * the message template
+ * @param startNanos
+ * the start time from {@link System#nanoTime()}
+ */
+ static void logTiming(Logger logger, Level level, Throwable exception, String message, long startNanos) {
+ if (!logger.isLoggable(level)) {
+ return;
+ }
+ String formatted = message.replace("{Elapsed}", formatElapsed(startNanos));
+ if (exception != null) {
+ logger.log(level, formatted, exception);
+ } else {
+ logger.log(level, formatted);
+ }
+ }
+}
diff --git a/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java b/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java
index 91dbd46424..fd96966906 100644
--- a/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java
+++ b/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java
@@ -111,6 +111,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess
request.setAvailableTools(config.getAvailableTools());
request.setExcludedTools(config.getExcludedTools());
request.setProvider(config.getProvider());
+ request.setEnableSessionTelemetry(config.getEnableSessionTelemetry());
request.setRequestUserInput(config.getOnUserInputRequest() != null ? true : null);
request.setHooks(config.getHooks() != null && config.getHooks().hasHooks() ? true : null);
request.setWorkingDirectory(config.getWorkingDirectory());
@@ -187,6 +188,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo
request.setAvailableTools(config.getAvailableTools());
request.setExcludedTools(config.getExcludedTools());
request.setProvider(config.getProvider());
+ request.setEnableSessionTelemetry(config.getEnableSessionTelemetry());
request.setRequestUserInput(config.getOnUserInputRequest() != null ? true : null);
request.setHooks(config.getHooks() != null && config.getHooks().hasHooks() ? true : null);
request.setWorkingDirectory(config.getWorkingDirectory());
diff --git a/src/main/java/com/github/copilot/sdk/json/CopilotClientOptions.java b/src/main/java/com/github/copilot/sdk/json/CopilotClientOptions.java
index dff72581a9..5517df631e 100644
--- a/src/main/java/com/github/copilot/sdk/json/CopilotClientOptions.java
+++ b/src/main/java/com/github/copilot/sdk/json/CopilotClientOptions.java
@@ -54,6 +54,7 @@ public class CopilotClientOptions {
private int port;
private TelemetryConfig telemetry;
private Integer sessionIdleTimeoutSeconds;
+ private boolean remote;
private String tcpConnectionToken;
private Boolean useLoggedInUser;
private boolean useStdio = true;
@@ -438,6 +439,37 @@ public CopilotClientOptions setPort(int port) {
return this;
}
+ /**
+ * Returns whether remote session support (Mission Control integration) is
+ * enabled.
+ *
+ * When {@code true}, sessions in a GitHub repository working directory are
+ * accessible from GitHub web and mobile.
+ *
+ * @return {@code true} if remote sessions are enabled
+ */
+ public boolean isRemote() {
+ return remote;
+ }
+
+ /**
+ * Enables remote session support (Mission Control integration).
+ *
+ * When {@code true}, sessions in a GitHub repository working directory are
+ * accessible from GitHub web and mobile.
+ *
+ * This option is only used when the SDK spawns the CLI process; it is ignored
+ * when connecting to an external server via {@link #setCliUrl(String)}.
+ *
+ * @param remote
+ * {@code true} to enable remote sessions
+ * @return this options instance for method chaining
+ */
+ public CopilotClientOptions setRemote(boolean remote) {
+ this.remote = remote;
+ return this;
+ }
+
/**
* Gets the OpenTelemetry configuration for the CLI server.
*
@@ -599,6 +631,7 @@ public CopilotClientOptions clone() {
copy.logLevel = this.logLevel;
copy.onListModels = this.onListModels;
copy.port = this.port;
+ copy.remote = this.remote;
copy.sessionIdleTimeoutSeconds = this.sessionIdleTimeoutSeconds;
copy.tcpConnectionToken = this.tcpConnectionToken;
copy.telemetry = this.telemetry;
diff --git a/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java b/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java
index 5243f99ec2..3a0b90f19a 100644
--- a/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java
+++ b/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java
@@ -52,6 +52,9 @@ public final class CreateSessionRequest {
@JsonProperty("provider")
private ProviderConfig provider;
+ @JsonProperty("enableSessionTelemetry")
+ private Boolean enableSessionTelemetry;
+
@JsonProperty("requestPermission")
private Boolean requestPermission;
@@ -207,6 +210,18 @@ public void setProvider(ProviderConfig provider) {
this.provider = provider;
}
+ /** Gets enable session telemetry flag. @return the flag */
+ public Boolean getEnableSessionTelemetry() {
+ return enableSessionTelemetry;
+ }
+
+ /**
+ * Sets enable session telemetry flag. @param enableSessionTelemetry the flag
+ */
+ public void setEnableSessionTelemetry(Boolean enableSessionTelemetry) {
+ this.enableSessionTelemetry = enableSessionTelemetry;
+ }
+
/** Gets request permission flag. @return the flag */
public Boolean getRequestPermission() {
return requestPermission;
diff --git a/src/main/java/com/github/copilot/sdk/json/ProviderConfig.java b/src/main/java/com/github/copilot/sdk/json/ProviderConfig.java
index 3b29956814..8947696c9a 100644
--- a/src/main/java/com/github/copilot/sdk/json/ProviderConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/ProviderConfig.java
@@ -57,6 +57,18 @@ public class ProviderConfig {
@JsonProperty("headers")
private Map headers;
+ @JsonProperty("modelId")
+ private String modelId;
+
+ @JsonProperty("wireModel")
+ private String wireModel;
+
+ @JsonProperty("maxPromptTokens")
+ private Integer maxPromptTokens;
+
+ @JsonProperty("maxOutputTokens")
+ private Integer maxOutputTokens;
+
/**
* Gets the provider type.
*
@@ -225,4 +237,109 @@ public ProviderConfig setHeaders(Map headers) {
this.headers = headers;
return this;
}
+
+ /**
+ * Gets the well-known model name used by the runtime.
+ *
+ * Used to look up agent configuration (tools, prompts, reasoning behavior) and
+ * default token limits. Also used as the wire model when
+ * {@link #getWireModel()} is not set.
+ *
+ * @return the model ID, or {@code null} if not set
+ */
+ public String getModelId() {
+ return modelId;
+ }
+
+ /**
+ * Sets the well-known model name used by the runtime.
+ *
+ * Used to look up agent configuration (tools, prompts, reasoning behavior) and
+ * default token limits. Also used as the wire model when
+ * {@link #getWireModel()} is not set. Falls back to
+ * {@link SessionConfig#getModel()}.
+ *
+ * @param modelId
+ * the model ID
+ * @return this config for method chaining
+ */
+ public ProviderConfig setModelId(String modelId) {
+ this.modelId = modelId;
+ return this;
+ }
+
+ /**
+ * Gets the model name sent to the provider API for inference.
+ *
+ * @return the wire model name, or {@code null} if not set
+ */
+ public String getWireModel() {
+ return wireModel;
+ }
+
+ /**
+ * Sets the model name sent to the provider API for inference.
+ *
+ * Use this when the provider's model name (e.g. an Azure deployment name or a
+ * custom fine-tune name) differs from {@link #getModelId()}. Falls back to
+ * {@link #getModelId()}, then {@link SessionConfig#getModel()}.
+ *
+ * @param wireModel
+ * the wire model name
+ * @return this config for method chaining
+ */
+ public ProviderConfig setWireModel(String wireModel) {
+ this.wireModel = wireModel;
+ return this;
+ }
+
+ /**
+ * Gets the maximum prompt token override.
+ *
+ * @return the max prompt tokens, or {@code null} if not set
+ */
+ public Integer getMaxPromptTokens() {
+ return maxPromptTokens;
+ }
+
+ /**
+ * Sets the maximum prompt tokens override.
+ *
+ * Overrides the resolved model's default max prompt tokens. The runtime
+ * triggers conversation compaction before sending a request when the prompt
+ * (system message, history, tool definitions, user message) would exceed this
+ * limit.
+ *
+ * @param maxPromptTokens
+ * the max prompt tokens
+ * @return this config for method chaining
+ */
+ public ProviderConfig setMaxPromptTokens(Integer maxPromptTokens) {
+ this.maxPromptTokens = maxPromptTokens;
+ return this;
+ }
+
+ /**
+ * Gets the maximum output token override.
+ *
+ * @return the max output tokens, or {@code null} if not set
+ */
+ public Integer getMaxOutputTokens() {
+ return maxOutputTokens;
+ }
+
+ /**
+ * Sets the maximum output tokens override.
+ *
+ * Overrides the resolved model's default max output tokens. When hit, the model
+ * stops generating and returns a truncated response.
+ *
+ * @param maxOutputTokens
+ * the max output tokens
+ * @return this config for method chaining
+ */
+ public ProviderConfig setMaxOutputTokens(Integer maxOutputTokens) {
+ this.maxOutputTokens = maxOutputTokens;
+ return this;
+ }
}
diff --git a/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java b/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java
index 3f0a0706d6..cb52c62e02 100644
--- a/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java
@@ -43,6 +43,7 @@ public class ResumeSessionConfig {
private List availableTools;
private List excludedTools;
private ProviderConfig provider;
+ private Boolean enableSessionTelemetry;
private String reasoningEffort;
private ModelCapabilitiesOverride modelCapabilities;
private PermissionHandler onPermissionRequest;
@@ -229,6 +230,38 @@ public ResumeSessionConfig setProvider(ProviderConfig provider) {
return this;
}
+ /**
+ * Enables or disables internal session telemetry for this session. When
+ * {@code false}, disables session telemetry. When {@code null} (the default) or
+ * {@code true}, telemetry is enabled for GitHub-authenticated sessions. When a
+ * custom {@link ProviderConfig} (BYOK) is configured, session telemetry is
+ * always disabled regardless of this setting. This is independent of
+ * {@link com.github.copilot.sdk.json.CopilotClientOptions#getTelemetry()
+ * CopilotClientOptions.TelemetryConfig}, which configures OpenTelemetry export
+ * for observability.
+ *
+ * @return whether session telemetry is enabled
+ */
+ public Boolean getEnableSessionTelemetry() {
+ return enableSessionTelemetry;
+ }
+
+ /**
+ * Enables or disables internal session telemetry for this session. When
+ * {@code false}, disables session telemetry. When {@code null} (the default) or
+ * {@code true}, telemetry is enabled for GitHub-authenticated sessions. When a
+ * custom {@link ProviderConfig} (BYOK) is configured, session telemetry is
+ * always disabled regardless of this setting.
+ *
+ * @param enableSessionTelemetry
+ * whether to enable session telemetry
+ * @return this config for method chaining
+ */
+ public ResumeSessionConfig setEnableSessionTelemetry(Boolean enableSessionTelemetry) {
+ this.enableSessionTelemetry = enableSessionTelemetry;
+ return this;
+ }
+
/**
* Gets the reasoning effort level.
*
@@ -781,6 +814,7 @@ public ResumeSessionConfig clone() {
copy.availableTools = this.availableTools != null ? new ArrayList<>(this.availableTools) : null;
copy.excludedTools = this.excludedTools != null ? new ArrayList<>(this.excludedTools) : null;
copy.provider = this.provider;
+ copy.enableSessionTelemetry = this.enableSessionTelemetry;
copy.reasoningEffort = this.reasoningEffort;
copy.modelCapabilities = this.modelCapabilities;
copy.onPermissionRequest = this.onPermissionRequest;
diff --git a/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java b/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java
index c5931c275f..9b2c17f1a4 100644
--- a/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java
+++ b/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java
@@ -53,6 +53,9 @@ public final class ResumeSessionRequest {
@JsonProperty("provider")
private ProviderConfig provider;
+ @JsonProperty("enableSessionTelemetry")
+ private Boolean enableSessionTelemetry;
+
@JsonProperty("requestPermission")
private Boolean requestPermission;
@@ -214,6 +217,18 @@ public void setProvider(ProviderConfig provider) {
this.provider = provider;
}
+ /** Gets enable session telemetry flag. @return the flag */
+ public Boolean getEnableSessionTelemetry() {
+ return enableSessionTelemetry;
+ }
+
+ /**
+ * Sets enable session telemetry flag. @param enableSessionTelemetry the flag
+ */
+ public void setEnableSessionTelemetry(Boolean enableSessionTelemetry) {
+ this.enableSessionTelemetry = enableSessionTelemetry;
+ }
+
/** Gets request permission flag. @return the flag */
public Boolean getRequestPermission() {
return requestPermission;
diff --git a/src/main/java/com/github/copilot/sdk/json/SessionConfig.java b/src/main/java/com/github/copilot/sdk/json/SessionConfig.java
index 4d571990c6..a4b2769b74 100644
--- a/src/main/java/com/github/copilot/sdk/json/SessionConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/SessionConfig.java
@@ -45,6 +45,7 @@ public class SessionConfig {
private List availableTools;
private List excludedTools;
private ProviderConfig provider;
+ private Boolean enableSessionTelemetry;
private PermissionHandler onPermissionRequest;
private UserInputHandler onUserInputRequest;
private SessionHooks hooks;
@@ -283,6 +284,38 @@ public SessionConfig setProvider(ProviderConfig provider) {
return this;
}
+ /**
+ * Enables or disables internal session telemetry for this session. When
+ * {@code false}, disables session telemetry. When {@code null} (the default) or
+ * {@code true}, telemetry is enabled for GitHub-authenticated sessions. When a
+ * custom {@link ProviderConfig} (BYOK) is configured, session telemetry is
+ * always disabled regardless of this setting. This is independent of
+ * {@link com.github.copilot.sdk.json.CopilotClientOptions#getTelemetry()
+ * CopilotClientOptions.TelemetryConfig}, which configures OpenTelemetry export
+ * for observability.
+ *
+ * @return whether session telemetry is enabled
+ */
+ public Boolean getEnableSessionTelemetry() {
+ return enableSessionTelemetry;
+ }
+
+ /**
+ * Enables or disables internal session telemetry for this session. When
+ * {@code false}, disables session telemetry. When {@code null} (the default) or
+ * {@code true}, telemetry is enabled for GitHub-authenticated sessions. When a
+ * custom {@link ProviderConfig} (BYOK) is configured, session telemetry is
+ * always disabled regardless of this setting.
+ *
+ * @param enableSessionTelemetry
+ * whether to enable session telemetry
+ * @return this config instance for method chaining
+ */
+ public SessionConfig setEnableSessionTelemetry(Boolean enableSessionTelemetry) {
+ this.enableSessionTelemetry = enableSessionTelemetry;
+ return this;
+ }
+
/**
* Gets the permission request handler.
*
@@ -835,6 +868,7 @@ public SessionConfig clone() {
copy.availableTools = this.availableTools != null ? new ArrayList<>(this.availableTools) : null;
copy.excludedTools = this.excludedTools != null ? new ArrayList<>(this.excludedTools) : null;
copy.provider = this.provider;
+ copy.enableSessionTelemetry = this.enableSessionTelemetry;
copy.onPermissionRequest = this.onPermissionRequest;
copy.onUserInputRequest = this.onUserInputRequest;
copy.hooks = this.hooks;
diff --git a/src/site/markdown/advanced.md b/src/site/markdown/advanced.md
index e1e08a2753..ccf386640f 100644
--- a/src/site/markdown/advanced.md
+++ b/src/site/markdown/advanced.md
@@ -383,6 +383,29 @@ var session = client.createSession(
> **Note:** The `bearerToken` option accepts a **static token string** only. The SDK does not refresh this token automatically. If your token expires, requests will fail and you'll need to create a new session with a fresh token.
+### Model Overrides
+
+Use `modelId` and `wireModel` to control model resolution and the model name on the wire:
+
+```java
+var session = client.createSession(
+ new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setProvider(new ProviderConfig()
+ .setType("openai")
+ .setBaseUrl("https://api.openai.com/v1")
+ .setApiKey("sk-...")
+ .setModelId("gpt-4o") // Runtime config lookup
+ .setWireModel("my-finetune-v3") // Sent to the provider API
+ .setMaxPromptTokens(100_000) // Override max prompt tokens
+ .setMaxOutputTokens(4096)) // Override max output tokens
+).get();
+```
+
+- **`modelId`** โ Well-known model name used by the runtime to look up agent configuration (tools, prompts, reasoning behavior) and default token limits. Also used as the wire model when `wireModel` is not set.
+- **`wireModel`** โ Model name sent to the provider API for inference. Use when the provider's model name (e.g., an Azure deployment name or a custom fine-tune name) differs from `modelId`.
+- **`maxPromptTokens`** โ Overrides the resolved model's default max prompt tokens. The runtime triggers conversation compaction when the prompt would exceed this limit.
+- **`maxOutputTokens`** โ Overrides the resolved model's default max output tokens.
+
### Microsoft Foundry Local
[Microsoft Foundry Local](https://foundrylocal.ai) lets you run AI models locally on your own device with an OpenAI-compatible API. Install it via the Foundry Local CLI, then point the SDK at your local endpoint:
@@ -1262,6 +1285,39 @@ This is more efficient than `listSessions()` when you already know the session I
---
+## Remote Sessions
+
+Remote sessions enable Mission Control integration, making sessions accessible from GitHub web and mobile. When enabled, sessions in a GitHub repository working directory receive a remote URL.
+
+### Enabling Remote Sessions
+
+Set `remote(true)` on the client options to enable remote session support for all sessions:
+
+```java
+var options = new CopilotClientOptions()
+ .setRemote(true)
+ .setCwd("/path/to/github-repo");
+
+try (var client = new CopilotClient(options)) {
+ var session = client.createSession(
+ new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ ).get();
+
+ // Listen for the remote URL info event
+ session.on(SessionInfoEvent.class, event -> {
+ System.out.println("Remote URL: " + event.getData());
+ });
+}
+```
+
+### Prerequisites
+
+- The user must be authenticated (GitHub token or logged-in user)
+- The session's working directory must be a GitHub repository
+- This option is only used when the SDK spawns the CLI process; it is ignored when connecting to an external server via `setCliUrl()`
+
+---
+
## Next Steps
- ๐ **[Documentation](documentation.html)** - Core concepts, events, streaming, models, tool filtering, reasoning effort
diff --git a/src/site/markdown/cookbook/error-handling.md b/src/site/markdown/cookbook/error-handling.md
index c192a33653..4542533f86 100644
--- a/src/site/markdown/cookbook/error-handling.md
+++ b/src/site/markdown/cookbook/error-handling.md
@@ -30,7 +30,7 @@ jbang BasicErrorHandling.java
**Code:**
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.generated.AssistantMessageEvent;
import com.github.copilot.sdk.json.MessageOptions;
@@ -64,7 +64,7 @@ public class BasicErrorHandling {
## Handling specific error types
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import com.github.copilot.sdk.CopilotClient;
import java.util.concurrent.ExecutionException;
@@ -99,7 +99,7 @@ public class SpecificErrorHandling {
## Timeout handling
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import com.github.copilot.sdk.CopilotSession;
import com.github.copilot.sdk.generated.AssistantMessageEvent;
import com.github.copilot.sdk.json.MessageOptions;
@@ -130,7 +130,7 @@ public class TimeoutHandling {
## Aborting a request
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import com.github.copilot.sdk.CopilotSession;
import com.github.copilot.sdk.json.MessageOptions;
import java.util.concurrent.Executors;
@@ -162,7 +162,7 @@ public class AbortRequest {
## Graceful shutdown
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import com.github.copilot.sdk.CopilotClient;
public class GracefulShutdown {
@@ -192,7 +192,7 @@ public class GracefulShutdown {
## Try-with-resources pattern
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.generated.AssistantMessageEvent;
import com.github.copilot.sdk.json.MessageOptions;
@@ -224,7 +224,7 @@ public class TryWithResources {
## Handling tool errors
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.generated.AssistantMessageEvent;
import com.github.copilot.sdk.json.MessageOptions;
diff --git a/src/site/markdown/cookbook/managing-local-files.md b/src/site/markdown/cookbook/managing-local-files.md
index b8ab60314b..c52e6eabe4 100644
--- a/src/site/markdown/cookbook/managing-local-files.md
+++ b/src/site/markdown/cookbook/managing-local-files.md
@@ -34,7 +34,7 @@ jbang ManagingLocalFiles.java
**Code:**
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.generated.AssistantMessageEvent;
import com.github.copilot.sdk.generated.SessionIdleEvent;
@@ -161,7 +161,7 @@ session.send(new MessageOptions().setPrompt(prompt));
## Interactive file organization
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import java.io.BufferedReader;
import java.io.InputStreamReader;
diff --git a/src/site/markdown/cookbook/multiple-sessions.md b/src/site/markdown/cookbook/multiple-sessions.md
index de0777ec51..f0072ea95c 100644
--- a/src/site/markdown/cookbook/multiple-sessions.md
+++ b/src/site/markdown/cookbook/multiple-sessions.md
@@ -30,7 +30,7 @@ jbang MultipleSessions.java
**Code:**
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.generated.AssistantMessageEvent;
import com.github.copilot.sdk.json.MessageOptions;
@@ -123,7 +123,7 @@ try {
## Managing session lifecycle with CompletableFuture
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import java.util.concurrent.CompletableFuture;
import java.util.List;
diff --git a/src/site/markdown/cookbook/persisting-sessions.md b/src/site/markdown/cookbook/persisting-sessions.md
index ed2c62bb0c..d0125588bc 100644
--- a/src/site/markdown/cookbook/persisting-sessions.md
+++ b/src/site/markdown/cookbook/persisting-sessions.md
@@ -30,7 +30,7 @@ jbang PersistingSessions.java
**Code:**
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.generated.AssistantMessageEvent;
import com.github.copilot.sdk.json.MessageOptions;
@@ -127,7 +127,7 @@ public class DeleteSession {
## Getting session history
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.generated.AssistantMessageEvent;
import com.github.copilot.sdk.generated.UserMessageEvent;
@@ -162,7 +162,7 @@ public class SessionHistory {
## Complete example with session management
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import java.util.Scanner;
public class SessionManager {
diff --git a/src/site/markdown/cookbook/pr-visualization.md b/src/site/markdown/cookbook/pr-visualization.md
index 7d51c826a1..91ae5fb874 100644
--- a/src/site/markdown/cookbook/pr-visualization.md
+++ b/src/site/markdown/cookbook/pr-visualization.md
@@ -34,7 +34,7 @@ jbang PRVisualization.java github/copilot-sdk
## Full example: PRVisualization.java
```java
-//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.2-beta-java.1
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.generated.AssistantMessageEvent;
import com.github.copilot.sdk.generated.ToolExecutionStartEvent;
diff --git a/src/test/java/com/github/copilot/sdk/CapiProxy.java b/src/test/java/com/github/copilot/sdk/CapiProxy.java
index 30f8434363..09c4e20161 100644
--- a/src/test/java/com/github/copilot/sdk/CapiProxy.java
+++ b/src/test/java/com/github/copilot/sdk/CapiProxy.java
@@ -56,10 +56,12 @@
public class CapiProxy implements AutoCloseable {
private static final ObjectMapper MAPPER = new ObjectMapper();
- private static final Pattern LISTENING_PATTERN = Pattern.compile("Listening: (http://[^\\s]+)");
+ private static final Pattern LISTENING_PATTERN = Pattern.compile("Listening: (http://[^\\s]+)(?:\\s+(\\{.*\\}))?$");
private Process process;
private String proxyUrl;
+ private String connectProxyUrl;
+ private String caFilePath;
private final HttpClient httpClient;
private BufferedReader stdoutReader;
@@ -96,6 +98,10 @@ public String start() throws IOException, InterruptedException {
: new ProcessBuilder("npx", "tsx", "server.ts");
pb.directory(harnessDir.toFile());
pb.redirectErrorStream(false);
+ // Tell the replaying proxy to fail fast on unmatched requests rather than
+ // forwarding them to the real API. Without this, unmatched requests hit the
+ // live API with a fake token and crash the proxy's JSON parser.
+ pb.environment().put("GITHUB_ACTIONS", "true");
process = pb.start();
@@ -137,7 +143,24 @@ public String start() throws IOException, InterruptedException {
throw new IOException("Unexpected proxy output: " + line);
}
- proxyUrl = matcher.group(1);
+ String url = matcher.group(1);
+
+ // Parse optional metadata (CONNECT proxy details)
+ String metadata = matcher.group(2);
+ if (metadata != null && !metadata.isEmpty()) {
+ try {
+ Map meta = MAPPER.readValue(metadata, new TypeReference